@dmitryvim/form-builder 0.2.9 → 0.2.10
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 +104 -40
- package/dist/browser/formbuilder.min.js +51 -51
- package/dist/browser/formbuilder.v0.2.10.min.js +322 -0
- package/dist/cjs/index.cjs +542 -138
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +534 -134
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +51 -51
- package/dist/types/instance/FormBuilderInstance.d.ts +5 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +6 -3
- package/dist/types/utils/enable-conditions.d.ts +18 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.9.min.js +0 -322
- package/dist/types/utils/display-conditions.d.ts +0 -17
package/dist/cjs/index.cjs
CHANGED
|
@@ -66,6 +66,41 @@ function validateSchema(schema) {
|
|
|
66
66
|
errors.push("Schema missing elements array");
|
|
67
67
|
return errors;
|
|
68
68
|
}
|
|
69
|
+
if ("columns" in schema && schema.columns !== void 0) {
|
|
70
|
+
const columns = schema.columns;
|
|
71
|
+
const validColumns = [1, 2, 3, 4];
|
|
72
|
+
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
73
|
+
errors.push(`schema.columns must be 1, 2, 3, or 4 (got ${columns})`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if ("prefillHints" in schema && schema.prefillHints) {
|
|
77
|
+
const prefillHints = schema.prefillHints;
|
|
78
|
+
if (Array.isArray(prefillHints)) {
|
|
79
|
+
prefillHints.forEach((hint, hintIndex) => {
|
|
80
|
+
if (!hint.label || typeof hint.label !== "string") {
|
|
81
|
+
errors.push(
|
|
82
|
+
`schema.prefillHints[${hintIndex}] must have a 'label' property of type string`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (!hint.values || typeof hint.values !== "object") {
|
|
86
|
+
errors.push(
|
|
87
|
+
`schema.prefillHints[${hintIndex}] must have a 'values' property of type object`
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
for (const fieldKey in hint.values) {
|
|
91
|
+
const fieldExists = schema.elements.some(
|
|
92
|
+
(element) => element.key === fieldKey
|
|
93
|
+
);
|
|
94
|
+
if (!fieldExists) {
|
|
95
|
+
errors.push(
|
|
96
|
+
`schema.prefillHints[${hintIndex}] references non-existent field "${fieldKey}"`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
69
104
|
function validateElements(elements, path) {
|
|
70
105
|
elements.forEach((element, index) => {
|
|
71
106
|
const elementPath = `${path}[${index}]`;
|
|
@@ -75,17 +110,17 @@ function validateSchema(schema) {
|
|
|
75
110
|
if (!element.key) {
|
|
76
111
|
errors.push(`${elementPath}: missing key`);
|
|
77
112
|
}
|
|
78
|
-
if (element.
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
113
|
+
if (element.enableIf) {
|
|
114
|
+
const enableIf = element.enableIf;
|
|
115
|
+
if (!enableIf.key || typeof enableIf.key !== "string") {
|
|
81
116
|
errors.push(
|
|
82
|
-
`${elementPath}:
|
|
117
|
+
`${elementPath}: enableIf must have a 'key' property of type string`
|
|
83
118
|
);
|
|
84
119
|
}
|
|
85
|
-
const hasOperator = "equals" in
|
|
120
|
+
const hasOperator = "equals" in enableIf;
|
|
86
121
|
if (!hasOperator) {
|
|
87
122
|
errors.push(
|
|
88
|
-
`${elementPath}:
|
|
123
|
+
`${elementPath}: enableIf must have at least one operator (equals, etc.)`
|
|
89
124
|
);
|
|
90
125
|
}
|
|
91
126
|
}
|
|
@@ -163,7 +198,7 @@ function clear(node) {
|
|
|
163
198
|
while (node.firstChild) node.removeChild(node.firstChild);
|
|
164
199
|
}
|
|
165
200
|
|
|
166
|
-
// src/utils/
|
|
201
|
+
// src/utils/enable-conditions.ts
|
|
167
202
|
function getValueByPath(data, path) {
|
|
168
203
|
if (!data || typeof data !== "object") {
|
|
169
204
|
return void 0;
|
|
@@ -189,18 +224,28 @@ function getValueByPath(data, path) {
|
|
|
189
224
|
}
|
|
190
225
|
return current;
|
|
191
226
|
}
|
|
192
|
-
function
|
|
227
|
+
function evaluateEnableCondition(condition, formData, containerData) {
|
|
228
|
+
var _a;
|
|
193
229
|
if (!condition || !condition.key) {
|
|
230
|
+
throw new Error("Invalid enableIf condition: must have a 'key' property");
|
|
231
|
+
}
|
|
232
|
+
const scope = (_a = condition.scope) != null ? _a : "relative";
|
|
233
|
+
let dataSource;
|
|
234
|
+
if (scope === "relative") {
|
|
235
|
+
dataSource = containerData != null ? containerData : formData;
|
|
236
|
+
} else if (scope === "absolute") {
|
|
237
|
+
dataSource = formData;
|
|
238
|
+
} else {
|
|
194
239
|
throw new Error(
|
|
195
|
-
|
|
240
|
+
`Invalid enableIf scope: must be "relative" or "absolute" (got "${scope}")`
|
|
196
241
|
);
|
|
197
242
|
}
|
|
198
|
-
const actualValue = getValueByPath(
|
|
243
|
+
const actualValue = getValueByPath(dataSource, condition.key);
|
|
199
244
|
if ("equals" in condition) {
|
|
200
245
|
return deepEqual(actualValue, condition.equals);
|
|
201
246
|
}
|
|
202
247
|
throw new Error(
|
|
203
|
-
`Invalid
|
|
248
|
+
`Invalid enableIf condition: no recognized operator (equals, etc.)`
|
|
204
249
|
);
|
|
205
250
|
}
|
|
206
251
|
function deepEqual(a, b) {
|
|
@@ -213,7 +258,7 @@ function deepEqual(a, b) {
|
|
|
213
258
|
} catch (e) {
|
|
214
259
|
if (e instanceof TypeError && (e.message.includes("circular") || e.message.includes("cyclic"))) {
|
|
215
260
|
console.warn(
|
|
216
|
-
"deepEqual: Circular reference detected in
|
|
261
|
+
"deepEqual: Circular reference detected in enableIf comparison, using reference equality"
|
|
217
262
|
);
|
|
218
263
|
return a === b;
|
|
219
264
|
}
|
|
@@ -266,7 +311,8 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
266
311
|
}
|
|
267
312
|
if (!state.config.readonly && ctx.instance) {
|
|
268
313
|
const handleChange = () => {
|
|
269
|
-
|
|
314
|
+
const value = textInput.value === "" ? null : textInput.value;
|
|
315
|
+
ctx.instance.triggerOnChange(pathKey, value);
|
|
270
316
|
};
|
|
271
317
|
textInput.addEventListener("blur", handleChange);
|
|
272
318
|
textInput.addEventListener("input", handleChange);
|
|
@@ -345,7 +391,8 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
345
391
|
}
|
|
346
392
|
if (!state.config.readonly && ctx.instance) {
|
|
347
393
|
const handleChange = () => {
|
|
348
|
-
|
|
394
|
+
const value2 = textInput.value === "" ? null : textInput.value;
|
|
395
|
+
ctx.instance.triggerOnChange(textInput.name, value2);
|
|
349
396
|
};
|
|
350
397
|
textInput.addEventListener("blur", handleChange);
|
|
351
398
|
textInput.addEventListener("input", handleChange);
|
|
@@ -513,20 +560,20 @@ function validateTextElement(element, key, context) {
|
|
|
513
560
|
}
|
|
514
561
|
};
|
|
515
562
|
if (element.multiple) {
|
|
516
|
-
const inputs = scopeRoot.querySelectorAll(
|
|
517
|
-
`[name^="${key}["]`
|
|
518
|
-
);
|
|
563
|
+
const inputs = scopeRoot.querySelectorAll(`[name^="${key}["]`);
|
|
519
564
|
const values = [];
|
|
565
|
+
const rawValues = [];
|
|
520
566
|
inputs.forEach((input, index) => {
|
|
521
567
|
var _a2;
|
|
522
568
|
const val = (_a2 = input == null ? void 0 : input.value) != null ? _a2 : "";
|
|
523
|
-
|
|
569
|
+
rawValues.push(val);
|
|
570
|
+
values.push(val === "" ? null : val);
|
|
524
571
|
validateTextInput(input, val, `${key}[${index}]`);
|
|
525
572
|
});
|
|
526
573
|
if (!skipValidation) {
|
|
527
574
|
const minCount = (_a = element.minCount) != null ? _a : 1;
|
|
528
575
|
const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
|
|
529
|
-
const filteredValues =
|
|
576
|
+
const filteredValues = rawValues.filter((v) => v.trim() !== "");
|
|
530
577
|
if (element.required && filteredValues.length === 0) {
|
|
531
578
|
errors.push(`${key}: required`);
|
|
532
579
|
}
|
|
@@ -539,17 +586,17 @@ function validateTextElement(element, key, context) {
|
|
|
539
586
|
}
|
|
540
587
|
return { value: values, errors };
|
|
541
588
|
} else {
|
|
542
|
-
const input = scopeRoot.querySelector(
|
|
543
|
-
`[name$="${key}"]`
|
|
544
|
-
);
|
|
589
|
+
const input = scopeRoot.querySelector(`[name$="${key}"]`);
|
|
545
590
|
const val = (_c = input == null ? void 0 : input.value) != null ? _c : "";
|
|
546
591
|
if (!skipValidation && element.required && val === "") {
|
|
547
592
|
errors.push(`${key}: required`);
|
|
548
593
|
markValidity(input, "required");
|
|
549
|
-
return { value:
|
|
594
|
+
return { value: null, errors };
|
|
550
595
|
}
|
|
551
|
-
|
|
552
|
-
|
|
596
|
+
if (input) {
|
|
597
|
+
validateTextInput(input, val, key);
|
|
598
|
+
}
|
|
599
|
+
return { value: val === "" ? null : val, errors };
|
|
553
600
|
}
|
|
554
601
|
}
|
|
555
602
|
function updateTextField(element, fieldPath, value, context) {
|
|
@@ -561,9 +608,7 @@ function updateTextField(element, fieldPath, value, context) {
|
|
|
561
608
|
);
|
|
562
609
|
return;
|
|
563
610
|
}
|
|
564
|
-
const inputs = scopeRoot.querySelectorAll(
|
|
565
|
-
`[name^="${fieldPath}["]`
|
|
566
|
-
);
|
|
611
|
+
const inputs = scopeRoot.querySelectorAll(`[name^="${fieldPath}["]`);
|
|
567
612
|
inputs.forEach((input, index) => {
|
|
568
613
|
if (index < value.length) {
|
|
569
614
|
input.value = value[index] != null ? String(value[index]) : "";
|
|
@@ -577,9 +622,7 @@ function updateTextField(element, fieldPath, value, context) {
|
|
|
577
622
|
);
|
|
578
623
|
}
|
|
579
624
|
} else {
|
|
580
|
-
const input = scopeRoot.querySelector(
|
|
581
|
-
`[name="${fieldPath}"]`
|
|
582
|
-
);
|
|
625
|
+
const input = scopeRoot.querySelector(`[name="${fieldPath}"]`);
|
|
583
626
|
if (input) {
|
|
584
627
|
input.value = value != null ? String(value) : "";
|
|
585
628
|
input.classList.remove("invalid");
|
|
@@ -600,7 +643,8 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
600
643
|
textareaInput.readOnly = state.config.readonly;
|
|
601
644
|
if (!state.config.readonly && ctx.instance) {
|
|
602
645
|
const handleChange = () => {
|
|
603
|
-
|
|
646
|
+
const value = textareaInput.value === "" ? null : textareaInput.value;
|
|
647
|
+
ctx.instance.triggerOnChange(pathKey, value);
|
|
604
648
|
};
|
|
605
649
|
textareaInput.addEventListener("blur", handleChange);
|
|
606
650
|
textareaInput.addEventListener("input", handleChange);
|
|
@@ -644,7 +688,8 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
644
688
|
textareaInput.readOnly = state.config.readonly;
|
|
645
689
|
if (!state.config.readonly && ctx.instance) {
|
|
646
690
|
const handleChange = () => {
|
|
647
|
-
|
|
691
|
+
const value2 = textareaInput.value === "" ? null : textareaInput.value;
|
|
692
|
+
ctx.instance.triggerOnChange(textareaInput.name, value2);
|
|
648
693
|
};
|
|
649
694
|
textareaInput.addEventListener("blur", handleChange);
|
|
650
695
|
textareaInput.addEventListener("input", handleChange);
|
|
@@ -1319,7 +1364,9 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
1319
1364
|
return newContainer;
|
|
1320
1365
|
}
|
|
1321
1366
|
function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
1322
|
-
const changeBtn = container.querySelector(
|
|
1367
|
+
const changeBtn = container.querySelector(
|
|
1368
|
+
".change-file-btn"
|
|
1369
|
+
);
|
|
1323
1370
|
if (changeBtn) {
|
|
1324
1371
|
changeBtn.onclick = (e) => {
|
|
1325
1372
|
e.stopPropagation();
|
|
@@ -1328,7 +1375,9 @@ function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
|
1328
1375
|
}
|
|
1329
1376
|
};
|
|
1330
1377
|
}
|
|
1331
|
-
const deleteBtn = container.querySelector(
|
|
1378
|
+
const deleteBtn = container.querySelector(
|
|
1379
|
+
".delete-file-btn"
|
|
1380
|
+
);
|
|
1332
1381
|
if (deleteBtn) {
|
|
1333
1382
|
deleteBtn.onclick = (e) => {
|
|
1334
1383
|
e.stopPropagation();
|
|
@@ -1370,7 +1419,9 @@ function renderUploadedVideoPreview(container, thumbnailUrl, videoType) {
|
|
|
1370
1419
|
source.src = thumbnailUrl;
|
|
1371
1420
|
source.type = videoType;
|
|
1372
1421
|
video.appendChild(source);
|
|
1373
|
-
video.appendChild(
|
|
1422
|
+
video.appendChild(
|
|
1423
|
+
document.createTextNode("Your browser does not support the video tag.")
|
|
1424
|
+
);
|
|
1374
1425
|
container.appendChild(video);
|
|
1375
1426
|
}
|
|
1376
1427
|
function renderDeleteButton(container, resourceId, state) {
|
|
@@ -1472,7 +1523,13 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
|
1472
1523
|
deps
|
|
1473
1524
|
);
|
|
1474
1525
|
} else {
|
|
1475
|
-
await renderUploadedFilePreview(
|
|
1526
|
+
await renderUploadedFilePreview(
|
|
1527
|
+
container,
|
|
1528
|
+
resourceId,
|
|
1529
|
+
fileName,
|
|
1530
|
+
meta,
|
|
1531
|
+
state
|
|
1532
|
+
);
|
|
1476
1533
|
}
|
|
1477
1534
|
}
|
|
1478
1535
|
async function renderFilePreviewReadonly(resourceId, state, fileName) {
|
|
@@ -1631,7 +1688,9 @@ function renderResourcePills(container, rids, state, onRemove) {
|
|
|
1631
1688
|
if (fileInput) fileInput.click();
|
|
1632
1689
|
};
|
|
1633
1690
|
textContainer.appendChild(uploadLink);
|
|
1634
|
-
textContainer.appendChild(
|
|
1691
|
+
textContainer.appendChild(
|
|
1692
|
+
document.createTextNode(` ${t("dragDropText", state)}`)
|
|
1693
|
+
);
|
|
1635
1694
|
container.appendChild(gridContainer);
|
|
1636
1695
|
container.appendChild(textContainer);
|
|
1637
1696
|
return;
|
|
@@ -2101,7 +2160,14 @@ function renderFileElement(element, ctx, wrapper, pathKey) {
|
|
|
2101
2160
|
const dragHandler = (files) => {
|
|
2102
2161
|
if (files.length > 0) {
|
|
2103
2162
|
const deps = { picker, fileUploadHandler, dragHandler };
|
|
2104
|
-
handleFileSelect(
|
|
2163
|
+
handleFileSelect(
|
|
2164
|
+
files[0],
|
|
2165
|
+
fileContainer,
|
|
2166
|
+
pathKey,
|
|
2167
|
+
state,
|
|
2168
|
+
deps,
|
|
2169
|
+
ctx.instance
|
|
2170
|
+
);
|
|
2105
2171
|
}
|
|
2106
2172
|
};
|
|
2107
2173
|
if (initial) {
|
|
@@ -2125,7 +2191,14 @@ function renderFileElement(element, ctx, wrapper, pathKey) {
|
|
|
2125
2191
|
picker.onchange = () => {
|
|
2126
2192
|
if (picker.files && picker.files.length > 0) {
|
|
2127
2193
|
const deps = { picker, fileUploadHandler, dragHandler };
|
|
2128
|
-
handleFileSelect(
|
|
2194
|
+
handleFileSelect(
|
|
2195
|
+
picker.files[0],
|
|
2196
|
+
fileContainer,
|
|
2197
|
+
pathKey,
|
|
2198
|
+
state,
|
|
2199
|
+
deps,
|
|
2200
|
+
ctx.instance
|
|
2201
|
+
);
|
|
2129
2202
|
}
|
|
2130
2203
|
};
|
|
2131
2204
|
fileWrapper.appendChild(fileContainer);
|
|
@@ -2191,8 +2264,22 @@ function renderFilesElement(element, ctx, wrapper, pathKey) {
|
|
|
2191
2264
|
const initialFiles = ctx.prefill[element.key] || [];
|
|
2192
2265
|
addPrefillFilesToIndex(initialFiles, state);
|
|
2193
2266
|
updateFilesList2();
|
|
2194
|
-
setupFilesDropHandler(
|
|
2195
|
-
|
|
2267
|
+
setupFilesDropHandler(
|
|
2268
|
+
filesContainer,
|
|
2269
|
+
initialFiles,
|
|
2270
|
+
state,
|
|
2271
|
+
updateFilesList2,
|
|
2272
|
+
pathKey,
|
|
2273
|
+
ctx.instance
|
|
2274
|
+
);
|
|
2275
|
+
setupFilesPickerHandler(
|
|
2276
|
+
filesPicker,
|
|
2277
|
+
initialFiles,
|
|
2278
|
+
state,
|
|
2279
|
+
updateFilesList2,
|
|
2280
|
+
pathKey,
|
|
2281
|
+
ctx.instance
|
|
2282
|
+
);
|
|
2196
2283
|
filesContainer.appendChild(list);
|
|
2197
2284
|
filesWrapper.appendChild(filesContainer);
|
|
2198
2285
|
filesWrapper.appendChild(filesPicker);
|
|
@@ -2255,8 +2342,22 @@ function renderMultipleFileElement(element, ctx, wrapper, pathKey) {
|
|
|
2255
2342
|
if (existingCount) existingCount.remove();
|
|
2256
2343
|
filesWrapper.appendChild(countInfo);
|
|
2257
2344
|
};
|
|
2258
|
-
setupFilesDropHandler(
|
|
2259
|
-
|
|
2345
|
+
setupFilesDropHandler(
|
|
2346
|
+
filesContainer,
|
|
2347
|
+
initialFiles,
|
|
2348
|
+
state,
|
|
2349
|
+
updateFilesDisplay,
|
|
2350
|
+
pathKey,
|
|
2351
|
+
ctx.instance
|
|
2352
|
+
);
|
|
2353
|
+
setupFilesPickerHandler(
|
|
2354
|
+
filesPicker,
|
|
2355
|
+
initialFiles,
|
|
2356
|
+
state,
|
|
2357
|
+
updateFilesDisplay,
|
|
2358
|
+
pathKey,
|
|
2359
|
+
ctx.instance
|
|
2360
|
+
);
|
|
2260
2361
|
updateFilesDisplay();
|
|
2261
2362
|
wrapper.appendChild(filesWrapper);
|
|
2262
2363
|
}
|
|
@@ -2746,9 +2847,7 @@ function validateColourElement(element, key, context) {
|
|
|
2746
2847
|
return normalized;
|
|
2747
2848
|
};
|
|
2748
2849
|
if (element.multiple) {
|
|
2749
|
-
const hexInputs = scopeRoot.querySelectorAll(
|
|
2750
|
-
`.colour-hex-input`
|
|
2751
|
-
);
|
|
2850
|
+
const hexInputs = scopeRoot.querySelectorAll(`[name^="${key}["].colour-hex-input`);
|
|
2752
2851
|
const values = [];
|
|
2753
2852
|
hexInputs.forEach((input, index) => {
|
|
2754
2853
|
var _a2;
|
|
@@ -2794,9 +2893,7 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
2794
2893
|
);
|
|
2795
2894
|
return;
|
|
2796
2895
|
}
|
|
2797
|
-
const hexInputs = scopeRoot.querySelectorAll(
|
|
2798
|
-
`.colour-hex-input`
|
|
2799
|
-
);
|
|
2896
|
+
const hexInputs = scopeRoot.querySelectorAll(`[name^="${fieldPath}["].colour-hex-input`);
|
|
2800
2897
|
hexInputs.forEach((hexInput, index) => {
|
|
2801
2898
|
if (index < value.length) {
|
|
2802
2899
|
const normalized = normalizeColourValue(value[index]);
|
|
@@ -2806,7 +2903,9 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
2806
2903
|
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
2807
2904
|
if (wrapper) {
|
|
2808
2905
|
const swatch = wrapper.querySelector(".colour-swatch");
|
|
2809
|
-
const colourInput = wrapper.querySelector(
|
|
2906
|
+
const colourInput = wrapper.querySelector(
|
|
2907
|
+
".colour-picker-hidden"
|
|
2908
|
+
);
|
|
2810
2909
|
if (swatch) {
|
|
2811
2910
|
swatch.style.backgroundColor = normalized;
|
|
2812
2911
|
}
|
|
@@ -2833,7 +2932,9 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
2833
2932
|
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
2834
2933
|
if (wrapper) {
|
|
2835
2934
|
const swatch = wrapper.querySelector(".colour-swatch");
|
|
2836
|
-
const colourInput = wrapper.querySelector(
|
|
2935
|
+
const colourInput = wrapper.querySelector(
|
|
2936
|
+
".colour-picker-hidden"
|
|
2937
|
+
);
|
|
2837
2938
|
if (swatch) {
|
|
2838
2939
|
swatch.style.backgroundColor = normalized;
|
|
2839
2940
|
}
|
|
@@ -2973,14 +3074,10 @@ function createSliderUI(value, pathKey, element, ctx, readonly) {
|
|
|
2973
3074
|
function renderSliderElement(element, ctx, wrapper, pathKey) {
|
|
2974
3075
|
var _a;
|
|
2975
3076
|
if (element.min === void 0 || element.min === null) {
|
|
2976
|
-
throw new Error(
|
|
2977
|
-
`Slider field "${element.key}" requires "min" property`
|
|
2978
|
-
);
|
|
3077
|
+
throw new Error(`Slider field "${element.key}" requires "min" property`);
|
|
2979
3078
|
}
|
|
2980
3079
|
if (element.max === void 0 || element.max === null) {
|
|
2981
|
-
throw new Error(
|
|
2982
|
-
`Slider field "${element.key}" requires "max" property`
|
|
2983
|
-
);
|
|
3080
|
+
throw new Error(`Slider field "${element.key}" requires "max" property`);
|
|
2984
3081
|
}
|
|
2985
3082
|
if (element.min >= element.max) {
|
|
2986
3083
|
throw new Error(
|
|
@@ -3010,14 +3107,10 @@ function renderSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
3010
3107
|
function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
3011
3108
|
var _a, _b;
|
|
3012
3109
|
if (element.min === void 0 || element.min === null) {
|
|
3013
|
-
throw new Error(
|
|
3014
|
-
`Slider field "${element.key}" requires "min" property`
|
|
3015
|
-
);
|
|
3110
|
+
throw new Error(`Slider field "${element.key}" requires "min" property`);
|
|
3016
3111
|
}
|
|
3017
3112
|
if (element.max === void 0 || element.max === null) {
|
|
3018
|
-
throw new Error(
|
|
3019
|
-
`Slider field "${element.key}" requires "max" property`
|
|
3020
|
-
);
|
|
3113
|
+
throw new Error(`Slider field "${element.key}" requires "max" property`);
|
|
3021
3114
|
}
|
|
3022
3115
|
if (element.min >= element.max) {
|
|
3023
3116
|
throw new Error(
|
|
@@ -3190,7 +3283,10 @@ function validateSliderElement(element, key, context) {
|
|
|
3190
3283
|
`;
|
|
3191
3284
|
const sliderContainer = input.closest(".slider-container");
|
|
3192
3285
|
if (sliderContainer && sliderContainer.nextSibling) {
|
|
3193
|
-
(_a2 = sliderContainer.parentNode) == null ? void 0 : _a2.insertBefore(
|
|
3286
|
+
(_a2 = sliderContainer.parentNode) == null ? void 0 : _a2.insertBefore(
|
|
3287
|
+
errorElement,
|
|
3288
|
+
sliderContainer.nextSibling
|
|
3289
|
+
);
|
|
3194
3290
|
} else if (sliderContainer) {
|
|
3195
3291
|
(_b2 = sliderContainer.parentNode) == null ? void 0 : _b2.appendChild(errorElement);
|
|
3196
3292
|
}
|
|
@@ -3362,6 +3458,33 @@ function updateSliderField(element, fieldPath, value, context) {
|
|
|
3362
3458
|
}
|
|
3363
3459
|
|
|
3364
3460
|
// src/components/container.ts
|
|
3461
|
+
function extractRootFormData(formRoot) {
|
|
3462
|
+
const data = {};
|
|
3463
|
+
const inputs = formRoot.querySelectorAll(
|
|
3464
|
+
"input, select, textarea"
|
|
3465
|
+
);
|
|
3466
|
+
inputs.forEach((input) => {
|
|
3467
|
+
const fieldName = input.getAttribute("name");
|
|
3468
|
+
if (fieldName && !fieldName.includes("[") && !fieldName.includes(".")) {
|
|
3469
|
+
if (input instanceof HTMLSelectElement) {
|
|
3470
|
+
data[fieldName] = input.value;
|
|
3471
|
+
} else if (input instanceof HTMLInputElement) {
|
|
3472
|
+
if (input.type === "checkbox") {
|
|
3473
|
+
data[fieldName] = input.checked;
|
|
3474
|
+
} else if (input.type === "radio") {
|
|
3475
|
+
if (input.checked) {
|
|
3476
|
+
data[fieldName] = input.value;
|
|
3477
|
+
}
|
|
3478
|
+
} else {
|
|
3479
|
+
data[fieldName] = input.value;
|
|
3480
|
+
}
|
|
3481
|
+
} else if (input instanceof HTMLTextAreaElement) {
|
|
3482
|
+
data[fieldName] = input.value;
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
});
|
|
3486
|
+
return data;
|
|
3487
|
+
}
|
|
3365
3488
|
var renderElementFunc = null;
|
|
3366
3489
|
function setRenderElement(fn) {
|
|
3367
3490
|
renderElementFunc = fn;
|
|
@@ -3421,7 +3544,7 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
3421
3544
|
prefill: ((_a = ctx.prefill) == null ? void 0 : _a[element.key]) || {},
|
|
3422
3545
|
// Sliced data for value population
|
|
3423
3546
|
formData: (_b = ctx.formData) != null ? _b : ctx.prefill,
|
|
3424
|
-
// Complete root data for
|
|
3547
|
+
// Complete root data for enableIf evaluation
|
|
3425
3548
|
state: ctx.state
|
|
3426
3549
|
};
|
|
3427
3550
|
element.elements.forEach((child) => {
|
|
@@ -3475,15 +3598,15 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
3475
3598
|
add.style.backgroundColor = "transparent";
|
|
3476
3599
|
});
|
|
3477
3600
|
add.onclick = () => {
|
|
3478
|
-
var _a2;
|
|
3479
3601
|
if (countItems() < max) {
|
|
3480
3602
|
const idx = countItems();
|
|
3603
|
+
const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
|
|
3481
3604
|
const subCtx = {
|
|
3482
3605
|
state: ctx.state,
|
|
3483
3606
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
3484
3607
|
prefill: {},
|
|
3485
|
-
formData:
|
|
3486
|
-
//
|
|
3608
|
+
formData: currentFormData
|
|
3609
|
+
// Current root data from DOM for enableIf
|
|
3487
3610
|
};
|
|
3488
3611
|
const item = document.createElement("div");
|
|
3489
3612
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -3532,7 +3655,9 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
3532
3655
|
};
|
|
3533
3656
|
const updateAddButton = () => {
|
|
3534
3657
|
const currentCount = countItems();
|
|
3535
|
-
const existingAddBtn = containerWrap.querySelector(
|
|
3658
|
+
const existingAddBtn = containerWrap.querySelector(
|
|
3659
|
+
".add-container-btn"
|
|
3660
|
+
);
|
|
3536
3661
|
if (existingAddBtn) {
|
|
3537
3662
|
existingAddBtn.disabled = currentCount >= max;
|
|
3538
3663
|
existingAddBtn.style.opacity = currentCount >= max ? "0.5" : "1";
|
|
@@ -3548,7 +3673,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
3548
3673
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
3549
3674
|
prefill: prefillObj || {},
|
|
3550
3675
|
formData: (_a2 = ctx.formData) != null ? _a2 : ctx.prefill
|
|
3551
|
-
// Complete root data for
|
|
3676
|
+
// Complete root data for enableIf
|
|
3552
3677
|
};
|
|
3553
3678
|
const item = document.createElement("div");
|
|
3554
3679
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -3600,7 +3725,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
3600
3725
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
3601
3726
|
prefill: {},
|
|
3602
3727
|
formData: (_d = ctx.formData) != null ? _d : ctx.prefill
|
|
3603
|
-
// Complete root data for
|
|
3728
|
+
// Complete root data for enableIf
|
|
3604
3729
|
};
|
|
3605
3730
|
const item = document.createElement("div");
|
|
3606
3731
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -3700,6 +3825,27 @@ function validateContainerElement(element, key, context) {
|
|
|
3700
3825
|
`[data-container-item="${key}[${i}]"]`
|
|
3701
3826
|
) || scopeRoot;
|
|
3702
3827
|
element.elements.forEach((child) => {
|
|
3828
|
+
var _a;
|
|
3829
|
+
if (child.enableIf) {
|
|
3830
|
+
try {
|
|
3831
|
+
const rootFormData = ((_a = context.instance) == null ? void 0 : _a.getState().formRoot) ? extractRootFormData(context.instance.getState().formRoot) : {};
|
|
3832
|
+
const shouldEnable = evaluateEnableCondition(
|
|
3833
|
+
child.enableIf,
|
|
3834
|
+
rootFormData,
|
|
3835
|
+
// Root form data for absolute scope
|
|
3836
|
+
itemData
|
|
3837
|
+
// Container data for relative scope
|
|
3838
|
+
);
|
|
3839
|
+
if (!shouldEnable) {
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3842
|
+
} catch (error) {
|
|
3843
|
+
console.error(
|
|
3844
|
+
`Error evaluating enableIf for field "${child.key}" in container "${key}[${i}]":`,
|
|
3845
|
+
error
|
|
3846
|
+
);
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3703
3849
|
if (child.hidden || child.type === "hidden") {
|
|
3704
3850
|
itemData[child.key] = child.default !== void 0 ? child.default : null;
|
|
3705
3851
|
} else {
|
|
@@ -3719,6 +3865,27 @@ function validateContainerElement(element, key, context) {
|
|
|
3719
3865
|
const containerData = {};
|
|
3720
3866
|
const containerContainer = scopeRoot.querySelector(`[data-container="${key}"]`) || scopeRoot;
|
|
3721
3867
|
element.elements.forEach((child) => {
|
|
3868
|
+
var _a;
|
|
3869
|
+
if (child.enableIf) {
|
|
3870
|
+
try {
|
|
3871
|
+
const rootFormData = ((_a = context.instance) == null ? void 0 : _a.getState().formRoot) ? extractRootFormData(context.instance.getState().formRoot) : {};
|
|
3872
|
+
const shouldEnable = evaluateEnableCondition(
|
|
3873
|
+
child.enableIf,
|
|
3874
|
+
rootFormData,
|
|
3875
|
+
// Root form data for absolute scope
|
|
3876
|
+
containerData
|
|
3877
|
+
// Container data for relative scope
|
|
3878
|
+
);
|
|
3879
|
+
if (!shouldEnable) {
|
|
3880
|
+
return;
|
|
3881
|
+
}
|
|
3882
|
+
} catch (error) {
|
|
3883
|
+
console.error(
|
|
3884
|
+
`Error evaluating enableIf for field "${child.key}" in container "${key}":`,
|
|
3885
|
+
error
|
|
3886
|
+
);
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3722
3889
|
if (child.hidden || child.type === "hidden") {
|
|
3723
3890
|
containerData[child.key] = child.default !== void 0 ? child.default : null;
|
|
3724
3891
|
} else {
|
|
@@ -3905,32 +4072,183 @@ if (typeof document !== "undefined") {
|
|
|
3905
4072
|
}
|
|
3906
4073
|
});
|
|
3907
4074
|
}
|
|
3908
|
-
function
|
|
3909
|
-
var _a;
|
|
3910
|
-
if (!element.
|
|
3911
|
-
return
|
|
4075
|
+
function shouldDisableElement(element, ctx) {
|
|
4076
|
+
var _a, _b;
|
|
4077
|
+
if (!element.enableIf) {
|
|
4078
|
+
return false;
|
|
3912
4079
|
}
|
|
3913
4080
|
try {
|
|
3914
|
-
const
|
|
3915
|
-
const
|
|
3916
|
-
|
|
3917
|
-
|
|
4081
|
+
const rootFormData = (_b = (_a = ctx.formData) != null ? _a : ctx.prefill) != null ? _b : {};
|
|
4082
|
+
const containerData = ctx.path ? getValueByPath(rootFormData, ctx.path) : void 0;
|
|
4083
|
+
const shouldEnable = evaluateEnableCondition(
|
|
4084
|
+
element.enableIf,
|
|
4085
|
+
rootFormData,
|
|
4086
|
+
containerData
|
|
3918
4087
|
);
|
|
3919
|
-
|
|
3920
|
-
const hiddenWrapper = document.createElement("div");
|
|
3921
|
-
hiddenWrapper.className = "fb-field-wrapper-hidden";
|
|
3922
|
-
hiddenWrapper.style.display = "none";
|
|
3923
|
-
hiddenWrapper.setAttribute("data-field-key", element.key);
|
|
3924
|
-
hiddenWrapper.setAttribute("data-conditionally-hidden", "true");
|
|
3925
|
-
return hiddenWrapper;
|
|
3926
|
-
}
|
|
4088
|
+
return !shouldEnable;
|
|
3927
4089
|
} catch (error) {
|
|
3928
4090
|
console.error(
|
|
3929
|
-
`Error evaluating
|
|
4091
|
+
`Error evaluating enableIf for field "${element.key}":`,
|
|
3930
4092
|
error
|
|
3931
4093
|
);
|
|
3932
4094
|
}
|
|
3933
|
-
return
|
|
4095
|
+
return false;
|
|
4096
|
+
}
|
|
4097
|
+
function extractDOMValue(fieldPath, formRoot) {
|
|
4098
|
+
const input = formRoot.querySelector(
|
|
4099
|
+
`[name="${fieldPath}"]`
|
|
4100
|
+
);
|
|
4101
|
+
if (!input) {
|
|
4102
|
+
return void 0;
|
|
4103
|
+
}
|
|
4104
|
+
if (input instanceof HTMLSelectElement) {
|
|
4105
|
+
return input.value;
|
|
4106
|
+
} else if (input instanceof HTMLInputElement) {
|
|
4107
|
+
if (input.type === "checkbox") {
|
|
4108
|
+
return input.checked;
|
|
4109
|
+
} else if (input.type === "radio") {
|
|
4110
|
+
const checked = formRoot.querySelector(
|
|
4111
|
+
`[name="${fieldPath}"]:checked`
|
|
4112
|
+
);
|
|
4113
|
+
return checked ? checked.value : void 0;
|
|
4114
|
+
} else {
|
|
4115
|
+
return input.value;
|
|
4116
|
+
}
|
|
4117
|
+
} else if (input instanceof HTMLTextAreaElement) {
|
|
4118
|
+
return input.value;
|
|
4119
|
+
}
|
|
4120
|
+
return void 0;
|
|
4121
|
+
}
|
|
4122
|
+
function reevaluateEnableIf(wrapper, element, ctx) {
|
|
4123
|
+
var _a, _b;
|
|
4124
|
+
if (!element.enableIf) {
|
|
4125
|
+
return;
|
|
4126
|
+
}
|
|
4127
|
+
const formRoot = ctx.state.formRoot;
|
|
4128
|
+
if (!formRoot) {
|
|
4129
|
+
console.error(`Cannot re-evaluate enableIf: formRoot is null`);
|
|
4130
|
+
return;
|
|
4131
|
+
}
|
|
4132
|
+
const condition = element.enableIf;
|
|
4133
|
+
const scope = (_a = condition.scope) != null ? _a : "relative";
|
|
4134
|
+
let rootFormData = {};
|
|
4135
|
+
const containerData = {};
|
|
4136
|
+
const effectiveScope = !ctx.path || ctx.path === "" ? "absolute" : scope;
|
|
4137
|
+
if (effectiveScope === "relative" && ctx.path) {
|
|
4138
|
+
const containerMatch = ctx.path.match(/^(.+)\[(\d+)\]$/);
|
|
4139
|
+
if (containerMatch) {
|
|
4140
|
+
const containerKey = containerMatch[1];
|
|
4141
|
+
const containerIndex = parseInt(containerMatch[2], 10);
|
|
4142
|
+
const containerItemElement = formRoot.querySelector(
|
|
4143
|
+
`[data-container-item="${containerKey}[${containerIndex}]"]`
|
|
4144
|
+
);
|
|
4145
|
+
if (containerItemElement) {
|
|
4146
|
+
const inputs = containerItemElement.querySelectorAll(
|
|
4147
|
+
"input, select, textarea"
|
|
4148
|
+
);
|
|
4149
|
+
inputs.forEach((input) => {
|
|
4150
|
+
const fieldName = input.getAttribute("name");
|
|
4151
|
+
if (fieldName) {
|
|
4152
|
+
const fieldKeyMatch = fieldName.match(/\.([^.[\]]+)$/);
|
|
4153
|
+
if (fieldKeyMatch) {
|
|
4154
|
+
const fieldKey = fieldKeyMatch[1];
|
|
4155
|
+
if (input instanceof HTMLSelectElement) {
|
|
4156
|
+
containerData[fieldKey] = input.value;
|
|
4157
|
+
} else if (input instanceof HTMLInputElement) {
|
|
4158
|
+
if (input.type === "checkbox") {
|
|
4159
|
+
containerData[fieldKey] = input.checked;
|
|
4160
|
+
} else if (input.type === "radio") {
|
|
4161
|
+
if (input.checked) {
|
|
4162
|
+
containerData[fieldKey] = input.value;
|
|
4163
|
+
}
|
|
4164
|
+
} else {
|
|
4165
|
+
containerData[fieldKey] = input.value;
|
|
4166
|
+
}
|
|
4167
|
+
} else if (input instanceof HTMLTextAreaElement) {
|
|
4168
|
+
containerData[fieldKey] = input.value;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
});
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
} else {
|
|
4176
|
+
const dependencyKey = condition.key;
|
|
4177
|
+
const dependencyValue = extractDOMValue(dependencyKey, formRoot);
|
|
4178
|
+
if (dependencyValue !== void 0) {
|
|
4179
|
+
rootFormData[dependencyKey] = dependencyValue;
|
|
4180
|
+
} else {
|
|
4181
|
+
rootFormData = (_b = ctx.formData) != null ? _b : ctx.prefill;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
try {
|
|
4185
|
+
const shouldEnable = evaluateEnableCondition(
|
|
4186
|
+
condition,
|
|
4187
|
+
rootFormData,
|
|
4188
|
+
containerData
|
|
4189
|
+
);
|
|
4190
|
+
if (shouldEnable) {
|
|
4191
|
+
wrapper.style.display = "";
|
|
4192
|
+
wrapper.classList.remove("fb-field-wrapper-disabled");
|
|
4193
|
+
wrapper.removeAttribute("data-conditionally-disabled");
|
|
4194
|
+
} else {
|
|
4195
|
+
wrapper.style.display = "none";
|
|
4196
|
+
wrapper.classList.add("fb-field-wrapper-disabled");
|
|
4197
|
+
wrapper.setAttribute("data-conditionally-disabled", "true");
|
|
4198
|
+
}
|
|
4199
|
+
} catch (error) {
|
|
4200
|
+
console.error(`Error re-evaluating enableIf for field "${element.key}":`, error);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
function setupEnableIfListeners(wrapper, element, ctx) {
|
|
4204
|
+
var _a;
|
|
4205
|
+
if (!element.enableIf) {
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
const formRoot = ctx.state.formRoot;
|
|
4209
|
+
if (!formRoot) {
|
|
4210
|
+
console.error(`Cannot setup enableIf listeners: formRoot is null`);
|
|
4211
|
+
return;
|
|
4212
|
+
}
|
|
4213
|
+
const condition = element.enableIf;
|
|
4214
|
+
const scope = (_a = condition.scope) != null ? _a : "relative";
|
|
4215
|
+
const dependencyKey = condition.key;
|
|
4216
|
+
let dependencyFieldPath;
|
|
4217
|
+
if (scope === "relative" && ctx.path) {
|
|
4218
|
+
dependencyFieldPath = `${ctx.path}.${dependencyKey}`;
|
|
4219
|
+
} else {
|
|
4220
|
+
dependencyFieldPath = dependencyKey;
|
|
4221
|
+
}
|
|
4222
|
+
const dependencyInput = formRoot.querySelector(
|
|
4223
|
+
`[name="${dependencyFieldPath}"]`
|
|
4224
|
+
);
|
|
4225
|
+
if (!dependencyInput) {
|
|
4226
|
+
const observer = new MutationObserver(() => {
|
|
4227
|
+
const input = formRoot.querySelector(
|
|
4228
|
+
`[name="${dependencyFieldPath}"]`
|
|
4229
|
+
);
|
|
4230
|
+
if (input) {
|
|
4231
|
+
input.addEventListener("change", () => {
|
|
4232
|
+
reevaluateEnableIf(wrapper, element, ctx);
|
|
4233
|
+
});
|
|
4234
|
+
input.addEventListener("input", () => {
|
|
4235
|
+
reevaluateEnableIf(wrapper, element, ctx);
|
|
4236
|
+
});
|
|
4237
|
+
observer.disconnect();
|
|
4238
|
+
}
|
|
4239
|
+
});
|
|
4240
|
+
observer.observe(formRoot, {
|
|
4241
|
+
childList: true,
|
|
4242
|
+
subtree: true
|
|
4243
|
+
});
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4246
|
+
dependencyInput.addEventListener("change", () => {
|
|
4247
|
+
reevaluateEnableIf(wrapper, element, ctx);
|
|
4248
|
+
});
|
|
4249
|
+
dependencyInput.addEventListener("input", () => {
|
|
4250
|
+
reevaluateEnableIf(wrapper, element, ctx);
|
|
4251
|
+
});
|
|
3934
4252
|
}
|
|
3935
4253
|
function createFieldLabel(element) {
|
|
3936
4254
|
const title = document.createElement("label");
|
|
@@ -4048,10 +4366,7 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
4048
4366
|
}
|
|
4049
4367
|
}
|
|
4050
4368
|
function renderElement2(element, ctx) {
|
|
4051
|
-
const
|
|
4052
|
-
if (hiddenElement) {
|
|
4053
|
-
return hiddenElement;
|
|
4054
|
-
}
|
|
4369
|
+
const initiallyDisabled = shouldDisableElement(element, ctx);
|
|
4055
4370
|
const wrapper = document.createElement("div");
|
|
4056
4371
|
wrapper.className = "mb-6 fb-field-wrapper";
|
|
4057
4372
|
wrapper.setAttribute("data-field-key", element.key);
|
|
@@ -4059,6 +4374,12 @@ function renderElement2(element, ctx) {
|
|
|
4059
4374
|
wrapper.appendChild(label);
|
|
4060
4375
|
const pathKey = pathJoin(ctx.path, element.key);
|
|
4061
4376
|
dispatchToRenderer(element, ctx, wrapper, pathKey);
|
|
4377
|
+
if (initiallyDisabled) {
|
|
4378
|
+
wrapper.style.display = "none";
|
|
4379
|
+
wrapper.classList.add("fb-field-wrapper-disabled");
|
|
4380
|
+
wrapper.setAttribute("data-conditionally-disabled", "true");
|
|
4381
|
+
}
|
|
4382
|
+
setupEnableIfListeners(wrapper, element, ctx);
|
|
4062
4383
|
return wrapper;
|
|
4063
4384
|
}
|
|
4064
4385
|
setRenderElement(renderElement2);
|
|
@@ -4717,15 +5038,16 @@ var FormBuilderInstance = class {
|
|
|
4717
5038
|
event.preventDefault();
|
|
4718
5039
|
event.stopPropagation();
|
|
4719
5040
|
const hintValuesJson = target.getAttribute("data-hint-values");
|
|
5041
|
+
const isRootHint = target.getAttribute("data-root-hint") === "true";
|
|
4720
5042
|
const containerKey = target.getAttribute("data-container-key");
|
|
4721
|
-
if (!hintValuesJson || !containerKey) {
|
|
5043
|
+
if (!hintValuesJson || !isRootHint && !containerKey) {
|
|
4722
5044
|
console.warn("Prefill hint missing required data attributes");
|
|
4723
5045
|
return;
|
|
4724
5046
|
}
|
|
4725
5047
|
try {
|
|
4726
5048
|
const hintValues = JSON.parse(hintValuesJson);
|
|
4727
5049
|
for (const fieldKey in hintValues) {
|
|
4728
|
-
const fullPath = `${containerKey}.${fieldKey}`;
|
|
5050
|
+
const fullPath = isRootHint ? fieldKey : `${containerKey}.${fieldKey}`;
|
|
4729
5051
|
const value = hintValues[fieldKey];
|
|
4730
5052
|
this.updateField(fullPath, value);
|
|
4731
5053
|
}
|
|
@@ -4733,6 +5055,27 @@ var FormBuilderInstance = class {
|
|
|
4733
5055
|
console.error("Error parsing prefill hint values:", error);
|
|
4734
5056
|
}
|
|
4735
5057
|
}
|
|
5058
|
+
/**
|
|
5059
|
+
* Create root-level prefill hints UI
|
|
5060
|
+
*/
|
|
5061
|
+
createRootPrefillHints(hints) {
|
|
5062
|
+
const hintsContainer = document.createElement("div");
|
|
5063
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-4";
|
|
5064
|
+
hints.forEach((hint) => {
|
|
5065
|
+
const hintButton = document.createElement("button");
|
|
5066
|
+
hintButton.type = "button";
|
|
5067
|
+
hintButton.className = "fb-prefill-hint";
|
|
5068
|
+
if (hint.icon) {
|
|
5069
|
+
hintButton.textContent = `${hint.icon} ${hint.label}`;
|
|
5070
|
+
} else {
|
|
5071
|
+
hintButton.textContent = hint.label;
|
|
5072
|
+
}
|
|
5073
|
+
hintButton.setAttribute("data-hint-values", JSON.stringify(hint.values));
|
|
5074
|
+
hintButton.setAttribute("data-root-hint", "true");
|
|
5075
|
+
hintsContainer.appendChild(hintButton);
|
|
5076
|
+
});
|
|
5077
|
+
return hintsContainer;
|
|
5078
|
+
}
|
|
4736
5079
|
/**
|
|
4737
5080
|
* Render form from schema
|
|
4738
5081
|
*/
|
|
@@ -4748,8 +5091,19 @@ var FormBuilderInstance = class {
|
|
|
4748
5091
|
clear(root);
|
|
4749
5092
|
root.setAttribute("data-fb-root", "true");
|
|
4750
5093
|
injectThemeVariables(root, this.state.config.theme);
|
|
4751
|
-
const
|
|
4752
|
-
|
|
5094
|
+
const rootContainer = document.createElement("div");
|
|
5095
|
+
rootContainer.className = "space-y-6";
|
|
5096
|
+
if (schema.prefillHints && !this.state.config.readonly) {
|
|
5097
|
+
const hintsContainer = this.createRootPrefillHints(schema.prefillHints);
|
|
5098
|
+
rootContainer.appendChild(hintsContainer);
|
|
5099
|
+
}
|
|
5100
|
+
const fieldsWrapper = document.createElement("div");
|
|
5101
|
+
const columns = schema.columns || 1;
|
|
5102
|
+
if (columns === 1) {
|
|
5103
|
+
fieldsWrapper.className = "space-y-4";
|
|
5104
|
+
} else {
|
|
5105
|
+
fieldsWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
5106
|
+
}
|
|
4753
5107
|
schema.elements.forEach((element) => {
|
|
4754
5108
|
if (element.hidden) {
|
|
4755
5109
|
return;
|
|
@@ -4758,13 +5112,14 @@ var FormBuilderInstance = class {
|
|
|
4758
5112
|
path: "",
|
|
4759
5113
|
prefill: prefill || {},
|
|
4760
5114
|
formData: prefill || {},
|
|
4761
|
-
// Pass complete root data for
|
|
5115
|
+
// Pass complete root data for enableIf evaluation
|
|
4762
5116
|
state: this.state,
|
|
4763
5117
|
instance: this
|
|
4764
5118
|
});
|
|
4765
|
-
|
|
5119
|
+
fieldsWrapper.appendChild(block);
|
|
4766
5120
|
});
|
|
4767
|
-
|
|
5121
|
+
rootContainer.appendChild(fieldsWrapper);
|
|
5122
|
+
root.appendChild(rootContainer);
|
|
4768
5123
|
if (!this.state.config.readonly) {
|
|
4769
5124
|
root.addEventListener("click", this.handlePrefillHintClick.bind(this));
|
|
4770
5125
|
}
|
|
@@ -4806,15 +5161,18 @@ var FormBuilderInstance = class {
|
|
|
4806
5161
|
};
|
|
4807
5162
|
setValidateElement(validateElement2);
|
|
4808
5163
|
this.state.schema.elements.forEach((element) => {
|
|
4809
|
-
if (element.
|
|
5164
|
+
if (element.enableIf) {
|
|
4810
5165
|
try {
|
|
4811
|
-
const
|
|
4812
|
-
|
|
5166
|
+
const shouldEnable = evaluateEnableCondition(
|
|
5167
|
+
element.enableIf,
|
|
5168
|
+
data
|
|
5169
|
+
);
|
|
5170
|
+
if (!shouldEnable) {
|
|
4813
5171
|
return;
|
|
4814
5172
|
}
|
|
4815
5173
|
} catch (error) {
|
|
4816
5174
|
console.error(
|
|
4817
|
-
`Error evaluating
|
|
5175
|
+
`Error evaluating enableIf for field "${element.key}" during validation:`,
|
|
4818
5176
|
error
|
|
4819
5177
|
);
|
|
4820
5178
|
}
|
|
@@ -4902,7 +5260,9 @@ var FormBuilderInstance = class {
|
|
|
4902
5260
|
}
|
|
4903
5261
|
if (element.type === "container" || element.type === "group") {
|
|
4904
5262
|
const containerElement = element;
|
|
4905
|
-
const nestedData = this.buildHiddenFieldsData(
|
|
5263
|
+
const nestedData = this.buildHiddenFieldsData(
|
|
5264
|
+
containerElement.elements
|
|
5265
|
+
);
|
|
4906
5266
|
if (Object.keys(nestedData).length > 0) {
|
|
4907
5267
|
if (!(key in data)) {
|
|
4908
5268
|
data[key] = nestedData;
|
|
@@ -4920,7 +5280,9 @@ var FormBuilderInstance = class {
|
|
|
4920
5280
|
*/
|
|
4921
5281
|
setFormData(data) {
|
|
4922
5282
|
if (!this.state.schema || !this.state.formRoot) {
|
|
4923
|
-
console.warn(
|
|
5283
|
+
console.warn(
|
|
5284
|
+
"setFormData: Form not initialized. Call renderForm() first."
|
|
5285
|
+
);
|
|
4924
5286
|
return;
|
|
4925
5287
|
}
|
|
4926
5288
|
for (const fieldPath in data) {
|
|
@@ -4934,20 +5296,27 @@ var FormBuilderInstance = class {
|
|
|
4934
5296
|
*/
|
|
4935
5297
|
updateField(fieldPath, value) {
|
|
4936
5298
|
if (!this.state.schema || !this.state.formRoot) {
|
|
4937
|
-
console.warn(
|
|
5299
|
+
console.warn(
|
|
5300
|
+
"updateField: Form not initialized. Call renderForm() first."
|
|
5301
|
+
);
|
|
4938
5302
|
return;
|
|
4939
5303
|
}
|
|
4940
5304
|
const schemaElement = this.findSchemaElement(fieldPath);
|
|
4941
5305
|
if (!schemaElement) {
|
|
4942
|
-
console.warn(
|
|
5306
|
+
console.warn(
|
|
5307
|
+
`updateField: Schema element not found for path "${fieldPath}"`
|
|
5308
|
+
);
|
|
4943
5309
|
return;
|
|
4944
5310
|
}
|
|
4945
5311
|
const domElement = this.findFormElementByFieldPath(fieldPath);
|
|
4946
5312
|
if (!domElement) {
|
|
4947
|
-
console.warn(
|
|
5313
|
+
console.warn(
|
|
5314
|
+
`updateField: DOM element not found for path "${fieldPath}"`
|
|
5315
|
+
);
|
|
4948
5316
|
return;
|
|
4949
5317
|
}
|
|
4950
5318
|
this.updateFieldValue(domElement, schemaElement, fieldPath, value);
|
|
5319
|
+
this.reevaluateConditionalFields();
|
|
4951
5320
|
if (this.state.config.onChange || this.state.config.onFieldChange) {
|
|
4952
5321
|
this.triggerOnChange(fieldPath, value);
|
|
4953
5322
|
}
|
|
@@ -4976,7 +5345,7 @@ var FormBuilderInstance = class {
|
|
|
4976
5345
|
}
|
|
4977
5346
|
}
|
|
4978
5347
|
/**
|
|
4979
|
-
* Re-evaluate all conditional fields (
|
|
5348
|
+
* Re-evaluate all conditional fields (enableIf) based on current form data
|
|
4980
5349
|
* This is called automatically when form data changes (via onChange events)
|
|
4981
5350
|
*/
|
|
4982
5351
|
reevaluateConditionalFields() {
|
|
@@ -4984,47 +5353,82 @@ var FormBuilderInstance = class {
|
|
|
4984
5353
|
const formData = this.validateForm(true).data;
|
|
4985
5354
|
const checkElements = (elements, currentPath) => {
|
|
4986
5355
|
elements.forEach((element) => {
|
|
5356
|
+
var _a, _b, _c;
|
|
4987
5357
|
const fullPath = currentPath ? `${currentPath}.${element.key}` : element.key;
|
|
4988
|
-
if (element.
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
5358
|
+
if (element.enableIf) {
|
|
5359
|
+
let fieldWrapper = null;
|
|
5360
|
+
if (currentPath) {
|
|
5361
|
+
const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
|
|
5362
|
+
if (pathMatch) {
|
|
5363
|
+
const containerKey = pathMatch[1];
|
|
5364
|
+
const containerIndex = pathMatch[2];
|
|
5365
|
+
const containerElement = this.state.formRoot.querySelector(
|
|
5366
|
+
`[data-container-item="${containerKey}[${containerIndex}]"]`
|
|
5367
|
+
);
|
|
5368
|
+
if (containerElement) {
|
|
5369
|
+
fieldWrapper = containerElement.querySelector(
|
|
5370
|
+
`[data-field-key="${element.key}"]`
|
|
5371
|
+
);
|
|
5372
|
+
}
|
|
5373
|
+
} else {
|
|
5374
|
+
const containerElement = this.state.formRoot.querySelector(
|
|
5375
|
+
`[data-container="${currentPath}"]`
|
|
5376
|
+
);
|
|
5377
|
+
if (containerElement) {
|
|
5378
|
+
fieldWrapper = containerElement.querySelector(
|
|
5379
|
+
`[data-field-key="${element.key}"]`
|
|
5380
|
+
);
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
} else {
|
|
5384
|
+
fieldWrapper = this.state.formRoot.querySelector(
|
|
5385
|
+
`[data-field-key="${element.key}"]`
|
|
5386
|
+
);
|
|
5387
|
+
}
|
|
5388
|
+
if (fieldWrapper) {
|
|
5389
|
+
const wrapper = fieldWrapper;
|
|
4994
5390
|
try {
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
5391
|
+
let containerData = void 0;
|
|
5392
|
+
const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
|
|
5393
|
+
if (scope === "relative" && currentPath) {
|
|
5394
|
+
containerData = getValueByPath(formData, currentPath);
|
|
5395
|
+
}
|
|
5396
|
+
const shouldEnable = evaluateEnableCondition(
|
|
5397
|
+
element.enableIf,
|
|
5398
|
+
formData,
|
|
5399
|
+
// Use complete formData for absolute scope
|
|
5400
|
+
containerData
|
|
5401
|
+
// Use container-specific data for relative scope
|
|
4999
5402
|
);
|
|
5000
|
-
const
|
|
5001
|
-
if (
|
|
5403
|
+
const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
|
|
5404
|
+
if (shouldEnable && isCurrentlyDisabled) {
|
|
5405
|
+
const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
|
|
5406
|
+
const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
|
|
5002
5407
|
const newWrapper = renderElement2(element, {
|
|
5003
|
-
path:
|
|
5004
|
-
// Use
|
|
5005
|
-
prefill:
|
|
5006
|
-
// Use complete formData for root-level elements
|
|
5408
|
+
path: currentPath,
|
|
5409
|
+
// Use container path (empty string for root-level)
|
|
5410
|
+
prefill: prefillContext,
|
|
5007
5411
|
formData,
|
|
5008
|
-
// Pass complete formData for
|
|
5412
|
+
// Pass complete formData for enableIf evaluation
|
|
5009
5413
|
state: this.state,
|
|
5010
5414
|
instance: this
|
|
5011
5415
|
});
|
|
5012
|
-
(
|
|
5013
|
-
} else if (!
|
|
5014
|
-
const
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
(
|
|
5416
|
+
(_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
|
|
5417
|
+
} else if (!shouldEnable && !isCurrentlyDisabled) {
|
|
5418
|
+
const disabledWrapper = document.createElement("div");
|
|
5419
|
+
disabledWrapper.className = "fb-field-wrapper-disabled";
|
|
5420
|
+
disabledWrapper.style.display = "none";
|
|
5421
|
+
disabledWrapper.setAttribute("data-field-key", element.key);
|
|
5422
|
+
disabledWrapper.setAttribute("data-conditionally-disabled", "true");
|
|
5423
|
+
(_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
|
|
5020
5424
|
}
|
|
5021
5425
|
} catch (error) {
|
|
5022
5426
|
console.error(
|
|
5023
|
-
`Error re-evaluating
|
|
5427
|
+
`Error re-evaluating enableIf for field "${element.key}" at path "${fullPath}":`,
|
|
5024
5428
|
error
|
|
5025
5429
|
);
|
|
5026
5430
|
}
|
|
5027
|
-
}
|
|
5431
|
+
}
|
|
5028
5432
|
}
|
|
5029
5433
|
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
5030
5434
|
const containerData = formData == null ? void 0 : formData[element.key];
|