@dmitryvim/form-builder 0.2.7 → 0.2.9
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 +3 -1
- package/dist/browser/formbuilder.min.js +116 -35
- package/dist/browser/formbuilder.v0.2.9.min.js +322 -0
- package/dist/cjs/index.cjs +724 -29
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +718 -29
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +116 -35
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/components/slider.d.ts +11 -0
- package/dist/types/instance/FormBuilderInstance.d.ts +4 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +20 -1
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.7.min.js +0 -241
package/dist/cjs/index.cjs
CHANGED
|
@@ -93,6 +93,43 @@ function validateSchema(schema) {
|
|
|
93
93
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
94
94
|
}
|
|
95
95
|
if (element.type === "container" && element.elements) {
|
|
96
|
+
if ("columns" in element && element.columns !== void 0) {
|
|
97
|
+
const columns = element.columns;
|
|
98
|
+
const validColumns = [1, 2, 3, 4];
|
|
99
|
+
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
100
|
+
errors.push(
|
|
101
|
+
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if ("prefillHints" in element && element.prefillHints) {
|
|
106
|
+
const prefillHints = element.prefillHints;
|
|
107
|
+
if (Array.isArray(prefillHints)) {
|
|
108
|
+
prefillHints.forEach((hint, hintIndex) => {
|
|
109
|
+
if (!hint.label || typeof hint.label !== "string") {
|
|
110
|
+
errors.push(
|
|
111
|
+
`${elementPath}: prefillHints[${hintIndex}] must have a 'label' property of type string`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (!hint.values || typeof hint.values !== "object") {
|
|
115
|
+
errors.push(
|
|
116
|
+
`${elementPath}: prefillHints[${hintIndex}] must have a 'values' property of type object`
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
for (const fieldKey in hint.values) {
|
|
120
|
+
const fieldExists = element.elements.some(
|
|
121
|
+
(childElement) => childElement.key === fieldKey
|
|
122
|
+
);
|
|
123
|
+
if (!fieldExists) {
|
|
124
|
+
errors.push(
|
|
125
|
+
`container "${element.key}": prefillHints[${hintIndex}] references non-existent field "${fieldKey}"`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
96
133
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
97
134
|
}
|
|
98
135
|
if (element.type === "select" && element.options) {
|
|
@@ -380,7 +417,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
380
417
|
font-size: var(--fb-font-size);
|
|
381
418
|
transition: all var(--fb-transition-duration);
|
|
382
419
|
`;
|
|
383
|
-
addBtn.textContent =
|
|
420
|
+
addBtn.textContent = "+";
|
|
384
421
|
addBtn.addEventListener("mouseenter", () => {
|
|
385
422
|
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
386
423
|
});
|
|
@@ -633,7 +670,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
633
670
|
removeBtn = document.createElement("button");
|
|
634
671
|
removeBtn.type = "button";
|
|
635
672
|
removeBtn.className = "remove-item-btn mt-1 px-2 py-1 text-red-600 hover:bg-red-50 rounded text-sm";
|
|
636
|
-
removeBtn.innerHTML = "\u2715
|
|
673
|
+
removeBtn.innerHTML = "\u2715";
|
|
637
674
|
removeBtn.onclick = () => {
|
|
638
675
|
const currentIndex = Array.from(container.children).indexOf(
|
|
639
676
|
item
|
|
@@ -661,7 +698,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
661
698
|
const addBtn = document.createElement("button");
|
|
662
699
|
addBtn.type = "button";
|
|
663
700
|
addBtn.className = "add-textarea-btn mt-2 px-3 py-1 text-blue-600 border border-blue-300 rounded hover:bg-blue-50 text-sm";
|
|
664
|
-
addBtn.textContent =
|
|
701
|
+
addBtn.textContent = "+";
|
|
665
702
|
addBtn.onclick = () => {
|
|
666
703
|
values.push(element.default || "");
|
|
667
704
|
addTextareaItem(element.default || "");
|
|
@@ -804,7 +841,7 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
804
841
|
const addBtn = document.createElement("button");
|
|
805
842
|
addBtn.type = "button";
|
|
806
843
|
addBtn.className = "add-number-btn mt-2 px-3 py-1 text-blue-600 border border-blue-300 rounded hover:bg-blue-50 text-sm";
|
|
807
|
-
addBtn.textContent =
|
|
844
|
+
addBtn.textContent = "+";
|
|
808
845
|
addBtn.onclick = () => {
|
|
809
846
|
values.push(element.default || "");
|
|
810
847
|
addNumberItem(element.default || "");
|
|
@@ -1090,7 +1127,7 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1090
1127
|
const addBtn = document.createElement("button");
|
|
1091
1128
|
addBtn.type = "button";
|
|
1092
1129
|
addBtn.className = "add-select-btn mt-2 px-3 py-1 text-blue-600 border border-blue-300 rounded hover:bg-blue-50 text-sm";
|
|
1093
|
-
addBtn.textContent =
|
|
1130
|
+
addBtn.textContent = "+";
|
|
1094
1131
|
addBtn.onclick = () => {
|
|
1095
1132
|
var _a2, _b2;
|
|
1096
1133
|
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
@@ -2623,7 +2660,7 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
2623
2660
|
font-size: var(--fb-font-size);
|
|
2624
2661
|
transition: all var(--fb-transition-duration);
|
|
2625
2662
|
`;
|
|
2626
|
-
addBtn.textContent =
|
|
2663
|
+
addBtn.textContent = "+";
|
|
2627
2664
|
addBtn.addEventListener("mouseenter", () => {
|
|
2628
2665
|
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
2629
2666
|
});
|
|
@@ -2808,6 +2845,522 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
2808
2845
|
}
|
|
2809
2846
|
}
|
|
2810
2847
|
|
|
2848
|
+
// src/components/slider.ts
|
|
2849
|
+
function positionToExponential(position, min, max) {
|
|
2850
|
+
if (min <= 0) {
|
|
2851
|
+
throw new Error("Exponential scale requires min > 0");
|
|
2852
|
+
}
|
|
2853
|
+
const logMin = Math.log(min);
|
|
2854
|
+
const logMax = Math.log(max);
|
|
2855
|
+
return Math.exp(logMin + position * (logMax - logMin));
|
|
2856
|
+
}
|
|
2857
|
+
function exponentialToPosition(value, min, max) {
|
|
2858
|
+
if (min <= 0) {
|
|
2859
|
+
throw new Error("Exponential scale requires min > 0");
|
|
2860
|
+
}
|
|
2861
|
+
const logMin = Math.log(min);
|
|
2862
|
+
const logMax = Math.log(max);
|
|
2863
|
+
const logValue = Math.log(value);
|
|
2864
|
+
return (logValue - logMin) / (logMax - logMin);
|
|
2865
|
+
}
|
|
2866
|
+
function alignToStep(value, step) {
|
|
2867
|
+
return Math.round(value / step) * step;
|
|
2868
|
+
}
|
|
2869
|
+
function createSliderUI(value, pathKey, element, ctx, readonly) {
|
|
2870
|
+
var _a;
|
|
2871
|
+
const container = document.createElement("div");
|
|
2872
|
+
container.className = "slider-container space-y-2";
|
|
2873
|
+
const sliderRow = document.createElement("div");
|
|
2874
|
+
sliderRow.className = "flex items-center gap-3";
|
|
2875
|
+
const slider = document.createElement("input");
|
|
2876
|
+
slider.type = "range";
|
|
2877
|
+
slider.name = pathKey;
|
|
2878
|
+
slider.className = "slider-input flex-1";
|
|
2879
|
+
slider.disabled = readonly;
|
|
2880
|
+
const scale = element.scale || "linear";
|
|
2881
|
+
const min = element.min;
|
|
2882
|
+
const max = element.max;
|
|
2883
|
+
const step = (_a = element.step) != null ? _a : 1;
|
|
2884
|
+
if (scale === "exponential") {
|
|
2885
|
+
if (min <= 0) {
|
|
2886
|
+
throw new Error(
|
|
2887
|
+
`Slider "${element.key}": exponential scale requires min > 0 (got ${min})`
|
|
2888
|
+
);
|
|
2889
|
+
}
|
|
2890
|
+
slider.min = "0";
|
|
2891
|
+
slider.max = "1000";
|
|
2892
|
+
slider.step = "1";
|
|
2893
|
+
const position = exponentialToPosition(value, min, max);
|
|
2894
|
+
slider.value = (position * 1e3).toString();
|
|
2895
|
+
} else {
|
|
2896
|
+
slider.min = min.toString();
|
|
2897
|
+
slider.max = max.toString();
|
|
2898
|
+
slider.step = step.toString();
|
|
2899
|
+
slider.value = value.toString();
|
|
2900
|
+
}
|
|
2901
|
+
slider.style.cssText = `
|
|
2902
|
+
height: 6px;
|
|
2903
|
+
border-radius: 3px;
|
|
2904
|
+
background: linear-gradient(
|
|
2905
|
+
to right,
|
|
2906
|
+
var(--fb-primary-color) 0%,
|
|
2907
|
+
var(--fb-primary-color) ${(value - min) / (max - min) * 100}%,
|
|
2908
|
+
var(--fb-border-color) ${(value - min) / (max - min) * 100}%,
|
|
2909
|
+
var(--fb-border-color) 100%
|
|
2910
|
+
);
|
|
2911
|
+
outline: none;
|
|
2912
|
+
transition: background 0.1s ease-in-out;
|
|
2913
|
+
cursor: ${readonly ? "not-allowed" : "pointer"};
|
|
2914
|
+
opacity: ${readonly ? "0.6" : "1"};
|
|
2915
|
+
`;
|
|
2916
|
+
const valueDisplay = document.createElement("span");
|
|
2917
|
+
valueDisplay.className = "slider-value";
|
|
2918
|
+
valueDisplay.style.cssText = `
|
|
2919
|
+
min-width: 60px;
|
|
2920
|
+
text-align: right;
|
|
2921
|
+
font-size: var(--fb-font-size);
|
|
2922
|
+
color: var(--fb-text-color);
|
|
2923
|
+
font-family: var(--fb-font-family-mono, monospace);
|
|
2924
|
+
font-weight: 500;
|
|
2925
|
+
`;
|
|
2926
|
+
valueDisplay.textContent = value.toFixed(step < 1 ? 2 : 0);
|
|
2927
|
+
sliderRow.appendChild(slider);
|
|
2928
|
+
sliderRow.appendChild(valueDisplay);
|
|
2929
|
+
container.appendChild(sliderRow);
|
|
2930
|
+
const labelsRow = document.createElement("div");
|
|
2931
|
+
labelsRow.className = "flex justify-between";
|
|
2932
|
+
labelsRow.style.cssText = `
|
|
2933
|
+
font-size: var(--fb-font-size-small);
|
|
2934
|
+
color: var(--fb-text-secondary-color);
|
|
2935
|
+
`;
|
|
2936
|
+
const minLabel = document.createElement("span");
|
|
2937
|
+
minLabel.textContent = min.toString();
|
|
2938
|
+
const maxLabel = document.createElement("span");
|
|
2939
|
+
maxLabel.textContent = max.toString();
|
|
2940
|
+
labelsRow.appendChild(minLabel);
|
|
2941
|
+
labelsRow.appendChild(maxLabel);
|
|
2942
|
+
container.appendChild(labelsRow);
|
|
2943
|
+
if (!readonly) {
|
|
2944
|
+
const updateValue = () => {
|
|
2945
|
+
let displayValue;
|
|
2946
|
+
if (scale === "exponential") {
|
|
2947
|
+
const position = parseFloat(slider.value) / 1e3;
|
|
2948
|
+
displayValue = positionToExponential(position, min, max);
|
|
2949
|
+
displayValue = alignToStep(displayValue, step);
|
|
2950
|
+
displayValue = Math.max(min, Math.min(max, displayValue));
|
|
2951
|
+
} else {
|
|
2952
|
+
displayValue = parseFloat(slider.value);
|
|
2953
|
+
displayValue = alignToStep(displayValue, step);
|
|
2954
|
+
}
|
|
2955
|
+
valueDisplay.textContent = displayValue.toFixed(step < 1 ? 2 : 0);
|
|
2956
|
+
const percentage = (displayValue - min) / (max - min) * 100;
|
|
2957
|
+
slider.style.background = `linear-gradient(
|
|
2958
|
+
to right,
|
|
2959
|
+
var(--fb-primary-color) 0%,
|
|
2960
|
+
var(--fb-primary-color) ${percentage}%,
|
|
2961
|
+
var(--fb-border-color) ${percentage}%,
|
|
2962
|
+
var(--fb-border-color) 100%
|
|
2963
|
+
)`;
|
|
2964
|
+
if (ctx.instance) {
|
|
2965
|
+
ctx.instance.triggerOnChange(pathKey, displayValue);
|
|
2966
|
+
}
|
|
2967
|
+
};
|
|
2968
|
+
slider.addEventListener("input", updateValue);
|
|
2969
|
+
slider.addEventListener("change", updateValue);
|
|
2970
|
+
}
|
|
2971
|
+
return container;
|
|
2972
|
+
}
|
|
2973
|
+
function renderSliderElement(element, ctx, wrapper, pathKey) {
|
|
2974
|
+
var _a;
|
|
2975
|
+
if (element.min === void 0 || element.min === null) {
|
|
2976
|
+
throw new Error(
|
|
2977
|
+
`Slider field "${element.key}" requires "min" property`
|
|
2978
|
+
);
|
|
2979
|
+
}
|
|
2980
|
+
if (element.max === void 0 || element.max === null) {
|
|
2981
|
+
throw new Error(
|
|
2982
|
+
`Slider field "${element.key}" requires "max" property`
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
if (element.min >= element.max) {
|
|
2986
|
+
throw new Error(
|
|
2987
|
+
`Slider field "${element.key}": min (${element.min}) must be less than max (${element.max})`
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
const state = ctx.state;
|
|
2991
|
+
const defaultValue = element.default !== void 0 ? element.default : (element.min + element.max) / 2;
|
|
2992
|
+
const initialValue = (_a = ctx.prefill[element.key]) != null ? _a : defaultValue;
|
|
2993
|
+
const sliderUI = createSliderUI(
|
|
2994
|
+
initialValue,
|
|
2995
|
+
pathKey,
|
|
2996
|
+
element,
|
|
2997
|
+
ctx,
|
|
2998
|
+
state.config.readonly
|
|
2999
|
+
);
|
|
3000
|
+
wrapper.appendChild(sliderUI);
|
|
3001
|
+
const hint = document.createElement("p");
|
|
3002
|
+
hint.className = "mt-1";
|
|
3003
|
+
hint.style.cssText = `
|
|
3004
|
+
font-size: var(--fb-font-size-small);
|
|
3005
|
+
color: var(--fb-text-secondary-color);
|
|
3006
|
+
`;
|
|
3007
|
+
hint.textContent = makeFieldHint(element);
|
|
3008
|
+
wrapper.appendChild(hint);
|
|
3009
|
+
}
|
|
3010
|
+
function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
3011
|
+
var _a, _b;
|
|
3012
|
+
if (element.min === void 0 || element.min === null) {
|
|
3013
|
+
throw new Error(
|
|
3014
|
+
`Slider field "${element.key}" requires "min" property`
|
|
3015
|
+
);
|
|
3016
|
+
}
|
|
3017
|
+
if (element.max === void 0 || element.max === null) {
|
|
3018
|
+
throw new Error(
|
|
3019
|
+
`Slider field "${element.key}" requires "max" property`
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
if (element.min >= element.max) {
|
|
3023
|
+
throw new Error(
|
|
3024
|
+
`Slider field "${element.key}": min (${element.min}) must be less than max (${element.max})`
|
|
3025
|
+
);
|
|
3026
|
+
}
|
|
3027
|
+
const state = ctx.state;
|
|
3028
|
+
const prefillValues = ctx.prefill[element.key] || [];
|
|
3029
|
+
const values = Array.isArray(prefillValues) ? [...prefillValues] : [];
|
|
3030
|
+
const minCount = (_a = element.minCount) != null ? _a : 1;
|
|
3031
|
+
const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
|
|
3032
|
+
const defaultValue = element.default !== void 0 ? element.default : (element.min + element.max) / 2;
|
|
3033
|
+
while (values.length < minCount) {
|
|
3034
|
+
values.push(defaultValue);
|
|
3035
|
+
}
|
|
3036
|
+
const container = document.createElement("div");
|
|
3037
|
+
container.className = "space-y-3";
|
|
3038
|
+
wrapper.appendChild(container);
|
|
3039
|
+
function updateIndices() {
|
|
3040
|
+
const items = container.querySelectorAll(".multiple-slider-item");
|
|
3041
|
+
items.forEach((item, index) => {
|
|
3042
|
+
const slider = item.querySelector("input[type=range]");
|
|
3043
|
+
if (slider) {
|
|
3044
|
+
slider.setAttribute("name", `${pathKey}[${index}]`);
|
|
3045
|
+
}
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
function addSliderItem(value = defaultValue, index = -1) {
|
|
3049
|
+
const itemWrapper = document.createElement("div");
|
|
3050
|
+
itemWrapper.className = "multiple-slider-item flex items-start gap-2";
|
|
3051
|
+
const tempPathKey = `${pathKey}[${container.children.length}]`;
|
|
3052
|
+
const sliderUI = createSliderUI(
|
|
3053
|
+
value,
|
|
3054
|
+
tempPathKey,
|
|
3055
|
+
element,
|
|
3056
|
+
ctx,
|
|
3057
|
+
state.config.readonly
|
|
3058
|
+
);
|
|
3059
|
+
sliderUI.style.flex = "1";
|
|
3060
|
+
itemWrapper.appendChild(sliderUI);
|
|
3061
|
+
if (index === -1) {
|
|
3062
|
+
container.appendChild(itemWrapper);
|
|
3063
|
+
} else {
|
|
3064
|
+
container.insertBefore(itemWrapper, container.children[index]);
|
|
3065
|
+
}
|
|
3066
|
+
updateIndices();
|
|
3067
|
+
return itemWrapper;
|
|
3068
|
+
}
|
|
3069
|
+
function updateRemoveButtons() {
|
|
3070
|
+
if (state.config.readonly) return;
|
|
3071
|
+
const items = container.querySelectorAll(".multiple-slider-item");
|
|
3072
|
+
const currentCount = items.length;
|
|
3073
|
+
items.forEach((item) => {
|
|
3074
|
+
let removeBtn = item.querySelector(
|
|
3075
|
+
".remove-item-btn"
|
|
3076
|
+
);
|
|
3077
|
+
if (!removeBtn) {
|
|
3078
|
+
removeBtn = document.createElement("button");
|
|
3079
|
+
removeBtn.type = "button";
|
|
3080
|
+
removeBtn.className = "remove-item-btn px-2 py-1 rounded";
|
|
3081
|
+
removeBtn.style.cssText = `
|
|
3082
|
+
color: var(--fb-error-color);
|
|
3083
|
+
background-color: transparent;
|
|
3084
|
+
transition: background-color var(--fb-transition-duration);
|
|
3085
|
+
margin-top: 8px;
|
|
3086
|
+
`;
|
|
3087
|
+
removeBtn.innerHTML = "\u2715";
|
|
3088
|
+
removeBtn.addEventListener("mouseenter", () => {
|
|
3089
|
+
removeBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3090
|
+
});
|
|
3091
|
+
removeBtn.addEventListener("mouseleave", () => {
|
|
3092
|
+
removeBtn.style.backgroundColor = "transparent";
|
|
3093
|
+
});
|
|
3094
|
+
removeBtn.onclick = () => {
|
|
3095
|
+
const currentIndex = Array.from(container.children).indexOf(
|
|
3096
|
+
item
|
|
3097
|
+
);
|
|
3098
|
+
if (container.children.length > minCount) {
|
|
3099
|
+
values.splice(currentIndex, 1);
|
|
3100
|
+
item.remove();
|
|
3101
|
+
updateIndices();
|
|
3102
|
+
updateAddButton();
|
|
3103
|
+
updateRemoveButtons();
|
|
3104
|
+
}
|
|
3105
|
+
};
|
|
3106
|
+
item.appendChild(removeBtn);
|
|
3107
|
+
}
|
|
3108
|
+
const disabled = currentCount <= minCount;
|
|
3109
|
+
removeBtn.disabled = disabled;
|
|
3110
|
+
removeBtn.style.opacity = disabled ? "0.5" : "1";
|
|
3111
|
+
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
3112
|
+
});
|
|
3113
|
+
}
|
|
3114
|
+
function updateAddButton() {
|
|
3115
|
+
const existingAddBtn = wrapper.querySelector(".add-slider-btn");
|
|
3116
|
+
if (existingAddBtn) existingAddBtn.remove();
|
|
3117
|
+
if (!state.config.readonly && values.length < maxCount) {
|
|
3118
|
+
const addBtn = document.createElement("button");
|
|
3119
|
+
addBtn.type = "button";
|
|
3120
|
+
addBtn.className = "add-slider-btn mt-2 px-3 py-1 rounded";
|
|
3121
|
+
addBtn.style.cssText = `
|
|
3122
|
+
color: var(--fb-primary-color);
|
|
3123
|
+
border: var(--fb-border-width) solid var(--fb-primary-color);
|
|
3124
|
+
background-color: transparent;
|
|
3125
|
+
font-size: var(--fb-font-size);
|
|
3126
|
+
transition: all var(--fb-transition-duration);
|
|
3127
|
+
`;
|
|
3128
|
+
addBtn.textContent = "+";
|
|
3129
|
+
addBtn.addEventListener("mouseenter", () => {
|
|
3130
|
+
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3131
|
+
});
|
|
3132
|
+
addBtn.addEventListener("mouseleave", () => {
|
|
3133
|
+
addBtn.style.backgroundColor = "transparent";
|
|
3134
|
+
});
|
|
3135
|
+
addBtn.onclick = () => {
|
|
3136
|
+
values.push(defaultValue);
|
|
3137
|
+
addSliderItem(defaultValue);
|
|
3138
|
+
updateAddButton();
|
|
3139
|
+
updateRemoveButtons();
|
|
3140
|
+
};
|
|
3141
|
+
wrapper.appendChild(addBtn);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
values.forEach((value) => addSliderItem(value));
|
|
3145
|
+
updateAddButton();
|
|
3146
|
+
updateRemoveButtons();
|
|
3147
|
+
const hint = document.createElement("p");
|
|
3148
|
+
hint.className = "mt-1";
|
|
3149
|
+
hint.style.cssText = `
|
|
3150
|
+
font-size: var(--fb-font-size-small);
|
|
3151
|
+
color: var(--fb-text-secondary-color);
|
|
3152
|
+
`;
|
|
3153
|
+
hint.textContent = makeFieldHint(element);
|
|
3154
|
+
wrapper.appendChild(hint);
|
|
3155
|
+
}
|
|
3156
|
+
function validateSliderElement(element, key, context) {
|
|
3157
|
+
var _a, _b, _c;
|
|
3158
|
+
const errors = [];
|
|
3159
|
+
const { scopeRoot, skipValidation } = context;
|
|
3160
|
+
if (element.min === void 0 || element.min === null) {
|
|
3161
|
+
throw new Error(
|
|
3162
|
+
`Slider validation: field "${key}" requires "min" property`
|
|
3163
|
+
);
|
|
3164
|
+
}
|
|
3165
|
+
if (element.max === void 0 || element.max === null) {
|
|
3166
|
+
throw new Error(
|
|
3167
|
+
`Slider validation: field "${key}" requires "max" property`
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
const min = element.min;
|
|
3171
|
+
const max = element.max;
|
|
3172
|
+
const step = (_a = element.step) != null ? _a : 1;
|
|
3173
|
+
const scale = element.scale || "linear";
|
|
3174
|
+
const markValidity = (input, errorMessage) => {
|
|
3175
|
+
var _a2, _b2;
|
|
3176
|
+
if (!input) return;
|
|
3177
|
+
const errorId = `error-${input.getAttribute("name") || Math.random().toString(36).substring(7)}`;
|
|
3178
|
+
let errorElement = document.getElementById(errorId);
|
|
3179
|
+
if (errorMessage) {
|
|
3180
|
+
input.classList.add("invalid");
|
|
3181
|
+
input.title = errorMessage;
|
|
3182
|
+
if (!errorElement) {
|
|
3183
|
+
errorElement = document.createElement("div");
|
|
3184
|
+
errorElement.id = errorId;
|
|
3185
|
+
errorElement.className = "error-message";
|
|
3186
|
+
errorElement.style.cssText = `
|
|
3187
|
+
color: var(--fb-error-color);
|
|
3188
|
+
font-size: var(--fb-font-size-small);
|
|
3189
|
+
margin-top: 0.25rem;
|
|
3190
|
+
`;
|
|
3191
|
+
const sliderContainer = input.closest(".slider-container");
|
|
3192
|
+
if (sliderContainer && sliderContainer.nextSibling) {
|
|
3193
|
+
(_a2 = sliderContainer.parentNode) == null ? void 0 : _a2.insertBefore(errorElement, sliderContainer.nextSibling);
|
|
3194
|
+
} else if (sliderContainer) {
|
|
3195
|
+
(_b2 = sliderContainer.parentNode) == null ? void 0 : _b2.appendChild(errorElement);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
errorElement.textContent = errorMessage;
|
|
3199
|
+
errorElement.style.display = "block";
|
|
3200
|
+
} else {
|
|
3201
|
+
input.classList.remove("invalid");
|
|
3202
|
+
input.title = "";
|
|
3203
|
+
if (errorElement) {
|
|
3204
|
+
errorElement.remove();
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
};
|
|
3208
|
+
const validateSliderValue = (slider, fieldKey) => {
|
|
3209
|
+
const rawValue = slider.value;
|
|
3210
|
+
if (!rawValue) {
|
|
3211
|
+
if (!skipValidation && element.required) {
|
|
3212
|
+
errors.push(`${fieldKey}: required`);
|
|
3213
|
+
markValidity(slider, "required");
|
|
3214
|
+
return null;
|
|
3215
|
+
}
|
|
3216
|
+
markValidity(slider, null);
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
let value;
|
|
3220
|
+
if (scale === "exponential") {
|
|
3221
|
+
const position = parseFloat(rawValue) / 1e3;
|
|
3222
|
+
value = positionToExponential(position, min, max);
|
|
3223
|
+
value = alignToStep(value, step);
|
|
3224
|
+
} else {
|
|
3225
|
+
value = parseFloat(rawValue);
|
|
3226
|
+
value = alignToStep(value, step);
|
|
3227
|
+
}
|
|
3228
|
+
if (!skipValidation) {
|
|
3229
|
+
if (value < min) {
|
|
3230
|
+
errors.push(`${fieldKey}: value ${value} < min ${min}`);
|
|
3231
|
+
markValidity(slider, `value must be >= ${min}`);
|
|
3232
|
+
return value;
|
|
3233
|
+
}
|
|
3234
|
+
if (value > max) {
|
|
3235
|
+
errors.push(`${fieldKey}: value ${value} > max ${max}`);
|
|
3236
|
+
markValidity(slider, `value must be <= ${max}`);
|
|
3237
|
+
return value;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
markValidity(slider, null);
|
|
3241
|
+
return value;
|
|
3242
|
+
};
|
|
3243
|
+
if (element.multiple) {
|
|
3244
|
+
const sliders = scopeRoot.querySelectorAll(
|
|
3245
|
+
`input[type="range"][name^="${key}["]`
|
|
3246
|
+
);
|
|
3247
|
+
const values = [];
|
|
3248
|
+
sliders.forEach((slider, index) => {
|
|
3249
|
+
const value = validateSliderValue(slider, `${key}[${index}]`);
|
|
3250
|
+
values.push(value);
|
|
3251
|
+
});
|
|
3252
|
+
if (!skipValidation) {
|
|
3253
|
+
const minCount = (_b = element.minCount) != null ? _b : 1;
|
|
3254
|
+
const maxCount = (_c = element.maxCount) != null ? _c : Infinity;
|
|
3255
|
+
const filteredValues = values.filter((v) => v !== null);
|
|
3256
|
+
if (element.required && filteredValues.length === 0) {
|
|
3257
|
+
errors.push(`${key}: required`);
|
|
3258
|
+
}
|
|
3259
|
+
if (filteredValues.length < minCount) {
|
|
3260
|
+
errors.push(`${key}: minimum ${minCount} items required`);
|
|
3261
|
+
}
|
|
3262
|
+
if (filteredValues.length > maxCount) {
|
|
3263
|
+
errors.push(`${key}: maximum ${maxCount} items allowed`);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
return { value: values, errors };
|
|
3267
|
+
} else {
|
|
3268
|
+
const slider = scopeRoot.querySelector(
|
|
3269
|
+
`input[type="range"][name="${key}"]`
|
|
3270
|
+
);
|
|
3271
|
+
if (!slider) {
|
|
3272
|
+
if (!skipValidation && element.required) {
|
|
3273
|
+
errors.push(`${key}: required`);
|
|
3274
|
+
}
|
|
3275
|
+
return { value: null, errors };
|
|
3276
|
+
}
|
|
3277
|
+
const value = validateSliderValue(slider, key);
|
|
3278
|
+
return { value, errors };
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
function updateSliderField(element, fieldPath, value, context) {
|
|
3282
|
+
var _a;
|
|
3283
|
+
const { scopeRoot } = context;
|
|
3284
|
+
const min = element.min;
|
|
3285
|
+
const max = element.max;
|
|
3286
|
+
const step = (_a = element.step) != null ? _a : 1;
|
|
3287
|
+
const scale = element.scale || "linear";
|
|
3288
|
+
if (element.multiple) {
|
|
3289
|
+
if (!Array.isArray(value)) {
|
|
3290
|
+
console.warn(
|
|
3291
|
+
`updateSliderField: Expected array for multiple field "${fieldPath}", got ${typeof value}`
|
|
3292
|
+
);
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
const sliders = scopeRoot.querySelectorAll(
|
|
3296
|
+
`input[type="range"][name^="${fieldPath}["]`
|
|
3297
|
+
);
|
|
3298
|
+
sliders.forEach((slider, index) => {
|
|
3299
|
+
if (index < value.length && value[index] !== null) {
|
|
3300
|
+
const numValue = Number(value[index]);
|
|
3301
|
+
if (scale === "exponential") {
|
|
3302
|
+
const position = exponentialToPosition(numValue, min, max);
|
|
3303
|
+
slider.value = (position * 1e3).toString();
|
|
3304
|
+
} else {
|
|
3305
|
+
slider.value = numValue.toString();
|
|
3306
|
+
}
|
|
3307
|
+
const sliderContainer = slider.closest(".slider-container");
|
|
3308
|
+
if (sliderContainer) {
|
|
3309
|
+
const valueDisplay = sliderContainer.querySelector(".slider-value");
|
|
3310
|
+
if (valueDisplay) {
|
|
3311
|
+
valueDisplay.textContent = numValue.toFixed(step < 1 ? 2 : 0);
|
|
3312
|
+
}
|
|
3313
|
+
const percentage = (numValue - min) / (max - min) * 100;
|
|
3314
|
+
slider.style.background = `linear-gradient(
|
|
3315
|
+
to right,
|
|
3316
|
+
var(--fb-primary-color) 0%,
|
|
3317
|
+
var(--fb-primary-color) ${percentage}%,
|
|
3318
|
+
var(--fb-border-color) ${percentage}%,
|
|
3319
|
+
var(--fb-border-color) 100%
|
|
3320
|
+
)`;
|
|
3321
|
+
}
|
|
3322
|
+
slider.classList.remove("invalid");
|
|
3323
|
+
slider.title = "";
|
|
3324
|
+
}
|
|
3325
|
+
});
|
|
3326
|
+
if (value.length !== sliders.length) {
|
|
3327
|
+
console.warn(
|
|
3328
|
+
`updateSliderField: Multiple field "${fieldPath}" has ${sliders.length} sliders but received ${value.length} values. Consider re-rendering for add/remove.`
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
} else {
|
|
3332
|
+
const slider = scopeRoot.querySelector(
|
|
3333
|
+
`input[type="range"][name="${fieldPath}"]`
|
|
3334
|
+
);
|
|
3335
|
+
if (slider && value !== null && value !== void 0) {
|
|
3336
|
+
const numValue = Number(value);
|
|
3337
|
+
if (scale === "exponential") {
|
|
3338
|
+
const position = exponentialToPosition(numValue, min, max);
|
|
3339
|
+
slider.value = (position * 1e3).toString();
|
|
3340
|
+
} else {
|
|
3341
|
+
slider.value = numValue.toString();
|
|
3342
|
+
}
|
|
3343
|
+
const sliderContainer = slider.closest(".slider-container");
|
|
3344
|
+
if (sliderContainer) {
|
|
3345
|
+
const valueDisplay = sliderContainer.querySelector(".slider-value");
|
|
3346
|
+
if (valueDisplay) {
|
|
3347
|
+
valueDisplay.textContent = numValue.toFixed(step < 1 ? 2 : 0);
|
|
3348
|
+
}
|
|
3349
|
+
const percentage = (numValue - min) / (max - min) * 100;
|
|
3350
|
+
slider.style.background = `linear-gradient(
|
|
3351
|
+
to right,
|
|
3352
|
+
var(--fb-primary-color) 0%,
|
|
3353
|
+
var(--fb-primary-color) ${percentage}%,
|
|
3354
|
+
var(--fb-border-color) ${percentage}%,
|
|
3355
|
+
var(--fb-border-color) 100%
|
|
3356
|
+
)`;
|
|
3357
|
+
}
|
|
3358
|
+
slider.classList.remove("invalid");
|
|
3359
|
+
slider.title = "";
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
|
|
2811
3364
|
// src/components/container.ts
|
|
2812
3365
|
var renderElementFunc = null;
|
|
2813
3366
|
function setRenderElement(fn) {
|
|
@@ -2821,6 +3374,24 @@ function renderElement(element, ctx) {
|
|
|
2821
3374
|
}
|
|
2822
3375
|
return renderElementFunc(element, ctx);
|
|
2823
3376
|
}
|
|
3377
|
+
function createPrefillHints(element, pathKey) {
|
|
3378
|
+
if (!element.prefillHints || element.prefillHints.length === 0) {
|
|
3379
|
+
return null;
|
|
3380
|
+
}
|
|
3381
|
+
const hintsContainer = document.createElement("div");
|
|
3382
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-4";
|
|
3383
|
+
element.prefillHints.forEach((hint, index) => {
|
|
3384
|
+
const hintButton = document.createElement("button");
|
|
3385
|
+
hintButton.type = "button";
|
|
3386
|
+
hintButton.className = "fb-prefill-hint";
|
|
3387
|
+
hintButton.textContent = hint.label;
|
|
3388
|
+
hintButton.setAttribute("data-hint-values", JSON.stringify(hint.values));
|
|
3389
|
+
hintButton.setAttribute("data-container-key", pathKey);
|
|
3390
|
+
hintButton.setAttribute("data-hint-index", String(index));
|
|
3391
|
+
hintsContainer.appendChild(hintButton);
|
|
3392
|
+
});
|
|
3393
|
+
return hintsContainer;
|
|
3394
|
+
}
|
|
2824
3395
|
function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
2825
3396
|
var _a, _b;
|
|
2826
3397
|
const containerWrap = document.createElement("div");
|
|
@@ -2831,9 +3402,20 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
2831
3402
|
const left = document.createElement("div");
|
|
2832
3403
|
left.className = "flex-1";
|
|
2833
3404
|
const itemsWrap = document.createElement("div");
|
|
2834
|
-
|
|
3405
|
+
const columns = element.columns || 1;
|
|
3406
|
+
if (columns === 1) {
|
|
3407
|
+
itemsWrap.className = "space-y-4";
|
|
3408
|
+
} else {
|
|
3409
|
+
itemsWrap.className = `grid grid-cols-${columns} gap-4`;
|
|
3410
|
+
}
|
|
2835
3411
|
containerWrap.appendChild(header);
|
|
2836
3412
|
header.appendChild(left);
|
|
3413
|
+
if (!ctx.state.config.readonly) {
|
|
3414
|
+
const hintsElement = createPrefillHints(element, pathKey);
|
|
3415
|
+
if (hintsElement) {
|
|
3416
|
+
containerWrap.appendChild(hintsElement);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
2837
3419
|
const subCtx = {
|
|
2838
3420
|
path: pathJoin(ctx.path, element.key),
|
|
2839
3421
|
prefill: ((_a = ctx.prefill) == null ? void 0 : _a[element.key]) || {},
|
|
@@ -2860,14 +3442,15 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2860
3442
|
header.className = "flex justify-between items-center mb-4";
|
|
2861
3443
|
const left = document.createElement("div");
|
|
2862
3444
|
left.className = "flex-1";
|
|
2863
|
-
const right = document.createElement("div");
|
|
2864
|
-
right.className = "flex gap-2";
|
|
2865
3445
|
const itemsWrap = document.createElement("div");
|
|
2866
3446
|
itemsWrap.className = "space-y-4";
|
|
2867
3447
|
containerWrap.appendChild(header);
|
|
2868
3448
|
header.appendChild(left);
|
|
2869
|
-
if (!state.config.readonly) {
|
|
2870
|
-
|
|
3449
|
+
if (!ctx.state.config.readonly) {
|
|
3450
|
+
const hintsElement = createPrefillHints(element, element.key);
|
|
3451
|
+
if (hintsElement) {
|
|
3452
|
+
containerWrap.appendChild(hintsElement);
|
|
3453
|
+
}
|
|
2871
3454
|
}
|
|
2872
3455
|
const min = (_a = element.minCount) != null ? _a : 0;
|
|
2873
3456
|
const max = (_b = element.maxCount) != null ? _b : Infinity;
|
|
@@ -2876,8 +3459,21 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2876
3459
|
const createAddButton = () => {
|
|
2877
3460
|
const add = document.createElement("button");
|
|
2878
3461
|
add.type = "button";
|
|
2879
|
-
add.className = "
|
|
2880
|
-
add.
|
|
3462
|
+
add.className = "add-container-btn mt-2 px-3 py-1 rounded";
|
|
3463
|
+
add.style.cssText = `
|
|
3464
|
+
color: var(--fb-primary-color);
|
|
3465
|
+
border: var(--fb-border-width) solid var(--fb-primary-color);
|
|
3466
|
+
background-color: transparent;
|
|
3467
|
+
font-size: var(--fb-font-size);
|
|
3468
|
+
transition: all var(--fb-transition-duration);
|
|
3469
|
+
`;
|
|
3470
|
+
add.textContent = "+";
|
|
3471
|
+
add.addEventListener("mouseenter", () => {
|
|
3472
|
+
add.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3473
|
+
});
|
|
3474
|
+
add.addEventListener("mouseleave", () => {
|
|
3475
|
+
add.style.backgroundColor = "transparent";
|
|
3476
|
+
});
|
|
2881
3477
|
add.onclick = () => {
|
|
2882
3478
|
var _a2;
|
|
2883
3479
|
if (countItems() < max) {
|
|
@@ -2892,16 +3488,35 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2892
3488
|
const item = document.createElement("div");
|
|
2893
3489
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
2894
3490
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
3491
|
+
const childWrapper = document.createElement("div");
|
|
3492
|
+
const columns = element.columns || 1;
|
|
3493
|
+
if (columns === 1) {
|
|
3494
|
+
childWrapper.className = "space-y-4";
|
|
3495
|
+
} else {
|
|
3496
|
+
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
3497
|
+
}
|
|
2895
3498
|
element.elements.forEach((child) => {
|
|
2896
3499
|
if (!child.hidden) {
|
|
2897
|
-
|
|
3500
|
+
childWrapper.appendChild(renderElement(child, subCtx));
|
|
2898
3501
|
}
|
|
2899
3502
|
});
|
|
3503
|
+
item.appendChild(childWrapper);
|
|
2900
3504
|
if (!state.config.readonly) {
|
|
2901
3505
|
const rem = document.createElement("button");
|
|
2902
3506
|
rem.type = "button";
|
|
2903
|
-
rem.className = "absolute top-2 right-2
|
|
2904
|
-
rem.
|
|
3507
|
+
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
3508
|
+
rem.style.cssText = `
|
|
3509
|
+
color: var(--fb-error-color);
|
|
3510
|
+
background-color: transparent;
|
|
3511
|
+
transition: background-color var(--fb-transition-duration);
|
|
3512
|
+
`;
|
|
3513
|
+
rem.textContent = "\u2715";
|
|
3514
|
+
rem.addEventListener("mouseenter", () => {
|
|
3515
|
+
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3516
|
+
});
|
|
3517
|
+
rem.addEventListener("mouseleave", () => {
|
|
3518
|
+
rem.style.backgroundColor = "transparent";
|
|
3519
|
+
});
|
|
2905
3520
|
rem.onclick = () => {
|
|
2906
3521
|
item.remove();
|
|
2907
3522
|
updateAddButton();
|
|
@@ -2917,16 +3532,14 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2917
3532
|
};
|
|
2918
3533
|
const updateAddButton = () => {
|
|
2919
3534
|
const currentCount = countItems();
|
|
2920
|
-
const
|
|
2921
|
-
if (
|
|
2922
|
-
|
|
2923
|
-
|
|
3535
|
+
const existingAddBtn = containerWrap.querySelector(".add-container-btn");
|
|
3536
|
+
if (existingAddBtn) {
|
|
3537
|
+
existingAddBtn.disabled = currentCount >= max;
|
|
3538
|
+
existingAddBtn.style.opacity = currentCount >= max ? "0.5" : "1";
|
|
3539
|
+
existingAddBtn.style.pointerEvents = currentCount >= max ? "none" : "auto";
|
|
2924
3540
|
}
|
|
2925
3541
|
left.innerHTML = `<span>${element.label || element.key}</span> <span class="text-sm text-gray-500">(${currentCount}/${max === Infinity ? "\u221E" : max})</span>`;
|
|
2926
3542
|
};
|
|
2927
|
-
if (!state.config.readonly) {
|
|
2928
|
-
right.appendChild(createAddButton());
|
|
2929
|
-
}
|
|
2930
3543
|
if (pre && Array.isArray(pre)) {
|
|
2931
3544
|
pre.forEach((prefillObj, idx) => {
|
|
2932
3545
|
var _a2;
|
|
@@ -2940,16 +3553,35 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2940
3553
|
const item = document.createElement("div");
|
|
2941
3554
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
2942
3555
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
3556
|
+
const childWrapper = document.createElement("div");
|
|
3557
|
+
const columns = element.columns || 1;
|
|
3558
|
+
if (columns === 1) {
|
|
3559
|
+
childWrapper.className = "space-y-4";
|
|
3560
|
+
} else {
|
|
3561
|
+
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
3562
|
+
}
|
|
2943
3563
|
element.elements.forEach((child) => {
|
|
2944
3564
|
if (!child.hidden) {
|
|
2945
|
-
|
|
3565
|
+
childWrapper.appendChild(renderElement(child, subCtx));
|
|
2946
3566
|
}
|
|
2947
3567
|
});
|
|
3568
|
+
item.appendChild(childWrapper);
|
|
2948
3569
|
if (!state.config.readonly) {
|
|
2949
3570
|
const rem = document.createElement("button");
|
|
2950
3571
|
rem.type = "button";
|
|
2951
|
-
rem.className = "absolute top-2 right-2
|
|
2952
|
-
rem.
|
|
3572
|
+
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
3573
|
+
rem.style.cssText = `
|
|
3574
|
+
color: var(--fb-error-color);
|
|
3575
|
+
background-color: transparent;
|
|
3576
|
+
transition: background-color var(--fb-transition-duration);
|
|
3577
|
+
`;
|
|
3578
|
+
rem.textContent = "\u2715";
|
|
3579
|
+
rem.addEventListener("mouseenter", () => {
|
|
3580
|
+
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3581
|
+
});
|
|
3582
|
+
rem.addEventListener("mouseleave", () => {
|
|
3583
|
+
rem.style.backgroundColor = "transparent";
|
|
3584
|
+
});
|
|
2953
3585
|
rem.onclick = () => {
|
|
2954
3586
|
item.remove();
|
|
2955
3587
|
updateAddButton();
|
|
@@ -2973,15 +3605,34 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2973
3605
|
const item = document.createElement("div");
|
|
2974
3606
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
2975
3607
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
3608
|
+
const childWrapper = document.createElement("div");
|
|
3609
|
+
const columns = element.columns || 1;
|
|
3610
|
+
if (columns === 1) {
|
|
3611
|
+
childWrapper.className = "space-y-4";
|
|
3612
|
+
} else {
|
|
3613
|
+
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
3614
|
+
}
|
|
2976
3615
|
element.elements.forEach((child) => {
|
|
2977
3616
|
if (!child.hidden) {
|
|
2978
|
-
|
|
3617
|
+
childWrapper.appendChild(renderElement(child, subCtx));
|
|
2979
3618
|
}
|
|
2980
3619
|
});
|
|
3620
|
+
item.appendChild(childWrapper);
|
|
2981
3621
|
const rem = document.createElement("button");
|
|
2982
3622
|
rem.type = "button";
|
|
2983
|
-
rem.className = "absolute top-2 right-2
|
|
2984
|
-
rem.
|
|
3623
|
+
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
3624
|
+
rem.style.cssText = `
|
|
3625
|
+
color: var(--fb-error-color);
|
|
3626
|
+
background-color: transparent;
|
|
3627
|
+
transition: background-color var(--fb-transition-duration);
|
|
3628
|
+
`;
|
|
3629
|
+
rem.textContent = "\u2715";
|
|
3630
|
+
rem.addEventListener("mouseenter", () => {
|
|
3631
|
+
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
3632
|
+
});
|
|
3633
|
+
rem.addEventListener("mouseleave", () => {
|
|
3634
|
+
rem.style.backgroundColor = "transparent";
|
|
3635
|
+
});
|
|
2985
3636
|
rem.onclick = () => {
|
|
2986
3637
|
if (countItems() > min) {
|
|
2987
3638
|
item.remove();
|
|
@@ -2994,6 +3645,9 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2994
3645
|
}
|
|
2995
3646
|
}
|
|
2996
3647
|
containerWrap.appendChild(itemsWrap);
|
|
3648
|
+
if (!state.config.readonly) {
|
|
3649
|
+
containerWrap.appendChild(createAddButton());
|
|
3650
|
+
}
|
|
2997
3651
|
updateAddButton();
|
|
2998
3652
|
wrapper.appendChild(containerWrap);
|
|
2999
3653
|
}
|
|
@@ -3368,6 +4022,13 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
3368
4022
|
renderColourElement(element, ctx, wrapper, pathKey);
|
|
3369
4023
|
}
|
|
3370
4024
|
break;
|
|
4025
|
+
case "slider":
|
|
4026
|
+
if (isMultiple) {
|
|
4027
|
+
renderMultipleSliderElement(element, ctx, wrapper, pathKey);
|
|
4028
|
+
} else {
|
|
4029
|
+
renderSliderElement(element, ctx, wrapper, pathKey);
|
|
4030
|
+
}
|
|
4031
|
+
break;
|
|
3371
4032
|
case "group":
|
|
3372
4033
|
renderGroupElement(element, ctx, wrapper, pathKey);
|
|
3373
4034
|
break;
|
|
@@ -3685,6 +4346,10 @@ var componentRegistry = {
|
|
|
3685
4346
|
validate: validateColourElement,
|
|
3686
4347
|
update: updateColourField
|
|
3687
4348
|
},
|
|
4349
|
+
slider: {
|
|
4350
|
+
validate: validateSliderElement,
|
|
4351
|
+
update: updateSliderField
|
|
4352
|
+
},
|
|
3688
4353
|
container: {
|
|
3689
4354
|
validate: validateContainerElement,
|
|
3690
4355
|
update: updateContainerField
|
|
@@ -4041,6 +4706,33 @@ var FormBuilderInstance = class {
|
|
|
4041
4706
|
this.renderFormLevelActions(allFormLevelActions, trueFormLevelActions);
|
|
4042
4707
|
}
|
|
4043
4708
|
}
|
|
4709
|
+
/**
|
|
4710
|
+
* Handle prefill hint click - updates container fields with hint values
|
|
4711
|
+
*/
|
|
4712
|
+
handlePrefillHintClick(event) {
|
|
4713
|
+
const target = event.target;
|
|
4714
|
+
if (!target.classList.contains("fb-prefill-hint")) {
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
event.preventDefault();
|
|
4718
|
+
event.stopPropagation();
|
|
4719
|
+
const hintValuesJson = target.getAttribute("data-hint-values");
|
|
4720
|
+
const containerKey = target.getAttribute("data-container-key");
|
|
4721
|
+
if (!hintValuesJson || !containerKey) {
|
|
4722
|
+
console.warn("Prefill hint missing required data attributes");
|
|
4723
|
+
return;
|
|
4724
|
+
}
|
|
4725
|
+
try {
|
|
4726
|
+
const hintValues = JSON.parse(hintValuesJson);
|
|
4727
|
+
for (const fieldKey in hintValues) {
|
|
4728
|
+
const fullPath = `${containerKey}.${fieldKey}`;
|
|
4729
|
+
const value = hintValues[fieldKey];
|
|
4730
|
+
this.updateField(fullPath, value);
|
|
4731
|
+
}
|
|
4732
|
+
} catch (error) {
|
|
4733
|
+
console.error("Error parsing prefill hint values:", error);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4044
4736
|
/**
|
|
4045
4737
|
* Render form from schema
|
|
4046
4738
|
*/
|
|
@@ -4073,6 +4765,9 @@ var FormBuilderInstance = class {
|
|
|
4073
4765
|
formEl.appendChild(block);
|
|
4074
4766
|
});
|
|
4075
4767
|
root.appendChild(formEl);
|
|
4768
|
+
if (!this.state.config.readonly) {
|
|
4769
|
+
root.addEventListener("click", this.handlePrefillHintClick.bind(this));
|
|
4770
|
+
}
|
|
4076
4771
|
if (this.state.config.readonly && this.state.externalActions && Array.isArray(this.state.externalActions)) {
|
|
4077
4772
|
this.renderExternalActions();
|
|
4078
4773
|
}
|