@dmitryvim/form-builder 0.5.3 → 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 +111 -111
- package/dist/browser/formbuilder.v0.5.4.min.js +1490 -0
- package/dist/cjs/index.cjs +139 -62
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +121 -57
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +111 -111
- package/dist/types/components/container.d.ts +1 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +19 -1
- 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.3.min.js +0 -1490
package/dist/esm/index.js
CHANGED
|
@@ -23,9 +23,7 @@ function isPlainObject(obj) {
|
|
|
23
23
|
return obj && typeof obj === "object" && obj.constructor === Object;
|
|
24
24
|
}
|
|
25
25
|
function escapeHtml(text) {
|
|
26
|
-
|
|
27
|
-
div.textContent = text;
|
|
28
|
-
return div.innerHTML;
|
|
26
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
29
27
|
}
|
|
30
28
|
function getElementLookupKey(element, state) {
|
|
31
29
|
if (element.key) {
|
|
@@ -61,7 +59,7 @@ function formatFileSize(bytes) {
|
|
|
61
59
|
}
|
|
62
60
|
function serializeHiddenValue(value) {
|
|
63
61
|
if (value === null || value === void 0) return "";
|
|
64
|
-
return
|
|
62
|
+
return JSON.stringify(value);
|
|
65
63
|
}
|
|
66
64
|
function deserializeHiddenValue(raw) {
|
|
67
65
|
if (raw === "") return null;
|
|
@@ -317,6 +315,14 @@ function validateSchema(schema) {
|
|
|
317
315
|
errors.push(`${elementPath}: missing key`);
|
|
318
316
|
}
|
|
319
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
|
+
}
|
|
320
326
|
if (element.type === "markdown") {
|
|
321
327
|
const content = element.content;
|
|
322
328
|
if (typeof content !== "string") {
|
|
@@ -943,7 +949,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
943
949
|
overflow-wrap: anywhere;
|
|
944
950
|
`;
|
|
945
951
|
textInput.name = pathKey;
|
|
946
|
-
textInput.placeholder = element.placeholder ?? "
|
|
952
|
+
textInput.placeholder = element.placeholder ?? t("placeholderText", state);
|
|
947
953
|
textInput.value = ctx.prefill[element.key] || element.default || "";
|
|
948
954
|
textInput.readOnly = readonly;
|
|
949
955
|
applySingleLineMode(textInput);
|
|
@@ -1055,6 +1061,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
1055
1061
|
updateIndices();
|
|
1056
1062
|
updateAddButton();
|
|
1057
1063
|
updateRemoveButtons();
|
|
1064
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1058
1065
|
};
|
|
1059
1066
|
chip.appendChild(rem);
|
|
1060
1067
|
}
|
|
@@ -1079,6 +1086,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
1079
1086
|
addChip(element.default || "");
|
|
1080
1087
|
updateAddButton();
|
|
1081
1088
|
updateRemoveButtons();
|
|
1089
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1082
1090
|
},
|
|
1083
1091
|
{ label: element.addLabel }
|
|
1084
1092
|
);
|
|
@@ -1192,7 +1200,7 @@ function validateTextElement(element, key, context) {
|
|
|
1192
1200
|
}
|
|
1193
1201
|
return { value: values, errors };
|
|
1194
1202
|
} else {
|
|
1195
|
-
const input = scopeRoot.querySelector(`[name
|
|
1203
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1196
1204
|
const val = input?.value ?? "";
|
|
1197
1205
|
if (!skipValidation && element.required && val === "") {
|
|
1198
1206
|
const msg = t("required", context.state);
|
|
@@ -1362,6 +1370,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1362
1370
|
updateIndices();
|
|
1363
1371
|
updateAddButton();
|
|
1364
1372
|
updateRemoveButtons();
|
|
1373
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1365
1374
|
}
|
|
1366
1375
|
};
|
|
1367
1376
|
item.appendChild(removeBtn);
|
|
@@ -1381,6 +1390,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
1381
1390
|
addTextareaItem(element.default || "");
|
|
1382
1391
|
updateAddButton();
|
|
1383
1392
|
updateRemoveButtons();
|
|
1393
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1384
1394
|
},
|
|
1385
1395
|
{ label: element.addLabel }
|
|
1386
1396
|
);
|
|
@@ -1672,6 +1682,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1672
1682
|
updateIndices();
|
|
1673
1683
|
updateAddButton();
|
|
1674
1684
|
updateRemoveButtons();
|
|
1685
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1675
1686
|
}
|
|
1676
1687
|
};
|
|
1677
1688
|
item.appendChild(removeBtn);
|
|
@@ -1691,6 +1702,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1691
1702
|
addNumberItem(element.default || "");
|
|
1692
1703
|
updateAddButton();
|
|
1693
1704
|
updateRemoveButtons();
|
|
1705
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1694
1706
|
},
|
|
1695
1707
|
{ label: element.addLabel }
|
|
1696
1708
|
);
|
|
@@ -1779,8 +1791,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1779
1791
|
return;
|
|
1780
1792
|
}
|
|
1781
1793
|
validateNumberInput(input, v, `${key}[${index}]`);
|
|
1782
|
-
|
|
1783
|
-
values.push(Number(v.toFixed(d)));
|
|
1794
|
+
values.push(applyDecimals(v, element.decimals));
|
|
1784
1795
|
});
|
|
1785
1796
|
if (!skipValidation) {
|
|
1786
1797
|
const { state } = context;
|
|
@@ -1799,7 +1810,7 @@ function validateNumberElement(element, key, context) {
|
|
|
1799
1810
|
}
|
|
1800
1811
|
return { value: values, errors };
|
|
1801
1812
|
} else {
|
|
1802
|
-
const input = scopeRoot.querySelector(`[name
|
|
1813
|
+
const input = scopeRoot.querySelector(`[name="${key}"]`);
|
|
1803
1814
|
const raw = input?.value ?? "";
|
|
1804
1815
|
const { state } = context;
|
|
1805
1816
|
if (!skipValidation && element.required && raw === "") {
|
|
@@ -1820,10 +1831,13 @@ function validateNumberElement(element, key, context) {
|
|
|
1820
1831
|
return { value: null, errors };
|
|
1821
1832
|
}
|
|
1822
1833
|
validateNumberInput(input, v, key);
|
|
1823
|
-
|
|
1824
|
-
return { value: Number(v.toFixed(d)), errors };
|
|
1834
|
+
return { value: applyDecimals(v, element.decimals), errors };
|
|
1825
1835
|
}
|
|
1826
1836
|
}
|
|
1837
|
+
function applyDecimals(v, decimals) {
|
|
1838
|
+
if (!Number.isInteger(decimals) || decimals < 0) return v;
|
|
1839
|
+
return Number(v.toFixed(decimals));
|
|
1840
|
+
}
|
|
1827
1841
|
function updateNumberField(element, fieldPath, value, context) {
|
|
1828
1842
|
const { scopeRoot } = context;
|
|
1829
1843
|
if (element.multiple) {
|
|
@@ -1976,6 +1990,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1976
1990
|
updateIndices();
|
|
1977
1991
|
updateAddButton();
|
|
1978
1992
|
updateRemoveButtons();
|
|
1993
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1979
1994
|
}
|
|
1980
1995
|
};
|
|
1981
1996
|
item.appendChild(removeBtn);
|
|
@@ -1996,6 +2011,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1996
2011
|
addSelectItem(defaultValue);
|
|
1997
2012
|
updateAddButton();
|
|
1998
2013
|
updateRemoveButtons();
|
|
2014
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
1999
2015
|
},
|
|
2000
2016
|
{ label: element.addLabel }
|
|
2001
2017
|
);
|
|
@@ -2081,7 +2097,7 @@ function validateSelectElement(element, key, context) {
|
|
|
2081
2097
|
return { value: values, errors };
|
|
2082
2098
|
} else {
|
|
2083
2099
|
const input = scopeRoot.querySelector(
|
|
2084
|
-
`[name
|
|
2100
|
+
`[name="${key}"]`
|
|
2085
2101
|
);
|
|
2086
2102
|
const val = input?.value ?? "";
|
|
2087
2103
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -2413,6 +2429,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2413
2429
|
updateIndices();
|
|
2414
2430
|
updateAddButton();
|
|
2415
2431
|
updateRemoveButtons();
|
|
2432
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
2416
2433
|
}
|
|
2417
2434
|
};
|
|
2418
2435
|
item.appendChild(removeBtn);
|
|
@@ -2433,6 +2450,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
2433
2450
|
addSwitcherItem(defaultValue);
|
|
2434
2451
|
updateAddButton();
|
|
2435
2452
|
updateRemoveButtons();
|
|
2453
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
2436
2454
|
},
|
|
2437
2455
|
{ label: element.addLabel }
|
|
2438
2456
|
);
|
|
@@ -2527,7 +2545,7 @@ function validateSwitcherElement(element, key, context) {
|
|
|
2527
2545
|
return { value: values, errors };
|
|
2528
2546
|
} else {
|
|
2529
2547
|
const input = scopeRoot.querySelector(
|
|
2530
|
-
`input[type="hidden"][name
|
|
2548
|
+
`input[type="hidden"][name="${key}"]`
|
|
2531
2549
|
);
|
|
2532
2550
|
const val = input?.value ?? "";
|
|
2533
2551
|
if (!skipValidation && element.required && val === "") {
|
|
@@ -5569,7 +5587,7 @@ function validateSingleFile(element, key, context) {
|
|
|
5569
5587
|
const { scopeRoot, skipValidation, state } = context;
|
|
5570
5588
|
const errors = [];
|
|
5571
5589
|
const input = scopeRoot.querySelector(
|
|
5572
|
-
`input[name
|
|
5590
|
+
`input[name="${key}"][type="hidden"]`
|
|
5573
5591
|
);
|
|
5574
5592
|
const rid = input?.value ?? "";
|
|
5575
5593
|
if (!skipValidation && element.required && rid === "") {
|
|
@@ -6001,6 +6019,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
6001
6019
|
updateIndices();
|
|
6002
6020
|
updateAddButton();
|
|
6003
6021
|
updateRemoveButtons();
|
|
6022
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6004
6023
|
}
|
|
6005
6024
|
};
|
|
6006
6025
|
item.appendChild(removeBtn);
|
|
@@ -6021,6 +6040,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
6021
6040
|
addColourItem(defaultColour);
|
|
6022
6041
|
updateAddButton();
|
|
6023
6042
|
updateRemoveButtons();
|
|
6043
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6024
6044
|
},
|
|
6025
6045
|
{ label: element.addLabel }
|
|
6026
6046
|
);
|
|
@@ -6457,6 +6477,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6457
6477
|
updateIndices();
|
|
6458
6478
|
updateAddButton();
|
|
6459
6479
|
updateRemoveButtons();
|
|
6480
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6460
6481
|
}
|
|
6461
6482
|
};
|
|
6462
6483
|
item.appendChild(removeBtn);
|
|
@@ -6476,6 +6497,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
6476
6497
|
addSliderItem(defaultValue);
|
|
6477
6498
|
updateAddButton();
|
|
6478
6499
|
updateRemoveButtons();
|
|
6500
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6479
6501
|
},
|
|
6480
6502
|
{ label: element.addLabel }
|
|
6481
6503
|
);
|
|
@@ -6743,6 +6765,8 @@ function extractRootFormData(formRoot) {
|
|
|
6743
6765
|
if (input.checked) {
|
|
6744
6766
|
data[fieldName] = input.value;
|
|
6745
6767
|
}
|
|
6768
|
+
} else if (input.dataset.hiddenField) {
|
|
6769
|
+
data[fieldName] = deserializeHiddenValue(input.value);
|
|
6746
6770
|
} else {
|
|
6747
6771
|
data[fieldName] = input.value;
|
|
6748
6772
|
}
|
|
@@ -6861,7 +6885,7 @@ function mountRemoveButton(item, onRemove, state, containerLabel) {
|
|
|
6861
6885
|
item.classList.add("fb-row-removable");
|
|
6862
6886
|
item.insertBefore(rem, item.firstChild);
|
|
6863
6887
|
}
|
|
6864
|
-
function renderMultipleContainerElement(element, ctx, wrapper,
|
|
6888
|
+
function renderMultipleContainerElement(element, ctx, wrapper, pathKey) {
|
|
6865
6889
|
const state = ctx.state;
|
|
6866
6890
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
6867
6891
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
@@ -6894,6 +6918,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6894
6918
|
if (countItems() <= min) return;
|
|
6895
6919
|
item.remove();
|
|
6896
6920
|
updateAddButton();
|
|
6921
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6897
6922
|
};
|
|
6898
6923
|
const createContainerItem = (idx, rowPrefill, formData) => {
|
|
6899
6924
|
const subCtx = {
|
|
@@ -6950,6 +6975,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6950
6975
|
itemsWrap.appendChild(item);
|
|
6951
6976
|
}
|
|
6952
6977
|
updateAddButton();
|
|
6978
|
+
ctx.instance?.triggerOnChange(pathKey);
|
|
6953
6979
|
};
|
|
6954
6980
|
let slideAddTile = null;
|
|
6955
6981
|
let slideAddUpdate = null;
|
|
@@ -7243,7 +7269,7 @@ function renderGroupElement(element, ctx, wrapper, pathKey) {
|
|
|
7243
7269
|
maxCount: element.repeat?.max
|
|
7244
7270
|
};
|
|
7245
7271
|
if (containerElement.multiple) {
|
|
7246
|
-
renderMultipleContainerElement(containerElement, ctx, wrapper);
|
|
7272
|
+
renderMultipleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7247
7273
|
} else {
|
|
7248
7274
|
renderSingleContainerElement(containerElement, ctx, wrapper, pathKey);
|
|
7249
7275
|
}
|
|
@@ -10240,15 +10266,19 @@ var componentRegistry = {
|
|
|
10240
10266
|
function getComponentOperations(elementType) {
|
|
10241
10267
|
return componentRegistry[elementType] || null;
|
|
10242
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
|
+
}
|
|
10243
10273
|
function validateElementWithComponent(element, key, context) {
|
|
10244
|
-
const ops =
|
|
10274
|
+
const ops = resolveOperations(element);
|
|
10245
10275
|
if (ops && ops.validate) {
|
|
10246
10276
|
return ops.validate(element, key, context);
|
|
10247
10277
|
}
|
|
10248
10278
|
return null;
|
|
10249
10279
|
}
|
|
10250
10280
|
function updateElementWithComponent(element, fieldPath, value, context) {
|
|
10251
|
-
const ops =
|
|
10281
|
+
const ops = resolveOperations(element);
|
|
10252
10282
|
if (ops && ops.update) {
|
|
10253
10283
|
ops.update(element, fieldPath, value, context);
|
|
10254
10284
|
return true;
|
|
@@ -10260,6 +10290,10 @@ function updateElementWithComponent(element, fieldPath, value, context) {
|
|
|
10260
10290
|
function showTooltip(tooltipId, button) {
|
|
10261
10291
|
const tooltip = document.getElementById(tooltipId);
|
|
10262
10292
|
if (!tooltip) return;
|
|
10293
|
+
if (!button.isConnected) {
|
|
10294
|
+
tooltip.remove();
|
|
10295
|
+
return;
|
|
10296
|
+
}
|
|
10263
10297
|
const isCurrentlyVisible = !tooltip.classList.contains("hidden");
|
|
10264
10298
|
document.querySelectorAll('[id^="tooltip-"]').forEach((t2) => {
|
|
10265
10299
|
t2.classList.add("hidden");
|
|
@@ -10349,6 +10383,8 @@ function extractDOMValue(fieldPath, formRoot) {
|
|
|
10349
10383
|
`[name="${fieldPath}"]:checked`
|
|
10350
10384
|
);
|
|
10351
10385
|
return checked ? checked.value : void 0;
|
|
10386
|
+
} else if (input.dataset.hiddenField) {
|
|
10387
|
+
return deserializeHiddenValue(input.value);
|
|
10352
10388
|
} else {
|
|
10353
10389
|
return input.value;
|
|
10354
10390
|
}
|
|
@@ -10484,7 +10520,7 @@ function createFieldLabel(element) {
|
|
|
10484
10520
|
}
|
|
10485
10521
|
return title;
|
|
10486
10522
|
}
|
|
10487
|
-
function createInfoButton(element) {
|
|
10523
|
+
function createInfoButton(element, state) {
|
|
10488
10524
|
const infoBtn = document.createElement("button");
|
|
10489
10525
|
infoBtn.type = "button";
|
|
10490
10526
|
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
@@ -10496,6 +10532,7 @@ function createInfoButton(element) {
|
|
|
10496
10532
|
tooltip.style.position = "fixed";
|
|
10497
10533
|
tooltip.textContent = element.description || element.hint || "Field information";
|
|
10498
10534
|
document.body.appendChild(tooltip);
|
|
10535
|
+
state.tooltipElements.add(tooltip);
|
|
10499
10536
|
infoBtn.onclick = (e) => {
|
|
10500
10537
|
e.preventDefault();
|
|
10501
10538
|
e.stopPropagation();
|
|
@@ -10503,7 +10540,7 @@ function createInfoButton(element) {
|
|
|
10503
10540
|
};
|
|
10504
10541
|
return infoBtn;
|
|
10505
10542
|
}
|
|
10506
|
-
function createLabelContainer(element) {
|
|
10543
|
+
function createLabelContainer(element, state) {
|
|
10507
10544
|
const label = document.createElement("div");
|
|
10508
10545
|
label.className = "flex items-center";
|
|
10509
10546
|
label.style.marginBottom = "var(--fb-label-margin-bottom, 2px)";
|
|
@@ -10511,7 +10548,7 @@ function createLabelContainer(element) {
|
|
|
10511
10548
|
const title = createFieldLabel(element);
|
|
10512
10549
|
label.appendChild(title);
|
|
10513
10550
|
if (element.description || element.hint) {
|
|
10514
|
-
const infoBtn = createInfoButton(element);
|
|
10551
|
+
const infoBtn = createInfoButton(element, state);
|
|
10515
10552
|
label.appendChild(infoBtn);
|
|
10516
10553
|
}
|
|
10517
10554
|
return label;
|
|
@@ -10586,7 +10623,7 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
10586
10623
|
break;
|
|
10587
10624
|
case "container":
|
|
10588
10625
|
if (isMultiple) {
|
|
10589
|
-
renderMultipleContainerElement(element, ctx, wrapper);
|
|
10626
|
+
renderMultipleContainerElement(element, ctx, wrapper, pathKey);
|
|
10590
10627
|
} else {
|
|
10591
10628
|
renderSingleContainerElement(element, ctx, wrapper, pathKey);
|
|
10592
10629
|
}
|
|
@@ -10639,7 +10676,7 @@ function renderElement2(element, ctx) {
|
|
|
10639
10676
|
wrapper.setAttribute("data-fb-width", element.width || "full");
|
|
10640
10677
|
const ops = getComponentOperations(element.type);
|
|
10641
10678
|
if (!ops?.ownsLabel) {
|
|
10642
|
-
const label = createLabelContainer(element);
|
|
10679
|
+
const label = createLabelContainer(element, ctx.state);
|
|
10643
10680
|
wrapper.appendChild(label);
|
|
10644
10681
|
}
|
|
10645
10682
|
const pathKey = pathJoin(ctx.path, element.key);
|
|
@@ -10857,7 +10894,8 @@ function createInstanceState(config) {
|
|
|
10857
10894
|
prefill: {},
|
|
10858
10895
|
syntheticElementIds: /* @__PURE__ */ new WeakMap(),
|
|
10859
10896
|
syntheticElementIdCounter: 0,
|
|
10860
|
-
enableIfObservers: /* @__PURE__ */ new Set()
|
|
10897
|
+
enableIfObservers: /* @__PURE__ */ new Set(),
|
|
10898
|
+
tooltipElements: /* @__PURE__ */ new Set()
|
|
10861
10899
|
};
|
|
10862
10900
|
}
|
|
10863
10901
|
function generateInstanceId() {
|
|
@@ -11237,7 +11275,11 @@ var FormBuilderInstance = class {
|
|
|
11237
11275
|
/**
|
|
11238
11276
|
* Trigger onChange callbacks with debouncing
|
|
11239
11277
|
* @param fieldPath - Optional field path for field-specific change events
|
|
11240
|
-
* @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.
|
|
11241
11283
|
*/
|
|
11242
11284
|
triggerOnChange(fieldPath, fieldValue) {
|
|
11243
11285
|
if (this.state.config.readonly) return;
|
|
@@ -11250,12 +11292,49 @@ var FormBuilderInstance = class {
|
|
|
11250
11292
|
if (this.state.config.onChange) {
|
|
11251
11293
|
this.state.config.onChange(formData);
|
|
11252
11294
|
}
|
|
11253
|
-
if (this.state.config.onFieldChange && fieldPath !== void 0
|
|
11254
|
-
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);
|
|
11255
11298
|
}
|
|
11256
11299
|
this.state.debounceTimer = null;
|
|
11257
11300
|
}, this.state.config.debounceMs);
|
|
11258
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
|
+
}
|
|
11259
11338
|
/**
|
|
11260
11339
|
* Register an external action that will be displayed as a button
|
|
11261
11340
|
* External actions can be form-level (no related_field) or field-level (with related_field)
|
|
@@ -11295,21 +11374,10 @@ var FormBuilderInstance = class {
|
|
|
11295
11374
|
*/
|
|
11296
11375
|
findFormElementByFieldPath(fieldPath) {
|
|
11297
11376
|
if (!this.state.formRoot) return null;
|
|
11298
|
-
|
|
11377
|
+
const element = this.state.formRoot.querySelector(
|
|
11299
11378
|
`[name="${fieldPath}"]`
|
|
11300
11379
|
);
|
|
11301
11380
|
if (element) return element;
|
|
11302
|
-
const variations = [
|
|
11303
|
-
fieldPath,
|
|
11304
|
-
fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
|
|
11305
|
-
fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
|
|
11306
|
-
];
|
|
11307
|
-
for (const variation of variations) {
|
|
11308
|
-
element = this.state.formRoot.querySelector(
|
|
11309
|
-
`[name="${variation}"]`
|
|
11310
|
-
);
|
|
11311
|
-
if (element) return element;
|
|
11312
|
-
}
|
|
11313
11381
|
const schemaElement = this.findSchemaElement(fieldPath);
|
|
11314
11382
|
if (!schemaElement) return null;
|
|
11315
11383
|
const fieldWrappers = this.state.formRoot.querySelectorAll(".fb-field-wrapper");
|
|
@@ -11559,6 +11627,7 @@ var FormBuilderInstance = class {
|
|
|
11559
11627
|
return;
|
|
11560
11628
|
}
|
|
11561
11629
|
this.disconnectEnableIfObservers();
|
|
11630
|
+
this.removeTooltipElements();
|
|
11562
11631
|
this.state.formRoot = root;
|
|
11563
11632
|
this.state.schema = schema;
|
|
11564
11633
|
this.state.externalActions = actions || null;
|
|
@@ -11658,24 +11727,12 @@ var FormBuilderInstance = class {
|
|
|
11658
11727
|
if (element.type === "markdown") {
|
|
11659
11728
|
return;
|
|
11660
11729
|
}
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
);
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
data[element.key] = deserializeHiddenValue(raw);
|
|
11668
|
-
} else {
|
|
11669
|
-
data[element.key] = element.default !== void 0 ? element.default : null;
|
|
11670
|
-
}
|
|
11671
|
-
} else {
|
|
11672
|
-
const result = validateElement2(element, { path: "" });
|
|
11673
|
-
if (result.skip) return;
|
|
11674
|
-
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
11675
|
-
Object.assign(data, result.value);
|
|
11676
|
-
} else if (element.key) {
|
|
11677
|
-
data[element.key] = result.value;
|
|
11678
|
-
}
|
|
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;
|
|
11679
11736
|
}
|
|
11680
11737
|
});
|
|
11681
11738
|
return {
|
|
@@ -11994,6 +12051,7 @@ var FormBuilderInstance = class {
|
|
|
11994
12051
|
this.state.debounceTimer = null;
|
|
11995
12052
|
}
|
|
11996
12053
|
this.disconnectEnableIfObservers();
|
|
12054
|
+
this.removeTooltipElements();
|
|
11997
12055
|
this.state.resourceIndex.clear();
|
|
11998
12056
|
if (this.state.formRoot) {
|
|
11999
12057
|
clear(this.state.formRoot);
|
|
@@ -12011,6 +12069,12 @@ var FormBuilderInstance = class {
|
|
|
12011
12069
|
}
|
|
12012
12070
|
this.state.enableIfObservers.clear();
|
|
12013
12071
|
}
|
|
12072
|
+
removeTooltipElements() {
|
|
12073
|
+
for (const tooltip of this.state.tooltipElements) {
|
|
12074
|
+
tooltip.remove();
|
|
12075
|
+
}
|
|
12076
|
+
this.state.tooltipElements.clear();
|
|
12077
|
+
}
|
|
12014
12078
|
};
|
|
12015
12079
|
|
|
12016
12080
|
// src/index.ts
|