@dmitryvim/form-builder 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser/formbuilder.min.js +118 -118
- package/dist/browser/formbuilder.v0.5.4.min.js +1490 -0
- package/dist/cjs/index.cjs +328 -156
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +305 -148
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +118 -118
- package/dist/types/components/container.d.ts +2 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +19 -1
- package/dist/types/types/config.d.ts +2 -0
- package/dist/types/types/state.d.ts +7 -0
- package/dist/types/utils/helpers.d.ts +16 -4
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.5.2.min.js +0 -1490
package/dist/cjs/index.cjs
CHANGED
|
@@ -19,6 +19,69 @@ function t(key, state, params) {
|
|
|
19
19
|
return text;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// src/utils/helpers.ts
|
|
23
|
+
function isElementReadonly(element, state, ctx) {
|
|
24
|
+
return element.readonly === true || state.config.readonly === true || (ctx == null ? void 0 : ctx.inheritedReadonly) === true;
|
|
25
|
+
}
|
|
26
|
+
function isPlainObject(obj) {
|
|
27
|
+
return obj && typeof obj === "object" && obj.constructor === Object;
|
|
28
|
+
}
|
|
29
|
+
function escapeHtml(text) {
|
|
30
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
31
|
+
}
|
|
32
|
+
function getElementLookupKey(element, state) {
|
|
33
|
+
if (element.key) {
|
|
34
|
+
return element.key;
|
|
35
|
+
}
|
|
36
|
+
const cached = state.syntheticElementIds.get(element);
|
|
37
|
+
if (cached !== void 0) {
|
|
38
|
+
return cached;
|
|
39
|
+
}
|
|
40
|
+
const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
|
|
41
|
+
state.syntheticElementIds.set(element, id);
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
44
|
+
function pathJoin(base, key) {
|
|
45
|
+
return base ? `${base}.${key}` : key;
|
|
46
|
+
}
|
|
47
|
+
function findDirectContainerRows(scopeRoot, containerPath) {
|
|
48
|
+
if (!containerPath) return [];
|
|
49
|
+
const all = scopeRoot.querySelectorAll("[data-container-item]");
|
|
50
|
+
return Array.from(all).filter((el) => {
|
|
51
|
+
const attr = el.getAttribute("data-container-item") || "";
|
|
52
|
+
if (!attr.startsWith(`${containerPath}[`)) return false;
|
|
53
|
+
return /^\[\d+\]$/.test(attr.slice(containerPath.length));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function clear(node) {
|
|
57
|
+
while (node.firstChild) node.removeChild(node.firstChild);
|
|
58
|
+
}
|
|
59
|
+
function formatFileSize(bytes) {
|
|
60
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
61
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
62
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
63
|
+
}
|
|
64
|
+
function serializeHiddenValue(value) {
|
|
65
|
+
if (value === null || value === void 0) return "";
|
|
66
|
+
return JSON.stringify(value);
|
|
67
|
+
}
|
|
68
|
+
function deserializeHiddenValue(raw) {
|
|
69
|
+
if (raw === "") return null;
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
} catch {
|
|
73
|
+
return raw;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function createHiddenInput(name, value) {
|
|
77
|
+
const input = document.createElement("input");
|
|
78
|
+
input.type = "hidden";
|
|
79
|
+
input.name = name;
|
|
80
|
+
input.setAttribute("data-hidden-field", "true");
|
|
81
|
+
input.value = serializeHiddenValue(value);
|
|
82
|
+
return input;
|
|
83
|
+
}
|
|
84
|
+
|
|
22
85
|
// src/utils/validation.ts
|
|
23
86
|
function addLengthHint(element, parts, state) {
|
|
24
87
|
if (element.minLength != null || element.maxLength != null) {
|
|
@@ -191,6 +254,65 @@ function validateSchema(schema) {
|
|
|
191
254
|
}
|
|
192
255
|
}
|
|
193
256
|
}
|
|
257
|
+
function validateCountBounds(element, elementPath, errors2) {
|
|
258
|
+
var _a, _b;
|
|
259
|
+
const el = element;
|
|
260
|
+
if (el.type === "group") {
|
|
261
|
+
if (!isPlainObject(el.repeat)) return;
|
|
262
|
+
checkBounds(
|
|
263
|
+
elementPath,
|
|
264
|
+
(_a = el.repeat) == null ? void 0 : _a.min,
|
|
265
|
+
(_b = el.repeat) == null ? void 0 : _b.max,
|
|
266
|
+
"repeat.min",
|
|
267
|
+
"repeat.max",
|
|
268
|
+
el.required === true,
|
|
269
|
+
errors2
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const isMultiple = el.multiple === true || el.type === "files";
|
|
274
|
+
if (!isMultiple) return;
|
|
275
|
+
checkBounds(
|
|
276
|
+
elementPath,
|
|
277
|
+
el.minCount,
|
|
278
|
+
el.maxCount,
|
|
279
|
+
"minCount",
|
|
280
|
+
"maxCount",
|
|
281
|
+
el.required === true,
|
|
282
|
+
errors2
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
function checkBounds(elementPath, minCount, maxCount, minName, maxName, requiredImpliesFloor, errors2) {
|
|
286
|
+
for (const [name, bound] of [
|
|
287
|
+
[minName, minCount],
|
|
288
|
+
[maxName, maxCount]
|
|
289
|
+
]) {
|
|
290
|
+
if (bound !== void 0 && typeof bound !== "number") {
|
|
291
|
+
errors2.push(
|
|
292
|
+
`${elementPath}: ${name} must be a number (got ${typeof bound})`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const min = typeof minCount === "number" ? minCount : void 0;
|
|
297
|
+
const max = typeof maxCount === "number" ? maxCount : void 0;
|
|
298
|
+
if (max !== void 0 && (max < 0 || Number.isNaN(max))) {
|
|
299
|
+
errors2.push(
|
|
300
|
+
`${elementPath}: ${maxName} must be a non-negative number or Infinity (got ${max})`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
if (min !== void 0 && (min < 0 || !Number.isFinite(min))) {
|
|
304
|
+
errors2.push(
|
|
305
|
+
`${elementPath}: ${minName} must be a finite non-negative number (got ${min})`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const effectiveMin = min != null ? min : requiredImpliesFloor ? 1 : void 0;
|
|
309
|
+
if (effectiveMin !== void 0 && max !== void 0 && effectiveMin > max) {
|
|
310
|
+
const shown = min !== void 0 ? `${minName} (${min})` : `required: true (implies ${minName} 1)`;
|
|
311
|
+
errors2.push(
|
|
312
|
+
`${elementPath}: ${shown} cannot be greater than ${maxName} (${max})`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
194
316
|
function validateElements(elements, path) {
|
|
195
317
|
elements.forEach((element, index) => {
|
|
196
318
|
const elementPath = `${path}[${index}]`;
|
|
@@ -200,6 +322,15 @@ function validateSchema(schema) {
|
|
|
200
322
|
if (!element.key && element.type !== "markdown") {
|
|
201
323
|
errors.push(`${elementPath}: missing key`);
|
|
202
324
|
}
|
|
325
|
+
validateCountBounds(element, elementPath, errors);
|
|
326
|
+
if (element.type === "number" && "decimals" in element) {
|
|
327
|
+
const decimals = element.decimals;
|
|
328
|
+
if (decimals !== void 0 && (!Number.isInteger(decimals) || decimals < 0)) {
|
|
329
|
+
errors.push(
|
|
330
|
+
`${elementPath}: decimals must be a non-negative integer (got ${JSON.stringify(decimals)})`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
203
334
|
if (element.type === "markdown") {
|
|
204
335
|
const content = element.content;
|
|
205
336
|
if (typeof content !== "string") {
|
|
@@ -280,71 +411,6 @@ function validateSchema(schema) {
|
|
|
280
411
|
return errors;
|
|
281
412
|
}
|
|
282
413
|
|
|
283
|
-
// src/utils/helpers.ts
|
|
284
|
-
function isElementReadonly(element, state, ctx) {
|
|
285
|
-
return element.readonly === true || state.config.readonly === true || (ctx == null ? void 0 : ctx.inheritedReadonly) === true;
|
|
286
|
-
}
|
|
287
|
-
function isPlainObject(obj) {
|
|
288
|
-
return obj && typeof obj === "object" && obj.constructor === Object;
|
|
289
|
-
}
|
|
290
|
-
function escapeHtml(text) {
|
|
291
|
-
const div = document.createElement("div");
|
|
292
|
-
div.textContent = text;
|
|
293
|
-
return div.innerHTML;
|
|
294
|
-
}
|
|
295
|
-
function getElementLookupKey(element, state) {
|
|
296
|
-
if (element.key) {
|
|
297
|
-
return element.key;
|
|
298
|
-
}
|
|
299
|
-
const cached = state.syntheticElementIds.get(element);
|
|
300
|
-
if (cached !== void 0) {
|
|
301
|
-
return cached;
|
|
302
|
-
}
|
|
303
|
-
const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
|
|
304
|
-
state.syntheticElementIds.set(element, id);
|
|
305
|
-
return id;
|
|
306
|
-
}
|
|
307
|
-
function pathJoin(base, key) {
|
|
308
|
-
return base ? `${base}.${key}` : key;
|
|
309
|
-
}
|
|
310
|
-
function findDirectContainerRows(scopeRoot, containerPath) {
|
|
311
|
-
if (!containerPath) return [];
|
|
312
|
-
const all = scopeRoot.querySelectorAll("[data-container-item]");
|
|
313
|
-
return Array.from(all).filter((el) => {
|
|
314
|
-
const attr = el.getAttribute("data-container-item") || "";
|
|
315
|
-
if (!attr.startsWith(`${containerPath}[`)) return false;
|
|
316
|
-
return /^\[\d+\]$/.test(attr.slice(containerPath.length));
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
function clear(node) {
|
|
320
|
-
while (node.firstChild) node.removeChild(node.firstChild);
|
|
321
|
-
}
|
|
322
|
-
function formatFileSize(bytes) {
|
|
323
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
324
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
325
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
326
|
-
}
|
|
327
|
-
function serializeHiddenValue(value) {
|
|
328
|
-
if (value === null || value === void 0) return "";
|
|
329
|
-
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
330
|
-
}
|
|
331
|
-
function deserializeHiddenValue(raw) {
|
|
332
|
-
if (raw === "") return null;
|
|
333
|
-
try {
|
|
334
|
-
return JSON.parse(raw);
|
|
335
|
-
} catch {
|
|
336
|
-
return raw;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
function createHiddenInput(name, value) {
|
|
340
|
-
const input = document.createElement("input");
|
|
341
|
-
input.type = "hidden";
|
|
342
|
-
input.name = name;
|
|
343
|
-
input.setAttribute("data-hidden-field", "true");
|
|
344
|
-
input.value = serializeHiddenValue(value);
|
|
345
|
-
return input;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
414
|
// src/utils/enable-conditions.ts
|
|
349
415
|
function getValueByPath(data, path) {
|
|
350
416
|
if (!data || typeof data !== "object") {
|
|
@@ -898,7 +964,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
898
964
|
overflow-wrap: anywhere;
|
|
899
965
|
`;
|
|
900
966
|
textInput.name = pathKey;
|
|
901
|
-
textInput.placeholder = (_a = element.placeholder) != null ? _a : "
|
|
967
|
+
textInput.placeholder = (_a = element.placeholder) != null ? _a : t("placeholderText", state);
|
|
902
968
|
textInput.value = ctx.prefill[element.key] || element.default || "";
|
|
903
969
|
textInput.readOnly = readonly;
|
|
904
970
|
applySingleLineMode(textInput);
|
|
@@ -998,6 +1064,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
998
1064
|
rem.setAttribute("aria-label", t("removeElement", state));
|
|
999
1065
|
rem.innerHTML = BIN_ICON_SVG;
|
|
1000
1066
|
rem.onclick = () => {
|
|
1067
|
+
var _a2;
|
|
1001
1068
|
const chips = list.querySelectorAll(".fb-chip");
|
|
1002
1069
|
const idx = Array.prototype.indexOf.call(chips, chip);
|
|
1003
1070
|
if (idx < 0) return;
|
|
@@ -1011,6 +1078,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
1011
1078
|
updateIndices();
|
|
1012
1079
|
updateAddButton();
|
|
1013
1080
|
updateRemoveButtons();
|
|
1081
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1014
1082
|
};
|
|
1015
1083
|
chip.appendChild(rem);
|
|
1016
1084
|
}
|
|
@@ -1031,10 +1099,12 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
1031
1099
|
const handle = createAddItemRow(
|
|
1032
1100
|
"text",
|
|
1033
1101
|
() => {
|
|
1102
|
+
var _a2;
|
|
1034
1103
|
values.push(element.default || "");
|
|
1035
1104
|
addChip(element.default || "");
|
|
1036
1105
|
updateAddButton();
|
|
1037
1106
|
updateRemoveButtons();
|
|
1107
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1038
1108
|
},
|
|
1039
1109
|
{ label: element.addLabel }
|
|
1040
1110
|
);
|
|
@@ -1151,7 +1221,7 @@ function validateTextElement(element, key, context) {
|
|
|
1151
1221
|
}
|
|
1152
1222
|
return { value: values, errors };
|
|
1153
1223
|
} else {
|
|
1154
|
-
const input = scopeRoot.querySelector(`[name
|
|
1224
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1155
1225
|
const val = (_c = input == null ? void 0 : input.value) != null ? _c : "";
|
|
1156
1226
|
if (!skipValidation && element.required && val === "") {
|
|
1157
1227
|
const msg = t("required", context.state);
|
|
@@ -1315,6 +1385,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1315
1385
|
removeBtn.className = "remove-item-btn mt-1 px-2 py-1 text-red-600 hover:bg-red-50 rounded text-sm";
|
|
1316
1386
|
removeBtn.innerHTML = "\u2715";
|
|
1317
1387
|
removeBtn.onclick = () => {
|
|
1388
|
+
var _a2;
|
|
1318
1389
|
const currentIndex = Array.from(container.children).indexOf(
|
|
1319
1390
|
item
|
|
1320
1391
|
);
|
|
@@ -1324,6 +1395,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1324
1395
|
updateIndices();
|
|
1325
1396
|
updateAddButton();
|
|
1326
1397
|
updateRemoveButtons();
|
|
1398
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1327
1399
|
}
|
|
1328
1400
|
};
|
|
1329
1401
|
item.appendChild(removeBtn);
|
|
@@ -1339,10 +1411,12 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1339
1411
|
const handle = createAddItemRow(
|
|
1340
1412
|
"textarea",
|
|
1341
1413
|
() => {
|
|
1414
|
+
var _a2;
|
|
1342
1415
|
values.push(element.default || "");
|
|
1343
1416
|
addTextareaItem(element.default || "");
|
|
1344
1417
|
updateAddButton();
|
|
1345
1418
|
updateRemoveButtons();
|
|
1419
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1346
1420
|
},
|
|
1347
1421
|
{ label: element.addLabel }
|
|
1348
1422
|
);
|
|
@@ -1628,6 +1702,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1628
1702
|
removeBtn.className = "remove-item-btn px-2 py-1 text-red-600 hover:bg-red-50 rounded";
|
|
1629
1703
|
removeBtn.innerHTML = "\u2715";
|
|
1630
1704
|
removeBtn.onclick = () => {
|
|
1705
|
+
var _a2;
|
|
1631
1706
|
const currentIndex = Array.from(container.children).indexOf(
|
|
1632
1707
|
item
|
|
1633
1708
|
);
|
|
@@ -1637,6 +1712,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1637
1712
|
updateIndices();
|
|
1638
1713
|
updateAddButton();
|
|
1639
1714
|
updateRemoveButtons();
|
|
1715
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1640
1716
|
}
|
|
1641
1717
|
};
|
|
1642
1718
|
item.appendChild(removeBtn);
|
|
@@ -1652,10 +1728,12 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1652
1728
|
const handle = createAddItemRow(
|
|
1653
1729
|
"number",
|
|
1654
1730
|
() => {
|
|
1731
|
+
var _a2;
|
|
1655
1732
|
values.push(element.default || "");
|
|
1656
1733
|
addNumberItem(element.default || "");
|
|
1657
1734
|
updateAddButton();
|
|
1658
1735
|
updateRemoveButtons();
|
|
1736
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1659
1737
|
},
|
|
1660
1738
|
{ label: element.addLabel }
|
|
1661
1739
|
);
|
|
@@ -1671,7 +1749,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1671
1749
|
updateRemoveButtons();
|
|
1672
1750
|
}
|
|
1673
1751
|
function validateNumberElement(element, key, context) {
|
|
1674
|
-
var _a, _b, _c
|
|
1752
|
+
var _a, _b, _c;
|
|
1675
1753
|
const errors = [];
|
|
1676
1754
|
const { scopeRoot, skipValidation } = context;
|
|
1677
1755
|
const markValidity = (input, errorMessage) => {
|
|
@@ -1731,7 +1809,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1731
1809
|
);
|
|
1732
1810
|
const values = [];
|
|
1733
1811
|
inputs.forEach((input, index) => {
|
|
1734
|
-
var _a2
|
|
1812
|
+
var _a2;
|
|
1735
1813
|
const raw = (_a2 = input == null ? void 0 : input.value) != null ? _a2 : "";
|
|
1736
1814
|
if (raw === "") {
|
|
1737
1815
|
values.push(null);
|
|
@@ -1747,8 +1825,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1747
1825
|
return;
|
|
1748
1826
|
}
|
|
1749
1827
|
validateNumberInput(input, v, `${key}[${index}]`);
|
|
1750
|
-
|
|
1751
|
-
values.push(Number(v.toFixed(d)));
|
|
1828
|
+
values.push(applyDecimals(v, element.decimals));
|
|
1752
1829
|
});
|
|
1753
1830
|
if (!skipValidation) {
|
|
1754
1831
|
const { state } = context;
|
|
@@ -1767,7 +1844,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1767
1844
|
}
|
|
1768
1845
|
return { value: values, errors };
|
|
1769
1846
|
} else {
|
|
1770
|
-
const input = scopeRoot.querySelector(`[name
|
|
1847
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1771
1848
|
const raw = (_c = input == null ? void 0 : input.value) != null ? _c : "";
|
|
1772
1849
|
const { state } = context;
|
|
1773
1850
|
if (!skipValidation && element.required && raw === "") {
|
|
@@ -1788,10 +1865,13 @@ function validateNumberElement(element, key, context) {
|
|
|
1788
1865
|
return { value: null, errors };
|
|
1789
1866
|
}
|
|
1790
1867
|
validateNumberInput(input, v, key);
|
|
1791
|
-
|
|
1792
|
-
return { value: Number(v.toFixed(d)), errors };
|
|
1868
|
+
return { value: applyDecimals(v, element.decimals), errors };
|
|
1793
1869
|
}
|
|
1794
1870
|
}
|
|
1871
|
+
function applyDecimals(v, decimals) {
|
|
1872
|
+
if (!Number.isInteger(decimals) || decimals < 0) return v;
|
|
1873
|
+
return Number(v.toFixed(decimals));
|
|
1874
|
+
}
|
|
1795
1875
|
function updateNumberField(element, fieldPath, value, context) {
|
|
1796
1876
|
const { scopeRoot } = context;
|
|
1797
1877
|
if (element.multiple) {
|
|
@@ -1938,6 +2018,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1938
2018
|
removeBtn.className = "remove-item-btn px-2 py-1 text-red-600 hover:bg-red-50 rounded";
|
|
1939
2019
|
removeBtn.innerHTML = "\u2715";
|
|
1940
2020
|
removeBtn.onclick = () => {
|
|
2021
|
+
var _a2;
|
|
1941
2022
|
const currentIndex = Array.from(container.children).indexOf(item);
|
|
1942
2023
|
if (container.children.length > minCount) {
|
|
1943
2024
|
values.splice(currentIndex, 1);
|
|
@@ -1945,6 +2026,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1945
2026
|
updateIndices();
|
|
1946
2027
|
updateAddButton();
|
|
1947
2028
|
updateRemoveButtons();
|
|
2029
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
1948
2030
|
}
|
|
1949
2031
|
};
|
|
1950
2032
|
item.appendChild(removeBtn);
|
|
@@ -1960,12 +2042,13 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1960
2042
|
const handle = createAddItemRow(
|
|
1961
2043
|
"select",
|
|
1962
2044
|
() => {
|
|
1963
|
-
var _a2, _b2;
|
|
2045
|
+
var _a2, _b2, _c2;
|
|
1964
2046
|
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1965
2047
|
values.push(defaultValue);
|
|
1966
2048
|
addSelectItem(defaultValue);
|
|
1967
2049
|
updateAddButton();
|
|
1968
2050
|
updateRemoveButtons();
|
|
2051
|
+
(_c2 = ctx.instance) == null ? void 0 : _c2.triggerOnChange(pathKey);
|
|
1969
2052
|
},
|
|
1970
2053
|
{ label: element.addLabel }
|
|
1971
2054
|
);
|
|
@@ -2055,7 +2138,7 @@ function validateSelectElement(element, key, context) {
|
|
|
2055
2138
|
return { value: values, errors };
|
|
2056
2139
|
} else {
|
|
2057
2140
|
const input = scopeRoot.querySelector(
|
|
2058
|
-
`[name
|
|
2141
|
+
`[name="${key}"]`
|
|
2059
2142
|
);
|
|
2060
2143
|
const val = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
2061
2144
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -2380,6 +2463,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2380
2463
|
removeBtn.style.backgroundColor = "transparent";
|
|
2381
2464
|
});
|
|
2382
2465
|
removeBtn.onclick = () => {
|
|
2466
|
+
var _a2;
|
|
2383
2467
|
const currentIndex = Array.from(container.children).indexOf(
|
|
2384
2468
|
item
|
|
2385
2469
|
);
|
|
@@ -2389,6 +2473,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2389
2473
|
updateIndices();
|
|
2390
2474
|
updateAddButton();
|
|
2391
2475
|
updateRemoveButtons();
|
|
2476
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
2392
2477
|
}
|
|
2393
2478
|
};
|
|
2394
2479
|
item.appendChild(removeBtn);
|
|
@@ -2404,12 +2489,13 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2404
2489
|
const handle = createAddItemRow(
|
|
2405
2490
|
"switcher",
|
|
2406
2491
|
() => {
|
|
2407
|
-
var _a2, _b2;
|
|
2492
|
+
var _a2, _b2, _c2;
|
|
2408
2493
|
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
2409
2494
|
values.push(defaultValue);
|
|
2410
2495
|
addSwitcherItem(defaultValue);
|
|
2411
2496
|
updateAddButton();
|
|
2412
2497
|
updateRemoveButtons();
|
|
2498
|
+
(_c2 = ctx.instance) == null ? void 0 : _c2.triggerOnChange(pathKey);
|
|
2413
2499
|
},
|
|
2414
2500
|
{ label: element.addLabel }
|
|
2415
2501
|
);
|
|
@@ -2508,7 +2594,7 @@ function validateSwitcherElement(element, key, context) {
|
|
|
2508
2594
|
return { value: values, errors };
|
|
2509
2595
|
} else {
|
|
2510
2596
|
const input = scopeRoot.querySelector(
|
|
2511
|
-
`input[type="hidden"][name
|
|
2597
|
+
`input[type="hidden"][name="${key}"]`
|
|
2512
2598
|
);
|
|
2513
2599
|
const val = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
2514
2600
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -5604,7 +5690,7 @@ function validateSingleFile(element, key, context) {
|
|
|
5604
5690
|
const { scopeRoot, skipValidation, state } = context;
|
|
5605
5691
|
const errors = [];
|
|
5606
5692
|
const input = scopeRoot.querySelector(
|
|
5607
|
-
`input[name
|
|
5693
|
+
`input[name="${key}"][type="hidden"]`
|
|
5608
5694
|
);
|
|
5609
5695
|
const rid = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
5610
5696
|
if (!skipValidation && element.required && rid === "") {
|
|
@@ -6028,6 +6114,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
6028
6114
|
removeBtn.style.backgroundColor = "transparent";
|
|
6029
6115
|
});
|
|
6030
6116
|
removeBtn.onclick = () => {
|
|
6117
|
+
var _a2;
|
|
6031
6118
|
const currentIndex = Array.from(container.children).indexOf(
|
|
6032
6119
|
item
|
|
6033
6120
|
);
|
|
@@ -6037,6 +6124,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
6037
6124
|
updateIndices();
|
|
6038
6125
|
updateAddButton();
|
|
6039
6126
|
updateRemoveButtons();
|
|
6127
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6040
6128
|
}
|
|
6041
6129
|
};
|
|
6042
6130
|
item.appendChild(removeBtn);
|
|
@@ -6052,11 +6140,13 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
6052
6140
|
const handle = createAddItemRow(
|
|
6053
6141
|
"colour",
|
|
6054
6142
|
() => {
|
|
6143
|
+
var _a2;
|
|
6055
6144
|
const defaultColour = element.default || "#000000";
|
|
6056
6145
|
values.push(defaultColour);
|
|
6057
6146
|
addColourItem(defaultColour);
|
|
6058
6147
|
updateAddButton();
|
|
6059
6148
|
updateRemoveButtons();
|
|
6149
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6060
6150
|
},
|
|
6061
6151
|
{ label: element.addLabel }
|
|
6062
6152
|
);
|
|
@@ -6490,6 +6580,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6490
6580
|
removeBtn.style.backgroundColor = "transparent";
|
|
6491
6581
|
});
|
|
6492
6582
|
removeBtn.onclick = () => {
|
|
6583
|
+
var _a2;
|
|
6493
6584
|
const currentIndex = Array.from(container.children).indexOf(
|
|
6494
6585
|
item
|
|
6495
6586
|
);
|
|
@@ -6499,6 +6590,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6499
6590
|
updateIndices();
|
|
6500
6591
|
updateAddButton();
|
|
6501
6592
|
updateRemoveButtons();
|
|
6593
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6502
6594
|
}
|
|
6503
6595
|
};
|
|
6504
6596
|
item.appendChild(removeBtn);
|
|
@@ -6514,10 +6606,12 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6514
6606
|
const handle = createAddItemRow(
|
|
6515
6607
|
"slider",
|
|
6516
6608
|
() => {
|
|
6609
|
+
var _a2;
|
|
6517
6610
|
values.push(defaultValue);
|
|
6518
6611
|
addSliderItem(defaultValue);
|
|
6519
6612
|
updateAddButton();
|
|
6520
6613
|
updateRemoveButtons();
|
|
6614
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6521
6615
|
},
|
|
6522
6616
|
{ label: element.addLabel }
|
|
6523
6617
|
);
|
|
@@ -6788,6 +6882,8 @@ function extractRootFormData(formRoot) {
|
|
|
6788
6882
|
if (input.checked) {
|
|
6789
6883
|
data[fieldName] = input.value;
|
|
6790
6884
|
}
|
|
6885
|
+
} else if (input.dataset.hiddenField) {
|
|
6886
|
+
data[fieldName] = deserializeHiddenValue(input.value);
|
|
6791
6887
|
} else {
|
|
6792
6888
|
data[fieldName] = input.value;
|
|
6793
6889
|
}
|
|
@@ -6878,11 +6974,14 @@ function getChildWrapperClass(columns) {
|
|
|
6878
6974
|
const cols = columns || 1;
|
|
6879
6975
|
return cols === 1 ? "fb-row" : `grid grid-cols-${cols} gap-2`;
|
|
6880
6976
|
}
|
|
6881
|
-
function mountRemoveButton(item, onRemove, state) {
|
|
6977
|
+
function mountRemoveButton(item, onRemove, state, containerLabel) {
|
|
6882
6978
|
const rem = document.createElement("button");
|
|
6883
6979
|
rem.type = "button";
|
|
6884
6980
|
rem.className = "fb-item-remove";
|
|
6885
|
-
rem.setAttribute(
|
|
6981
|
+
rem.setAttribute(
|
|
6982
|
+
"aria-label",
|
|
6983
|
+
containerLabel ? t("removeRowFrom", state, { label: containerLabel }) : t("removeRow", state)
|
|
6984
|
+
);
|
|
6886
6985
|
rem.style.cssText = `
|
|
6887
6986
|
width: 24px;
|
|
6888
6987
|
height: 24px;
|
|
@@ -6905,7 +7004,7 @@ function mountRemoveButton(item, onRemove, state) {
|
|
|
6905
7004
|
item.classList.add("fb-row-removable");
|
|
6906
7005
|
item.insertBefore(rem, item.firstChild);
|
|
6907
7006
|
}
|
|
6908
|
-
function renderMultipleContainerElement(element, ctx, wrapper,
|
|
7007
|
+
function renderMultipleContainerElement(element, ctx, wrapper, pathKey) {
|
|
6909
7008
|
var _a, _b, _c, _d;
|
|
6910
7009
|
const state = ctx.state;
|
|
6911
7010
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
@@ -6936,9 +7035,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6936
7035
|
);
|
|
6937
7036
|
const countItems = () => directRows().length;
|
|
6938
7037
|
const handleRemoveItem = (item) => {
|
|
7038
|
+
var _a2;
|
|
6939
7039
|
if (countItems() <= min) return;
|
|
6940
7040
|
item.remove();
|
|
6941
7041
|
updateAddButton();
|
|
7042
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6942
7043
|
};
|
|
6943
7044
|
const createContainerItem = (idx, rowPrefill, formData) => {
|
|
6944
7045
|
const subCtx = {
|
|
@@ -6972,11 +7073,17 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6972
7073
|
});
|
|
6973
7074
|
item.appendChild(childWrapper);
|
|
6974
7075
|
if (!containerIsReadonly) {
|
|
6975
|
-
mountRemoveButton(
|
|
7076
|
+
mountRemoveButton(
|
|
7077
|
+
item,
|
|
7078
|
+
() => handleRemoveItem(item),
|
|
7079
|
+
state,
|
|
7080
|
+
element.label
|
|
7081
|
+
);
|
|
6976
7082
|
}
|
|
6977
7083
|
return item;
|
|
6978
7084
|
};
|
|
6979
7085
|
const handleAddItem = () => {
|
|
7086
|
+
var _a2;
|
|
6980
7087
|
if (countItems() >= max) return;
|
|
6981
7088
|
const item = createContainerItem(
|
|
6982
7089
|
takeRowIndex(),
|
|
@@ -6991,6 +7098,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6991
7098
|
itemsWrap.appendChild(item);
|
|
6992
7099
|
}
|
|
6993
7100
|
updateAddButton();
|
|
7101
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
|
|
6994
7102
|
};
|
|
6995
7103
|
let slideAddTile = null;
|
|
6996
7104
|
let slideAddUpdate = null;
|
|
@@ -7138,7 +7246,7 @@ function validateContainerElement(element, key, context) {
|
|
|
7138
7246
|
);
|
|
7139
7247
|
if (childResult.spread && childResult.value !== null && typeof childResult.value === "object") {
|
|
7140
7248
|
Object.assign(itemData, childResult.value);
|
|
7141
|
-
} else {
|
|
7249
|
+
} else if (!childResult.skip && child.key) {
|
|
7142
7250
|
itemData[child.key] = childResult.value;
|
|
7143
7251
|
}
|
|
7144
7252
|
});
|
|
@@ -7180,7 +7288,7 @@ function validateContainerElement(element, key, context) {
|
|
|
7180
7288
|
);
|
|
7181
7289
|
if (childResult.spread && childResult.value !== null && typeof childResult.value === "object") {
|
|
7182
7290
|
Object.assign(containerData, childResult.value);
|
|
7183
|
-
} else {
|
|
7291
|
+
} else if (!childResult.skip && child.key) {
|
|
7184
7292
|
containerData[child.key] = childResult.value;
|
|
7185
7293
|
}
|
|
7186
7294
|
}
|
|
@@ -7200,15 +7308,18 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
7200
7308
|
);
|
|
7201
7309
|
return;
|
|
7202
7310
|
}
|
|
7311
|
+
const rows = findDirectContainerRows(scopeRoot, fieldPath);
|
|
7203
7312
|
value.forEach((itemValue, index) => {
|
|
7204
|
-
|
|
7313
|
+
var _a;
|
|
7314
|
+
const rowPath = (_a = rows[index]) == null ? void 0 : _a.getAttribute("data-container-item");
|
|
7315
|
+
if (isPlainObject(itemValue) && rowPath) {
|
|
7205
7316
|
element.elements.forEach((childElement) => {
|
|
7206
|
-
var
|
|
7317
|
+
var _a2, _b;
|
|
7207
7318
|
if (childElement.type === "markdown" || !childElement.key) return;
|
|
7208
|
-
const childPath = `${
|
|
7319
|
+
const childPath = `${rowPath}.${childElement.key}`;
|
|
7209
7320
|
if (childElement.type === "richinput" && childElement.flatOutput) {
|
|
7210
7321
|
const richChild = childElement;
|
|
7211
|
-
const textKey = (
|
|
7322
|
+
const textKey = (_a2 = richChild.textKey) != null ? _a2 : "text";
|
|
7212
7323
|
const filesKey = (_b = richChild.filesKey) != null ? _b : "files";
|
|
7213
7324
|
const containerValue = itemValue;
|
|
7214
7325
|
const compositeValue = {};
|
|
@@ -7228,10 +7339,9 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
7228
7339
|
});
|
|
7229
7340
|
}
|
|
7230
7341
|
});
|
|
7231
|
-
|
|
7232
|
-
if (value.length !== existingContainers.length) {
|
|
7342
|
+
if (value.length !== rows.length) {
|
|
7233
7343
|
console.warn(
|
|
7234
|
-
`updateContainerField: Multiple container field "${fieldPath}"
|
|
7344
|
+
`updateContainerField: Multiple container field "${fieldPath}" received ${value.length} items for ${rows.length} rendered rows. Rows are not added or removed here \u2014 re-render for add/remove.`
|
|
7235
7345
|
);
|
|
7236
7346
|
}
|
|
7237
7347
|
} else {
|
|
@@ -7290,7 +7400,7 @@ function renderGroupElement(element, ctx, wrapper, pathKey) {
|
|
|
7290
7400
|
maxCount: (_b = element.repeat) == null ? void 0 : _b.max
|
|
7291
7401
|
};
|
|
7292
7402
|
if (containerElement.multiple) {
|
|
7293
|
-
renderMultipleContainerElement(containerElement, ctx, wrapper);
|
|
7403
|
+
renderMultipleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7294
7404
|
} else {
|
|
7295
7405
|
renderSingleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7296
7406
|
}
|
|
@@ -10358,15 +10468,19 @@ var componentRegistry = {
|
|
|
10358
10468
|
function getComponentOperations(elementType) {
|
|
10359
10469
|
return componentRegistry[elementType] || null;
|
|
10360
10470
|
}
|
|
10471
|
+
function resolveOperations(element) {
|
|
10472
|
+
const isHiddenField = element.type !== "markdown" && (element.type === "hidden" || Boolean(element.hidden));
|
|
10473
|
+
return isHiddenField ? componentRegistry.hidden : getComponentOperations(element.type);
|
|
10474
|
+
}
|
|
10361
10475
|
function validateElementWithComponent(element, key, context) {
|
|
10362
|
-
const ops =
|
|
10476
|
+
const ops = resolveOperations(element);
|
|
10363
10477
|
if (ops && ops.validate) {
|
|
10364
10478
|
return ops.validate(element, key, context);
|
|
10365
10479
|
}
|
|
10366
10480
|
return null;
|
|
10367
10481
|
}
|
|
10368
10482
|
function updateElementWithComponent(element, fieldPath, value, context) {
|
|
10369
|
-
const ops =
|
|
10483
|
+
const ops = resolveOperations(element);
|
|
10370
10484
|
if (ops && ops.update) {
|
|
10371
10485
|
ops.update(element, fieldPath, value, context);
|
|
10372
10486
|
return true;
|
|
@@ -10378,6 +10492,10 @@ function updateElementWithComponent(element, fieldPath, value, context) {
|
|
|
10378
10492
|
function showTooltip(tooltipId, button) {
|
|
10379
10493
|
const tooltip = document.getElementById(tooltipId);
|
|
10380
10494
|
if (!tooltip) return;
|
|
10495
|
+
if (!button.isConnected) {
|
|
10496
|
+
tooltip.remove();
|
|
10497
|
+
return;
|
|
10498
|
+
}
|
|
10381
10499
|
const isCurrentlyVisible = !tooltip.classList.contains("hidden");
|
|
10382
10500
|
document.querySelectorAll('[id^="tooltip-"]').forEach((t2) => {
|
|
10383
10501
|
t2.classList.add("hidden");
|
|
@@ -10468,6 +10586,8 @@ function extractDOMValue(fieldPath, formRoot) {
|
|
|
10468
10586
|
`[name="${fieldPath}"]:checked`
|
|
10469
10587
|
);
|
|
10470
10588
|
return checked ? checked.value : void 0;
|
|
10589
|
+
} else if (input.dataset.hiddenField) {
|
|
10590
|
+
return deserializeHiddenValue(input.value);
|
|
10471
10591
|
} else {
|
|
10472
10592
|
return input.value;
|
|
10473
10593
|
}
|
|
@@ -10606,7 +10726,7 @@ function createFieldLabel(element) {
|
|
|
10606
10726
|
}
|
|
10607
10727
|
return title;
|
|
10608
10728
|
}
|
|
10609
|
-
function createInfoButton(element) {
|
|
10729
|
+
function createInfoButton(element, state) {
|
|
10610
10730
|
const infoBtn = document.createElement("button");
|
|
10611
10731
|
infoBtn.type = "button";
|
|
10612
10732
|
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
@@ -10618,6 +10738,7 @@ function createInfoButton(element) {
|
|
|
10618
10738
|
tooltip.style.position = "fixed";
|
|
10619
10739
|
tooltip.textContent = element.description || element.hint || "Field information";
|
|
10620
10740
|
document.body.appendChild(tooltip);
|
|
10741
|
+
state.tooltipElements.add(tooltip);
|
|
10621
10742
|
infoBtn.onclick = (e) => {
|
|
10622
10743
|
e.preventDefault();
|
|
10623
10744
|
e.stopPropagation();
|
|
@@ -10625,7 +10746,7 @@ function createInfoButton(element) {
|
|
|
10625
10746
|
};
|
|
10626
10747
|
return infoBtn;
|
|
10627
10748
|
}
|
|
10628
|
-
function createLabelContainer(element) {
|
|
10749
|
+
function createLabelContainer(element, state) {
|
|
10629
10750
|
const label = document.createElement("div");
|
|
10630
10751
|
label.className = "flex items-center";
|
|
10631
10752
|
label.style.marginBottom = "var(--fb-label-margin-bottom, 2px)";
|
|
@@ -10633,7 +10754,7 @@ function createLabelContainer(element) {
|
|
|
10633
10754
|
const title = createFieldLabel(element);
|
|
10634
10755
|
label.appendChild(title);
|
|
10635
10756
|
if (element.description || element.hint) {
|
|
10636
|
-
const infoBtn = createInfoButton(element);
|
|
10757
|
+
const infoBtn = createInfoButton(element, state);
|
|
10637
10758
|
label.appendChild(infoBtn);
|
|
10638
10759
|
}
|
|
10639
10760
|
return label;
|
|
@@ -10708,7 +10829,7 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
10708
10829
|
break;
|
|
10709
10830
|
case "container":
|
|
10710
10831
|
if (isMultiple) {
|
|
10711
|
-
renderMultipleContainerElement(element, ctx, wrapper);
|
|
10832
|
+
renderMultipleContainerElement(element, ctx, wrapper, pathKey);
|
|
10712
10833
|
} else {
|
|
10713
10834
|
renderSingleContainerElement(element, ctx, wrapper, pathKey);
|
|
10714
10835
|
}
|
|
@@ -10761,7 +10882,7 @@ function renderElement2(element, ctx) {
|
|
|
10761
10882
|
wrapper.setAttribute("data-fb-width", element.width || "full");
|
|
10762
10883
|
const ops = getComponentOperations(element.type);
|
|
10763
10884
|
if (!(ops == null ? void 0 : ops.ownsLabel)) {
|
|
10764
|
-
const label = createLabelContainer(element);
|
|
10885
|
+
const label = createLabelContainer(element, ctx.state);
|
|
10765
10886
|
wrapper.appendChild(label);
|
|
10766
10887
|
}
|
|
10767
10888
|
const pathKey = pathJoin(ctx.path, element.key);
|
|
@@ -10800,6 +10921,8 @@ var defaultConfig = {
|
|
|
10800
10921
|
en: {
|
|
10801
10922
|
// UI texts
|
|
10802
10923
|
removeElement: "Remove",
|
|
10924
|
+
removeRow: "Remove row",
|
|
10925
|
+
removeRowFrom: "Remove row from {label}",
|
|
10803
10926
|
clickDragText: "Click or drag file",
|
|
10804
10927
|
clickDragTextMultiple: "Click or drag files",
|
|
10805
10928
|
noFileSelected: "No file selected",
|
|
@@ -10873,6 +10996,8 @@ var defaultConfig = {
|
|
|
10873
10996
|
ru: {
|
|
10874
10997
|
// UI texts
|
|
10875
10998
|
removeElement: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
|
|
10999
|
+
removeRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
|
|
11000
|
+
removeRowFrom: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443 \u0438\u0437 \xAB{label}\xBB",
|
|
10876
11001
|
clickDragText: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B",
|
|
10877
11002
|
clickDragTextMultiple: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B",
|
|
10878
11003
|
noFileSelected: "\u0424\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D",
|
|
@@ -10975,7 +11100,8 @@ function createInstanceState(config) {
|
|
|
10975
11100
|
prefill: {},
|
|
10976
11101
|
syntheticElementIds: /* @__PURE__ */ new WeakMap(),
|
|
10977
11102
|
syntheticElementIdCounter: 0,
|
|
10978
|
-
enableIfObservers: /* @__PURE__ */ new Set()
|
|
11103
|
+
enableIfObservers: /* @__PURE__ */ new Set(),
|
|
11104
|
+
tooltipElements: /* @__PURE__ */ new Set()
|
|
10979
11105
|
};
|
|
10980
11106
|
}
|
|
10981
11107
|
function generateInstanceId() {
|
|
@@ -11262,6 +11388,18 @@ var exampleThemes = {
|
|
|
11262
11388
|
};
|
|
11263
11389
|
|
|
11264
11390
|
// src/instance/FormBuilderInstance.ts
|
|
11391
|
+
function findOwnField(scope, lookupKey, ownBoundary) {
|
|
11392
|
+
const matches = scope.querySelectorAll(
|
|
11393
|
+
`[data-field-key="${lookupKey}"]`
|
|
11394
|
+
);
|
|
11395
|
+
for (const el of Array.from(matches)) {
|
|
11396
|
+
const boundary = el.closest(
|
|
11397
|
+
"[data-container-item], [data-container]"
|
|
11398
|
+
);
|
|
11399
|
+
if (boundary === ownBoundary) return el;
|
|
11400
|
+
}
|
|
11401
|
+
return null;
|
|
11402
|
+
}
|
|
11265
11403
|
var FormBuilderInstance = class {
|
|
11266
11404
|
constructor(config) {
|
|
11267
11405
|
this.instanceId = generateInstanceId();
|
|
@@ -11343,7 +11481,11 @@ var FormBuilderInstance = class {
|
|
|
11343
11481
|
/**
|
|
11344
11482
|
* Trigger onChange callbacks with debouncing
|
|
11345
11483
|
* @param fieldPath - Optional field path for field-specific change events
|
|
11346
|
-
* @param fieldValue - Optional field value for field-specific change events
|
|
11484
|
+
* @param fieldValue - Optional field value for field-specific change events.
|
|
11485
|
+
* When omitted while fieldPath is given, the value is read from the
|
|
11486
|
+
* freshly extracted form data at debounce time — used by structural
|
|
11487
|
+
* changes (multi-item add/remove), where the handler has no cheap
|
|
11488
|
+
* current value but the array is trivially derivable after the fact.
|
|
11347
11489
|
*/
|
|
11348
11490
|
triggerOnChange(fieldPath, fieldValue) {
|
|
11349
11491
|
if (this.state.config.readonly) return;
|
|
@@ -11356,12 +11498,49 @@ var FormBuilderInstance = class {
|
|
|
11356
11498
|
if (this.state.config.onChange) {
|
|
11357
11499
|
this.state.config.onChange(formData);
|
|
11358
11500
|
}
|
|
11359
|
-
if (this.state.config.onFieldChange && fieldPath !== void 0
|
|
11360
|
-
this.
|
|
11501
|
+
if (this.state.config.onFieldChange && fieldPath !== void 0) {
|
|
11502
|
+
const resolvedValue = fieldValue !== void 0 ? fieldValue : this.resolveDomPathValue(formData.data, fieldPath);
|
|
11503
|
+
this.state.config.onFieldChange(fieldPath, resolvedValue, formData);
|
|
11361
11504
|
}
|
|
11362
11505
|
this.state.debounceTimer = null;
|
|
11363
11506
|
}, this.state.config.debounceMs);
|
|
11364
11507
|
}
|
|
11508
|
+
/**
|
|
11509
|
+
* Resolve a DOM field path against the extracted form data.
|
|
11510
|
+
*
|
|
11511
|
+
* A plain getValueByPath is wrong for paths inside a multiple container:
|
|
11512
|
+
* row markers keep gaps after a deletion (`s[2]` may be the first surviving
|
|
11513
|
+
* row) while the extracted array is re-packed contiguously — the naive
|
|
11514
|
+
* lookup would read a different row, or nothing. Each `[N]` segment is
|
|
11515
|
+
* mapped from its marker to the row's position among the container's
|
|
11516
|
+
* rendered rows, the same DOM order extraction used to build the array.
|
|
11517
|
+
* A bracketed segment that is not a container marker (a multi-value leaf
|
|
11518
|
+
* like `tags[1]`, whose indices are contiguous) falls back to the index.
|
|
11519
|
+
*/
|
|
11520
|
+
resolveDomPathValue(data, domPath) {
|
|
11521
|
+
let cur = data;
|
|
11522
|
+
let domPrefix = "";
|
|
11523
|
+
for (const seg of domPath.split(".")) {
|
|
11524
|
+
if (cur === null || cur === void 0) return void 0;
|
|
11525
|
+
const marker = seg.match(/^(.+)\[(\d+)\]$/);
|
|
11526
|
+
if (!marker) {
|
|
11527
|
+
domPrefix = domPrefix ? `${domPrefix}.${seg}` : seg;
|
|
11528
|
+
cur = cur[seg];
|
|
11529
|
+
continue;
|
|
11530
|
+
}
|
|
11531
|
+
const key = marker[1];
|
|
11532
|
+
domPrefix = domPrefix ? `${domPrefix}.${key}` : key;
|
|
11533
|
+
const arr = cur[key];
|
|
11534
|
+
if (!Array.isArray(arr)) return void 0;
|
|
11535
|
+
const rows = this.state.formRoot ? findDirectContainerRows(this.state.formRoot, domPrefix) : [];
|
|
11536
|
+
domPrefix = `${domPrefix}[${marker[2]}]`;
|
|
11537
|
+
const pos = rows.findIndex(
|
|
11538
|
+
(row) => row.getAttribute("data-container-item") === domPrefix
|
|
11539
|
+
);
|
|
11540
|
+
cur = pos >= 0 ? arr[pos] : arr[parseInt(marker[2], 10)];
|
|
11541
|
+
}
|
|
11542
|
+
return cur;
|
|
11543
|
+
}
|
|
11365
11544
|
/**
|
|
11366
11545
|
* Register an external action that will be displayed as a button
|
|
11367
11546
|
* External actions can be form-level (no related_field) or field-level (with related_field)
|
|
@@ -11401,21 +11580,10 @@ var FormBuilderInstance = class {
|
|
|
11401
11580
|
*/
|
|
11402
11581
|
findFormElementByFieldPath(fieldPath) {
|
|
11403
11582
|
if (!this.state.formRoot) return null;
|
|
11404
|
-
|
|
11583
|
+
const element = this.state.formRoot.querySelector(
|
|
11405
11584
|
`[name="${fieldPath}"]`
|
|
11406
11585
|
);
|
|
11407
11586
|
if (element) return element;
|
|
11408
|
-
const variations = [
|
|
11409
|
-
fieldPath,
|
|
11410
|
-
fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
|
|
11411
|
-
fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
|
|
11412
|
-
];
|
|
11413
|
-
for (const variation of variations) {
|
|
11414
|
-
element = this.state.formRoot.querySelector(
|
|
11415
|
-
`[name="${variation}"]`
|
|
11416
|
-
);
|
|
11417
|
-
if (element) return element;
|
|
11418
|
-
}
|
|
11419
11587
|
const schemaElement = this.findSchemaElement(fieldPath);
|
|
11420
11588
|
if (!schemaElement) return null;
|
|
11421
11589
|
const fieldWrappers = this.state.formRoot.querySelectorAll(".fb-field-wrapper");
|
|
@@ -11665,6 +11833,7 @@ var FormBuilderInstance = class {
|
|
|
11665
11833
|
return;
|
|
11666
11834
|
}
|
|
11667
11835
|
this.disconnectEnableIfObservers();
|
|
11836
|
+
this.removeTooltipElements();
|
|
11668
11837
|
this.state.formRoot = root;
|
|
11669
11838
|
this.state.schema = schema;
|
|
11670
11839
|
this.state.externalActions = actions || null;
|
|
@@ -11750,7 +11919,6 @@ var FormBuilderInstance = class {
|
|
|
11750
11919
|
};
|
|
11751
11920
|
setValidateElement(validateElement2);
|
|
11752
11921
|
this.state.schema.elements.forEach((element) => {
|
|
11753
|
-
var _a;
|
|
11754
11922
|
if (element.enableIf) {
|
|
11755
11923
|
try {
|
|
11756
11924
|
const shouldEnable = evaluateEnableCondition(element.enableIf, data);
|
|
@@ -11767,24 +11935,12 @@ var FormBuilderInstance = class {
|
|
|
11767
11935
|
if (element.type === "markdown") {
|
|
11768
11936
|
return;
|
|
11769
11937
|
}
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
);
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
data[element.key] = deserializeHiddenValue(raw);
|
|
11777
|
-
} else {
|
|
11778
|
-
data[element.key] = element.default !== void 0 ? element.default : null;
|
|
11779
|
-
}
|
|
11780
|
-
} else {
|
|
11781
|
-
const result = validateElement2(element, { path: "" });
|
|
11782
|
-
if (result.skip) return;
|
|
11783
|
-
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
11784
|
-
Object.assign(data, result.value);
|
|
11785
|
-
} else if (element.key) {
|
|
11786
|
-
data[element.key] = result.value;
|
|
11787
|
-
}
|
|
11938
|
+
const result = validateElement2(element, { path: "" });
|
|
11939
|
+
if (result.skip) return;
|
|
11940
|
+
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
11941
|
+
Object.assign(data, result.value);
|
|
11942
|
+
} else if (element.key) {
|
|
11943
|
+
data[element.key] = result.value;
|
|
11788
11944
|
}
|
|
11789
11945
|
});
|
|
11790
11946
|
return {
|
|
@@ -11988,29 +12144,29 @@ var FormBuilderInstance = class {
|
|
|
11988
12144
|
const formRoot = this.state.formRoot;
|
|
11989
12145
|
const lookupKey = getElementLookupKey(element, this.state);
|
|
11990
12146
|
if (!currentPath) {
|
|
11991
|
-
return formRoot
|
|
12147
|
+
return findOwnField(formRoot, lookupKey, null);
|
|
11992
12148
|
}
|
|
11993
12149
|
const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
|
|
11994
12150
|
if (pathMatch) {
|
|
11995
12151
|
const containerEl2 = formRoot.querySelector(
|
|
11996
12152
|
`[data-container-item="${pathMatch[1]}[${pathMatch[2]}]"]`
|
|
11997
12153
|
);
|
|
11998
|
-
return containerEl2 ? containerEl2
|
|
12154
|
+
return containerEl2 ? findOwnField(containerEl2, lookupKey, containerEl2) : null;
|
|
11999
12155
|
}
|
|
12000
12156
|
const containerEl = formRoot.querySelector(
|
|
12001
12157
|
`[data-container="${currentPath}"]`
|
|
12002
12158
|
);
|
|
12003
|
-
return containerEl ? containerEl
|
|
12159
|
+
return containerEl ? findOwnField(containerEl, lookupKey, containerEl) : null;
|
|
12004
12160
|
}
|
|
12005
12161
|
/**
|
|
12006
12162
|
* Apply enableIf show/hide logic to a single field wrapper.
|
|
12007
12163
|
* Extracted to reduce cyclomatic complexity of checkElements.
|
|
12008
12164
|
*/
|
|
12009
|
-
applyEnableIfVisibility(element, wrapper,
|
|
12165
|
+
applyEnableIfVisibility(element, wrapper, domPath, dataPath, fullDomPath, formData) {
|
|
12010
12166
|
var _a, _b, _c, _d;
|
|
12011
12167
|
try {
|
|
12012
12168
|
const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
|
|
12013
|
-
const containerData = scope === "relative" &&
|
|
12169
|
+
const containerData = scope === "relative" && dataPath ? getValueByPath(formData, dataPath) : void 0;
|
|
12014
12170
|
const shouldEnable = evaluateEnableCondition(
|
|
12015
12171
|
element.enableIf,
|
|
12016
12172
|
formData,
|
|
@@ -12018,10 +12174,12 @@ var FormBuilderInstance = class {
|
|
|
12018
12174
|
);
|
|
12019
12175
|
const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
|
|
12020
12176
|
if (shouldEnable && isCurrentlyDisabled) {
|
|
12021
|
-
const containerPrefill =
|
|
12177
|
+
const containerPrefill = dataPath ? getValueByPath(formData, dataPath) : formData;
|
|
12022
12178
|
const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
|
|
12023
12179
|
const newWrapper = renderElement2(element, {
|
|
12024
|
-
path:
|
|
12180
|
+
// DOM path: the re-rendered control's `name` must match the row it
|
|
12181
|
+
// lives in, not the row's position in the extracted array.
|
|
12182
|
+
path: domPath,
|
|
12025
12183
|
prefill: prefillContext,
|
|
12026
12184
|
formData,
|
|
12027
12185
|
state: this.state,
|
|
@@ -12041,7 +12199,7 @@ var FormBuilderInstance = class {
|
|
|
12041
12199
|
}
|
|
12042
12200
|
} catch (error) {
|
|
12043
12201
|
console.error(
|
|
12044
|
-
`Error re-evaluating enableIf for field "${(_d = element.key) != null ? _d : "<no key>"}" at path "${
|
|
12202
|
+
`Error re-evaluating enableIf for field "${(_d = element.key) != null ? _d : "<no key>"}" at path "${fullDomPath}":`,
|
|
12045
12203
|
error
|
|
12046
12204
|
);
|
|
12047
12205
|
}
|
|
@@ -12053,40 +12211,47 @@ var FormBuilderInstance = class {
|
|
|
12053
12211
|
reevaluateConditionalFields() {
|
|
12054
12212
|
if (!this.state.schema || !this.state.formRoot) return;
|
|
12055
12213
|
const formData = this.validateForm(true).data;
|
|
12056
|
-
const checkElements = (elements,
|
|
12214
|
+
const checkElements = (elements, domPath, dataPath) => {
|
|
12057
12215
|
elements.forEach((element) => {
|
|
12058
|
-
var _a
|
|
12059
|
-
const
|
|
12216
|
+
var _a;
|
|
12217
|
+
const key = (_a = element.key) != null ? _a : "";
|
|
12218
|
+
const fullDomPath = domPath ? `${domPath}.${key}` : key;
|
|
12219
|
+
const fullDataPath = dataPath ? `${dataPath}.${key}` : key;
|
|
12060
12220
|
if (element.enableIf) {
|
|
12061
|
-
const fieldWrapper = this.findFieldWrapper(element,
|
|
12221
|
+
const fieldWrapper = this.findFieldWrapper(element, domPath);
|
|
12062
12222
|
if (fieldWrapper) {
|
|
12063
12223
|
this.applyEnableIfVisibility(
|
|
12064
12224
|
element,
|
|
12065
12225
|
fieldWrapper,
|
|
12066
|
-
|
|
12067
|
-
|
|
12226
|
+
domPath,
|
|
12227
|
+
dataPath,
|
|
12228
|
+
fullDomPath,
|
|
12068
12229
|
formData
|
|
12069
12230
|
);
|
|
12070
12231
|
}
|
|
12071
12232
|
}
|
|
12072
12233
|
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
12073
|
-
const containerData = element.key ? getValueByPath(formData,
|
|
12234
|
+
const containerData = element.key ? getValueByPath(formData, fullDataPath) : void 0;
|
|
12074
12235
|
if (Array.isArray(containerData)) {
|
|
12075
12236
|
const directItems = findDirectContainerRows(
|
|
12076
12237
|
this.state.formRoot,
|
|
12077
|
-
|
|
12238
|
+
fullDomPath
|
|
12078
12239
|
);
|
|
12079
|
-
directItems.forEach((el) => {
|
|
12080
|
-
const
|
|
12081
|
-
checkElements(
|
|
12240
|
+
directItems.forEach((el, rowIndex) => {
|
|
12241
|
+
const marker = el.getAttribute("data-container-item") || "";
|
|
12242
|
+
checkElements(
|
|
12243
|
+
element.elements,
|
|
12244
|
+
marker,
|
|
12245
|
+
`${fullDataPath}[${rowIndex}]`
|
|
12246
|
+
);
|
|
12082
12247
|
});
|
|
12083
12248
|
} else {
|
|
12084
|
-
checkElements(element.elements,
|
|
12249
|
+
checkElements(element.elements, fullDomPath, fullDataPath);
|
|
12085
12250
|
}
|
|
12086
12251
|
}
|
|
12087
12252
|
});
|
|
12088
12253
|
};
|
|
12089
|
-
checkElements(this.state.schema.elements, "");
|
|
12254
|
+
checkElements(this.state.schema.elements, "", "");
|
|
12090
12255
|
}
|
|
12091
12256
|
/**
|
|
12092
12257
|
* Destroy instance and clean up resources
|
|
@@ -12098,6 +12263,7 @@ var FormBuilderInstance = class {
|
|
|
12098
12263
|
this.state.debounceTimer = null;
|
|
12099
12264
|
}
|
|
12100
12265
|
this.disconnectEnableIfObservers();
|
|
12266
|
+
this.removeTooltipElements();
|
|
12101
12267
|
this.state.resourceIndex.clear();
|
|
12102
12268
|
if (this.state.formRoot) {
|
|
12103
12269
|
clear(this.state.formRoot);
|
|
@@ -12115,6 +12281,12 @@ var FormBuilderInstance = class {
|
|
|
12115
12281
|
}
|
|
12116
12282
|
this.state.enableIfObservers.clear();
|
|
12117
12283
|
}
|
|
12284
|
+
removeTooltipElements() {
|
|
12285
|
+
for (const tooltip of this.state.tooltipElements) {
|
|
12286
|
+
tooltip.remove();
|
|
12287
|
+
}
|
|
12288
|
+
this.state.tooltipElements.clear();
|
|
12289
|
+
}
|
|
12118
12290
|
};
|
|
12119
12291
|
|
|
12120
12292
|
// src/index.ts
|