@skalfa/skalfa-component 1.0.23 → 1.0.25
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/dist/input/Input.component.js +1 -1
- package/dist/input/InputCheckbox.component.js +1 -1
- package/dist/input/InputCurrency.component.js +1 -1
- package/dist/input/InputDate.component.js +1 -1
- package/dist/input/InputDatetime.component.js +1 -1
- package/dist/input/InputDocument.component.js +1 -1
- package/dist/input/InputImage.component.js +1 -1
- package/dist/input/InputNumber.component.js +1 -1
- package/dist/input/InputPassword.component.js +1 -1
- package/dist/input/InputRadio.component.js +1 -1
- package/dist/input/InputTime.component.js +1 -1
- package/dist/input/Select.component.d.ts +5 -3
- package/dist/input/Select.component.js +155 -54
- package/dist/supervision/FormSupervision.component.d.ts +16 -1
- package/dist/supervision/FormSupervision.component.js +129 -10
- package/package.json +1 -1
- package/src/input/Input.component.tsx +1 -1
- package/src/input/InputCheckbox.component.tsx +1 -1
- package/src/input/InputCurrency.component.tsx +1 -1
- package/src/input/InputDate.component.tsx +1 -1
- package/src/input/InputDatetime.component.tsx +1 -1
- package/src/input/InputDocument.component.tsx +1 -1
- package/src/input/InputImage.component.tsx +1 -1
- package/src/input/InputNumber.component.tsx +1 -1
- package/src/input/InputPassword.component.tsx +1 -1
- package/src/input/InputRadio.component.tsx +1 -1
- package/src/input/InputTime.component.tsx +1 -1
- package/src/input/Select.component.tsx +195 -35
- package/src/supervision/FormSupervision.component.tsx +196 -32
|
@@ -13,7 +13,9 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
|
|
|
13
13
|
const [modal, setModal] = (0, react_1.useState)(false);
|
|
14
14
|
const [fresh, setFresh] = (0, react_1.useState)(true);
|
|
15
15
|
const [mapGroups, setMapGroups] = (0, react_1.useState)({});
|
|
16
|
-
const [
|
|
16
|
+
const [watchState, setWatchState] = (0, react_1.useState)({});
|
|
17
|
+
const watchRef = (0, react_1.useRef)({});
|
|
18
|
+
const [{ formControl, setRegister, setUnregister, values, setValues, errors, setErrors, setDefaultValues, submit, loading, confirm, },] = (0, _utils_1.useForm)(submitControl, payload, confirmation, (data) => {
|
|
17
19
|
onSuccess?.(data);
|
|
18
20
|
setModal("success");
|
|
19
21
|
setTimeout(() => setModal(false), 1000);
|
|
@@ -25,6 +27,82 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
|
|
|
25
27
|
else
|
|
26
28
|
setModal("failed");
|
|
27
29
|
});
|
|
30
|
+
// ==============================>
|
|
31
|
+
// ## Watch: collect watchers from fields
|
|
32
|
+
// ==============================>
|
|
33
|
+
const collectWatchers = (fieldList, prefix) => {
|
|
34
|
+
const result = [];
|
|
35
|
+
for (const f of fieldList) {
|
|
36
|
+
const inputType = f.type || "default";
|
|
37
|
+
const name = prefix ? `${prefix}.${f.construction?.name}` : f.construction?.name || "";
|
|
38
|
+
if (inputType === "cluster") {
|
|
39
|
+
const cluster = f.construction;
|
|
40
|
+
const groupKey = prefix ? `${prefix}.${cluster.name}` : cluster.name;
|
|
41
|
+
const group = mapGroups[groupKey] || [];
|
|
42
|
+
for (const gIndex of group) {
|
|
43
|
+
result.push(...collectWatchers(cluster.fields, `${cluster.name}[${gIndex}]`));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (f.onWatch) {
|
|
47
|
+
result.push({ name, onWatch: f.onWatch, construction: f.construction });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
// ==============================>
|
|
53
|
+
// ## Watch: execute watchers on value change
|
|
54
|
+
// ==============================>
|
|
55
|
+
(0, react_1.useEffect)(() => {
|
|
56
|
+
const watchers = collectWatchers(fields);
|
|
57
|
+
if (watchers.length === 0) {
|
|
58
|
+
if (Object.keys(watchRef.current).length > 0) {
|
|
59
|
+
watchRef.current = {};
|
|
60
|
+
setWatchState({});
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const valMap = {};
|
|
65
|
+
values.forEach((v) => { valMap[v.name] = v.value; });
|
|
66
|
+
const nextState = {};
|
|
67
|
+
const valueUpdates = [];
|
|
68
|
+
for (const { name, onWatch, construction } of watchers) {
|
|
69
|
+
const prev = watchRef.current[name] || {};
|
|
70
|
+
const action = onWatch({ values: valMap, self: name, prev });
|
|
71
|
+
if (!action)
|
|
72
|
+
continue;
|
|
73
|
+
nextState[name] = action;
|
|
74
|
+
if (action.hidden && !prev.hidden)
|
|
75
|
+
setUnregister(name);
|
|
76
|
+
if (action.required !== prev.required) {
|
|
77
|
+
const baseValidations = Array.isArray(construction?.validations) ? [...construction.validations] : [];
|
|
78
|
+
const newValidations = action.required ? (baseValidations.includes("required") ? baseValidations : [...baseValidations, "required"]) : baseValidations.filter((v) => v !== "required");
|
|
79
|
+
setRegister({ name, validations: newValidations });
|
|
80
|
+
}
|
|
81
|
+
if (action.reset) {
|
|
82
|
+
const cur = valMap[name];
|
|
83
|
+
if (cur != null && cur !== "")
|
|
84
|
+
valueUpdates.push({ name, value: "" });
|
|
85
|
+
}
|
|
86
|
+
else if (action.value !== undefined && action.value !== valMap[name]) {
|
|
87
|
+
valueUpdates.push({ name, value: action.value });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (JSON.stringify(watchRef.current) !== JSON.stringify(nextState)) {
|
|
91
|
+
watchRef.current = nextState;
|
|
92
|
+
setWatchState(nextState);
|
|
93
|
+
}
|
|
94
|
+
if (valueUpdates.length > 0) {
|
|
95
|
+
const merged = [...values];
|
|
96
|
+
for (const upd of valueUpdates) {
|
|
97
|
+
const idx = merged.findIndex(v => v.name === upd.name);
|
|
98
|
+
if (idx >= 0)
|
|
99
|
+
merged[idx] = upd;
|
|
100
|
+
else
|
|
101
|
+
merged.push(upd);
|
|
102
|
+
}
|
|
103
|
+
setValues(merged);
|
|
104
|
+
}
|
|
105
|
+
}, [values, fields, mapGroups]);
|
|
28
106
|
const GroupsFromDefaults = (defaults) => {
|
|
29
107
|
const groups = {};
|
|
30
108
|
Object.keys(defaults).forEach((key) => {
|
|
@@ -53,18 +131,42 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
|
|
|
53
131
|
resetFresh();
|
|
54
132
|
}, [fields]);
|
|
55
133
|
(0, react_1.useEffect)(() => {
|
|
134
|
+
const minGroups = {};
|
|
135
|
+
const initialValues = [];
|
|
136
|
+
const processMinClusters = (formList, prefix) => {
|
|
137
|
+
formList.forEach((form) => {
|
|
138
|
+
if (form.type === "cluster" && form.construction) {
|
|
139
|
+
const { name: mapName, fields: innerForms, min } = form.construction;
|
|
140
|
+
const groupKey = prefix ? `${prefix}.${mapName}` : mapName;
|
|
141
|
+
const minCount = Math.max(0, min || 0);
|
|
142
|
+
if (minCount > 0) {
|
|
143
|
+
minGroups[groupKey] = Array.from({ length: minCount }, (_, i) => i);
|
|
144
|
+
for (let gIndex = 0; gIndex < minCount; gIndex++) {
|
|
145
|
+
innerForms.forEach((inner) => {
|
|
146
|
+
const fieldName = `${groupKey}[${gIndex}].${inner.construction?.name}`;
|
|
147
|
+
initialValues.push({ name: fieldName, value: "" });
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
processMinClusters(fields);
|
|
56
155
|
if (defaultValue) {
|
|
57
156
|
setDefaultValues(defaultValue);
|
|
58
157
|
const derivedGroups = GroupsFromDefaults(defaultValue);
|
|
59
|
-
setMapGroups(derivedGroups);
|
|
158
|
+
setMapGroups({ ...minGroups, ...derivedGroups });
|
|
60
159
|
resetFresh();
|
|
61
160
|
}
|
|
62
161
|
else {
|
|
63
162
|
setDefaultValues(null);
|
|
64
|
-
setMapGroups(
|
|
163
|
+
setMapGroups(minGroups);
|
|
164
|
+
if (initialValues.length > 0) {
|
|
165
|
+
setValues(initialValues);
|
|
166
|
+
}
|
|
65
167
|
resetFresh();
|
|
66
168
|
}
|
|
67
|
-
}, [defaultValue]);
|
|
169
|
+
}, [defaultValue, fields]);
|
|
68
170
|
const generateColClass = (col) => String(col).split(" ").map((c) => (c.includes(":") ? `${c.replace(":", ":col-span-")}` : `col-span-${c}`)).join(" ");
|
|
69
171
|
const inputMap = {
|
|
70
172
|
default: __1.InputComponent,
|
|
@@ -85,13 +187,29 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
|
|
|
85
187
|
const renderInput = (form, key, prefix) => {
|
|
86
188
|
const inputType = form.type || "default";
|
|
87
189
|
const name = prefix ? `${prefix}.${form.construction?.name}` : form.construction?.name || "input_name";
|
|
88
|
-
|
|
190
|
+
const valMap = {};
|
|
191
|
+
values.forEach((v) => { valMap[v.name] = v.value; });
|
|
192
|
+
if (form?.onHide?.(valMap))
|
|
193
|
+
return null;
|
|
194
|
+
const ws = watchState[name];
|
|
195
|
+
if (ws?.hidden)
|
|
89
196
|
return null;
|
|
90
197
|
if (inputType === "cluster") {
|
|
91
|
-
const { name: mapName, fields: innerForms, label, tip, wrap, className } = form.construction;
|
|
198
|
+
const { name: mapName, fields: innerForms, label, tip, wrap, className, min } = form.construction;
|
|
199
|
+
const minCount = Math.max(0, min || 0);
|
|
92
200
|
const groupKey = prefix ? `${prefix}.${mapName}` : mapName;
|
|
93
|
-
const
|
|
94
|
-
const
|
|
201
|
+
const defaultGroup = minCount > 0 ? Array.from({ length: minCount }, (_, i) => i) : [];
|
|
202
|
+
const group = mapGroups[groupKey] ?? defaultGroup;
|
|
203
|
+
const showDeleteButton = group.length > minCount;
|
|
204
|
+
const addGroup = () => {
|
|
205
|
+
const nextIndex = group.length;
|
|
206
|
+
setMapGroups((prev) => ({ ...prev, [groupKey]: [...group, nextIndex] }));
|
|
207
|
+
const newGroupValues = innerForms.map((inner) => ({
|
|
208
|
+
name: `${groupKey}[${nextIndex}].${inner.construction?.name}`,
|
|
209
|
+
value: "",
|
|
210
|
+
}));
|
|
211
|
+
setValues([...values, ...newGroupValues]);
|
|
212
|
+
};
|
|
95
213
|
const removeGroup = (index) => {
|
|
96
214
|
const filteredGroup = group.filter((_, i) => i !== index);
|
|
97
215
|
const newGroup = filteredGroup.map((_, i) => i);
|
|
@@ -116,14 +234,15 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
|
|
|
116
234
|
});
|
|
117
235
|
setValues(updatedValues);
|
|
118
236
|
};
|
|
119
|
-
|
|
237
|
+
const clusterError = errors?.find((err) => err.name === groupKey || err.name === mapName)?.error;
|
|
238
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("flex flex-col gap-4", generateColClass(form.col || "12")), children: [label && (0, jsx_runtime_1.jsx)("p", { className: "input-label", children: label }), clusterError && (0, jsx_runtime_1.jsx)("small", { className: "input-error-message", children: clusterError }), group.map((gIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("relative pr-8", wrap && "p-4 rounded border", className), children: [label && (0, jsx_runtime_1.jsxs)("p", { className: "input-label", children: [label, " ", gIndex + 1] }), tip && (0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-tip"), children: tip }), (label || tip) && (0, jsx_runtime_1.jsx)("div", { className: "mb-2" }), (0, jsx_runtime_1.jsx)("div", { className: "w-full grid grid-cols-12 gap-4", children: innerForms.map((inner, i) => renderInput(inner, i, `${groupKey}[${gIndex}]`)) }), showDeleteButton && ((0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/times", paint: "danger", variant: "outline", size: "xs", className: (0, _utils_1.cn)("absolute top-10 right-2 translate-x-[50%] -translate-y-[50%]", wrap && "translate-x-0 -translate-y-0 top-1 right-1"), onClick: () => removeGroup(gIndex) }))] }, gIndex))), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/plus", label: `${l.base.add ? l.base.add() : "Add"} ${label || mapName}`, variant: "outline", size: "sm", onClick: addGroup }) })] }, key));
|
|
120
239
|
}
|
|
121
240
|
if (inputType === "custom") {
|
|
122
241
|
const customRender = form.construction;
|
|
123
242
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)(form.className, generateColClass(form.col || "12")), children: customRender?.({ formControl, values, setValues, errors, setErrors, setRegister, prefixName: prefix }) }, key));
|
|
124
243
|
}
|
|
125
244
|
const Component = inputMap[inputType] || __1.InputComponent;
|
|
126
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)(form.className, generateColClass(form.col || "12")), children: (0, jsx_runtime_1.jsx)(Component, { ...form.construction, ...formControl(name) }) }, key));
|
|
245
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)(form.className, generateColClass(form.col || "12")), children: (0, jsx_runtime_1.jsx)(Component, { ...form.construction, ...formControl(name), disabled: ws?.disabled, readOnly: ws?.readonly, name: name }) }, key));
|
|
127
246
|
};
|
|
128
247
|
(0, react_1.useEffect)(() => {
|
|
129
248
|
(modal == "failed") && setModal(false);
|
package/package.json
CHANGED
|
@@ -72,7 +72,7 @@ export function InputComponent({
|
|
|
72
72
|
// =========================>
|
|
73
73
|
// ## Initial
|
|
74
74
|
// =========================>
|
|
75
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, props.type == "file")
|
|
75
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, props.type == "file", unregister)
|
|
76
76
|
const randomId = useInputRandomId()
|
|
77
77
|
|
|
78
78
|
|
|
@@ -70,7 +70,7 @@ export function InputCheckboxComponent({
|
|
|
70
70
|
// =========================>
|
|
71
71
|
// ## initial
|
|
72
72
|
// =========================>
|
|
73
|
-
const inputHandler = useInputHandler(name, value, validations, register, false)
|
|
73
|
+
const inputHandler = useInputHandler(name, value, validations, register, false, unregister)
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
// =========================>
|
|
@@ -55,7 +55,7 @@ export function InputCurrencyComponent({
|
|
|
55
55
|
// =========================>
|
|
56
56
|
// ## Initial
|
|
57
57
|
// =========================>
|
|
58
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
58
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
59
59
|
const randomId = useInputRandomId()
|
|
60
60
|
|
|
61
61
|
|
|
@@ -54,7 +54,7 @@ export function InputDateComponent({
|
|
|
54
54
|
// =========================>
|
|
55
55
|
// ## Initial
|
|
56
56
|
// =========================>
|
|
57
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
57
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
58
58
|
const randomId = useInputRandomId()
|
|
59
59
|
|
|
60
60
|
|
|
@@ -61,7 +61,7 @@ export function InputDatetimeComponent({
|
|
|
61
61
|
// =========================>
|
|
62
62
|
// ## Initial
|
|
63
63
|
// =========================>
|
|
64
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
64
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
65
65
|
const randomId = useInputRandomId()
|
|
66
66
|
|
|
67
67
|
|
|
@@ -73,7 +73,7 @@ export function InputDocumentComponent({
|
|
|
73
73
|
// =========================>
|
|
74
74
|
// ## Initial
|
|
75
75
|
// =========================>
|
|
76
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, props.type == "file")
|
|
76
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, props.type == "file", unregister)
|
|
77
77
|
const randomId = useInputRandomId()
|
|
78
78
|
|
|
79
79
|
// =========================>
|
|
@@ -56,7 +56,7 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
56
56
|
const [cropSrc, setCropSrc] = useState<string | null>(null);
|
|
57
57
|
const [openCrop, setOpenCrop] = useState(false);
|
|
58
58
|
|
|
59
|
-
const inputHandler = useInputHandler(name, value, validations, register, true);
|
|
59
|
+
const inputHandler = useInputHandler(name, value, validations, register, true, unregister);
|
|
60
60
|
const [invalidMessage, setInvalidMessage] = useValidation(inputHandler.value, validations, invalid, inputHandler.idle);
|
|
61
61
|
|
|
62
62
|
useEffect(() => {
|
|
@@ -53,7 +53,7 @@ export function InputNumberComponent({
|
|
|
53
53
|
// =========================>
|
|
54
54
|
// ## Initial
|
|
55
55
|
// =========================>
|
|
56
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
56
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
57
57
|
const randomId = useInputRandomId()
|
|
58
58
|
|
|
59
59
|
|
|
@@ -55,7 +55,7 @@ export function InputPasswordComponent({
|
|
|
55
55
|
// =========================>
|
|
56
56
|
// ## Initial
|
|
57
57
|
// =========================>
|
|
58
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
58
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
59
59
|
const randomId = useInputRandomId()
|
|
60
60
|
const randomConfirmId = useInputRandomId()
|
|
61
61
|
|
|
@@ -68,7 +68,7 @@ export function InputRadioComponent({
|
|
|
68
68
|
// =========================>
|
|
69
69
|
// ## Initial
|
|
70
70
|
// =========================>
|
|
71
|
-
const inputHandler = useInputHandler(name, value, validations, register, false)
|
|
71
|
+
const inputHandler = useInputHandler(name, value, validations, register, false, unregister)
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
// =========================>
|
|
@@ -53,7 +53,7 @@ export function InputTimeComponent({
|
|
|
53
53
|
// =========================>
|
|
54
54
|
// ## Initial
|
|
55
55
|
// =========================>
|
|
56
|
-
const inputHandler = useInputHandler(props.name, value, validations, register, false)
|
|
56
|
+
const inputHandler = useInputHandler(props.name, value, validations, register, false, unregister)
|
|
57
57
|
const randomId = useInputRandomId()
|
|
58
58
|
|
|
59
59
|
|