From a24a06002081ad71a78ffeff9072725ba39cf121 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 17 Feb 2026 20:05:31 +0000
Subject: [PATCH] =minor changes, particularly around the JVB_CHILD_URL pattern
---
assets/js/concise/FormController.js | 156 ++++++++++++++++++++++++++++++++++++----------------
1 files changed, 108 insertions(+), 48 deletions(-)
diff --git a/assets/js/concise/FormController.js b/assets/js/concise/FormController.js
index 8996cbf..70e5b6a 100644
--- a/assets/js/concise/FormController.js
+++ b/assets/js/concise/FormController.js
@@ -92,7 +92,7 @@
input: 'input[type="number"]'
},
limits: {
- hasLimit: '[data-limit]',
+ hasLimit: '[data-maxlength]',
limit: '.limit',
current: '.current',
}
@@ -327,6 +327,25 @@
let field = this.getField(e.target);
+ // Check if this input lives inside a collection field
+ const collectionField = e.target.closest('[data-field-type="repeater"], [data-field-type="tag-list"]');
+ if (collectionField) {
+ // Dependencies still need checking
+ if (this.dependencies.has(field.dataset.field)) {
+ let dependency = this.dependencies.get(field.dataset.field);
+ dependency.items.forEach(item => {
+ this.checkFieldDependency(item, field.dataset.field);
+ });
+ }
+ const collectionName = collectionField.dataset.field;
+ window.debouncer.schedule(
+ `collection:${collectionName}`,
+ () => this.updateCollectionField(collectionField),
+ 150
+ );
+ return;
+ }
+
//Dependencies
if (this.dependencies.has(field.dataset.field)) {
let dependency = this.dependencies.get(field.dataset.field);
@@ -335,11 +354,6 @@
});
}
- if (field.dataset.fieldType === 'repeater' || field.dataset.fieldType === 'tag-list') {
- this.updateCollectionField(field);
- return;
- }
-
let form = this.getForm(e.target);
this.updateItem(field.dataset.field, this.getFieldValue(e.target), form);
}
@@ -354,10 +368,18 @@
window.debouncer.cancel(`form:${form.id}:validate:${fieldName}`);
this.validateField(e.target);
+ // If inside a collection, update the whole collection instead
+ const collectionField = e.target.closest('[data-field-type="repeater"], [data-field-type="tag-list"]');
+ if (collectionField) {
+ this.updateCollectionField(collectionField);
+ return;
+ }
+
this.updateItem(fieldName, this.getFieldValue(e.target), form);
}
handleInput(e){
+ if (e.target.closest('[data-ignore]') || this.isRestoring) return;
let form = this.getForm(e.target);
if (!form) return;
@@ -719,7 +741,8 @@
}
}
- let inputs = Array.from(container.querySelectorAll(this.inputSelectors));
+ let inputs = Array.from(container.querySelectorAll(this.inputSelectors))
+ .filter(input => !input.closest('.ql-clipboard'));
inputs.map(input => {
this.getItem(input, config?.id);
});
@@ -808,6 +831,7 @@
element: repeater,
field: this.getField(repeater),
sortable: false,
+ rows: []
};
if (!config.ui.add) return;
@@ -823,9 +847,8 @@
let index = config.ui.items?.children?.length??0;
el.dataset.index = index;
-
manyRefs.inputs?.forEach(input => {
- window.prefixInput(input, `${data.repeater.dataset.field}:${index}:`, el);
+ window.prefixInput(input, `${data.repeater.dataset.field}:${index}:`, el, false, true);
});
}
},
@@ -840,6 +863,7 @@
}
});
}
+
repeater.dataset.repeaterId = config.id;
this.addRepeaterListeners(repeater);
this.repeaters.set(config.id, config);
@@ -862,8 +886,18 @@
addRepeaterRow(repeater) {
let data = {};
data.repeater = repeater;
- repeater.append(this.templates.create(repeater.dataset.repeaterId, data));
- this.initializeFields(repeater, this.getField(repeater).config??{});
+ let config = this.repeaters.get(repeater.dataset.repeaterId);
+
+ let row = this.templates.create(repeater.dataset.repeaterId, data);
+ config.rows.push({
+ element: row,
+ fields: Array.from(row.querySelectorAll('[data-field]'))
+ });
+ this.repeaters.set(config.id, config);
+ config.ui.items.append(row);
+
+ let form = this.getForm(repeater);
+ this.initializeFields(repeater, form);
this.a11y.announce('Row added');
}
removeRepeaterRow(row) {
@@ -901,7 +935,7 @@
el.dataset.index = index;
manyRefs.inputs?.forEach(input => {
let wrapper = input.closest('.tag-item');
- window.prefixInput(input, `${data.fieldName}:${index}:`, wrapper)
+ window.prefixInput(input, `${data.fieldName}:${index}:`, wrapper, false, true)
});
if (refs.label) {
@@ -919,11 +953,11 @@
}
addTagListListeners(el) {
el.addEventListener('click', this.tagListClick);
- el.addEventListener('keypress', this.tagListInput, {passive: true})
+ el.addEventListener('keypress', this.tagListInput);
}
removeTagListListeners(el) {
el.removeEventListener('click', this.tagListClick);
- el.removeEventListener('keypress', this.tagListInput)
+ el.removeEventListener('keypress', this.tagListInput);
}
handleTagListClick(e) {
@@ -1129,25 +1163,32 @@
}
});
}
- checkForCharacterLimits(form) {
- if (!form.querySelector(this.selectors.limits.hasLimit)) return;
- this.countUpdaters = this.updateCount.bind(this);
+ checkForCharacterLimits(form) {
+ if (!form.querySelector(this.selectors.limits.hasLimit)) return;
+ this.countUpdaters = this.updateCount.bind(this);
- form.querySelectorAll(`${this.selectors.limits.hasLimit}`).forEach(input => {
- let id = window.generateID('limit');
- input.dataset.charLimitId = id;
- let config = {
- element: input,
- form: form.dataset.formId,
- ui: window.uiFromSelectors(this.selectors.limits, input.closest('.field'))
- };
- config.ui.limit.textContent = input.dataset.limit;
- this.charLimits.set(id, config);
+ form.querySelectorAll(this.selectors.limits.hasLimit).forEach(field => {
+ const input = this.getFieldInput(field);
+ if (!input) return;
- this.addCharacterLimitListeners(input);
- });
+ let id = window.generateID('limit');
+ input.dataset.charLimitId = id;
+ input.dataset.limit = field.dataset.maxlength;
+ let config = {
+ element: input,
+ form: form.dataset.formId,
+ ui: window.uiFromSelectors(this.selectors.limits, field)
+ };
+
+ if (config.ui.limit) {
+ config.ui.limit.textContent = field.dataset.maxlength;
}
+
+ this.charLimits.set(id, config);
+ this.addCharacterLimitListeners(input);
+ });
+ }
addCharacterLimitListeners(input) {
input.addEventListener('input', this.countUpdaters, {passive: true});
}
@@ -1250,7 +1291,9 @@
window.prefixInput(
input,
`${fieldName}:${index}:`,
- item // Pass the item as wrapper for label lookup
+ item,
+ false,
+ true
);
});
});
@@ -1274,7 +1317,7 @@
if (!form) return;
// Get all current data for the collection
- const value = this.getFieldValue(field.querySelector('input, select, textarea'));
+ const value = this.getFieldValue(field);
this.updateItem(field.dataset.field, value, form);
}
/**********************************************************************
@@ -1557,10 +1600,13 @@
case 'selector':
case 'upload':
+ case 'gallery':
+ case 'image':
return this.getHiddenInputValue(element, conf, fieldName);
case 'true-false':
- return element.value === '1'||element.value === 'on'||element.value ==='true';
+ case 'toggle-text':
+ return element.checked;
case 'checkbox':
// Handle multi-checkbox (name ends with [])
if (element.name.endsWith('[]')) {
@@ -1614,20 +1660,31 @@
return typeof value === 'object' && Object.keys(value).length === 0;
}
getRepeaterValue(element, conf) {
- if (!conf.container) {
- conf.container = conf.field?.querySelector('.repeater-items');
- this.saveItem(conf);
- }
+ const items = element.querySelector('.repeater-items');
+ if (!items) return [];
+ let ignore = ['image_data','image-title','image-caption','image-description','image-alt-text']
let value = [];
- Array.from(conf.container.children).forEach(row => {
+ Array.from(items.children).forEach(row => {
let rowData = {};
row.querySelectorAll('[data-field]').forEach(field => {
- rowData[field.dataset.field] = this.getFieldValue(field);
+ if (!ignore.includes(field.dataset.field)) {
+ const input = this.getFieldInput(field);
+ if (input) {
+ rowData[field.dataset.field] = this.getFieldValue(input);
+ }
+ }
});
value.push(rowData);
});
return value;
}
+ getFieldInput(field) {
+ // For quill fields, target the specific editor textarea
+ const quillTextarea = field.querySelector('textarea[data-editor]');
+ if (quillTextarea) return quillTextarea;
+
+ return field.querySelector(this.inputSelectors);
+ }
getTagListValue(element, conf) {
if (!conf.container) {
conf.container = conf.field?.querySelector('.tag-items');
@@ -1655,13 +1712,14 @@
});
return value;
}
- getHiddenInputValue(element, conf, fieldName) {
- if (!conf.value) {
- conf.value = conf.field?.querySelector(`input[type=hidden][name="${fieldName}"]`);
- this.saveItem(conf);
- }
- return conf.value.value;
+ getHiddenInputValue(element, conf, fieldName) {
+ if (!conf.value) {
+ conf.value = conf.field?.querySelector(`input[type=hidden][name="${fieldName}"]`)
+ || conf.field?.querySelector(`input[type=hidden]`);
+ this.saveItem(conf);
}
+ return conf.value?.value ?? '';
+ }
/**
* Format field value for display in summary
@@ -1701,6 +1759,8 @@
case 'selector':
case 'upload':
+ case 'image': //legacy, shouldn't be needed
+ case 'gallery': //legacy, shouldn't be needed
// These might need special handling depending on your needs
return this.formatHiddenFieldForSummary(value, input, fieldType);
@@ -1823,7 +1883,7 @@
* Format hidden field types (upload, selector)
*/
formatHiddenFieldForSummary(value, input, fieldType) {
- if (fieldType === 'upload') {
+ if (['upload', 'gallery', 'image'].includes(fieldType)) {
// Get upload preview images if available
const uploadField = input.field?.querySelector('[data-upload-field]');
if (uploadField) {
@@ -1965,10 +2025,10 @@
});
this.tagLists.clear();
}
- if(this.charLimits.size > 0) {
+ if (this.charLimits.size > 0) {
Array.from(this.charLimits.values()).forEach(charLimit => {
- charLimit.removeEventListener('input', this.countUpdaters);
- })
+ charLimit.element.removeEventListener('input', this.countUpdaters);
+ });
}
this.inputs.clear();
this.forms.clear();
--
Gitblit v1.10.0