@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.
- package/README.md +1 -0
- package/dist/browser/formbuilder.min.js +86 -58
- package/dist/browser/formbuilder.v0.2.18.min.js +386 -0
- package/dist/cjs/index.cjs +508 -24
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +499 -24
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +86 -58
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/components/switcher.d.ts +13 -0
- package/dist/types/components/textarea.d.ts +2 -1
- package/dist/types/types/config.d.ts +1 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +9 -1
- package/package.json +2 -2
- package/dist/browser/formbuilder.v0.2.16.min.js +0 -358
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
4117
|
-
for (let i = 0; i < itemCount; i++) {
|
|
4578
|
+
containerWrappers.forEach((itemContainer) => {
|
|
4118
4579
|
const itemData = {};
|
|
4119
|
-
const
|
|
4120
|
-
|
|
4121
|
-
)
|
|
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}[${
|
|
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}[${
|
|
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
|
-
|
|
5815
|
-
|
|
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);
|