@dmitryvim/form-builder 0.2.16 → 0.2.18

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.
@@ -729,6 +729,21 @@ function updateTextField(element, fieldPath, value, context) {
729
729
  }
730
730
 
731
731
  // src/components/textarea.ts
732
+ function applyAutoExpand(textarea) {
733
+ textarea.style.overflow = "hidden";
734
+ textarea.style.resize = "none";
735
+ const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
736
+ textarea.rows = Math.max(1, lineCount);
737
+ const resize = () => {
738
+ if (!textarea.isConnected) return;
739
+ textarea.style.height = "0";
740
+ textarea.style.height = `${textarea.scrollHeight}px`;
741
+ };
742
+ textarea.addEventListener("input", resize);
743
+ setTimeout(() => {
744
+ if (textarea.isConnected) resize();
745
+ }, 0);
746
+ }
732
747
  function renderTextareaElement(element, ctx, wrapper, pathKey) {
733
748
  const state = ctx.state;
734
749
  const textareaWrapper = document.createElement("div");
@@ -749,6 +764,9 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
749
764
  textareaInput.addEventListener("blur", handleChange);
750
765
  textareaInput.addEventListener("input", handleChange);
751
766
  }
767
+ if (element.autoExpand || state.config.readonly) {
768
+ applyAutoExpand(textareaInput);
769
+ }
752
770
  textareaWrapper.appendChild(textareaInput);
753
771
  if (!state.config.readonly && (element.minLength != null || element.maxLength != null)) {
754
772
  const counter = createCharCounter(element, textareaInput, true);
@@ -798,6 +816,9 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
798
816
  textareaInput.addEventListener("blur", handleChange);
799
817
  textareaInput.addEventListener("input", handleChange);
800
818
  }
819
+ if (element.autoExpand || state.config.readonly) {
820
+ applyAutoExpand(textareaInput);
821
+ }
801
822
  textareaContainer.appendChild(textareaInput);
802
823
  if (!state.config.readonly && (element.minLength != null || element.maxLength != null)) {
803
824
  const counter = createCharCounter(element, textareaInput, true);
@@ -899,6 +920,24 @@ function validateTextareaElement(element, key, context) {
899
920
  }
900
921
  function updateTextareaField(element, fieldPath, value, context) {
901
922
  updateTextField(element, fieldPath, value, context);
923
+ const { scopeRoot, state } = context;
924
+ const shouldAutoExpand = element.autoExpand || state.config.readonly;
925
+ if (!shouldAutoExpand) return;
926
+ if (element.multiple) {
927
+ const textareas = scopeRoot.querySelectorAll(
928
+ `textarea[name^="${fieldPath}["]`
929
+ );
930
+ textareas.forEach((textarea) => {
931
+ textarea.dispatchEvent(new Event("input"));
932
+ });
933
+ } else {
934
+ const textarea = scopeRoot.querySelector(
935
+ `textarea[name="${fieldPath}"]`
936
+ );
937
+ if (textarea) {
938
+ textarea.dispatchEvent(new Event("input"));
939
+ }
940
+ }
902
941
  }
903
942
 
904
943
  // src/components/number.ts
@@ -1567,6 +1606,430 @@ function updateSelectField(element, fieldPath, value, context) {
1567
1606
  }
1568
1607
  }
1569
1608
 
