@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.
@@ -27,9 +27,7 @@ function isPlainObject(obj) {
27
27
  return obj && typeof obj === "object" && obj.constructor === Object;
28
28
  }
29
29
  function escapeHtml(text) {
30
- const div = document.createElement("div");
31
- div.textContent = text;
32
- return div.innerHTML;
30
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
33
31
  }
34
32
  function getElementLookupKey(element, state) {
35
33
  if (element.key) {
@@ -65,7 +63,7 @@ function formatFileSize(bytes) {
65
63
  }
66
64
  function serializeHiddenValue(value) {
67
65
  if (value === null || value === void 0) return "";
68
- return typeof value === "object" ? JSON.stringify(value) : String(value);
66
+ return JSON.stringify(value);
69
67
  }
70
68
  function deserializeHiddenValue(raw) {
71
69
  if (raw === "") return null;
@@ -325,6 +323,14 @@ function validateSchema(schema) {
325
323
  errors.push(`${elementPath}: missing key`);
326
324
  }
327
325
  validateCountBounds(element, elementPath, errors);
326
+ if (element.type === "number" && "decimals" in element) {
327
+ const decimals = element.decimals;
328
+ if (decimals !== void 0 && (!Number.isInteger(decimals) || decimals < 0)) {
329
+ errors.push(
330
+ `${elementPath}: decimals must be a non-negative integer (got ${JSON.stringify(decimals)})`
331
+ );
332
+ }
333
+ }
328
334
  if (element.type === "markdown") {
329
335
  const content = element.content;
330
336
  if (typeof content !== "string") {
@@ -958,7 +964,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
958
964
  overflow-wrap: anywhere;
959
965
  `;
960
966
  textInput.name = pathKey;
961
- textInput.placeholder = (_a = element.placeholder) != null ? _a : "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442";
967
+ textInput.placeholder = (_a = element.placeholder) != null ? _a : t("placeholderText", state);
962
968
  textInput.value = ctx.prefill[element.key] || element.default || "";
963
969
  textInput.readOnly = readonly;
964
970
  applySingleLineMode(textInput);
@@ -1058,6 +1064,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
1058
1064
  rem.setAttribute("aria-label", t("removeElement", state));
1059
1065
  rem.innerHTML = BIN_ICON_SVG;
1060
1066
  rem.onclick = () => {
1067
+ var _a2;
1061
1068
  const chips = list.querySelectorAll(".fb-chip");
1062
1069
  const idx = Array.prototype.indexOf.call(chips, chip);
1063
1070
  if (idx < 0) return;
@@ -1071,6 +1078,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
1071
1078
  updateIndices();
1072
1079
  updateAddButton();
1073
1080
  updateRemoveButtons();
1081
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1074
1082
  };
1075
1083
  chip.appendChild(rem);
1076
1084
  }
@@ -1091,10 +1099,12 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
1091
1099
  const handle = createAddItemRow(
1092
1100
  "text",
1093
1101
  () => {
1102
+ var _a2;
1094
1103
  values.push(element.default || "");
1095
1104
  addChip(element.default || "");
1096
1105
  updateAddButton();
1097
1106
  updateRemoveButtons();
1107
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1098
1108
  },
1099
1109
  { label: element.addLabel }
1100
1110
  );
@@ -1211,7 +1221,7 @@ function validateTextElement(element, key, context) {
1211
1221
  }
1212
1222
  return { value: values, errors };
1213
1223
  } else {
1214
- const input = scopeRoot.querySelector(`[name$="${key}"]`);
1224
+ const input = scopeRoot.querySelector(`[name="${key}"]`);
1215
1225
  const val = (_c = input == null ? void 0 : input.value) != null ? _c : "";
1216
1226
  if (!skipValidation && element.required && val === "") {
1217
1227
  const msg = t("required", context.state);
@@ -1375,6 +1385,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
1375
1385
  removeBtn.className = "remove-item-btn mt-1 px-2 py-1 text-red-600 hover:bg-red-50 rounded text-sm";
1376
1386
  removeBtn.innerHTML = "\u2715";
1377
1387
  removeBtn.onclick = () => {
1388
+ var _a2;
1378
1389
  const currentIndex = Array.from(container.children).indexOf(
1379
1390
  item
1380
1391
  );
@@ -1384,6 +1395,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
1384
1395
  updateIndices();
1385
1396
  updateAddButton();
1386
1397
  updateRemoveButtons();
1398
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1387
1399
  }
1388
1400
  };
1389
1401
  item.appendChild(removeBtn);
@@ -1399,10 +1411,12 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
1399
1411
  const handle = createAddItemRow(
1400
1412
  "textarea",
1401
1413
  () => {
1414
+ var _a2;
1402
1415
  values.push(element.default || "");
1403
1416
  addTextareaItem(element.default || "");
1404
1417
  updateAddButton();
1405
1418
  updateRemoveButtons();
1419
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1406
1420
  },
1407
1421
  { label: element.addLabel }
1408
1422
  );
@@ -1688,6 +1702,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
1688
1702
  removeBtn.className = "remove-item-btn px-2 py-1 text-red-600 hover:bg-red-50 rounded";
1689
1703
  removeBtn.innerHTML = "\u2715";
1690
1704
  removeBtn.onclick = () => {
1705
+ var _a2;
1691
1706
  const currentIndex = Array.from(container.children).indexOf(
1692
1707
  item
1693
1708
  );
@@ -1697,6 +1712,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
1697
1712
  updateIndices();
1698
1713
  updateAddButton();
1699
1714
  updateRemoveButtons();
1715
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1700
1716
  }
1701
1717
  };
1702
1718
  item.appendChild(removeBtn);
@@ -1712,10 +1728,12 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
1712
1728
  const handle = createAddItemRow(
1713
1729
  "number",
1714
1730
  () => {
1731
+ var _a2;
1715
1732
  values.push(element.default || "");
1716
1733
  addNumberItem(element.default || "");
1717
1734
  updateAddButton();
1718
1735
  updateRemoveButtons();
1736
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
1719
1737
  },
1720
1738
  { label: element.addLabel }
1721
1739
  );
@@ -1731,7 +1749,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
1731
1749
  updateRemoveButtons();
1732
1750
  }
1733
1751
  function validateNumberElement(element, key, context) {
1734
- var _a, _b, _c, _d, _e;
1752
+ var _a, _b, _c;
1735
1753
  const errors = [];
1736
1754
  const { scopeRoot, skipValidation } = context;
1737
1755
  const markValidity = (input, errorMessage) => {
@@ -1791,7 +1809,7 @@ function validateNumberElement(element, key, context) {
1791
1809
  );
1792
1810
  const values = [];
1793
1811
  inputs.forEach((input, index) => {
1794
- var _a2, _b2, _c2;
1812
+ var _a2;
1795
1813
  const raw = (_a2 = input == null ? void 0 : input.value) != null ? _a2 : "";
1796
1814
  if (raw === "") {
1797
1815
  values.push(null);
@@ -1807,8 +1825,7 @@ function validateNumberElement(element, key, context) {
1807
1825
  return;
1808
1826
  }
1809
1827
  validateNumberInput(input, v, `${key}[${index}]`);
1810
- const d = Number.isInteger((_b2 = element.decimals) != null ? _b2 : 0) ? (_c2 = element.decimals) != null ? _c2 : 0 : 0;
1811
- values.push(Number(v.toFixed(d)));
1828
+ values.push(applyDecimals(v, element.decimals));
1812
1829
  });
1813
1830
  if (!skipValidation) {
1814
1831
  const { state } = context;
@@ -1827,7 +1844,7 @@ function validateNumberElement(element, key, context) {
1827
1844
  }
1828
1845
  return { value: values, errors };
1829
1846
  } else {
1830
- const input = scopeRoot.querySelector(`[name$="${key}"]`);
1847
+ const input = scopeRoot.querySelector(`[name="${key}"]`);
1831
1848
  const raw = (_c = input == null ? void 0 : input.value) != null ? _c : "";
1832
1849
  const { state } = context;
1833
1850
  if (!skipValidation && element.required && raw === "") {
@@ -1848,10 +1865,13 @@ function validateNumberElement(element, key, context) {
1848
1865
  return { value: null, errors };
1849
1866
  }
1850
1867
  validateNumberInput(input, v, key);
1851
- const d = Number.isInteger((_d = element.decimals) != null ? _d : 0) ? (_e = element.decimals) != null ? _e : 0 : 0;
1852
- return { value: Number(v.toFixed(d)), errors };
1868
+ return { value: applyDecimals(v, element.decimals), errors };
1853
1869
  }
1854
1870
  }
1871
+ function applyDecimals(v, decimals) {
1872
+ if (!Number.isInteger(decimals) || decimals < 0) return v;
1873
+ return Number(v.toFixed(decimals));
1874
+ }
1855
1875
  function updateNumberField(element, fieldPath, value, context) {
1856
1876
  const { scopeRoot } = context;
1857
1877
  if (element.multiple) {
@@ -1998,6 +2018,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
1998
2018
  removeBtn.className = "remove-item-btn px-2 py-1 text-red-600 hover:bg-red-50 rounded";
1999
2019
  removeBtn.innerHTML = "\u2715";
2000
2020
  removeBtn.onclick = () => {
2021
+ var _a2;
2001
2022
  const currentIndex = Array.from(container.children).indexOf(item);
2002
2023
  if (container.children.length > minCount) {
2003
2024
  values.splice(currentIndex, 1);
@@ -2005,6 +2026,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
2005
2026
  updateIndices();
2006
2027
  updateAddButton();
2007
2028
  updateRemoveButtons();
2029
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
2008
2030
  }
2009
2031
  };
2010
2032
  item.appendChild(removeBtn);
@@ -2020,12 +2042,13 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
2020
2042
  const handle = createAddItemRow(
2021
2043
  "select",
2022
2044
  () => {
2023
- var _a2, _b2;
2045
+ var _a2, _b2, _c2;
2024
2046
  const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
2025
2047
  values.push(defaultValue);
2026
2048
  addSelectItem(defaultValue);
2027
2049
  updateAddButton();
2028
2050
  updateRemoveButtons();
2051
+ (_c2 = ctx.instance) == null ? void 0 : _c2.triggerOnChange(pathKey);
2029
2052
  },
2030
2053
  { label: element.addLabel }
2031
2054
  );
@@ -2115,7 +2138,7 @@ function validateSelectElement(element, key, context) {
2115
2138
  return { value: values, errors };
2116
2139
  } else {
2117
2140
  const input = scopeRoot.querySelector(
2118
- `[name$="${key}"]`
2141
+ `[name="${key}"]`
2119
2142
  );
2120
2143
  const val = (_a = input == null ? void 0 : input.value) != null ? _a : "";
2121
2144
  if (!skipValidation && element.required && val === "") {
@@ -2440,6 +2463,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
2440
2463
  removeBtn.style.backgroundColor = "transparent";
2441
2464
  });
2442
2465
  removeBtn.onclick = () => {
2466
+ var _a2;
2443
2467
  const currentIndex = Array.from(container.children).indexOf(
2444
2468
  item
2445
2469
  );
@@ -2449,6 +2473,7 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
2449
2473
  updateIndices();
2450
2474
  updateAddButton();
2451
2475
  updateRemoveButtons();
2476
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
2452
2477
  }
2453
2478
  };
2454
2479
  item.appendChild(removeBtn);
@@ -2464,12 +2489,13 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
2464
2489
  const handle = createAddItemRow(
2465
2490
  "switcher",
2466
2491
  () => {
2467
- var _a2, _b2;
2492
+ var _a2, _b2, _c2;
2468
2493
  const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
2469
2494
  values.push(defaultValue);
2470
2495
  addSwitcherItem(defaultValue);
2471
2496
  updateAddButton();
2472
2497
  updateRemoveButtons();
2498
+ (_c2 = ctx.instance) == null ? void 0 : _c2.triggerOnChange(pathKey);
2473
2499
  },
2474
2500
  { label: element.addLabel }
2475
2501
  );
@@ -2568,7 +2594,7 @@ function validateSwitcherElement(element, key, context) {
2568
2594
  return { value: values, errors };
2569
2595
  } else {
2570
2596
  const input = scopeRoot.querySelector(
2571
- `input[type="hidden"][name$="${key}"]`
2597
+ `input[type="hidden"][name="${key}"]`
2572
2598
  );
2573
2599
  const val = (_a = input == null ? void 0 : input.value) != null ? _a : "";
2574
2600
  if (!skipValidation && element.required && val === "") {
@@ -5664,7 +5690,7 @@ function validateSingleFile(element, key, context) {
5664
5690
  const { scopeRoot, skipValidation, state } = context;
5665
5691
  const errors = [];
5666
5692
  const input = scopeRoot.querySelector(
5667
- `input[name$="${key}"][type="hidden"]`
5693
+ `input[name="${key}"][type="hidden"]`
5668
5694
  );
5669
5695
  const rid = (_a = input == null ? void 0 : input.value) != null ? _a : "";
5670
5696
  if (!skipValidation && element.required && rid === "") {
@@ -6088,6 +6114,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
6088
6114
  removeBtn.style.backgroundColor = "transparent";
6089
6115
  });
6090
6116
  removeBtn.onclick = () => {
6117
+ var _a2;
6091
6118
  const currentIndex = Array.from(container.children).indexOf(
6092
6119
  item
6093
6120
  );
@@ -6097,6 +6124,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
6097
6124
  updateIndices();
6098
6125
  updateAddButton();
6099
6126
  updateRemoveButtons();
6127
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
6100
6128
  }
6101
6129
  };
6102
6130
  item.appendChild(removeBtn);
@@ -6112,11 +6140,13 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
6112
6140
  const handle = createAddItemRow(
6113
6141
  "colour",
6114
6142
  () => {
6143
+ var _a2;
6115
6144
  const defaultColour = element.default || "#000000";
6116
6145
  values.push(defaultColour);
6117
6146
  addColourItem(defaultColour);
6118
6147
  updateAddButton();
6119
6148
  updateRemoveButtons();
6149
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
6120
6150
  },
6121
6151
  { label: element.addLabel }
6122
6152
  );
@@ -6550,6 +6580,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
6550
6580
  removeBtn.style.backgroundColor = "transparent";
6551
6581
  });
6552
6582
  removeBtn.onclick = () => {
6583
+ var _a2;
6553
6584
  const currentIndex = Array.from(container.children).indexOf(
6554
6585
  item
6555
6586
  );
@@ -6559,6 +6590,7 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
6559
6590
  updateIndices();
6560
6591
  updateAddButton();
6561
6592
  updateRemoveButtons();
6593
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
6562
6594
  }
6563
6595
  };
6564
6596
  item.appendChild(removeBtn);
@@ -6574,10 +6606,12 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
6574
6606
  const handle = createAddItemRow(
6575
6607
  "slider",
6576
6608
  () => {
6609
+ var _a2;
6577
6610
  values.push(defaultValue);
6578
6611
  addSliderItem(defaultValue);
6579
6612
  updateAddButton();
6580
6613
  updateRemoveButtons();
6614
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
6581
6615
  },
6582
6616
  { label: element.addLabel }
6583
6617
  );
@@ -6848,6 +6882,8 @@ function extractRootFormData(formRoot) {
6848
6882
  if (input.checked) {
6849
6883
  data[fieldName] = input.value;
6850
6884
  }
6885
+ } else if (input.dataset.hiddenField) {
6886
+ data[fieldName] = deserializeHiddenValue(input.value);
6851
6887
  } else {
6852
6888
  data[fieldName] = input.value;
6853
6889
  }
@@ -6968,7 +7004,7 @@ function mountRemoveButton(item, onRemove, state, containerLabel) {
6968
7004
  item.classList.add("fb-row-removable");
6969
7005
  item.insertBefore(rem, item.firstChild);
6970
7006
  }
6971
- function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
7007
+ function renderMultipleContainerElement(element, ctx, wrapper, pathKey) {
6972
7008
  var _a, _b, _c, _d;
6973
7009
  const state = ctx.state;
6974
7010
  const containerIsReadonly = isElementReadonly(element, state, ctx);
@@ -6999,9 +7035,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
6999
7035
  );
7000
7036
  const countItems = () => directRows().length;
7001
7037
  const handleRemoveItem = (item) => {
7038
+ var _a2;
7002
7039
  if (countItems() <= min) return;
7003
7040
  item.remove();
7004
7041
  updateAddButton();
7042
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
7005
7043
  };
7006
7044
  const createContainerItem = (idx, rowPrefill, formData) => {
7007
7045
  const subCtx = {
@@ -7045,6 +7083,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
7045
7083
  return item;
7046
7084
  };
7047
7085
  const handleAddItem = () => {
7086
+ var _a2;
7048
7087
  if (countItems() >= max) return;
7049
7088
  const item = createContainerItem(
7050
7089
  takeRowIndex(),
@@ -7059,6 +7098,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
7059
7098
  itemsWrap.appendChild(item);
7060
7099
  }
7061
7100
  updateAddButton();
7101
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey);
7062
7102
  };
7063
7103
  let slideAddTile = null;
7064
7104
  let slideAddUpdate = null;
@@ -7360,7 +7400,7 @@ function renderGroupElement(element, ctx, wrapper, pathKey) {
7360
7400
  maxCount: (_b = element.repeat) == null ? void 0 : _b.max
7361
7401
  };
7362
7402
  if (containerElement.multiple) {
7363
- renderMultipleContainerElement(containerElement, ctx, wrapper);
7403
+ renderMultipleContainerElement(containerElement, ctx, wrapper, pathKey);
7364
7404
  } else {
7365
7405
  renderSingleContainerElement(containerElement, ctx, wrapper, pathKey);
7366
7406
  }
@@ -10428,15 +10468,19 @@ var componentRegistry = {
10428
10468
  function getComponentOperations(elementType) {
10429
10469
  return componentRegistry[elementType] || null;
10430
10470
  }
10471
+ function resolveOperations(element) {
10472
+ const isHiddenField = element.type !== "markdown" && (element.type === "hidden" || Boolean(element.hidden));
10473
+ return isHiddenField ? componentRegistry.hidden : getComponentOperations(element.type);
10474
+ }
10431
10475
  function validateElementWithComponent(element, key, context) {
10432
- const ops = getComponentOperations(element.type);
10476
+ const ops = resolveOperations(element);
10433
10477
  if (ops && ops.validate) {
10434
10478
  return ops.validate(element, key, context);
10435
10479
  }
10436
10480
  return null;
10437
10481
  }
10438
10482
  function updateElementWithComponent(element, fieldPath, value, context) {
10439
- const ops = getComponentOperations(element.type);
10483
+ const ops = resolveOperations(element);
10440
10484
  if (ops && ops.update) {
10441
10485
  ops.update(element, fieldPath, value, context);
10442
10486
  return true;
@@ -10448,6 +10492,10 @@ function updateElementWithComponent(element, fieldPath, value, context) {
10448
10492
  function showTooltip(tooltipId, button) {
10449
10493
  const tooltip = document.getElementById(tooltipId);
10450
10494
  if (!tooltip) return;
10495
+ if (!button.isConnected) {
10496
+ tooltip.remove();
10497
+ return;
10498
+ }
10451
10499
  const isCurrentlyVisible = !tooltip.classList.contains("hidden");
10452
10500
  document.querySelectorAll('[id^="tooltip-"]').forEach((t2) => {
10453
10501
  t2.classList.add("hidden");
@@ -10538,6 +10586,8 @@ function extractDOMValue(fieldPath, formRoot) {
10538
10586
  `[name="${fieldPath}"]:checked`
10539
10587
  );
10540
10588
  return checked ? checked.value : void 0;
10589
+ } else if (input.dataset.hiddenField) {
10590
+ return deserializeHiddenValue(input.value);
10541
10591
  } else {
10542
10592
  return input.value;
10543
10593
  }
@@ -10676,7 +10726,7 @@ function createFieldLabel(element) {
10676
10726
  }
10677
10727
  return title;
10678
10728
  }
10679
- function createInfoButton(element) {
10729
+ function createInfoButton(element, state) {
10680
10730
  const infoBtn = document.createElement("button");
10681
10731
  infoBtn.type = "button";
10682
10732
  infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
@@ -10688,6 +10738,7 @@ function createInfoButton(element) {
10688
10738
  tooltip.style.position = "fixed";
10689
10739
  tooltip.textContent = element.description || element.hint || "Field information";
10690
10740
  document.body.appendChild(tooltip);
10741
+ state.tooltipElements.add(tooltip);
10691
10742
  infoBtn.onclick = (e) => {
10692
10743
  e.preventDefault();
10693
10744
  e.stopPropagation();
@@ -10695,7 +10746,7 @@ function createInfoButton(element) {
10695
10746
  };
10696
10747
  return infoBtn;
10697
10748
  }
10698
- function createLabelContainer(element) {
10749
+ function createLabelContainer(element, state) {
10699
10750
  const label = document.createElement("div");
10700
10751
  label.className = "flex items-center";
10701
10752
  label.style.marginBottom = "var(--fb-label-margin-bottom, 2px)";
@@ -10703,7 +10754,7 @@ function createLabelContainer(element) {
10703
10754
  const title = createFieldLabel(element);
10704
10755
  label.appendChild(title);
10705
10756
  if (element.description || element.hint) {
10706
- const infoBtn = createInfoButton(element);
10757
+ const infoBtn = createInfoButton(element, state);
10707
10758
  label.appendChild(infoBtn);
10708
10759
  }
10709
10760
  return label;
@@ -10778,7 +10829,7 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
10778
10829
  break;
10779
10830
  case "container":
10780
10831
  if (isMultiple) {
10781
- renderMultipleContainerElement(element, ctx, wrapper);
10832
+ renderMultipleContainerElement(element, ctx, wrapper, pathKey);
10782
10833
  } else {
10783
10834
  renderSingleContainerElement(element, ctx, wrapper, pathKey);
10784
10835
  }
@@ -10831,7 +10882,7 @@ function renderElement2(element, ctx) {
10831
10882
  wrapper.setAttribute("data-fb-width", element.width || "full");
10832
10883
  const ops = getComponentOperations(element.type);
10833
10884
  if (!(ops == null ? void 0 : ops.ownsLabel)) {
10834
- const label = createLabelContainer(element);
10885
+ const label = createLabelContainer(element, ctx.state);
10835
10886
  wrapper.appendChild(label);
10836
10887
  }
10837
10888
  const pathKey = pathJoin(ctx.path, element.key);
@@ -11049,7 +11100,8 @@ function createInstanceState(config) {
11049
11100
  prefill: {},
11050
11101
  syntheticElementIds: /* @__PURE__ */ new WeakMap(),
11051
11102
  syntheticElementIdCounter: 0,
11052
- enableIfObservers: /* @__PURE__ */ new Set()
11103
+ enableIfObservers: /* @__PURE__ */ new Set(),
11104
+ tooltipElements: /* @__PURE__ */ new Set()
11053
11105
  };
11054
11106
  }
11055
11107
  function generateInstanceId() {
@@ -11429,7 +11481,11 @@ var FormBuilderInstance = class {
11429
11481
  /**
11430
11482
  * Trigger onChange callbacks with debouncing
11431
11483
  * @param fieldPath - Optional field path for field-specific change events
11432
- * @param fieldValue - Optional field value for field-specific change events
11484
+ * @param fieldValue - Optional field value for field-specific change events.
11485
+ * When omitted while fieldPath is given, the value is read from the
11486
+ * freshly extracted form data at debounce time — used by structural
11487
+ * changes (multi-item add/remove), where the handler has no cheap
11488
+ * current value but the array is trivially derivable after the fact.
11433
11489
  */
11434
11490
  triggerOnChange(fieldPath, fieldValue) {
11435
11491
  if (this.state.config.readonly) return;
@@ -11442,12 +11498,49 @@ var FormBuilderInstance = class {
11442
11498
  if (this.state.config.onChange) {
11443
11499
  this.state.config.onChange(formData);
11444
11500
  }
11445
- if (this.state.config.onFieldChange && fieldPath !== void 0 && fieldValue !== void 0) {
11446
- this.state.config.onFieldChange(fieldPath, fieldValue, formData);
11501
+ if (this.state.config.onFieldChange && fieldPath !== void 0) {
11502
+ const resolvedValue = fieldValue !== void 0 ? fieldValue : this.resolveDomPathValue(formData.data, fieldPath);
11503
+ this.state.config.onFieldChange(fieldPath, resolvedValue, formData);
11447
11504
  }
11448
11505
  this.state.debounceTimer = null;
11449
11506
  }, this.state.config.debounceMs);
11450
11507
  }
11508
+ /**
11509
+ * Resolve a DOM field path against the extracted form data.
11510
+ *
11511
+ * A plain getValueByPath is wrong for paths inside a multiple container:
11512
+ * row markers keep gaps after a deletion (`s[2]` may be the first surviving
11513
+ * row) while the extracted array is re-packed contiguously — the naive
11514
+ * lookup would read a different row, or nothing. Each `[N]` segment is
11515
+ * mapped from its marker to the row's position among the container's
11516
+ * rendered rows, the same DOM order extraction used to build the array.
11517
+ * A bracketed segment that is not a container marker (a multi-value leaf
11518
+ * like `tags[1]`, whose indices are contiguous) falls back to the index.
11519
+ */
11520
+ resolveDomPathValue(data, domPath) {
11521
+ let cur = data;
11522
+ let domPrefix = "";
11523
+ for (const seg of domPath.split(".")) {
11524
+ if (cur === null || cur === void 0) return void 0;
11525
+ const marker = seg.match(/^(.+)\[(\d+)\]$/);
11526
+ if (!marker) {
11527
+ domPrefix = domPrefix ? `${domPrefix}.${seg}` : seg;
11528
+ cur = cur[seg];
11529
+ continue;
11530
+ }
11531
+ const key = marker[1];
11532
+ domPrefix = domPrefix ? `${domPrefix}.${key}` : key;
11533
+ const arr = cur[key];
11534
+ if (!Array.isArray(arr)) return void 0;
11535
+ const rows = this.state.formRoot ? findDirectContainerRows(this.state.formRoot, domPrefix) : [];
11536
+ domPrefix = `${domPrefix}[${marker[2]}]`;
11537
+ const pos = rows.findIndex(
11538
+ (row) => row.getAttribute("data-container-item") === domPrefix
11539
+ );
11540
+ cur = pos >= 0 ? arr[pos] : arr[parseInt(marker[2], 10)];
11541
+ }
11542
+ return cur;
11543
+ }
11451
11544
  /**
11452
11545
  * Register an external action that will be displayed as a button
11453
11546
  * External actions can be form-level (no related_field) or field-level (with related_field)
@@ -11487,21 +11580,10 @@ var FormBuilderInstance = class {
11487
11580
  */
11488
11581
  findFormElementByFieldPath(fieldPath) {
11489
11582
  if (!this.state.formRoot) return null;
11490
- let element = this.state.formRoot.querySelector(
11583
+ const element = this.state.formRoot.querySelector(
11491
11584
  `[name="${fieldPath}"]`
11492
11585
  );
11493
11586
  if (element) return element;
11494
- const variations = [
11495
- fieldPath,
11496
- fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
11497
- fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
11498
- ];
11499
- for (const variation of variations) {
11500
- element = this.state.formRoot.querySelector(
11501
- `[name="${variation}"]`
11502
- );
11503
- if (element) return element;
11504
- }
11505
11587
  const schemaElement = this.findSchemaElement(fieldPath);
11506
11588
  if (!schemaElement) return null;
11507
11589
  const fieldWrappers = this.state.formRoot.querySelectorAll(".fb-field-wrapper");
@@ -11751,6 +11833,7 @@ var FormBuilderInstance = class {
11751
11833
  return;
11752
11834
  }
11753
11835
  this.disconnectEnableIfObservers();
11836
+ this.removeTooltipElements();
11754
11837
  this.state.formRoot = root;
11755
11838
  this.state.schema = schema;
11756
11839
  this.state.externalActions = actions || null;
@@ -11836,7 +11919,6 @@ var FormBuilderInstance = class {
11836
11919
  };
11837
11920
  setValidateElement(validateElement2);
11838
11921
  this.state.schema.elements.forEach((element) => {
11839
- var _a;
11840
11922
  if (element.enableIf) {
11841
11923
  try {
11842
11924
  const shouldEnable = evaluateEnableCondition(element.enableIf, data);
@@ -11853,24 +11935,12 @@ var FormBuilderInstance = class {
11853
11935
  if (element.type === "markdown") {
11854
11936
  return;
11855
11937
  }
11856
- if (element.hidden || element.type === "hidden") {
11857
- const hiddenInput = this.state.formRoot.querySelector(
11858
- `input[type="hidden"][data-hidden-field="true"][name="${element.key}"]`
11859
- );
11860
- const raw = (_a = hiddenInput == null ? void 0 : hiddenInput.value) != null ? _a : "";
11861
- if (raw !== "") {
11862
- data[element.key] = deserializeHiddenValue(raw);
11863
- } else {
11864
- data[element.key] = element.default !== void 0 ? element.default : null;
11865
- }
11866
- } else {
11867
- const result = validateElement2(element, { path: "" });
11868
- if (result.skip) return;
11869
- if (result.spread && result.value !== null && typeof result.value === "object") {
11870
- Object.assign(data, result.value);
11871
- } else if (element.key) {
11872
- data[element.key] = result.value;
11873
- }
11938
+ const result = validateElement2(element, { path: "" });
11939
+ if (result.skip) return;
11940
+ if (result.spread && result.value !== null && typeof result.value === "object") {
11941
+ Object.assign(data, result.value);
11942
+ } else if (element.key) {
11943
+ data[element.key] = result.value;
11874
11944
  }
11875
11945
  });
11876
11946
  return {
@@ -12193,6 +12263,7 @@ var FormBuilderInstance = class {
12193
12263
  this.state.debounceTimer = null;
12194
12264
  }
12195
12265
  this.disconnectEnableIfObservers();
12266
+ this.removeTooltipElements();
12196
12267
  this.state.resourceIndex.clear();
12197
12268
  if (this.state.formRoot) {
12198
12269
  clear(this.state.formRoot);
@@ -12210,6 +12281,12 @@ var FormBuilderInstance = class {
12210
12281
  }
12211
12282
  this.state.enableIfObservers.clear();
12212
12283
  }
12284
+ removeTooltipElements() {
12285
+ for (const tooltip of this.state.tooltipElements) {
12286
+ tooltip.remove();
12287
+ }
12288
+ this.state.tooltipElements.clear();
12289
+ }
12213
12290
  };
12214
12291
 
12215
12292
  // src/index.ts