Jake Vanderwerf
2026-02-04 2127b1bdd73ecd2423e443992da4b442f5a3c1a3
assets/js/concise/FormController.js
@@ -73,7 +73,7 @@
         },
         tagList: {
            tagList: '.field.tag-list',         //querySelectorAll
            input: '.tag-input-row',
            input: '.row',
            add: '.add-tag',
            remove: '.remove-tag',
            label: '.tag-label',
@@ -325,6 +325,7 @@
      if (e.target.closest('[data-ignore]') || this.isRestoring) return;
      let field = this.getField(e.target);
      //Dependencies
      if (this.dependencies.has(field.dataset.field)) {
         let dependency = this.dependencies.get(field.dataset.field);
@@ -333,6 +334,11 @@
         });
      }
      if (Object.hasOwn(field.dataset, 'repeater-id') || Object.hasOwn(field.dataset,'tag-list-id')) {
         this.updateCollectionField(field);
         return;
      }
      let form = this.getForm(e.target);
      this.updateItem(field.dataset.field, this.getFieldValue(e.target), form);
   }
@@ -377,9 +383,6 @@
      if (this.subscribers.size > 0) {
         e.preventDefault();
         console.log('Cancelling scheduled backup and manually backing up');
         if (form.options.cache) {
            this.cancelBackup();
@@ -608,8 +611,11 @@
                        this.removeQuantityListeners(item.element);
                        break;
                  }
                  if (check.has(item.id)) {
                     check.delete(item.id);
                  }
               });
               check.delete(item.id);
            }
         }
@@ -790,6 +796,7 @@
               }
            }
         checkForRepeaters(form) {
            if (!form.querySelector(this.selectors.repeater.repeater)) return;
            form.querySelectorAll(this.selectors.repeater.repeater).forEach(repeater => {
@@ -802,7 +809,7 @@
                  sortable: false,
               };
               if (!config.ui.addButton) return;
               if (!config.ui.add) return;
               let template = repeater.querySelector('template');
               this.templates.define(
@@ -814,9 +821,10 @@
                     setup({el, refs, manyRefs, data}) {
                        let index = config.ui.items?.children?.length??0;
                        el.dataset.index = index;
                        manyRefs.inputs?.forEach(input => {
                           let wrapper = el.closest('[data-field]');
                           window.prefixInput(input, `${el.dataset.fieldName}:${index}:`, wrapper);
                           window.prefixInput(input, `${data.repeater.dataset.fieldName}:${index}:`, el);
                        });
                     }
                  },
@@ -845,13 +853,18 @@
            }
            handleRepeaterClick(e) {
               if (e.target.matches(this.selectors.repeater.add)) {
                  console.log('Add Repeater Row');
                  this.addRepeaterRow(e.target.closest('[data-repeater-id]'));
               } else if (e.target.matches(this.selectors.repeater.remove)) {
                  this.removeRepeaterRow(e.target);
                  console.log('Remove Repeater Row');
                  this.removeRepeaterRow(e.target.closest('[data-index]'));
               }
            }
            addRepeaterRow(repeater) {
               repeater.append(this.templates.create(repeater.dataset.repeaterId));
               let data = {};
               data.repeater = repeater;
               repeater.append(this.templates.create(repeater.dataset.repeaterId, data));
               this.initializeFields(repeater, this.getField(repeater).config??{});
               this.a11y.announce('Row added');
            }
            removeRepeaterRow(row) {
@@ -982,6 +995,8 @@
                  config.ui.items.append(newItem);
                  config.ui.inputs[0]?.focus();
                  this.updateCollectionField(tagList);
                  this.a11y.announce('Item added');
               }
               removeTagListItem(tag) {
@@ -1207,6 +1222,28 @@
            );
         });
      });
      this.updateCollectionField(container);
   }
   /**
    * Update the entire repeater/tagList field data
    * Call this whenever rows are added, removed, or reordered
    */
   updateCollectionField(element) {
      const field = element.closest('[data-field]');
      if (!field) return;
      const fieldType = field.dataset.fieldType;
      if (!['repeater', 'tag-list'].includes(fieldType)) return;
      const form = this.getForm(element);
      if (!form) return;
      // Get all current data for the collection
      const value = this.getFieldValue(field.querySelector('input, select, textarea'));
      this.updateItem(field.dataset.field, value, form);
   }
   /**********************************************************************
    VALIDATION