1609
+ // src/components/switcher.ts
1610
+ function applySelectedStyle(btn) {
1611
+ btn.style.backgroundColor = "var(--fb-primary-color)";
1612
+ btn.style.color = "#ffffff";
1613
+ btn.style.borderColor = "var(--fb-primary-color)";
1614
+ }
1615
+ function applyUnselectedStyle(btn) {
1616
+ btn.style.backgroundColor = "transparent";
1617
+ btn.style.color = "var(--fb-text-color)";
1618
+ btn.style.borderColor = "var(--fb-border-color)";
1619
+ }
1620
+ function buildSegmentedGroup(element, currentValue, hiddenInput, readonly, onChange) {
1621
+ const options = element.options || [];
1622
+ const group = document.createElement("div");
1623
+ group.className = "fb-switcher-group";
1624
+ group.style.cssText = `
1625
+ display: inline-flex;
1626
+ flex-direction: row;
1627
+ flex-wrap: nowrap;
1628
+ `;
1629
+ const buttons = [];
1630
+ options.forEach((option, index) => {
1631
+ const btn = document.createElement("button");
1632
+ btn.type = "button";
1633
+ btn.className = "fb-switcher-btn";
1634
+ btn.dataset.value = option.value;
1635
+ btn.textContent = option.label;
1636
+ btn.style.cssText = `
1637
+ padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
1638
+ font-size: var(--fb-font-size);
1639
+ border-width: var(--fb-border-width);
1640
+ border-style: solid;
1641
+ cursor: ${readonly ? "default" : "pointer"};
1642
+ transition: background-color var(--fb-transition-duration), color var(--fb-transition-duration), border-color var(--fb-transition-duration);
1643
+ white-space: nowrap;
1644
+ line-height: 1.25;
1645
+ outline: none;
1646
+ `;
1647
+ if (options.length === 1) {
1648
+ btn.style.borderRadius = "var(--fb-border-radius)";
1649
+ } else if (index === 0) {
1650
+ btn.style.borderRadius = "var(--fb-border-radius) 0 0 var(--fb-border-radius)";
1651
+ btn.style.borderRightWidth = "0";
1652
+ } else if (index === options.length - 1) {
1653
+ btn.style.borderRadius = "0 var(--fb-border-radius) var(--fb-border-radius) 0";
1654
+ } else {
1655
+ btn.style.borderRadius = "0";
1656
+ btn.style.borderRightWidth = "0";
1657
+ }
1658
+ if (option.value === currentValue) {
1659
+ applySelectedStyle(btn);
1660
+ } else {
1661
+ applyUnselectedStyle(btn);
1662
+ }
1663
+ if (!readonly) {
1664
+ btn.addEventListener("click", () => {
1665
+ hiddenInput.value = option.value;
1666
+ buttons.forEach((b) => {
1667
+ if (b.dataset.value === option.value) {
1668
+ applySelectedStyle(b);
1669
+ } else {
1670
+ applyUnselectedStyle(b);
1671
+ }
1672
+ });
1673
+ if (onChange) {
1674
+ onChange(option.value);
1675
+ }
1676
+ });
1677
+ btn.addEventListener("mouseenter", () => {
1678
+ if (hiddenInput.value !== option.value) {
1679
+ btn.style.backgroundColor = "var(--fb-background-hover-color)";
1680
+ }
1681
+ });
1682
+ btn.addEventListener("mouseleave", () => {
1683
+ if (hiddenInput.value !== option.value) {
1684
+ btn.style.backgroundColor = "transparent";
1685
+ }
1686
+ });
1687
+ }
1688
+ buttons.push(btn);
1689
+ group.appendChild(btn);
1690
+ });
1691
+ return group;
1692
+ }
1693
+ function renderSwitcherElement(element, ctx, wrapper, pathKey) {
1694
+ var _a, _b;
1695
+ const state = ctx.state;
1696
+ const initialValue = String((_b = (_a = ctx.prefill[element.key]) != null ? _a : element.default) != null ? _b : "");
1697
+ const hiddenInput = document.createElement("input");
1698
+ hiddenInput.type = "hidden";
1699
+ hiddenInput.name = pathKey;
1700
+ hiddenInput.value = initialValue;
1701
+ const readonly = state.config.readonly;
1702
+ const onChange = !readonly && ctx.instance ? (value) => {
1703
+ ctx.instance.triggerOnChange(pathKey, value);
1704
+ } : null;
1705
+ const group = buildSegmentedGroup(
1706
+ element,
1707
+ initialValue,
1708
+ hiddenInput,
1709
+ readonly,
1710
+ onChange
1711
+ );
1712
+ wrapper.appendChild(hiddenInput);
1713
+ wrapper.appendChild(group);
1714
+ if (!readonly) {
1715
+ const hint = document.createElement("p");
1716
+ hint.className = "text-xs text-gray-500 mt-1";
1717
+ hint.textContent = makeFieldHint(element, state);
1718
+ wrapper.appendChild(hint);
1719
+ }
1720
+ }
1721
+ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
1722
+ var _a, _b, _c, _d;
1723
+ const state = ctx.state;
1724
+ const prefillValues = ctx.prefill[element.key] || [];
1725
+ const values = Array.isArray(prefillValues) ? [...prefillValues] : [];
1726
+ const minCount = (_a = element.minCount) != null ? _a : 1;
1727
+ const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
1728
+ while (values.length < minCount) {
1729
+ values.push(element.default || ((_d = (_c = element.options) == null ? void 0 : _c[0]) == null ? void 0 : _d.value) || "");
1730
+ }
1731
+ const readonly = state.config.readonly;
1732
+ const container = document.createElement("div");
1733
+ container.className = "space-y-2";
1734
+ wrapper.appendChild(container);
1735
+ function updateIndices() {
1736
+ const items = container.querySelectorAll(".multiple-switcher-item");
1737
+ items.forEach((item, index) => {
1738
+ const input = item.querySelector("input[type=hidden]");
1739
+ if (input) {
1740
+ input.name = `${pathKey}[${index}]`;
1741
+ }
1742
+ });
1743
+ }
1744
+ function addSwitcherItem(value = "", index = -1) {
1745
+ const currentIndex = index === -1 ? container.children.length : index;
1746
+ const itemPathKey = `${pathKey}[${currentIndex}]`;
1747
+ const itemWrapper = document.createElement("div");
1748
+ itemWrapper.className = "multiple-switcher-item flex items-center gap-2";
1749
+ const hiddenInput = document.createElement("input");
1750
+ hiddenInput.type = "hidden";
1751
+ hiddenInput.name = itemPathKey;
1752
+ hiddenInput.value = value;
1753
+ itemWrapper.appendChild(hiddenInput);
1754
+ const onChange = !readonly && ctx.instance ? (val) => {
1755
+ ctx.instance.triggerOnChange(hiddenInput.name, val);
1756
+ } : null;
1757
+ const group = buildSegmentedGroup(
1758
+ element,
1759
+ value,
1760
+ hiddenInput,
1761
+ readonly,
1762
+ onChange
1763
+ );
1764
+ itemWrapper.appendChild(group);
1765
+ if (index === -1) {
1766
+ container.appendChild(itemWrapper);
1767
+ } else {
1768
+ container.insertBefore(itemWrapper, container.children[index]);
1769
+ }
1770
+ updateIndices();
1771
+ return itemWrapper;
1772
+ }
1773
+ function updateRemoveButtons() {
1774
+ if (readonly) return;
1775
+ const items = container.querySelectorAll(".multiple-switcher-item");
1776
+ const currentCount = items.length;
1777
+ items.forEach((item) => {
1778
+ let removeBtn = item.querySelector(
1779
+ ".remove-item-btn"
1780
+ );
1781
+ if (!removeBtn) {
1782
+ removeBtn = document.createElement("button");
1783
+ removeBtn.type = "button";
1784
+ removeBtn.className = "remove-item-btn px-2 py-1 rounded";
1785
+ removeBtn.style.cssText = `
1786
+ color: var(--fb-error-color);
1787
+ background-color: transparent;
1788
+ transition: background-color var(--fb-transition-duration);
1789
+ `;
1790
+ removeBtn.innerHTML = "\u2715";
1791
+ removeBtn.addEventListener("mouseenter", () => {
1792
+ removeBtn.style.backgroundColor = "var(--fb-background-hover-color)";
1793
+ });
1794
+ removeBtn.addEventListener("mouseleave", () => {
1795
+ removeBtn.style.backgroundColor = "transparent";
1796
+ });
1797
+ removeBtn.onclick = () => {
1798
+ const currentIndex = Array.from(container.children).indexOf(
1799
+ item
1800
+ );
1801
+ if (container.children.length > minCount) {
1802
+ values.splice(currentIndex, 1);
1803
+ item.remove();
1804
+ updateIndices();
1805
+ updateAddButton();
1806
+ updateRemoveButtons();
1807
+ }
1808
+ };
1809
+ item.appendChild(removeBtn);
1810
+ }
1811
+ const disabled = currentCount <= minCount;
1812
+ removeBtn.disabled = disabled;
1813
+ removeBtn.style.opacity = disabled ? "0.5" : "1";
1814
+ removeBtn.style.pointerEvents = disabled ? "none" : "auto";
1815
+ });
1816
+ }
1817
+ let addRow = null;
1818
+ let countDisplay = null;
1819
+ if (!readonly) {
1820
+ addRow = document.createElement("div");
1821
+ addRow.className = "flex items-center gap-3 mt-2";
1822
+ const addBtn = document.createElement("button");
1823
+ addBtn.type = "button";
1824
+ addBtn.className = "add-switcher-btn px-3 py-1 rounded";
1825
+ addBtn.style.cssText = `
1826
+ color: var(--fb-primary-color);
1827
+ border: var(--fb-border-width) solid var(--fb-primary-color);
1828
+ background-color: transparent;
1829
+ font-size: var(--fb-font-size);
1830
+ transition: all var(--fb-transition-duration);
1831
+ `;
1832
+ addBtn.textContent = "+";
1833
+ addBtn.addEventListener("mouseenter", () => {
1834
+ addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
1835
+ });
1836
+ addBtn.addEventListener("mouseleave", () => {
1837
+ addBtn.style.backgroundColor = "transparent";
1838
+ });
1839
+ addBtn.onclick = () => {
1840
+ var _a2, _b2;
1841
+ const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
1842
+ values.push(defaultValue);
1843
+ addSwitcherItem(defaultValue);
1844
+ updateAddButton();
1845
+ updateRemoveButtons();
1846
+ };
1847
+ countDisplay = document.createElement("span");
1848
+ countDisplay.className = "text-sm text-gray-500";
1849
+ addRow.appendChild(addBtn);
1850
+ addRow.appendChild(countDisplay);
1851
+ wrapper.appendChild(addRow);
1852
+ }
1853
+ function updateAddButton() {
1854
+ if (!addRow || !countDisplay) return;
1855
+ const addBtn = addRow.querySelector(
1856
+ ".add-switcher-btn"
1857
+ );
1858
+ if (addBtn) {
1859
+ const disabled = values.length >= maxCount;
1860
+ addBtn.disabled = disabled;
1861
+ addBtn.style.opacity = disabled ? "0.5" : "1";
1862
+ addBtn.style.pointerEvents = disabled ? "none" : "auto";
1863
+ }
1864
+ countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
1865
+ }
1866
+ values.forEach((value) => addSwitcherItem(value));
1867
+ updateAddButton();
1868
+ updateRemoveButtons();
1869
+ if (!readonly) {
1870
+ const hint = document.createElement("p");
1871
+ hint.className = "text-xs text-gray-500 mt-1";
1872
+ hint.textContent = makeFieldHint(element, state);
1873
+ wrapper.appendChild(hint);
1874
+ }
1875
+ }
1876
+ function validateSwitcherElement(element, key, context) {
1877
+ var _a;
1878
+ const errors = [];
1879
+ const { scopeRoot, skipValidation } = context;
1880
+ const markValidity = (input, errorMessage) => {
1881
+ var _a2, _b;
1882
+ if (!input) return;
1883
+ const errorId = `error-${input.getAttribute("name") || Math.random().toString(36).substring(7)}`;
1884
+ let errorElement = document.getElementById(errorId);
1885
+ if (errorMessage) {
1886
+ input.classList.add("invalid");
1887
+ input.title = errorMessage;
1888
+ if (!errorElement) {
1889
+ errorElement = document.createElement("div");
1890
+ errorElement.id = errorId;
1891
+ errorElement.className = "error-message";
1892
+ errorElement.style.cssText = `
1893
+ color: var(--fb-error-color);
1894
+ font-size: var(--fb-font-size-small);
1895
+ margin-top: 0.25rem;
1896
+ `;
1897
+ if (input.nextSibling) {
1898
+ (_a2 = input.parentNode) == null ? void 0 : _a2.insertBefore(errorElement, input.nextSibling);
1899
+ } else {
1900
+ (_b = input.parentNode) == null ? void 0 : _b.appendChild(errorElement);
1901
+ }
1902
+ }
1903
+ errorElement.textContent = errorMessage;
1904
+ errorElement.style.display = "block";
1905
+ } else {
1906
+ input.classList.remove("invalid");
1907
+ input.title = "";
1908
+ if (errorElement) {
1909
+ errorElement.remove();
1910
+ }
1911
+ }
1912
+ };
1913
+ const validateMultipleCount = (fieldKey, values, el, filterFn) => {
1914
+ var _a2, _b;
1915
+ if (skipValidation) return;
1916
+ const { state } = context;
1917
+ const filteredValues = values.filter(filterFn);
1918
+ const minCount = "minCount" in el ? (_a2 = el.minCount) != null ? _a2 : 1 : 1;
1919
+ const maxCount = "maxCount" in el ? (_b = el.maxCount) != null ? _b : Infinity : Infinity;
1920
+ if (el.required && filteredValues.length === 0) {
1921
+ errors.push(`${fieldKey}: ${t("required", state)}`);
1922
+ }
1923
+ if (filteredValues.length < minCount) {
1924
+ errors.push(`${fieldKey}: ${t("minItems", state, { min: minCount })}`);
1925
+ }
1926
+ if (filteredValues.length > maxCount) {
1927
+ errors.push(`${fieldKey}: ${t("maxItems", state, { max: maxCount })}`);
1928
+ }
1929
+ };
1930
+ const validOptionValues = new Set(
1931
+ "options" in element ? element.options.map((o) => o.value) : []
1932
+ );
1933
+ if ("multiple" in element && element.multiple) {
1934
+ const inputs = scopeRoot.querySelectorAll(
1935
+ `input[type="hidden"][name^="${key}["]`
1936
+ );
1937
+ const values = [];
1938
+ inputs.forEach((input) => {
1939
+ var _a2;
1940
+ const val = (_a2 = input == null ? void 0 : input.value) != null ? _a2 : "";
1941
+ values.push(val);
1942
+ if (!skipValidation && val !== "" && !validOptionValues.has(val)) {
1943
+ const msg = t("invalidOption", context.state);
1944
+ markValidity(input, msg);
1945
+ errors.push(`${key}: ${msg}`);
1946
+ } else {
1947
+ markValidity(input, null);
1948
+ }
1949
+ });
1950
+ validateMultipleCount(key, values, element, (v) => v !== "");
1951
+ return { value: values, errors };
1952
+ } else {
1953
+ const input = scopeRoot.querySelector(
1954
+ `input[type="hidden"][name$="${key}"]`
1955
+ );
1956
+ const val = (_a = input == null ? void 0 : input.value) != null ? _a : "";
1957
+ if (!skipValidation && element.required && val === "") {
1958
+ const msg = t("required", context.state);
1959
+ errors.push(`${key}: ${msg}`);
1960
+ markValidity(input, msg);
1961
+ return { value: null, errors };
1962
+ }
1963
+ if (!skipValidation && val !== "" && !validOptionValues.has(val)) {
1964
+ const msg = t("invalidOption", context.state);
1965
+ errors.push(`${key}: ${msg}`);
1966
+ markValidity(input, msg);
1967
+ return { value: null, errors };
1968
+ }
1969
+ markValidity(input, null);
1970
+ return { value: val === "" ? null : val, errors };
1971
+ }
1972
+ }
1973
+ function updateSwitcherField(element, fieldPath, value, context) {
1974
+ var _a;
1975
+ const { scopeRoot } = context;
1976
+ if ("multiple" in element && element.multiple) {
1977
+ if (!Array.isArray(value)) {
1978
+ console.warn(
1979
+ `updateSwitcherField: Expected array for multiple field "${fieldPath}", got ${typeof value}`
1980
+ );
1981
+ return;
1982
+ }
1983
+ const inputs = scopeRoot.querySelectorAll(
1984
+ `input[type="hidden"][name^="${fieldPath}["]`
1985
+ );
1986
+ inputs.forEach((input, index) => {
1987
+ var _a2;
1988
+ if (index < value.length) {
1989
+ const newVal = value[index] != null ? String(value[index]) : "";
1990
+ input.value = newVal;
1991
+ const group = (_a2 = input.parentElement) == null ? void 0 : _a2.querySelector(".fb-switcher-group");
1992
+ if (group) {
1993
+ group.querySelectorAll(".fb-switcher-btn").forEach((btn) => {
1994
+ if (btn.dataset.value === newVal) {
1995
+ applySelectedStyle(btn);
1996
+ } else {
1997
+ applyUnselectedStyle(btn);
1998
+ }
1999
+ });
2000
+ }
2001
+ input.classList.remove("invalid");
2002
+ input.title = "";
2003
+ }
2004
+ });
2005
+ if (value.length !== inputs.length) {
2006
+ console.warn(
2007
+ `updateSwitcherField: Multiple field "${fieldPath}" has ${inputs.length} inputs but received ${value.length} values. Consider re-rendering for add/remove.`
2008
+ );
2009
+ }
2010
+ } else {
2011
+ const input = scopeRoot.querySelector(
2012
+ `input[type="hidden"][name="${fieldPath}"]`
2013
+ );
2014
+ if (input) {
2015
+ const newVal = value != null ? String(value) : "";
2016
+ input.value = newVal;
2017
+ const group = (_a = input.parentElement) == null ? void 0 : _a.querySelector(".fb-switcher-group");
2018
+ if (group) {
2019
+ group.querySelectorAll(".fb-switcher-btn").forEach((btn) => {
2020
+ if (btn.dataset.value === newVal) {
2021
+ applySelectedStyle(btn);
2022
+ } else {
2023
+ applyUnselectedStyle(btn);
2024
+ }
2025
+ });
2026
+ }
2027
+ input.classList.remove("invalid");
2028
+ input.title = "";
2029
+ }
2030
+ }
2031
+ }
2032
+
1570
2033
  // src/components/file.ts
