@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/esm/index.js
CHANGED
|
@@ -15,6 +15,69 @@ function t(key, state, params) {
|
|
|
15
15
|
return text;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// src/utils/helpers.ts
|
|
19
|
+
function isElementReadonly(element, state, ctx) {
|
|
20
|
+
return element.readonly === true || state.config.readonly === true || ctx?.inheritedReadonly === true;
|
|
21
|
+
}
|
|
22
|
+
function isPlainObject(obj) {
|
|
23
|
+
return obj && typeof obj === "object" && obj.constructor === Object;
|
|
24
|
+
}
|
|
25
|
+
function escapeHtml(text) {
|
|
26
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
27
|
+
}
|
|
28
|
+
function getElementLookupKey(element, state) {
|
|
29
|
+
if (element.key) {
|
|
30
|
+
return element.key;
|
|
31
|
+
}
|
|
32
|
+
const cached = state.syntheticElementIds.get(element);
|
|
33
|
+
if (cached !== void 0) {
|
|
34
|
+
return cached;
|
|
35
|
+
}
|
|
36
|
+
const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
|
|
37
|
+
state.syntheticElementIds.set(element, id);
|
|
38
|
+
return id;
|
|
39
|
+
}
|
|
40
|
+
function pathJoin(base, key) {
|
|
41
|
+
return base ? `${base}.${key}` : key;
|
|
42
|
+
}
|
|
43
|
+
function findDirectContainerRows(scopeRoot, containerPath) {
|
|
44
|
+
if (!containerPath) return [];
|
|
45
|
+
const all = scopeRoot.querySelectorAll("[data-container-item]");
|
|
46
|
+
return Array.from(all).filter((el) => {
|
|
47
|
+
const attr = el.getAttribute("data-container-item") || "";
|
|
48
|
+
if (!attr.startsWith(`${containerPath}[`)) return false;
|
|
49
|
+
return /^\[\d+\]$/.test(attr.slice(containerPath.length));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function clear(node) {
|
|
53
|
+
while (node.firstChild) node.removeChild(node.firstChild);
|
|
54
|
+
}
|
|
55
|
+
function formatFileSize(bytes) {
|
|
56
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
57
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
58
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
59
|
+
}
|
|
60
|
+
function serializeHiddenValue(value) {
|
|
61
|
+
if (value === null || value === void 0) return "";
|
|
62
|
+
return JSON.stringify(value);
|
|
63
|
+
}
|
|
64
|
+
function deserializeHiddenValue(raw) {
|
|
65
|
+
if (raw === "") return null;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(raw);
|
|
68
|
+
} catch {
|
|
69
|
+
return raw;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function createHiddenInput(name, value) {
|
|
73
|
+
const input = document.createElement("input");
|
|
74
|
+
input.type = "hidden";
|
|
75
|
+
input.name = name;
|
|
76
|
+
input.setAttribute("data-hidden-field", "true");
|
|
77
|
+
input.value = serializeHiddenValue(value);
|
|
78
|
+
return input;
|
|
79
|
+
}
|
|
80
|
+
|
|
18
81
|
// src/utils/validation.ts
|
|
19
82
|
function addLengthHint(element, parts, state) {
|
|
20
83
|
if (element.minLength != null || element.maxLength != null) {
|
|
@@ -184,6 +247,64 @@ function validateSchema(schema) {
|
|
|
184
247
|
}
|
|
185
248
|
}
|
|
186
249
|
}
|
|
250
|
+
function validateCountBounds(element, elementPath, errors2) {
|
|
251
|
+
const el = element;
|
|
252
|
+
if (el.type === "group") {
|
|
253
|
+
if (!isPlainObject(el.repeat)) return;
|
|
254
|
+
checkBounds(
|
|
255
|
+
elementPath,
|
|
256
|
+
el.repeat?.min,
|
|
257
|
+
el.repeat?.max,
|
|
258
|
+
"repeat.min",
|
|
259
|
+
"repeat.max",
|
|
260
|
+
el.required === true,
|
|
261
|
+
errors2
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const isMultiple = el.multiple === true || el.type === "files";
|
|
266
|
+
if (!isMultiple) return;
|
|
267
|
+
checkBounds(
|
|
268
|
+
elementPath,
|
|
269
|
+
el.minCount,
|
|
270
|
+
el.maxCount,
|
|
271
|
+
"minCount",
|
|
272
|
+
"maxCount",
|
|
273
|
+
el.required === true,
|
|
274
|
+
errors2
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
function checkBounds(elementPath, minCount, maxCount, minName, maxName, requiredImpliesFloor, errors2) {
|
|
278
|
+
for (const [name, bound] of [
|
|
279
|
+
[minName, minCount],
|
|
280
|
+
[maxName, maxCount]
|
|
281
|
+
]) {
|
|
282
|
+
if (bound !== void 0 && typeof bound !== "number") {
|
|
283
|
+
errors2.push(
|
|
284
|
+
`${elementPath}: ${name} must be a number (got ${typeof bound})`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const min = typeof minCount === "number" ? minCount : void 0;
|
|
289
|
+
const max = typeof maxCount === "number" ? maxCount : void 0;
|
|
290
|
+
if (max !== void 0 && (max < 0 || Number.isNaN(max))) {
|
|
291
|
+
errors2.push(
|
|
292
|
+
`${elementPath}: ${maxName} must be a non-negative number or Infinity (got ${max})`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (min !== void 0 && (min < 0 || !Number.isFinite(min))) {
|
|
296
|
+
errors2.push(
|
|
297
|
+
`${elementPath}: ${minName} must be a finite non-negative number (got ${min})`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
const effectiveMin = min ?? (requiredImpliesFloor ? 1 : void 0);
|
|
301
|
+
if (effectiveMin !== void 0 && max !== void 0 && effectiveMin > max) {
|
|
302
|
+
const shown = min !== void 0 ? `${minName} (${min})` : `required: true (implies ${minName} 1)`;
|
|
303
|
+
errors2.push(
|
|
304
|
+
`${elementPath}: ${shown} cannot be greater than ${maxName} (${max})`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
187
308
|
function validateElements(elements, path) {
|
|
188
309
|
elements.forEach((element, index) => {
|
|
189
310
|
const elementPath = `${path}[${index}]`;
|
|
@@ -193,6 +314,15 @@ function validateSchema(schema) {
|
|
|
193
314
|
if (!element.key && element.type !== "markdown") {
|
|
194
315
|
errors.push(`${elementPath}: missing key`);
|
|
195
316
|
}
|
|
317
|
+
validateCountBounds(element, elementPath, errors);
|
|
318
|
+
if (element.type === "number" && "decimals" in element) {
|
|
319
|
+
const decimals = element.decimals;
|
|
320
|
+
if (decimals !== void 0 && (!Number.isInteger(decimals) || decimals < 0)) {
|
|
321
|
+
errors.push(
|
|
322
|
+
`${elementPath}: decimals must be a non-negative integer (got ${JSON.stringify(decimals)})`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
196
326
|
if (element.type === "markdown") {
|
|
197
327
|
const content = element.content;
|
|
198
328
|
if (typeof content !== "string") {
|
|
@@ -273,71 +403,6 @@ function validateSchema(schema) {
|
|
|
273
403
|
return errors;
|
|
274
404
|
}
|
|
275
405
|
|
|
276
|
-
// src/utils/helpers.ts
|
|
277
|
-
function isElementReadonly(element, state, ctx) {
|
|
278
|
-
return element.readonly === true || state.config.readonly === true || ctx?.inheritedReadonly === true;
|
|
279
|
-
}
|
|
280
|
-
function isPlainObject(obj) {
|
|
281
|
-
return obj && typeof obj === "object" && obj.constructor === Object;
|
|
282
|
-
}
|
|
283
|
-
function escapeHtml(text) {
|
|
284
|
-
const div = document.createElement("div");
|
|
285
|
-
div.textContent = text;
|
|
286
|
-
return div.innerHTML;
|
|
287
|
-
}
|
|
288
|
-
function getElementLookupKey(element, state) {
|
|
289
|
-
if (element.key) {
|
|
290
|
-
return element.key;
|
|
291
|
-
}
|
|
292
|
-
const cached = state.syntheticElementIds.get(element);
|
|
293
|
-
if (cached !== void 0) {
|
|
294
|
-
return cached;
|
|
295
|
-
}
|
|
296
|
-
const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
|
|
297
|
-
state.syntheticElementIds.set(element, id);
|
|
298
|
-
return id;
|
|
299
|
-
}
|
|
300
|
-
function pathJoin(base, key) {
|
|
301
|
-
return base ? `${base}.${key}` : key;
|
|
302
|
-
}
|
|
303
|
-
function findDirectContainerRows(scopeRoot, containerPath) {
|
|
304
|
-
if (!containerPath) return [];
|
|
305
|
-
const all = scopeRoot.querySelectorAll("[data-container-item]");
|
|
306
|
-
return Array.from(all).filter((el) => {
|
|
307
|
-
const attr = el.getAttribute("data-container-item") || "";
|
|
308
|
-
if (!attr.startsWith(`${containerPath}[`)) return false;
|
|
309
|
-
return /^\[\d+\]$/.test(attr.slice(containerPath.length));
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
function clear(node) {
|
|
313
|
-
while (node.firstChild) node.removeChild(node.firstChild);
|
|
314
|
-
}
|
|
315
|
-
function formatFileSize(bytes) {
|
|
316
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
317
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
318
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
319
|
-
}
|
|
320
|
-
function serializeHiddenValue(value) {
|
|
321
|
-
if (value === null || value === void 0) return "";
|
|
322
|
-
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
323
|
-
}
|
|
324
|
-
function deserializeHiddenValue(raw) {
|
|
325
|
-
if (raw === "") return null;
|
|
326
|
-
try {
|
|
327
|
-
return JSON.parse(raw);
|
|
328
|
-
} catch {
|
|
329
|
-
return raw;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
function createHiddenInput(name, value) {
|
|
333
|
-
const input = document.createElement("input");
|
|
334
|
-
input.type = "hidden";
|
|
335
|
-
input.name = name;
|
|
336
|
-
input.setAttribute("data-hidden-field", "true");
|
|
337
|
-
input.value = serializeHiddenValue(value);
|
|
338
|
-
return input;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
406
|
// src/utils/enable-conditions.ts
|
|
342
407
|
function getValueByPath(data, path) {
|
|
343
408
|
if (!data || typeof data !== "object") {
|
|
@@ -884,7 +949,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
884
949
|
overflow-wrap: anywhere;
|
|
885
950
|
`;
|
|
886
951
|
textInput.name = pathKey;
|
|
887
|
-
textInput.placeholder = element.placeholder ?? "
|
|
952
|
+
textInput.placeholder = element.placeholder ?? t("placeholderText", state);
|
|
888
953
|
textInput.value = ctx.prefill[element.key] || element.default || "";
|
|
889
954
|
textInput.readOnly = readonly;
|
|
890
955
|
applySingleLineMode(textInput);
|
|
@@ -996,6 +1061,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
996
1061
|
updateIndices();
|
|
997
1062
|
updateAddButton();
|
|
998
1063
|
updateRemoveButtons();
|
|
1064
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
999
1065
|
};
|
|
1000
1066
|
chip.appendChild(rem);
|
|
1001
1067
|
}
|
|
@@ -1020,6 +1086,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
1020
1086
|
addChip(element.default || "");
|
|
1021
1087
|
updateAddButton();
|
|
1022
1088
|
updateRemoveButtons();
|
|
1089
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1023
1090
|
},
|
|
1024
1091
|
{ label: element.addLabel }
|
|
1025
1092
|
);
|
|
@@ -1133,7 +1200,7 @@ function validateTextElement(element, key, context) {
|
|
|
1133
1200
|
}
|
|
1134
1201
|
return { value: values, errors };
|
|
1135
1202
|
} else {
|
|
1136
|
-
const input = scopeRoot.querySelector(`[name
|
|
1203
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1137
1204
|
const val = input?.value ?? "";
|
|
1138
1205
|
if (!skipValidation && element.required && val === "") {
|
|
1139
1206
|
const msg = t("required", context.state);
|
|
@@ -1303,6 +1370,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1303
1370
|
updateIndices();
|
|
1304
1371
|
updateAddButton();
|
|
1305
1372
|
updateRemoveButtons();
|
|
1373
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1306
1374
|
}
|
|
1307
1375
|
};
|
|
1308
1376
|
item.appendChild(removeBtn);
|
|
@@ -1322,6 +1390,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1322
1390
|
addTextareaItem(element.default || "");
|
|
1323
1391
|
updateAddButton();
|
|
1324
1392
|
updateRemoveButtons();
|
|
1393
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1325
1394
|
},
|
|
1326
1395
|
{ label: element.addLabel }
|
|
1327
1396
|
);
|
|
@@ -1613,6 +1682,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1613
1682
|
updateIndices();
|
|
1614
1683
|
updateAddButton();
|
|
1615
1684
|
updateRemoveButtons();
|
|
1685
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1616
1686
|
}
|
|
1617
1687
|
};
|
|
1618
1688
|
item.appendChild(removeBtn);
|
|
@@ -1632,6 +1702,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1632
1702
|
addNumberItem(element.default || "");
|
|
1633
1703
|
updateAddButton();
|
|
1634
1704
|
updateRemoveButtons();
|
|
1705
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1635
1706
|
},
|
|
1636
1707
|
{ label: element.addLabel }
|
|
1637
1708
|
);
|
|
@@ -1720,8 +1791,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1720
1791
|
return;
|
|
1721
1792
|
}
|
|
1722
1793
|
validateNumberInput(input, v, `${key}[${index}]`);
|
|
1723
|
-
|
|
1724
|
-
values.push(Number(v.toFixed(d)));
|
|
1794
|
+
values.push(applyDecimals(v, element.decimals));
|
|
1725
1795
|
});
|
|
1726
1796
|
if (!skipValidation) {
|
|
1727
1797
|
const { state } = context;
|
|
@@ -1740,7 +1810,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1740
1810
|
}
|
|
1741
1811
|
return { value: values, errors };
|
|
1742
1812
|
} else {
|
|
1743
|
-
const input = scopeRoot.querySelector(`[name
|
|
1813
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1744
1814
|
const raw = input?.value ?? "";
|
|
1745
1815
|
const { state } = context;
|
|
1746
1816
|
if (!skipValidation && element.required && raw === "") {
|
|
@@ -1761,10 +1831,13 @@ function validateNumberElement(element, key, context) {
|
|
|
1761
1831
|
return { value: null, errors };
|
|
1762
1832
|
}
|
|
1763
1833
|
validateNumberInput(input, v, key);
|
|
1764
|
-
|
|
1765
|
-
return { value: Number(v.toFixed(d)), errors };
|
|
1834
|
+
return { value: applyDecimals(v, element.decimals), errors };
|
|
1766
1835
|
}
|
|
1767
1836
|
}
|
|
1837
|
+
function applyDecimals(v, decimals) {
|
|
1838
|
+
if (!Number.isInteger(decimals) || decimals < 0) return v;
|
|
1839
|
+
return Number(v.toFixed(decimals));
|
|
1840
|
+
}
|
|
1768
1841
|
function updateNumberField(element, fieldPath, value, context) {
|
|
1769
1842
|
const { scopeRoot } = context;
|
|
1770
1843
|
if (element.multiple) {
|
|
@@ -1917,6 +1990,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1917
1990
|
updateIndices();
|
|
1918
1991
|
updateAddButton();
|
|
1919
1992
|
updateRemoveButtons();
|
|
1993
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1920
1994
|
}
|
|
1921
1995
|
};
|
|
1922
1996
|
item.appendChild(removeBtn);
|
|
@@ -1937,6 +2011,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1937
2011
|
addSelectItem(defaultValue);
|
|
1938
2012
|
updateAddButton();
|
|
1939
2013
|
updateRemoveButtons();
|
|
2014
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1940
2015
|
},
|
|
1941
2016
|
{ label: element.addLabel }
|
|
1942
2017
|
);
|
|
@@ -2022,7 +2097,7 @@ function validateSelectElement(element, key, context) {
|
|
|
2022
2097
|
return { value: values, errors };
|
|
2023
2098
|
} else {
|
|
2024
2099
|
const input = scopeRoot.querySelector(
|
|
2025
|
-
`[name
|
|
2100
|
+
`[name="${key}"]`
|
|
2026
2101
|
);
|
|
2027
2102
|
const val = input?.value ?? "";
|
|
2028
2103
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -2354,6 +2429,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2354
2429
|
updateIndices();
|
|
2355
2430
|
updateAddButton();
|
|
2356
2431
|
updateRemoveButtons();
|
|
2432
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
2357
2433
|
}
|
|
2358
2434
|
};
|
|
2359
2435
|
item.appendChild(removeBtn);
|
|
@@ -2374,6 +2450,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2374
2450
|
addSwitcherItem(defaultValue);
|
|
2375
2451
|
updateAddButton();
|
|
2376
2452
|
updateRemoveButtons();
|
|
2453
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
2377
2454
|
},
|
|
2378
2455
|
{ label: element.addLabel }
|
|
2379
2456
|
);
|
|
@@ -2468,7 +2545,7 @@ function validateSwitcherElement(element, key, context) {
|
|
|
2468
2545
|
return { value: values, errors };
|
|
2469
2546
|
} else {
|
|
2470
2547
|
const input = scopeRoot.querySelector(
|
|
2471
|
-
`input[type="hidden"][name
|
|
2548
|
+
`input[type="hidden"][name="${key}"]`
|
|
2472
2549
|
);
|
|
2473
2550
|
const val = input?.value ?? "";
|
|
2474
2551
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -5510,7 +5587,7 @@ function validateSingleFile(element, key, context) {
|
|
|
5510
5587
|
const { scopeRoot, skipValidation, state } = context;
|
|
5511
5588
|
const errors = [];
|
|
5512
5589
|
const input = scopeRoot.querySelector(
|
|
5513
|
-
`input[name
|
|
5590
|
+
`input[name="${key}"][type="hidden"]`
|
|
5514
5591
|
);
|
|
5515
5592
|
const rid = input?.value ?? "";
|
|
5516
5593
|
if (!skipValidation && element.required && rid === "") {
|
|
@@ -5942,6 +6019,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
5942
6019
|
updateIndices();
|
|
5943
6020
|
updateAddButton();
|
|
5944
6021
|
updateRemoveButtons();
|
|
6022
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
5945
6023
|
}
|
|
5946
6024
|
};
|
|
5947
6025
|
item.appendChild(removeBtn);
|
|
@@ -5962,6 +6040,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
5962
6040
|
addColourItem(defaultColour);
|
|
5963
6041
|
updateAddButton();
|
|
5964
6042
|
updateRemoveButtons();
|
|
6043
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
5965
6044
|
},
|
|
5966
6045
|
{ label: element.addLabel }
|
|
5967
6046
|
);
|
|
@@ -6398,6 +6477,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6398
6477
|
updateIndices();
|
|
6399
6478
|
updateAddButton();
|
|
6400
6479
|
updateRemoveButtons();
|
|
6480
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6401
6481
|
}
|
|
6402
6482
|
};
|
|
6403
6483
|
item.appendChild(removeBtn);
|
|
@@ -6417,6 +6497,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6417
6497
|
addSliderItem(defaultValue);
|
|
6418
6498
|
updateAddButton();
|
|
6419
6499
|
updateRemoveButtons();
|
|
6500
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6420
6501
|
},
|
|
6421
6502
|
{ label: element.addLabel }
|
|
6422
6503
|
);
|
|
@@ -6684,6 +6765,8 @@ function extractRootFormData(formRoot) {
|
|
|
6684
6765
|
if (input.checked) {
|
|
6685
6766
|
data[fieldName] = input.value;
|
|
6686
6767
|
}
|
|
6768
|
+
} else if (input.dataset.hiddenField) {
|
|
6769
|
+
data[fieldName] = deserializeHiddenValue(input.value);
|
|
6687
6770
|
} else {
|
|
6688
6771
|
data[fieldName] = input.value;
|
|
6689
6772
|
}
|
|
@@ -6772,11 +6855,14 @@ function getChildWrapperClass(columns) {
|
|
|
6772
6855
|
const cols = columns || 1;
|
|
6773
6856
|
return cols === 1 ? "fb-row" : `grid grid-cols-${cols} gap-2`;
|
|
6774
6857
|
}
|
|
6775
|
-
function mountRemoveButton(item, onRemove, state) {
|
|
6858
|
+
function mountRemoveButton(item, onRemove, state, containerLabel) {
|
|
6776
6859
|
const rem = document.createElement("button");
|
|
6777
6860
|
rem.type = "button";
|
|
6778
6861
|
rem.className = "fb-item-remove";
|
|
6779
|
-
rem.setAttribute(
|
|
6862
|
+
rem.setAttribute(
|
|
6863
|
+
"aria-label",
|
|
6864
|
+
containerLabel ? t("removeRowFrom", state, { label: containerLabel }) : t("removeRow", state)
|
|
6865
|
+
);
|
|
6780
6866
|
rem.style.cssText = `
|
|
6781
6867
|
width: 24px;
|
|
6782
6868
|
height: 24px;
|
|
@@ -6799,7 +6885,7 @@ function mountRemoveButton(item, onRemove, state) {
|
|
|
6799
6885
|
item.classList.add("fb-row-removable");
|
|
6800
6886
|
item.insertBefore(rem, item.firstChild);
|
|
6801
6887
|
}
|
|
6802
|
-
function renderMultipleContainerElement(element, ctx, wrapper,
|
|
6888
|
+
function renderMultipleContainerElement(element, ctx, wrapper, pathKey) {
|
|
6803
6889
|
const state = ctx.state;
|
|
6804
6890
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
6805
6891
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
@@ -6832,6 +6918,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6832
6918
|
if (countItems() <= min) return;
|
|
6833
6919
|
item.remove();
|
|
6834
6920
|
updateAddButton();
|
|
6921
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6835
6922
|
};
|
|
6836
6923
|
const createContainerItem = (idx, rowPrefill, formData) => {
|
|
6837
6924
|
const subCtx = {
|
|
@@ -6864,7 +6951,12 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6864
6951
|
});
|
|
6865
6952
|
item.appendChild(childWrapper);
|
|
6866
6953
|
if (!containerIsReadonly) {
|
|
6867
|
-
mountRemoveButton(
|
|
6954
|
+
mountRemoveButton(
|
|
6955
|
+
item,
|
|
6956
|
+
() => handleRemoveItem(item),
|
|
6957
|
+
state,
|
|
6958
|
+
element.label
|
|
6959
|
+
);
|
|
6868
6960
|
}
|
|
6869
6961
|
return item;
|
|
6870
6962
|
};
|
|
@@ -6883,6 +6975,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6883
6975
|
itemsWrap.appendChild(item);
|
|
6884
6976
|
}
|
|
6885
6977
|
updateAddButton();
|
|
6978
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6886
6979
|
};
|
|
6887
6980
|
let slideAddTile = null;
|
|
6888
6981
|
let slideAddUpdate = null;
|
|
@@ -7027,7 +7120,7 @@ function validateContainerElement(element, key, context) {
|
|
|
7027
7120
|
);
|
|
7028
7121
|
if (childResult.spread && childResult.value !== null && typeof childResult.value === "object") {
|
|
7029
7122
|
Object.assign(itemData, childResult.value);
|
|
7030
|
-
} else {
|
|
7123
|
+
} else if (!childResult.skip && child.key) {
|
|
7031
7124
|
itemData[child.key] = childResult.value;
|
|
7032
7125
|
}
|
|
7033
7126
|
});
|
|
@@ -7068,7 +7161,7 @@ function validateContainerElement(element, key, context) {
|
|
|
7068
7161
|
);
|
|
7069
7162
|
if (childResult.spread && childResult.value !== null && typeof childResult.value === "object") {
|
|
7070
7163
|
Object.assign(containerData, childResult.value);
|
|
7071
|
-
} else {
|
|
7164
|
+
} else if (!childResult.skip && child.key) {
|
|
7072
7165
|
containerData[child.key] = childResult.value;
|
|
7073
7166
|
}
|
|
7074
7167
|
}
|
|
@@ -7088,11 +7181,13 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
7088
7181
|
);
|
|
7089
7182
|
return;
|
|
7090
7183
|
}
|
|
7184
|
+
const rows = findDirectContainerRows(scopeRoot, fieldPath);
|
|
7091
7185
|
value.forEach((itemValue, index) => {
|
|
7092
|
-
|
|
7186
|
+
const rowPath = rows[index]?.getAttribute("data-container-item");
|
|
7187
|
+
if (isPlainObject(itemValue) && rowPath) {
|
|
7093
7188
|
element.elements.forEach((childElement) => {
|
|
7094
7189
|
if (childElement.type === "markdown" || !childElement.key) return;
|
|
7095
|
-
const childPath = `${
|
|
7190
|
+
const childPath = `${rowPath}.${childElement.key}`;
|
|
7096
7191
|
if (childElement.type === "richinput" && childElement.flatOutput) {
|
|
7097
7192
|
const richChild = childElement;
|
|
7098
7193
|
const textKey = richChild.textKey ?? "text";
|
|
@@ -7115,10 +7210,9 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
7115
7210
|
});
|
|
7116
7211
|
}
|
|
7117
7212
|
});
|
|
7118
|
-
|
|
7119
|
-
if (value.length !== existingContainers.length) {
|
|
7213
|
+
if (value.length !== rows.length) {
|
|
7120
7214
|
console.warn(
|
|
7121
|
-
`updateContainerField: Multiple container field "${fieldPath}"
|
|
7215
|
+
`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.`
|
|
7122
7216
|
);
|
|
7123
7217
|
}
|
|
7124
7218
|
} else {
|
|
@@ -7175,7 +7269,7 @@ function renderGroupElement(element, ctx, wrapper, pathKey) {
|
|
|
7175
7269
|
maxCount: element.repeat?.max
|
|
7176
7270
|
};
|
|
7177
7271
|
if (containerElement.multiple) {
|
|
7178
|
-
renderMultipleContainerElement(containerElement, ctx, wrapper);
|
|
7272
|
+
renderMultipleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7179
7273
|
} else {
|
|
7180
7274
|
renderSingleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7181
7275
|
}
|
|
@@ -10172,15 +10266,19 @@ var componentRegistry = {
|
|
|
10172
10266
|
function getComponentOperations(elementType) {
|
|
10173
10267
|
return componentRegistry[elementType] || null;
|
|
10174
10268
|
}
|
|
10269
|
+
function resolveOperations(element) {
|
|
10270
|
+
const isHiddenField = element.type !== "markdown" && (element.type === "hidden" || Boolean(element.hidden));
|
|
10271
|
+
return isHiddenField ? componentRegistry.hidden : getComponentOperations(element.type);
|
|
10272
|
+
}
|
|
10175
10273
|
function validateElementWithComponent(element, key, context) {
|
|
10176
|
-
const ops =
|
|
10274
|
+
const ops = resolveOperations(element);
|
|
10177
10275
|
if (ops && ops.validate) {
|
|
10178
10276
|
return ops.validate(element, key, context);
|
|
10179
10277
|
}
|
|
10180
10278
|
return null;
|
|
10181
10279
|
}
|
|
10182
10280
|
function updateElementWithComponent(element, fieldPath, value, context) {
|
|
10183
|
-
const ops =
|
|
10281
|
+
const ops = resolveOperations(element);
|
|
10184
10282
|
if (ops && ops.update) {
|
|
10185
10283
|
ops.update(element, fieldPath, value, context);
|
|
10186
10284
|
return true;
|
|
@@ -10192,6 +10290,10 @@ function updateElementWithComponent(element, fieldPath, value, context) {
|
|
|
10192
10290
|
function showTooltip(tooltipId, button) {
|
|
10193
10291
|
const tooltip = document.getElementById(tooltipId);
|
|
10194
10292
|
if (!tooltip) return;
|
|
10293
|
+
if (!button.isConnected) {
|
|
10294
|
+
tooltip.remove();
|
|
10295
|
+
return;
|
|
10296
|
+
}
|
|
10195
10297
|
const isCurrentlyVisible = !tooltip.classList.contains("hidden");
|
|
10196
10298
|
document.querySelectorAll('[id^="tooltip-"]').forEach((t2) => {
|
|
10197
10299
|
t2.classList.add("hidden");
|
|
@@ -10281,6 +10383,8 @@ function extractDOMValue(fieldPath, formRoot) {
|
|
|
10281
10383
|
`[name="${fieldPath}"]:checked`
|
|
10282
10384
|
);
|
|
10283
10385
|
return checked ? checked.value : void 0;
|
|
10386
|
+
} else if (input.dataset.hiddenField) {
|
|
10387
|
+
return deserializeHiddenValue(input.value);
|
|
10284
10388
|
} else {
|
|
10285
10389
|
return input.value;
|
|
10286
10390
|
}
|
|
@@ -10416,7 +10520,7 @@ function createFieldLabel(element) {
|
|
|
10416
10520
|
}
|
|
10417
10521
|
return title;
|
|
10418
10522
|
}
|
|
10419
|
-
function createInfoButton(element) {
|
|
10523
|
+
function createInfoButton(element, state) {
|
|
10420
10524
|
const infoBtn = document.createElement("button");
|
|
10421
10525
|
infoBtn.type = "button";
|
|
10422
10526
|
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
@@ -10428,6 +10532,7 @@ function createInfoButton(element) {
|
|
|
10428
10532
|
tooltip.style.position = "fixed";
|
|
10429
10533
|
tooltip.textContent = element.description || element.hint || "Field information";
|
|
10430
10534
|
document.body.appendChild(tooltip);
|
|
10535
|
+
state.tooltipElements.add(tooltip);
|
|
10431
10536
|
infoBtn.onclick = (e) => {
|
|
10432
10537
|
e.preventDefault();
|
|
10433
10538
|
e.stopPropagation();
|
|
@@ -10435,7 +10540,7 @@ function createInfoButton(element) {
|
|
|
10435
10540
|
};
|
|
10436
10541
|
return infoBtn;
|
|
10437
10542
|
}
|
|
10438
|
-
function createLabelContainer(element) {
|
|
10543
|
+
function createLabelContainer(element, state) {
|
|
10439
10544
|
const label = document.createElement("div");
|
|
10440
10545
|
label.className = "flex items-center";
|
|
10441
10546
|
label.style.marginBottom = "var(--fb-label-margin-bottom, 2px)";
|
|
@@ -10443,7 +10548,7 @@ function createLabelContainer(element) {
|
|
|
10443
10548
|
const title = createFieldLabel(element);
|
|
10444
10549
|
label.appendChild(title);
|
|
10445
10550
|
if (element.description || element.hint) {
|
|
10446
|
-
const infoBtn = createInfoButton(element);
|
|
10551
|
+
const infoBtn = createInfoButton(element, state);
|
|
10447
10552
|
label.appendChild(infoBtn);
|
|
10448
10553
|
}
|
|
10449
10554
|
return label;
|
|
@@ -10518,7 +10623,7 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
10518
10623
|
break;
|
|
10519
10624
|
case "container":
|
|
10520
10625
|
if (isMultiple) {
|
|
10521
|
-
renderMultipleContainerElement(element, ctx, wrapper);
|
|
10626
|
+
renderMultipleContainerElement(element, ctx, wrapper, pathKey);
|
|
10522
10627
|
} else {
|
|
10523
10628
|
renderSingleContainerElement(element, ctx, wrapper, pathKey);
|
|
10524
10629
|
}
|
|
@@ -10571,7 +10676,7 @@ function renderElement2(element, ctx) {
|
|
|
10571
10676
|
wrapper.setAttribute("data-fb-width", element.width || "full");
|
|
10572
10677
|
const ops = getComponentOperations(element.type);
|
|
10573
10678
|
if (!ops?.ownsLabel) {
|
|
10574
|
-
const label = createLabelContainer(element);
|
|
10679
|
+
const label = createLabelContainer(element, ctx.state);
|
|
10575
10680
|
wrapper.appendChild(label);
|
|
10576
10681
|
}
|
|
10577
10682
|
const pathKey = pathJoin(ctx.path, element.key);
|
|
@@ -10610,6 +10715,8 @@ var defaultConfig = {
|
|
|
10610
10715
|
en: {
|
|
10611
10716
|
// UI texts
|
|
10612
10717
|
removeElement: "Remove",
|
|
10718
|
+
removeRow: "Remove row",
|
|
10719
|
+
removeRowFrom: "Remove row from {label}",
|
|
10613
10720
|
clickDragText: "Click or drag file",
|
|
10614
10721
|
clickDragTextMultiple: "Click or drag files",
|
|
10615
10722
|
noFileSelected: "No file selected",
|
|
@@ -10683,6 +10790,8 @@ var defaultConfig = {
|
|
|
10683
10790
|
ru: {
|
|
10684
10791
|
// UI texts
|
|
10685
10792
|
removeElement: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
|
|
10793
|
+
removeRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
|
|
10794
|
+
removeRowFrom: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443 \u0438\u0437 \xAB{label}\xBB",
|
|
10686
10795
|
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",
|
|
10687
10796
|
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",
|
|
10688
10797
|
noFileSelected: "\u0424\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D",
|
|
@@ -10785,7 +10894,8 @@ function createInstanceState(config) {
|
|
|
10785
10894
|
prefill: {},
|
|
10786
10895
|
syntheticElementIds: /* @__PURE__ */ new WeakMap(),
|
|
10787
10896
|
syntheticElementIdCounter: 0,
|
|
10788
|
-
enableIfObservers: /* @__PURE__ */ new Set()
|
|
10897
|
+
enableIfObservers: /* @__PURE__ */ new Set(),
|
|
10898
|
+
tooltipElements: /* @__PURE__ */ new Set()
|
|
10789
10899
|
};
|
|
10790
10900
|
}
|
|
10791
10901
|
function generateInstanceId() {
|
|
@@ -11072,6 +11182,18 @@ var exampleThemes = {
|
|
|
11072
11182
|
};
|
|
11073
11183
|
|
|
11074
11184
|
// src/instance/FormBuilderInstance.ts
|
|
11185
|
+
function findOwnField(scope, lookupKey, ownBoundary) {
|
|
11186
|
+
const matches = scope.querySelectorAll(
|
|
11187
|
+
`[data-field-key="${lookupKey}"]`
|
|
11188
|
+
);
|
|
11189
|
+
for (const el of Array.from(matches)) {
|
|
11190
|
+
const boundary = el.closest(
|
|
11191
|
+
"[data-container-item], [data-container]"
|
|
11192
|
+
);
|
|
11193
|
+
if (boundary === ownBoundary) return el;
|
|
11194
|
+
}
|
|
11195
|
+
return null;
|
|
11196
|
+
}
|
|
11075
11197
|
var FormBuilderInstance = class {
|
|
11076
11198
|
constructor(config) {
|
|
11077
11199
|
this.instanceId = generateInstanceId();
|
|
@@ -11153,7 +11275,11 @@ var FormBuilderInstance = class {
|
|
|
11153
11275
|
/**
|
|
11154
11276
|
* Trigger onChange callbacks with debouncing
|
|
11155
11277
|
* @param fieldPath - Optional field path for field-specific change events
|
|
11156
|
-
* @param fieldValue - Optional field value for field-specific change events
|
|
11278
|
+
* @param fieldValue - Optional field value for field-specific change events.
|
|
11279
|
+
* When omitted while fieldPath is given, the value is read from the
|
|
11280
|
+
* freshly extracted form data at debounce time — used by structural
|
|
11281
|
+
* changes (multi-item add/remove), where the handler has no cheap
|
|
11282
|
+
* current value but the array is trivially derivable after the fact.
|
|
11157
11283
|
*/
|
|
11158
11284
|
triggerOnChange(fieldPath, fieldValue) {
|
|
11159
11285
|
if (this.state.config.readonly) return;
|
|
@@ -11166,12 +11292,49 @@ var FormBuilderInstance = class {
|
|
|
11166
11292
|
if (this.state.config.onChange) {
|
|
11167
11293
|
this.state.config.onChange(formData);
|
|
11168
11294
|
}
|
|
11169
|
-
if (this.state.config.onFieldChange && fieldPath !== void 0
|
|
11170
|
-
this.
|
|
11295
|
+
if (this.state.config.onFieldChange && fieldPath !== void 0) {
|
|
11296
|
+
const resolvedValue = fieldValue !== void 0 ? fieldValue : this.resolveDomPathValue(formData.data, fieldPath);
|
|
11297
|
+
this.state.config.onFieldChange(fieldPath, resolvedValue, formData);
|
|
11171
11298
|
}
|
|
11172
11299
|
this.state.debounceTimer = null;
|
|
11173
11300
|
}, this.state.config.debounceMs);
|
|
11174
11301
|
}
|
|
11302
|
+
/**
|
|
11303
|
+
* Resolve a DOM field path against the extracted form data.
|
|
11304
|
+
*
|
|
11305
|
+
* A plain getValueByPath is wrong for paths inside a multiple container:
|
|
11306
|
+
* row markers keep gaps after a deletion (`s[2]` may be the first surviving
|
|
11307
|
+
* row) while the extracted array is re-packed contiguously — the naive
|
|
11308
|
+
* lookup would read a different row, or nothing. Each `[N]` segment is
|
|
11309
|
+
* mapped from its marker to the row's position among the container's
|
|
11310
|
+
* rendered rows, the same DOM order extraction used to build the array.
|
|
11311
|
+
* A bracketed segment that is not a container marker (a multi-value leaf
|
|
11312
|
+
* like `tags[1]`, whose indices are contiguous) falls back to the index.
|
|
11313
|
+
*/
|
|
11314
|
+
resolveDomPathValue(data, domPath) {
|
|
11315
|
+
let cur = data;
|
|
11316
|
+
let domPrefix = "";
|
|
11317
|
+
for (const seg of domPath.split(".")) {
|
|
11318
|
+
if (cur === null || cur === void 0) return void 0;
|
|
11319
|
+
const marker = seg.match(/^(.+)\[(\d+)\]$/);
|
|
11320
|
+
if (!marker) {
|
|
11321
|
+
domPrefix = domPrefix ? `${domPrefix}.${seg}` : seg;
|
|
11322
|
+
cur = cur[seg];
|
|
11323
|
+
continue;
|
|
11324
|
+
}
|
|
11325
|
+
const key = marker[1];
|
|
11326
|
+
domPrefix = domPrefix ? `${domPrefix}.${key}` : key;
|
|
11327
|
+
const arr = cur[key];
|
|
11328
|
+
if (!Array.isArray(arr)) return void 0;
|
|
11329
|
+
const rows = this.state.formRoot ? findDirectContainerRows(this.state.formRoot, domPrefix) : [];
|
|
11330
|
+
domPrefix = `${domPrefix}[${marker[2]}]`;
|
|
11331
|
+
const pos = rows.findIndex(
|
|
11332
|
+
(row) => row.getAttribute("data-container-item") === domPrefix
|
|
11333
|
+
);
|
|
11334
|
+
cur = pos >= 0 ? arr[pos] : arr[parseInt(marker[2], 10)];
|
|
11335
|
+
}
|
|
11336
|
+
return cur;
|
|
11337
|
+
}
|
|
11175
11338
|
/**
|
|
11176
11339
|
* Register an external action that will be displayed as a button
|
|
11177
11340
|
* External actions can be form-level (no related_field) or field-level (with related_field)
|
|
@@ -11211,21 +11374,10 @@ var FormBuilderInstance = class {
|
|
|
11211
11374
|
*/
|
|
11212
11375
|
findFormElementByFieldPath(fieldPath) {
|
|
11213
11376
|
if (!this.state.formRoot) return null;
|
|
11214
|
-
|
|
11377
|
+
const element = this.state.formRoot.querySelector(
|
|
11215
11378
|
`[name="${fieldPath}"]`
|
|
11216
11379
|
);
|
|
11217
11380
|
if (element) return element;
|
|
11218
|
-
const variations = [
|
|
11219
|
-
fieldPath,
|
|
11220
|
-
fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
|
|
11221
|
-
fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
|
|
11222
|
-
];
|
|
11223
|
-
for (const variation of variations) {
|
|
11224
|
-
element = this.state.formRoot.querySelector(
|
|
11225
|
-
`[name="${variation}"]`
|
|
11226
|
-
);
|
|
11227
|
-
if (element) return element;
|
|
11228
|
-
}
|
|
11229
11381
|
const schemaElement = this.findSchemaElement(fieldPath);
|
|
11230
11382
|
if (!schemaElement) return null;
|
|
11231
11383
|
const fieldWrappers = this.state.formRoot.querySelectorAll(".fb-field-wrapper");
|
|
@@ -11475,6 +11627,7 @@ var FormBuilderInstance = class {
|
|
|
11475
11627
|
return;
|
|
11476
11628
|
}
|
|
11477
11629
|
this.disconnectEnableIfObservers();
|
|
11630
|
+
this.removeTooltipElements();
|
|
11478
11631
|
this.state.formRoot = root;
|
|
11479
11632
|
this.state.schema = schema;
|
|
11480
11633
|
this.state.externalActions = actions || null;
|
|
@@ -11574,24 +11727,12 @@ var FormBuilderInstance = class {
|
|
|
11574
11727
|
if (element.type === "markdown") {
|
|
11575
11728
|
return;
|
|
11576
11729
|
}
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
);
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
data[element.key] = deserializeHiddenValue(raw);
|
|
11584
|
-
} else {
|
|
11585
|
-
data[element.key] = element.default !== void 0 ? element.default : null;
|
|
11586
|
-
}
|
|
11587
|
-
} else {
|
|
11588
|
-
const result = validateElement2(element, { path: "" });
|
|
11589
|
-
if (result.skip) return;
|
|
11590
|
-
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
11591
|
-
Object.assign(data, result.value);
|
|
11592
|
-
} else if (element.key) {
|
|
11593
|
-
data[element.key] = result.value;
|
|
11594
|
-
}
|
|
11730
|
+
const result = validateElement2(element, { path: "" });
|
|
11731
|
+
if (result.skip) return;
|
|
11732
|
+
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
11733
|
+
Object.assign(data, result.value);
|
|
11734
|
+
} else if (element.key) {
|
|
11735
|
+
data[element.key] = result.value;
|
|
11595
11736
|
}
|
|
11596
11737
|
});
|
|
11597
11738
|
return {
|
|
@@ -11794,28 +11935,28 @@ var FormBuilderInstance = class {
|
|
|
11794
11935
|
const formRoot = this.state.formRoot;
|
|
11795
11936
|
const lookupKey = getElementLookupKey(element, this.state);
|
|
11796
11937
|
if (!currentPath) {
|
|
11797
|
-
return formRoot
|
|
11938
|
+
return findOwnField(formRoot, lookupKey, null);
|
|
11798
11939
|
}
|
|
11799
11940
|
const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
|
|
11800
11941
|
if (pathMatch) {
|
|
11801
11942
|
const containerEl2 = formRoot.querySelector(
|
|
11802
11943
|
`[data-container-item="${pathMatch[1]}[${pathMatch[2]}]"]`
|
|
11803
11944
|
);
|
|
11804
|
-
return containerEl2 ? containerEl2
|
|
11945
|
+
return containerEl2 ? findOwnField(containerEl2, lookupKey, containerEl2) : null;
|
|
11805
11946
|
}
|
|
11806
11947
|
const containerEl = formRoot.querySelector(
|
|
11807
11948
|
`[data-container="${currentPath}"]`
|
|
11808
11949
|
);
|
|
11809
|
-
return containerEl ? containerEl
|
|
11950
|
+
return containerEl ? findOwnField(containerEl, lookupKey, containerEl) : null;
|
|
11810
11951
|
}
|
|
11811
11952
|
/**
|
|
11812
11953
|
* Apply enableIf show/hide logic to a single field wrapper.
|
|
11813
11954
|
* Extracted to reduce cyclomatic complexity of checkElements.
|
|
11814
11955
|
*/
|
|
11815
|
-
applyEnableIfVisibility(element, wrapper,
|
|
11956
|
+
applyEnableIfVisibility(element, wrapper, domPath, dataPath, fullDomPath, formData) {
|
|
11816
11957
|
try {
|
|
11817
11958
|
const scope = element.enableIf.scope ?? "relative";
|
|
11818
|
-
const containerData = scope === "relative" &&
|
|
11959
|
+
const containerData = scope === "relative" && dataPath ? getValueByPath(formData, dataPath) : void 0;
|
|
11819
11960
|
const shouldEnable = evaluateEnableCondition(
|
|
11820
11961
|
element.enableIf,
|
|
11821
11962
|
formData,
|
|
@@ -11823,10 +11964,12 @@ var FormBuilderInstance = class {
|
|
|
11823
11964
|
);
|
|
11824
11965
|
const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
|
|
11825
11966
|
if (shouldEnable && isCurrentlyDisabled) {
|
|
11826
|
-
const containerPrefill =
|
|
11967
|
+
const containerPrefill = dataPath ? getValueByPath(formData, dataPath) : formData;
|
|
11827
11968
|
const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
|
|
11828
11969
|
const newWrapper = renderElement2(element, {
|
|
11829
|
-
path:
|
|
11970
|
+
// DOM path: the re-rendered control's `name` must match the row it
|
|
11971
|
+
// lives in, not the row's position in the extracted array.
|
|
11972
|
+
path: domPath,
|
|
11830
11973
|
prefill: prefillContext,
|
|
11831
11974
|
formData,
|
|
11832
11975
|
state: this.state,
|
|
@@ -11846,7 +11989,7 @@ var FormBuilderInstance = class {
|
|
|
11846
11989
|
}
|
|
11847
11990
|
} catch (error) {
|
|
11848
11991
|
console.error(
|
|
11849
|
-
`Error re-evaluating enableIf for field "${element.key ?? "<no key>"}" at path "${
|
|
11992
|
+
`Error re-evaluating enableIf for field "${element.key ?? "<no key>"}" at path "${fullDomPath}":`,
|
|
11850
11993
|
error
|
|
11851
11994
|
);
|
|
11852
11995
|
}
|
|
@@ -11858,39 +12001,46 @@ var FormBuilderInstance = class {
|
|
|
11858
12001
|
reevaluateConditionalFields() {
|
|
11859
12002
|
if (!this.state.schema || !this.state.formRoot) return;
|
|
11860
12003
|
const formData = this.validateForm(true).data;
|
|
11861
|
-
const checkElements = (elements,
|
|
12004
|
+
const checkElements = (elements, domPath, dataPath) => {
|
|
11862
12005
|
elements.forEach((element) => {
|
|
11863
|
-
const
|
|
12006
|
+
const key = element.key ?? "";
|
|
12007
|
+
const fullDomPath = domPath ? `${domPath}.${key}` : key;
|
|
12008
|
+
const fullDataPath = dataPath ? `${dataPath}.${key}` : key;
|
|
11864
12009
|
if (element.enableIf) {
|
|
11865
|
-
const fieldWrapper = this.findFieldWrapper(element,
|
|
12010
|
+
const fieldWrapper = this.findFieldWrapper(element, domPath);
|
|
11866
12011
|
if (fieldWrapper) {
|
|
11867
12012
|
this.applyEnableIfVisibility(
|
|
11868
12013
|
element,
|
|
11869
12014
|
fieldWrapper,
|
|
11870
|
-
|
|
11871
|
-
|
|
12015
|
+
domPath,
|
|
12016
|
+
dataPath,
|
|
12017
|
+
fullDomPath,
|
|
11872
12018
|
formData
|
|
11873
12019
|
);
|
|
11874
12020
|
}
|
|
11875
12021
|
}
|
|
11876
12022
|
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
11877
|
-
const containerData = element.key ? getValueByPath(formData,
|
|
12023
|
+
const containerData = element.key ? getValueByPath(formData, fullDataPath) : void 0;
|
|
11878
12024
|
if (Array.isArray(containerData)) {
|
|
11879
12025
|
const directItems = findDirectContainerRows(
|
|
11880
12026
|
this.state.formRoot,
|
|
11881
|
-
|
|
12027
|
+
fullDomPath
|
|
11882
12028
|
);
|
|
11883
|
-
directItems.forEach((el) => {
|
|
11884
|
-
const
|
|
11885
|
-
checkElements(
|
|
12029
|
+
directItems.forEach((el, rowIndex) => {
|
|
12030
|
+
const marker = el.getAttribute("data-container-item") || "";
|
|
12031
|
+
checkElements(
|
|
12032
|
+
element.elements,
|
|
12033
|
+
marker,
|
|
12034
|
+
`${fullDataPath}[${rowIndex}]`
|
|
12035
|
+
);
|
|
11886
12036
|
});
|
|
11887
12037
|
} else {
|
|
11888
|
-
checkElements(element.elements,
|
|
12038
|
+
checkElements(element.elements, fullDomPath, fullDataPath);
|
|
11889
12039
|
}
|
|
11890
12040
|
}
|
|
11891
12041
|
});
|
|
11892
12042
|
};
|
|
11893
|
-
checkElements(this.state.schema.elements, "");
|
|
12043
|
+
checkElements(this.state.schema.elements, "", "");
|
|
11894
12044
|
}
|
|
11895
12045
|
/**
|
|
11896
12046
|
* Destroy instance and clean up resources
|
|
@@ -11901,6 +12051,7 @@ var FormBuilderInstance = class {
|
|
|
11901
12051
|
this.state.debounceTimer = null;
|
|
11902
12052
|
}
|
|
11903
12053
|
this.disconnectEnableIfObservers();
|
|
12054
|
+
this.removeTooltipElements();
|
|
11904
12055
|
this.state.resourceIndex.clear();
|
|
11905
12056
|
if (this.state.formRoot) {
|
|
11906
12057
|
clear(this.state.formRoot);
|
|
@@ -11918,6 +12069,12 @@ var FormBuilderInstance = class {
|
|
|
11918
12069
|
}
|
|
11919
12070
|
this.state.enableIfObservers.clear();
|
|
11920
12071
|
}
|
|
12072
|
+
removeTooltipElements() {
|
|
12073
|
+
for (const tooltip of this.state.tooltipElements) {
|
|
12074
|
+
tooltip.remove();
|
|
12075
|
+
}
|
|
12076
|
+
this.state.tooltipElements.clear();
|
|
12077
|
+
}
|
|
11921
12078
|
};
|
|
11922
12079
|
|
|
11923
12080
|
// src/index.ts
|