@skalfa/skalfa-component 1.0.22 → 1.0.24
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.js +1 -1
- package/dist/supervision/FormSupervision.component.d.ts +16 -1
- package/dist/supervision/FormSupervision.component.js +129 -10
- package/dist/supervision/TableSupervision.component.js +2 -2
- package/dist/table/Table.component.js +1 -1
- 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 +1 -1
- package/src/supervision/FormSupervision.component.tsx +196 -32
- package/src/supervision/TableSupervision.component.tsx +2 -2
- package/src/table/Table.component.tsx +1 -1
|
@@ -15,7 +15,7 @@ function InputComponent({ label, tip, leftIcon, rightIcon, className = "", value
|
|
|
15
15
|
// =========================>
|
|
16
16
|
// ## Initial
|
|
17
17
|
// =========================>
|
|
18
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, props.type == "file");
|
|
18
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, props.type == "file", unregister);
|
|
19
19
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
20
20
|
// =========================>
|
|
21
21
|
// ## Invalid handler
|
|
@@ -14,7 +14,7 @@ function InputCheckboxComponent({ name, label, tip, vertical, className = "", cl
|
|
|
14
14
|
// =========================>
|
|
15
15
|
// ## initial
|
|
16
16
|
// =========================>
|
|
17
|
-
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false);
|
|
17
|
+
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false, unregister);
|
|
18
18
|
// =========================>
|
|
19
19
|
// ## Invalid handler
|
|
20
20
|
// =========================>
|
|
@@ -9,7 +9,7 @@ function InputCurrencyComponent({ label, tip, leftIcon, rightIcon, value, invali
|
|
|
9
9
|
// =========================>
|
|
10
10
|
// ## Initial
|
|
11
11
|
// =========================>
|
|
12
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
12
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
13
13
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
14
14
|
// =========================>
|
|
15
15
|
// ## Invalid handler
|
|
@@ -19,7 +19,7 @@ function InputDateComponent({ label, tip, leftIcon, rightIcon, value, invalid, v
|
|
|
19
19
|
// =========================>
|
|
20
20
|
// ## Initial
|
|
21
21
|
// =========================>
|
|
22
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
22
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
23
23
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
24
24
|
// =========================>
|
|
25
25
|
// ## Invalid handler
|
|
@@ -20,7 +20,7 @@ function InputDatetimeComponent({ label, tip, leftIcon, rightIcon, value, invali
|
|
|
20
20
|
// =========================>
|
|
21
21
|
// ## Initial
|
|
22
22
|
// =========================>
|
|
23
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
23
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
24
24
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
25
25
|
// =========================>
|
|
26
26
|
// ## Invalid handler
|
|
@@ -23,7 +23,7 @@ function InputDocumentComponent({ label, tip, leftIcon, rightIcon, className = "
|
|
|
23
23
|
// =========================>
|
|
24
24
|
// ## Initial
|
|
25
25
|
// =========================>
|
|
26
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, props.type == "file");
|
|
26
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, props.type == "file", unregister);
|
|
27
27
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
28
28
|
// =========================>
|
|
29
29
|
// ## Invalid handler
|
|
@@ -17,7 +17,7 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect = "1/1"
|
|
|
17
17
|
const [drag, setDrag] = (0, react_1.useState)(false);
|
|
18
18
|
const [cropSrc, setCropSrc] = (0, react_1.useState)(null);
|
|
19
19
|
const [openCrop, setOpenCrop] = (0, react_1.useState)(false);
|
|
20
|
-
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, true);
|
|
20
|
+
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, true, unregister);
|
|
21
21
|
const [invalidMessage, setInvalidMessage] = (0, _utils_1.useValidation)(inputHandler.value, validations, invalid, inputHandler.idle);
|
|
22
22
|
(0, react_1.useEffect)(() => {
|
|
23
23
|
if (value) {
|
|
@@ -9,7 +9,7 @@ function InputNumberComponent({ label, tip, leftIcon, rightIcon, value, invalid,
|
|
|
9
9
|
// =========================>
|
|
10
10
|
// ## Initial
|
|
11
11
|
// =========================>
|
|
12
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
12
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
13
13
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
14
14
|
// =========================>
|
|
15
15
|
// ## Invalid handler
|
|
@@ -15,7 +15,7 @@ function InputPasswordComponent({ label, tip, leftIcon, rightIcon, value, invali
|
|
|
15
15
|
// =========================>
|
|
16
16
|
// ## Initial
|
|
17
17
|
// =========================>
|
|
18
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
18
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
19
19
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
20
20
|
const randomConfirmId = (0, _utils_1.useInputRandomId)();
|
|
21
21
|
// =========================>
|
|
@@ -12,7 +12,7 @@ function InputRadioComponent({ name, label, tip, vertical, value, disabled, inva
|
|
|
12
12
|
// =========================>
|
|
13
13
|
// ## Initial
|
|
14
14
|
// =========================>
|
|
15
|
-
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false);
|
|
15
|
+
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false, unregister);
|
|
16
16
|
// =========================>
|
|
17
17
|
// ## Invalid handler
|
|
18
18
|
// =========================>
|
|
@@ -15,7 +15,7 @@ function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, v
|
|
|
15
15
|
// =========================>
|
|
16
16
|
// ## Initial
|
|
17
17
|
// =========================>
|
|
18
|
-
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false);
|
|
18
|
+
const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
|
|
19
19
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
20
20
|
// =========================>
|
|
21
21
|
// ## Invalid handler
|
|
@@ -21,7 +21,7 @@ function SelectComponent({ name, label, placeholder, tip, leftIcon, rightIcon, v
|
|
|
21
21
|
// =========================>
|
|
22
22
|
// ## Initial
|
|
23
23
|
// =========================>
|
|
24
|
-
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false);
|
|
24
|
+
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, false, unregister);
|
|
25
25
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
26
26
|
// =========================>
|
|
27
27
|
// ## Invalid handler
|
|
@@ -24,6 +24,7 @@ type ClusterConstruction = {
|
|
|
24
24
|
tip: string;
|
|
25
25
|
fields: FormType[];
|
|
26
26
|
wrap: boolean;
|
|
27
|
+
min?: number;
|
|
27
28
|
/** Use custom class with: "label::", "tip::", "error::", "icon::", "suggest::", "suggest-item::". */
|
|
28
29
|
className: string;
|
|
29
30
|
};
|
|
@@ -44,12 +45,26 @@ type ConstructionMap = {
|
|
|
44
45
|
custom: formCustomConstructionProps;
|
|
45
46
|
};
|
|
46
47
|
type TypeKeys = keyof ConstructionMap;
|
|
48
|
+
export type WatchContext = {
|
|
49
|
+
values: Record<string, any>;
|
|
50
|
+
self: string;
|
|
51
|
+
prev: WatchAction;
|
|
52
|
+
};
|
|
53
|
+
export type WatchAction = {
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
hidden?: boolean;
|
|
56
|
+
value?: any;
|
|
57
|
+
required?: boolean;
|
|
58
|
+
readonly?: boolean;
|
|
59
|
+
reset?: boolean;
|
|
60
|
+
};
|
|
47
61
|
export interface FormType<T extends TypeKeys = keyof ConstructionMap> {
|
|
48
62
|
col?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | string;
|
|
49
63
|
className?: string;
|
|
50
64
|
construction?: ConstructionMap[T];
|
|
51
65
|
type?: T;
|
|
52
|
-
onHide?: (values: any) => boolean;
|
|
66
|
+
onHide?: (values: Record<string, any>) => boolean;
|
|
67
|
+
onWatch?: (ctx: WatchContext) => WatchAction | undefined;
|
|
53
68
|
}
|
|
54
69
|
export interface formSupervisionProps {
|
|
55
70
|
title?: string;
|
|
@@ -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);
|
|
@@ -213,10 +213,10 @@ function TableSupervisionComponent({ title, id, fetchControl, columnControl, for
|
|
|
213
213
|
} }) }, "button-add"));
|
|
214
214
|
}
|
|
215
215
|
if (cb == "IMPORT") {
|
|
216
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "px-1.5 rounded-md relative", children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/
|
|
216
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "px-1.5 rounded-md relative", children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/import", variant: "outline", className: "!text-foreground", onClick: () => setToggle(`MODAL_IMPORT_${toggleKey}`), size: "sm" }) }, "import"));
|
|
217
217
|
}
|
|
218
218
|
if (cb == "EXPORT") {
|
|
219
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "px-1.5 rounded-md relative", children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/
|
|
219
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "px-1.5 rounded-md relative", children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/export", variant: "outline", className: "!text-foreground", onClick: () => setToggle(`MODAL_EXPORT_${toggleKey}`), size: "sm" }) }, "export-excel"));
|
|
220
220
|
}
|
|
221
221
|
if (cb == "PRINT") {
|
|
222
222
|
return ((0, jsx_runtime_1.jsx)("div", { className: "px-1.5 rounded-md relative", children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/file-pdf", label: l.base.print ? l.base.print() : "Print", variant: "outline", className: "!text-foreground", onClick: () => setToggle(`MODAL_PRINT_${toggleKey}`), size: "sm" }) }, "export-pdf"));
|
|
@@ -97,7 +97,7 @@ function TableComponent({ id, controlBar, columns, data, pagination, loading, so
|
|
|
97
97
|
selector: c.selector,
|
|
98
98
|
type: typeof c?.filterable == "object" ? c?.filterable?.type : "text",
|
|
99
99
|
options: typeof c?.filterable == "object" && c?.filterable?.type == "select" ? c?.filterable?.options : undefined
|
|
100
|
-
})), onFilter: (filters) => onChangeFilter?.(filters), filter: filter, className: (0, _utils_1.pcn)(className, "controller-bar") || "" })), (0, jsx_runtime_1.jsx)("div", { className: "relative", children: (0, jsx_runtime_1.jsx)(ScrollContainer_component_1.ScrollContainerComponent, { scrollFloating: !isSm && block, className:
|
|
100
|
+
})), onFilter: (filters) => onChangeFilter?.(filters), filter: filter, className: (0, _utils_1.pcn)(className, "controller-bar") || "" })), (0, jsx_runtime_1.jsx)("div", { className: "relative", children: (0, jsx_runtime_1.jsx)(ScrollContainer_component_1.ScrollContainerComponent, { scrollFloating: !isSm && block, className: `w-full ${block ? "pb-12" : ""}`, onScroll: (e) => {
|
|
101
101
|
actionColumnRef.current?.clientWidth && e.scrollLeft &&
|
|
102
102
|
setShowFloatingAction(e.scrollLeft + e.clientWidth <= e.scrollWidth - actionColumnRef.current?.clientWidth);
|
|
103
103
|
}, footer: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: block && pagination && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "py-6" }), (0, jsx_runtime_1.jsx)("div", { className: "my-2 absolute bottom-0 w-full", children: (0, jsx_runtime_1.jsx)(Pagination_component_1.PaginationComponent, { ...pagination }) })] })) }), children: loading ? ((0, jsx_runtime_1.jsx)("div", { className: "w-max min-w-full", children: (0, jsx_runtime_1.jsx)("div", { className: "table-loading-container", children: (0, jsx_runtime_1.jsx)("h1", { className: "table-loading-text", children: l.base.tableLoading ? l.base.tableLoading() : "" }) }) })) : !data || !data.length ? ((0, jsx_runtime_1.jsx)("div", { className: "table-empty-container", children: (0, jsx_runtime_1.jsx)("h1", { className: "table-empty-text", children: l.base.tableEmpty ? l.base.tableEmpty() : "Data empty" }) })) : ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: !isSm || !responsiveControl?.mobile ? ((0, jsx_runtime_1.jsxs)("div", { className: "w-max min-w-full", children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("table-head-row", (0, _utils_1.pcn)(className, "row")), children: [!!actionBulking && ((0, jsx_runtime_1.jsx)("div", { className: "table-head-column w-max", children: (0, jsx_runtime_1.jsx)(Checkbox_component_1.CheckboxComponent, { name: "selected_table", className: "w-5 h-5", checked: data.length > 0 && checks?.length === data.length, onChange: () => data.length > 0 && checks?.length === data.length ? onChangeChecks?.([]) : onChangeChecks?.(data.map((d) => d.id)) }) })), !noIndex && (0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)("table-head-column w-8", (0, _utils_1.pcn)(className, "head-column")), children: "#" }), renderHead()] }), (0, jsx_runtime_1.jsx)("div", { className: "table-body", children: data.map((item, key) => {
|
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
|
|
|
@@ -105,7 +105,7 @@ export function SelectComponent({
|
|
|
105
105
|
// =========================>
|
|
106
106
|
// ## Initial
|
|
107
107
|
// =========================>
|
|
108
|
-
const inputHandler = useInputHandler(name, value, validations, register, false)
|
|
108
|
+
const inputHandler = useInputHandler(name, value, validations, register, false, unregister)
|
|
109
109
|
const randomId = useInputRandomId()
|
|
110
110
|
|
|
111
111
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import React, { ReactNode, useEffect, useState } from "react";
|
|
3
|
+
import React, { ReactNode, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { ApiType, cn, pcn, FormErrorType, FormRegisterType, FormValueType, useForm, ValidationRules, DBSchema } from "@utils";
|
|
5
5
|
import {
|
|
6
6
|
InputCheckboxComponent,
|
|
@@ -65,6 +65,7 @@ type ClusterConstruction = {
|
|
|
65
65
|
tip : string;
|
|
66
66
|
fields : FormType[];
|
|
67
67
|
wrap : boolean;
|
|
68
|
+
min ?: number;
|
|
68
69
|
|
|
69
70
|
/** Use custom class with: "label::", "tip::", "error::", "icon::", "suggest::", "suggest-item::". */
|
|
70
71
|
className : string;
|
|
@@ -89,12 +90,28 @@ type ConstructionMap = {
|
|
|
89
90
|
|
|
90
91
|
type TypeKeys = keyof ConstructionMap;
|
|
91
92
|
|
|
93
|
+
export type WatchContext = {
|
|
94
|
+
values : Record<string, any>
|
|
95
|
+
self : string
|
|
96
|
+
prev : WatchAction
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type WatchAction = {
|
|
100
|
+
disabled ?: boolean
|
|
101
|
+
hidden ?: boolean
|
|
102
|
+
value ?: any
|
|
103
|
+
required ?: boolean
|
|
104
|
+
readonly ?: boolean
|
|
105
|
+
reset ?: boolean
|
|
106
|
+
}
|
|
107
|
+
|
|
92
108
|
export interface FormType<T extends TypeKeys = keyof ConstructionMap> {
|
|
93
109
|
col ?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | string;
|
|
94
110
|
className ?: string;
|
|
95
111
|
construction ?: ConstructionMap[T];
|
|
96
112
|
type ?: T;
|
|
97
|
-
onHide ?: (values: any) => boolean;
|
|
113
|
+
onHide ?: (values: Record<string, any>) => boolean;
|
|
114
|
+
onWatch ?: (ctx: WatchContext) => WatchAction | undefined;
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
export interface formSupervisionProps {
|
|
@@ -130,14 +147,17 @@ export function FormSupervisionComponent({
|
|
|
130
147
|
}: formSupervisionProps) {
|
|
131
148
|
const l = useLang();
|
|
132
149
|
|
|
133
|
-
const [modal, setModal]
|
|
134
|
-
const [fresh, setFresh]
|
|
135
|
-
const [mapGroups, setMapGroups]
|
|
150
|
+
const [modal, setModal] = useState<boolean | "success" | "failed">(false);
|
|
151
|
+
const [fresh, setFresh] = useState<boolean>(true);
|
|
152
|
+
const [mapGroups, setMapGroups] = useState<Record<string, number[]>>({});
|
|
153
|
+
const [watchState, setWatchState] = useState<Record<string, WatchAction>>({});
|
|
154
|
+
const watchRef = useRef<Record<string, WatchAction>>({});
|
|
136
155
|
|
|
137
156
|
const [
|
|
138
157
|
{
|
|
139
158
|
formControl,
|
|
140
159
|
setRegister,
|
|
160
|
+
setUnregister,
|
|
141
161
|
values,
|
|
142
162
|
setValues,
|
|
143
163
|
errors,
|
|
@@ -164,6 +184,95 @@ export function FormSupervisionComponent({
|
|
|
164
184
|
}
|
|
165
185
|
);
|
|
166
186
|
|
|
187
|
+
// ==============================>
|
|
188
|
+
// ## Watch: collect watchers from fields
|
|
189
|
+
// ==============================>
|
|
190
|
+
const collectWatchers = (fieldList: FormType[], prefix?: string): { name: string, onWatch: NonNullable<FormType['onWatch']>, construction: any }[] => {
|
|
191
|
+
const result: { name: string, onWatch: NonNullable<FormType['onWatch']>, construction: any }[] = [];
|
|
192
|
+
|
|
193
|
+
for (const f of fieldList) {
|
|
194
|
+
const inputType = f.type || "default";
|
|
195
|
+
const name = prefix ? `${prefix}.${f.construction?.name}` : f.construction?.name || "";
|
|
196
|
+
|
|
197
|
+
if (inputType === "cluster") {
|
|
198
|
+
const cluster = f.construction as ClusterConstruction;
|
|
199
|
+
const groupKey = prefix ? `${prefix}.${cluster.name}` : cluster.name;
|
|
200
|
+
const group = mapGroups[groupKey] || [];
|
|
201
|
+
|
|
202
|
+
for (const gIndex of group) {
|
|
203
|
+
result.push(...collectWatchers(cluster.fields, `${cluster.name}[${gIndex}]`));
|
|
204
|
+
}
|
|
205
|
+
} else if (f.onWatch) {
|
|
206
|
+
result.push({ name, onWatch: f.onWatch, construction: f.construction });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// ==============================>
|
|
214
|
+
// ## Watch: execute watchers on value change
|
|
215
|
+
// ==============================>
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
const watchers = collectWatchers(fields);
|
|
218
|
+
if (watchers.length === 0) {
|
|
219
|
+
if (Object.keys(watchRef.current).length > 0) {
|
|
220
|
+
watchRef.current = {};
|
|
221
|
+
setWatchState({});
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const valMap: Record<string, any> = {};
|
|
227
|
+
values.forEach((v) => { valMap[v.name] = v.value; });
|
|
228
|
+
|
|
229
|
+
const nextState : Record<string, WatchAction> = {};
|
|
230
|
+
const valueUpdates : FormValueType[] = [];
|
|
231
|
+
|
|
232
|
+
for (const { name, onWatch, construction } of watchers) {
|
|
233
|
+
const prev = watchRef.current[name] || {};
|
|
234
|
+
const action = onWatch({ values: valMap, self: name, prev });
|
|
235
|
+
|
|
236
|
+
if (!action) continue;
|
|
237
|
+
|
|
238
|
+
nextState[name] = action;
|
|
239
|
+
|
|
240
|
+
if (action.hidden && !prev.hidden) setUnregister(name);
|
|
241
|
+
|
|
242
|
+
if (action.required !== prev.required) {
|
|
243
|
+
const baseValidations = Array.isArray(construction?.validations) ? [...construction.validations] : [];
|
|
244
|
+
const newValidations = action.required ? (baseValidations.includes("required") ? baseValidations : [...baseValidations, "required"]) : baseValidations.filter((v: string) => v !== "required");
|
|
245
|
+
|
|
246
|
+
setRegister({ name, validations: newValidations });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (action.reset) {
|
|
250
|
+
const cur = valMap[name];
|
|
251
|
+
|
|
252
|
+
if (cur != null && cur !== "") valueUpdates.push({ name, value: "" });
|
|
253
|
+
} else if (action.value !== undefined && action.value !== valMap[name]) {
|
|
254
|
+
valueUpdates.push({ name, value: action.value });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (JSON.stringify(watchRef.current) !== JSON.stringify(nextState)) {
|
|
259
|
+
watchRef.current = nextState;
|
|
260
|
+
setWatchState(nextState);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (valueUpdates.length > 0) {
|
|
264
|
+
const merged = [...values];
|
|
265
|
+
|
|
266
|
+
for (const upd of valueUpdates) {
|
|
267
|
+
const idx = merged.findIndex(v => v.name === upd.name);
|
|
268
|
+
if (idx >= 0) merged[idx] = upd;
|
|
269
|
+
else merged.push(upd);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
setValues(merged);
|
|
273
|
+
}
|
|
274
|
+
}, [values, fields, mapGroups]);
|
|
275
|
+
|
|
167
276
|
const GroupsFromDefaults = (defaults: Record<string, any>): Record<string, number[]> => {
|
|
168
277
|
const groups: Record<string, Set<number>> = {};
|
|
169
278
|
|
|
@@ -201,19 +310,46 @@ export function FormSupervisionComponent({
|
|
|
201
310
|
}, [fields]);
|
|
202
311
|
|
|
203
312
|
useEffect(() => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
313
|
+
const minGroups: Record<string, number[]> = {};
|
|
314
|
+
const initialValues: FormValueType[] = [];
|
|
315
|
+
|
|
316
|
+
const processMinClusters = (formList: FormType[], prefix?: string) => {
|
|
317
|
+
formList.forEach((form) => {
|
|
318
|
+
if (form.type === "cluster" && form.construction) {
|
|
319
|
+
const { name: mapName, fields: innerForms, min } = form.construction as ClusterConstruction;
|
|
320
|
+
const groupKey = prefix ? `${prefix}.${mapName}` : mapName;
|
|
321
|
+
const minCount = Math.max(0, min || 0);
|
|
322
|
+
|
|
323
|
+
if (minCount > 0) {
|
|
324
|
+
minGroups[groupKey] = Array.from({ length: minCount }, (_, i) => i);
|
|
325
|
+
|
|
326
|
+
for (let gIndex = 0; gIndex < minCount; gIndex++) {
|
|
327
|
+
innerForms.forEach((inner) => {
|
|
328
|
+
const fieldName = `${groupKey}[${gIndex}].${inner.construction?.name}`;
|
|
329
|
+
initialValues.push({ name: fieldName, value: "" });
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
processMinClusters(fields);
|
|
338
|
+
|
|
339
|
+
if (defaultValue) {
|
|
340
|
+
setDefaultValues(defaultValue);
|
|
341
|
+
const derivedGroups = GroupsFromDefaults(defaultValue);
|
|
342
|
+
setMapGroups({ ...minGroups, ...derivedGroups });
|
|
343
|
+
resetFresh();
|
|
344
|
+
} else {
|
|
345
|
+
setDefaultValues(null);
|
|
346
|
+
setMapGroups(minGroups);
|
|
347
|
+
if (initialValues.length > 0) {
|
|
348
|
+
setValues(initialValues);
|
|
349
|
+
}
|
|
350
|
+
resetFresh();
|
|
351
|
+
}
|
|
352
|
+
}, [defaultValue, fields]);
|
|
217
353
|
|
|
218
354
|
const generateColClass = (col: string | number) => String(col).split(" ").map((c) => (c.includes(":") ? `${c.replace(":", ":col-span-")}` : `col-span-${c}`)).join(" ");
|
|
219
355
|
|
|
@@ -238,15 +374,35 @@ export function FormSupervisionComponent({
|
|
|
238
374
|
const inputType = form.type || "default";
|
|
239
375
|
const name = prefix ? `${prefix}.${form.construction?.name}` : form.construction?.name || "input_name";
|
|
240
376
|
|
|
241
|
-
|
|
377
|
+
const valMap: Record<string, any> = {};
|
|
378
|
+
values.forEach((v) => { valMap[v.name] = v.value; });
|
|
379
|
+
|
|
380
|
+
if (form?.onHide?.(valMap)) return null;
|
|
381
|
+
|
|
382
|
+
const ws = watchState[name];
|
|
383
|
+
if (ws?.hidden) return null;
|
|
242
384
|
|
|
243
385
|
if (inputType === "cluster") {
|
|
244
|
-
const { name: mapName, fields: innerForms, label, tip, wrap, className } = form.construction as ClusterConstruction;
|
|
386
|
+
const { name: mapName, fields: innerForms, label, tip, wrap, className, min } = form.construction as ClusterConstruction;
|
|
387
|
+
const minCount = Math.max(0, min || 0);
|
|
245
388
|
|
|
246
389
|
const groupKey = prefix ? `${prefix}.${mapName}` : mapName;
|
|
247
|
-
const
|
|
390
|
+
const defaultGroup = minCount > 0 ? Array.from({ length: minCount }, (_, i) => i) : [];
|
|
391
|
+
const group = mapGroups[groupKey] ?? defaultGroup;
|
|
392
|
+
|
|
393
|
+
const showDeleteButton = group.length > minCount;
|
|
248
394
|
|
|
249
|
-
const addGroup = () =>
|
|
395
|
+
const addGroup = () => {
|
|
396
|
+
const nextIndex = group.length;
|
|
397
|
+
setMapGroups((prev) => ({ ...prev, [groupKey]: [...group, nextIndex] }));
|
|
398
|
+
|
|
399
|
+
const newGroupValues = innerForms.map((inner) => ({
|
|
400
|
+
name: `${groupKey}[${nextIndex}].${inner.construction?.name}`,
|
|
401
|
+
value: "",
|
|
402
|
+
}));
|
|
403
|
+
|
|
404
|
+
setValues([...values, ...newGroupValues]);
|
|
405
|
+
};
|
|
250
406
|
|
|
251
407
|
const removeGroup = (index: number) => {
|
|
252
408
|
const filteredGroup = group.filter((_, i) => i !== index);
|
|
@@ -275,8 +431,12 @@ export function FormSupervisionComponent({
|
|
|
275
431
|
setValues(updatedValues);
|
|
276
432
|
};
|
|
277
433
|
|
|
434
|
+
const clusterError = errors?.find((err: FormErrorType) => err.name === groupKey || err.name === mapName)?.error;
|
|
435
|
+
|
|
278
436
|
return (
|
|
279
437
|
<div key={key} className={cn("flex flex-col gap-4", generateColClass(form.col || "12"))}>
|
|
438
|
+
{label && <p className="input-label">{label}</p>}
|
|
439
|
+
{clusterError && <small className="input-error-message">{clusterError}</small>}
|
|
280
440
|
{group.map((gIndex) => (
|
|
281
441
|
<div key={gIndex} className={cn("relative pr-8", wrap && "p-4 rounded border", className)}>
|
|
282
442
|
{label && <p className="input-label">{label} {gIndex + 1}</p>}
|
|
@@ -284,17 +444,19 @@ export function FormSupervisionComponent({
|
|
|
284
444
|
{(label || tip) && <div className="mb-2"></div>}
|
|
285
445
|
|
|
286
446
|
<div className="w-full grid grid-cols-12 gap-4">
|
|
287
|
-
{innerForms.map((inner, i) => renderInput(inner, i, `${
|
|
447
|
+
{innerForms.map((inner, i) => renderInput(inner, i, `${groupKey}[${gIndex}]`))}
|
|
288
448
|
</div>
|
|
289
449
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
450
|
+
{showDeleteButton && (
|
|
451
|
+
<ButtonComponent
|
|
452
|
+
icon={"solid/times"}
|
|
453
|
+
paint="danger"
|
|
454
|
+
variant="outline"
|
|
455
|
+
size="xs"
|
|
456
|
+
className={cn("absolute top-10 right-2 translate-x-[50%] -translate-y-[50%]", wrap && "translate-x-0 -translate-y-0 top-1 right-1")}
|
|
457
|
+
onClick={() => removeGroup(gIndex)}
|
|
458
|
+
/>
|
|
459
|
+
)}
|
|
298
460
|
</div>
|
|
299
461
|
))}
|
|
300
462
|
|
|
@@ -326,7 +488,9 @@ export function FormSupervisionComponent({
|
|
|
326
488
|
<Component
|
|
327
489
|
{...(form.construction as any)}
|
|
328
490
|
{...formControl(name)}
|
|
329
|
-
|
|
491
|
+
disabled={ws?.disabled}
|
|
492
|
+
readOnly={ws?.readonly}
|
|
493
|
+
name={name}
|
|
330
494
|
/>
|
|
331
495
|
</div>
|
|
332
496
|
);
|
|
@@ -431,7 +431,7 @@ export function TableSupervisionComponent({
|
|
|
431
431
|
return (
|
|
432
432
|
<div className="px-1.5 rounded-md relative" key={"import"}>
|
|
433
433
|
<ButtonComponent
|
|
434
|
-
icon={"solid/
|
|
434
|
+
icon={"solid/import"}
|
|
435
435
|
variant="outline"
|
|
436
436
|
className="!text-foreground"
|
|
437
437
|
onClick={() => setToggle(`MODAL_IMPORT_${toggleKey}`)}
|
|
@@ -445,7 +445,7 @@ export function TableSupervisionComponent({
|
|
|
445
445
|
return (
|
|
446
446
|
<div className="px-1.5 rounded-md relative" key={"export-excel"}>
|
|
447
447
|
<ButtonComponent
|
|
448
|
-
icon={"solid/
|
|
448
|
+
icon={"solid/export"}
|
|
449
449
|
variant="outline"
|
|
450
450
|
className="!text-foreground"
|
|
451
451
|
onClick={() => setToggle(`MODAL_EXPORT_${toggleKey}`)}
|
|
@@ -293,7 +293,7 @@ export function TableComponent({
|
|
|
293
293
|
<div className="relative">
|
|
294
294
|
<ScrollContainerComponent
|
|
295
295
|
scrollFloating={!isSm && block}
|
|
296
|
-
className=
|
|
296
|
+
className={`w-full ${block ? "pb-12" : ""}`}
|
|
297
297
|
onScroll={(e) => {
|
|
298
298
|
actionColumnRef.current?.clientWidth && e.scrollLeft &&
|
|
299
299
|
setShowFloatingAction(e.scrollLeft + e.clientWidth <= e.scrollWidth - actionColumnRef.current?.clientWidth);
|