@dmitryvim/form-builder 0.2.17 → 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 +99 -71
- package/dist/browser/formbuilder.v0.2.18.min.js +386 -0
- package/dist/cjs/index.cjs +478 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +469 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +99 -71
- 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.17.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");
|
|
@@ -4617,6 +5080,13 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
4617
5080
|
renderSelectElement(element, ctx, wrapper, pathKey);
|
|
4618
5081
|
}
|
|
4619
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;
|
|
4620
5090
|
case "file":
|
|
4621
5091
|
if (isMultiple) {
|
|
4622
5092
|
renderMultipleFileElement(element, ctx, wrapper, pathKey);
|
|
@@ -4740,7 +5210,8 @@ var defaultConfig = {
|
|
|
4740
5210
|
invalidHexColour: "Invalid hex color",
|
|
4741
5211
|
minFiles: "Minimum {min} files required",
|
|
4742
5212
|
maxFiles: "Maximum {max} files allowed",
|
|
4743
|
-
unsupportedFieldType: "Unsupported field type: {type}"
|
|
5213
|
+
unsupportedFieldType: "Unsupported field type: {type}",
|
|
5214
|
+
invalidOption: "Invalid option"
|
|
4744
5215
|
},
|
|
4745
5216
|
ru: {
|
|
4746
5217
|
// UI texts
|
|
@@ -4785,7 +5256,8 @@ var defaultConfig = {
|
|
|
4785
5256
|
invalidHexColour: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0446\u0432\u0435\u0442\u0430",
|
|
4786
5257
|
minFiles: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {min} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
4787
5258
|
maxFiles: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
4788
|
-
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"
|
|
4789
5261
|
}
|
|
4790
5262
|
},
|
|
4791
5263
|
theme: {}
|
|
@@ -5030,6 +5502,10 @@ var componentRegistry = {
|
|
|
5030
5502
|
validate: validateSelectElement,
|
|
5031
5503
|
update: updateSelectField
|
|
5032
5504
|
},
|
|
5505
|
+
switcher: {
|
|
5506
|
+
validate: validateSwitcherElement,
|
|
5507
|
+
update: updateSwitcherField
|
|
5508
|
+
},
|
|
5033
5509
|
file: {
|
|
5034
5510
|
validate: validateFileElement,
|
|
5035
5511
|
update: updateFileField
|