1571
2034
  function renderLocalImagePreview(container, file, fileName, state) {
1572
2035
  const img = document.createElement("img");
@@ -3927,10 +4390,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
3927
4390
  rem.addEventListener("mouseleave", () => {
3928
4391
  rem.style.backgroundColor = "transparent";
3929
4392
  });
3930
- rem.onclick = () => {
3931
- item.remove();
3932
- updateAddButton();
3933
- };
4393
+ rem.onclick = () => handleRemoveItem(item);
3934
4394
  item.style.position = "relative";
3935
4395
  item.appendChild(rem);
3936
4396
  }
@@ -3952,6 +4412,10 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
3952
4412
  }
3953
4413
  countDisplay.textContent = `${currentCount}/${max === Infinity ? "\u221E" : max}`;
3954
4414
  };
4415
+ const handleRemoveItem = (item) => {
4416
+ item.remove();
4417
+ updateAddButton();
4418
+ };
3955
4419
  if (pre && Array.isArray(pre)) {
3956
4420
  pre.forEach((prefillObj, idx) => {
3957
4421
  var _a2;
@@ -3996,10 +4460,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
3996
4460
  rem.addEventListener("mouseleave", () => {
3997
4461
  rem.style.backgroundColor = "transparent";
3998
4462
  });
3999
- rem.onclick = () => {
4000
- item.remove();
4001
- updateAddButton();
4002
- };
4463
+ rem.onclick = () => handleRemoveItem(item);
4003
4464
  item.style.position = "relative";
4004
4465
  item.appendChild(rem);
4005
4466
  }
@@ -4050,8 +4511,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
4050
4511
  });
4051
4512
  rem.onclick = () => {
4052
4513
  if (countItems() > min) {
4053
- item.remove();
4054
- updateAddButton();
4514
+ handleRemoveItem(item);
4055
4515
  }
4056
4516
  };
4057
4517
  item.style.position = "relative";
@@ -4110,15 +4570,16 @@ function validateContainerElement(element, key, context) {
4110
4570
  "[data-container-item]"
4111
4571
  );
4112
4572
  const containerWrappers = Array.from(allContainerWrappers).filter((el) => {
4113
- const attr = el.getAttribute("data-container-item");
4114
- return attr == null ? void 0 : attr.startsWith(`${key}[`);
4573
+ const attr = el.getAttribute("data-container-item") || "";
4574
+ if (!attr.startsWith(`${key}[`)) return false;
4575
+ const suffix = attr.slice(key.length);
4576
+ return /^\[\d+\]$/.test(suffix);
4115
4577
  });
4116
- const itemCount = containerWrappers.length;
4117
- for (let i = 0; i < itemCount; i++) {
4578
+ containerWrappers.forEach((itemContainer) => {
4118
4579
  const itemData = {};
4119
- const itemContainer = scopeRoot.querySelector(
4120
- `[data-container-item="${key}[${i}]"]`
4121
- ) || scopeRoot;
4580
+ const containerAttr = itemContainer.getAttribute("data-container-item") || "";
4581
+ const indexMatch = containerAttr.match(/\[(\d+)\]$/);
4582
+ const domIndex = indexMatch ? parseInt(indexMatch[1], 10) : 0;
4122
4583
  element.elements.forEach((child) => {
4123
4584
  var _a;
4124
4585
  if (child.enableIf) {
@@ -4136,7 +4597,7 @@ function validateContainerElement(element, key, context) {
4136
4597
  }
4137
4598
  } catch (error) {
4138
4599
  console.error(
4139
- `Error evaluating enableIf for field "${child.key}" in container "${key}[${i}]":`,
4600
+ `Error evaluating enableIf for field "${child.key}" in container "${key}[${domIndex}]":`,
4140
4601
  error
4141
4602
  );
4142
4603
  }
@@ -4144,7 +4605,7 @@ function validateContainerElement(element, key, context) {
4144
4605
  if (child.hidden || child.type === "hidden") {
4145
4606
  itemData[child.key] = child.default !== void 0 ? child.default : null;
4146
4607
  } else {
4147
- const childKey = `${key}[${i}].${child.key}`;
4608
+ const childKey = `${key}[${domIndex}].${child.key}`;
4148
4609
  itemData[child.key] = validateElement(
4149
4610
  { ...child, key: childKey },
4150
4611
  { path },
@@ -4153,7 +4614,7 @@ function validateContainerElement(element, key, context) {
4153
4614
  }
4154
4615
  });
4155
4616
  items.push(itemData);
4156
- }
4617
+ });
4157
4618
  validateContainerCount(key, items, element);
4158
4619
  return { value: items, errors };
4159
4620
  } else {
@@ -4619,6 +5080,13 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
4619
5080
  renderSelectElement(element, ctx, wrapper, pathKey);
4620
5081
  }
4621
5082
  break;
5083
+ case "switcher":
5084
+ if (isMultiple) {
5085
+ renderMultipleSwitcherElement(element, ctx, wrapper, pathKey);
5086
+ } else {
5087
+ renderSwitcherElement(element, ctx, wrapper, pathKey);
5088
+ }
5089
+ break;
4622
5090
  case "file":
4623
5091
  if (isMultiple) {
4624
5092
  renderMultipleFileElement(element, ctx, wrapper, pathKey);
@@ -4742,7 +5210,8 @@ var defaultConfig = {
4742
5210
  invalidHexColour: "Invalid hex color",
4743
5211
  minFiles: "Minimum {min} files required",
4744
5212
  maxFiles: "Maximum {max} files allowed",
4745
- unsupportedFieldType: "Unsupported field type: {type}"
5213
+ unsupportedFieldType: "Unsupported field type: {type}",
5214
+ invalidOption: "Invalid option"
4746
5215
  },
4747
5216
  ru: {
4748
5217
  // UI texts
@@ -4787,7 +5256,8 @@ var defaultConfig = {
4787
5256
  invalidHexColour: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0446\u0432\u0435\u0442\u0430",
4788
5257
  minFiles: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {min} \u0444\u0430\u0439\u043B\u043E\u0432",
4789
5258
  maxFiles: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max} \u0444\u0430\u0439\u043B\u043E\u0432",
4790
- unsupportedFieldType: "\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0442\u0438\u043F \u043F\u043E\u043B\u044F: {type}"
5259
+ unsupportedFieldType: "\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0442\u0438\u043F \u043F\u043E\u043B\u044F: {type}",
5260
+ invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"
4791
5261
  }
4792
5262
  },
4793
5263
  theme: {}
@@ -5032,6 +5502,10 @@ var componentRegistry = {
5032
5502
  validate: validateSelectElement,
5033
5503
  update: updateSelectField
5034
5504
  },
5505
+ switcher: {
5506
+ validate: validateSwitcherElement,
5507
+ update: updateSwitcherField
5508
+ },
5035
5509
  file: {
5036
5510
  validate: validateFileElement,
5037
5511
  update: updateFileField
@@ -5811,8 +6285,18 @@ var FormBuilderInstance = class {
5811
6285
  if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
5812
6286
  const containerData = formData == null ? void 0 : formData[element.key];
5813
6287
  if (Array.isArray(containerData)) {
5814
- containerData.forEach((_, index) => {
5815
- checkElements(element.elements, `${fullPath}[${index}]`);
6288
+ const containerItems = this.state.formRoot.querySelectorAll(
6289
+ `[data-container-item]`
6290
+ );
6291
+ const directItems = Array.from(containerItems).filter((el) => {
6292
+ const attr = el.getAttribute("data-container-item") || "";
6293
+ if (!attr.startsWith(`${fullPath}[`)) return false;
6294
+ const suffix = attr.slice(fullPath.length);
6295
+ return /^\[\d+\]$/.test(suffix);
6296
+ });
6297
+ directItems.forEach((el) => {
6298
+ const attr = el.getAttribute("data-container-item") || "";
6299
+ checkElements(element.elements, attr);
5816
6300
  });
5817
6301
  } else {
5818
6302
  checkElements(element.elements, fullPath);