react-form-manage 1.0.6-beta.0 → 1.0.6-beta.2
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/CHANGELOG.md +52 -0
- package/dist/components/Form/FormCleanUp.js +46 -0
- package/dist/components/Form/FormItem.d.ts +11 -17
- package/dist/components/Form/FormItem.js +46 -0
- package/dist/components/Form/FormList.d.ts +23 -7
- package/dist/components/Form/FormList.js +11 -0
- package/dist/components/Form/InputWrapper.d.ts +10 -5
- package/dist/components/Form/InputWrapper.js +7 -0
- package/dist/components/Input.d.ts +4 -5
- package/dist/components/Input.js +8 -0
- package/dist/constants/validation.js +33 -0
- package/dist/contexts/FormContext.js +2 -0
- package/dist/hooks/useFormItemControl.js +390 -0
- package/dist/hooks/useFormListControl.js +280 -0
- package/dist/index.d.ts +8 -185
- package/dist/index.js +9 -0
- package/dist/providers/Form.d.ts +15 -10
- package/dist/providers/Form.js +322 -0
- package/dist/stores/formStore.js +304 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/public.d.ts +51 -0
- package/dist/utils/obj.util.js +24 -0
- package/package.json +30 -45
- package/src/App.css +49 -0
- package/src/App.tsx +36 -0
- package/src/assets/react.svg +1 -0
- package/src/components/Form/FormCleanUp.tsx +57 -0
- package/src/components/Form/FormItem.tsx +72 -0
- package/src/components/Form/FormList.tsx +22 -0
- package/src/components/Form/InputWrapper.tsx +24 -0
- package/src/components/Input.jsx +13 -0
- package/src/components/Input.tsx +21 -0
- package/src/constants/validation.ts +63 -0
- package/src/contexts/FormContext.ts +3 -0
- package/src/hooks/useFormItemControl.ts +558 -0
- package/src/hooks/useFormListControl.ts +378 -0
- package/src/index.css +68 -0
- package/src/index.ts +35 -0
- package/src/main.tsx +10 -0
- package/src/providers/Form.tsx +440 -0
- package/src/stores/formStore.ts +477 -0
- package/src/types/index.ts +1 -0
- package/src/types/public.ts +50 -0
- package/src/utils/obj.util.ts +42 -0
- package/dist/App.d.ts +0 -2
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.d.ts +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.esm.d.ts +0 -1
- package/dist/index.esm.js +0 -2
- package/dist/index.esm.js.map +0 -1
- package/src/types/components.d.ts +0 -135
- /package/dist/{main.d.ts → types/public.js} +0 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { get, isNil } from "lodash";
|
|
2
|
+
import { useTaskEffect } from "minh-custom-hooks-release";
|
|
3
|
+
import { useEffect, useMemo } from "react";
|
|
4
|
+
import { useShallow } from "zustand/react/shallow"; // Import useShallow
|
|
5
|
+
import { IS_ALPHABET_STRING_AND_NUMBER_REGEX, IS_EMAIL_REGEX, IS_NAME_REGEX, IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX, IS_NO_SPACE_ALPHABET_STRING_REGEX, IS_NOSPACE_STRING_AND_NUMBER_REGEX, IS_PASSWORD_REGEX, IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX, IS_POSITIVE_STRING_NUMBER_REGEX, IS_STRING_AND_NUMBER_REGEX, IS_STRING_NUMBER_REGEX, IS_USERNAME_REGEX, IS_VIETNAMESE_PHONE_NUMBER_REGEX, } from "../constants/validation";
|
|
6
|
+
import { useFormContext } from "../providers/Form";
|
|
7
|
+
import { useFormCleanUp, useFormListeners, useFormStore, } from "../stores/formStore";
|
|
8
|
+
const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
|
|
9
|
+
export default function useFormItemControl({ formName, form, name, initialValue, formItemId, rules, elementRef, }) {
|
|
10
|
+
const contextForm = useFormContext();
|
|
11
|
+
const { value, setData, getCacheData, getFormValues } = useFormStore(useShallow((state) => {
|
|
12
|
+
return {
|
|
13
|
+
value: get(state.forms, `${formName || form?.formName || contextForm?.formName}.${name}`),
|
|
14
|
+
setData: state.setData,
|
|
15
|
+
getCacheData: state.getCacheData,
|
|
16
|
+
getFormValues: state.getFormValues,
|
|
17
|
+
};
|
|
18
|
+
}));
|
|
19
|
+
const { setCleanUpStack } = useFormCleanUp(useShallow((state) => ({
|
|
20
|
+
setCleanUpStack: state.setCleanUpStack,
|
|
21
|
+
})));
|
|
22
|
+
const { initValue: internalInitValue, setInitData, getInitData, } = useFormStore(useShallow((state) => {
|
|
23
|
+
// console.log(
|
|
24
|
+
// "Shallow get initValue: ",
|
|
25
|
+
// formName || form?.formName || contextForm?.formName,
|
|
26
|
+
// name,
|
|
27
|
+
// get(
|
|
28
|
+
// state.initialValues,
|
|
29
|
+
// `${formName || form?.formName || contextForm?.formName}.${name}`
|
|
30
|
+
// )
|
|
31
|
+
// );
|
|
32
|
+
return {
|
|
33
|
+
initValue: get(state.initialValues, `${formName || form?.formName || contextForm?.formName}.${name}`),
|
|
34
|
+
setInitData: state.setInitData,
|
|
35
|
+
getInitData: state.getInitData,
|
|
36
|
+
};
|
|
37
|
+
}));
|
|
38
|
+
const { listener, setListener } = useFormListeners(useShallow((state) => {
|
|
39
|
+
// console.log(
|
|
40
|
+
// "Get listener from store: ",
|
|
41
|
+
// state.listeners.find((l) => l.formItemId === formItemId)
|
|
42
|
+
// );
|
|
43
|
+
return {
|
|
44
|
+
listener: state.listeners.find((l) => l.formItemId === formItemId),
|
|
45
|
+
setListener: state.setListener,
|
|
46
|
+
};
|
|
47
|
+
}));
|
|
48
|
+
const onInitData = (value) => {
|
|
49
|
+
// console.log(
|
|
50
|
+
// "Init:",
|
|
51
|
+
// formName || form?.formName || contextForm?.formName,
|
|
52
|
+
// name,
|
|
53
|
+
// value
|
|
54
|
+
// );
|
|
55
|
+
setInitData(formName || form?.formName || contextForm?.formName, name, value);
|
|
56
|
+
};
|
|
57
|
+
const onFocus = () => {
|
|
58
|
+
setListener({
|
|
59
|
+
formItemId,
|
|
60
|
+
isTouched: true,
|
|
61
|
+
isDirty: listener?.isDirty,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
const emitFocus = () => {
|
|
65
|
+
if (elementRef?.current) {
|
|
66
|
+
elementRef.current.focus();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const onChange = (value, options) => {
|
|
70
|
+
if (options?.notTriggerDirty !== true) {
|
|
71
|
+
setListener({
|
|
72
|
+
formItemId,
|
|
73
|
+
isDirty: true,
|
|
74
|
+
isTouched: listener?.isTouched,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
setData(formName || form?.formName || contextForm?.formName, name, value);
|
|
78
|
+
};
|
|
79
|
+
const onReset = (value) => {
|
|
80
|
+
console.log("Reset", value, getInitData(formName || form?.formName || contextForm?.formName, name));
|
|
81
|
+
onChange(isNil(value)
|
|
82
|
+
? getInitData(formName || form?.formName || contextForm?.formName, name)
|
|
83
|
+
: value);
|
|
84
|
+
};
|
|
85
|
+
// validate field rules
|
|
86
|
+
const internalRules = useMemo(() => {
|
|
87
|
+
return rules || [];
|
|
88
|
+
}, [rules]);
|
|
89
|
+
const { data: errors, state } = useTaskEffect({
|
|
90
|
+
async task() {
|
|
91
|
+
const listErrors = [];
|
|
92
|
+
const formValues = getFormValues(formName || form?.formName || contextForm?.formName);
|
|
93
|
+
const ruleToTask = internalRules?.map((r, index) => async () => {
|
|
94
|
+
const ruleName = r?.name || index;
|
|
95
|
+
const fieldValue = value?.toString?.();
|
|
96
|
+
if (typeof r.handler === "function") {
|
|
97
|
+
if (!(await r.handler(value, formValues))) {
|
|
98
|
+
listErrors.push({
|
|
99
|
+
ruleName,
|
|
100
|
+
message: r.message,
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (VALID_PREMITIVE_TYPE.includes(typeof value)) {
|
|
106
|
+
// REQUIRE
|
|
107
|
+
if (r.required && !(fieldValue?.toString()?.trim()?.length > 0)) {
|
|
108
|
+
// tempMessage.push("This field is required!");
|
|
109
|
+
listErrors.push({
|
|
110
|
+
ruleName,
|
|
111
|
+
message: r.message,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Min Max
|
|
116
|
+
if (!isNil(r.min) || !isNil(r.max)) {
|
|
117
|
+
if (isNil(value)) {
|
|
118
|
+
listErrors.push({
|
|
119
|
+
ruleName,
|
|
120
|
+
message: r.message,
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if ((r.min ?? Number.MIN_SAFE_INTEGER) <=
|
|
125
|
+
(r.max ?? Number.MAX_SAFE_INTEGER)) {
|
|
126
|
+
if (!isNil(r.min)) {
|
|
127
|
+
if (!isNaN(Number(fieldValue)) && Number(fieldValue) <= r.min) {
|
|
128
|
+
// tempMessage.push(MIN_INVALID_MESSAGE);
|
|
129
|
+
listErrors.push({
|
|
130
|
+
ruleName,
|
|
131
|
+
message: r.message,
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!isNil(r.max)) {
|
|
137
|
+
if (!isNaN(Number(fieldValue)) && Number(fieldValue) >= r.max) {
|
|
138
|
+
// tempMessage.push(MAX_INVALID_MESSAGE);
|
|
139
|
+
listErrors.push({
|
|
140
|
+
ruleName,
|
|
141
|
+
message: r.message,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// MIN LENGTH MAX LENGTH
|
|
149
|
+
if (!isNil(r.minLength) || !isNil(r.maxLength)) {
|
|
150
|
+
if ((r.minLength ?? Number.MIN_SAFE_INTEGER) <=
|
|
151
|
+
(r.maxLength ?? Number.MAX_SAFE_INTEGER)) {
|
|
152
|
+
if (!isNil(r.minLength)) {
|
|
153
|
+
if (fieldValue?.length <= r.minLength) {
|
|
154
|
+
// tempMessage.push(MIN_INVALID_LENGTH_MESSAGE);
|
|
155
|
+
listErrors.push({
|
|
156
|
+
ruleName,
|
|
157
|
+
message: r.message,
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!isNil(r.maxLength)) {
|
|
163
|
+
if (fieldValue?.length >= r.maxLength) {
|
|
164
|
+
// tempMessage.push(MAX_INVALID_LENGTH_MESSAGE);
|
|
165
|
+
listErrors.push({
|
|
166
|
+
ruleName,
|
|
167
|
+
message: r.message,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Password
|
|
175
|
+
if (r.isPassword && !IS_PASSWORD_REGEX.test(fieldValue)) {
|
|
176
|
+
// tempMessage.push(IS_PASSWORD_INVALID_MESSAGE);
|
|
177
|
+
listErrors.push({
|
|
178
|
+
ruleName,
|
|
179
|
+
message: r.message,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Username
|
|
184
|
+
if (r.isUsername && !IS_USERNAME_REGEX.test(fieldValue)) {
|
|
185
|
+
// tempMessage.push(IS_USERNAME_INVALID_MESSAGE);
|
|
186
|
+
listErrors.push({
|
|
187
|
+
ruleName,
|
|
188
|
+
message: r.message,
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Is Vietnamese Phone number
|
|
193
|
+
if (r.isPhoneNumber &&
|
|
194
|
+
!IS_VIETNAMESE_PHONE_NUMBER_REGEX.test(fieldValue)) {
|
|
195
|
+
// tempMessage.push(IS_VIETNAMESE_PHONE_NUMBER_INVALID_MESSAGE);
|
|
196
|
+
listErrors.push({
|
|
197
|
+
ruleName,
|
|
198
|
+
message: r.message,
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// String number
|
|
203
|
+
if (r.isStringNumber && !IS_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
204
|
+
// tempMessage.push(IS_STRING_NUMBER_INVALID_MESSAGE);
|
|
205
|
+
listErrors.push({
|
|
206
|
+
ruleName,
|
|
207
|
+
message: r.message,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// String positive number
|
|
212
|
+
if (r.isPositiveStringNumber &&
|
|
213
|
+
!IS_POSITIVE_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
214
|
+
// tempMessage.push(IS_POSITIVE_STRING_NUMBER_INVALID_MESSAGE);
|
|
215
|
+
listErrors.push({
|
|
216
|
+
ruleName,
|
|
217
|
+
message: r.message,
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// String positive integer number
|
|
222
|
+
if (r.isPositiveIntegerStringNumber &&
|
|
223
|
+
!IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
224
|
+
// tempMessage.push(
|
|
225
|
+
listErrors.push({
|
|
226
|
+
ruleName,
|
|
227
|
+
message: r.message,
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
// IS_POSITIVE_INTEGER_STRING_NUMBER_INVALID_MESSAGE
|
|
231
|
+
// );
|
|
232
|
+
}
|
|
233
|
+
// Email
|
|
234
|
+
if (r.isEmail && !IS_EMAIL_REGEX.test(fieldValue)) {
|
|
235
|
+
// tempMessage.push(IS_EMAIL_INVALID_MESSAGE);
|
|
236
|
+
listErrors.push({
|
|
237
|
+
ruleName,
|
|
238
|
+
message: r.message,
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
// Vietnamese Name
|
|
243
|
+
if (r.isVietnameseName && !IS_NAME_REGEX.test(fieldValue)) {
|
|
244
|
+
// tempMessage.push(IS_NAME_INVALID_MESSAGE);
|
|
245
|
+
listErrors.push({
|
|
246
|
+
ruleName,
|
|
247
|
+
message: r.message,
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// String and number without whitespace
|
|
252
|
+
if (r.isNoSpaceStringAndNumber &&
|
|
253
|
+
!IS_NOSPACE_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
254
|
+
// tempMessage.push(IS_NOSPACE_STRING_AND_NUMBER_MESSAGE);
|
|
255
|
+
listErrors.push({
|
|
256
|
+
ruleName,
|
|
257
|
+
message: r.message,
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// String and number with
|
|
262
|
+
if (r.isStringAndNumber &&
|
|
263
|
+
!IS_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
264
|
+
// tempMessage.push(IS_STRING_AND_NUMBER_MESSAGE);
|
|
265
|
+
listErrors.push({
|
|
266
|
+
ruleName,
|
|
267
|
+
message: r.message,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (r.isNoSpaceOnlyAlphabetStringAndNumber &&
|
|
272
|
+
!IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
273
|
+
// tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_MESSAGE);
|
|
274
|
+
listErrors.push({
|
|
275
|
+
ruleName,
|
|
276
|
+
message: r.message,
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (r.isOnlyAlphabetStringAndNumber &&
|
|
281
|
+
!IS_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
282
|
+
// tempMessage.push(IS_ALPHABET_STRING_AND_NUMBER_MESSAGE);
|
|
283
|
+
listErrors.push({
|
|
284
|
+
ruleName,
|
|
285
|
+
message: r.message,
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (r.isNoSpaceAlphabetString &&
|
|
290
|
+
!IS_NO_SPACE_ALPHABET_STRING_REGEX.test(fieldValue)) {
|
|
291
|
+
// tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_MESSAGE);
|
|
292
|
+
listErrors.push({
|
|
293
|
+
ruleName,
|
|
294
|
+
message: r.message,
|
|
295
|
+
});
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
// console.log("Trigger validate: ", listErrors);
|
|
301
|
+
for (const rAsync of ruleToTask) {
|
|
302
|
+
await rAsync();
|
|
303
|
+
}
|
|
304
|
+
setListener({ formItemId, internalErrors: listErrors });
|
|
305
|
+
return listErrors;
|
|
306
|
+
},
|
|
307
|
+
deps: [internalRules, value],
|
|
308
|
+
onError(err) {
|
|
309
|
+
console.log(err);
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
// useEffect(() => {
|
|
313
|
+
// console.log({ name, errors });
|
|
314
|
+
// }, [errors]);
|
|
315
|
+
// useEffect(() => {
|
|
316
|
+
// console.log({ name, listener });
|
|
317
|
+
// }, [listener]);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
// console.log({ internalInitValue, value, initialValue });
|
|
320
|
+
// console.log("Init item value: ", name, value, internalInitValue);
|
|
321
|
+
// console.log("internalInitValue: ", internalInitValue);
|
|
322
|
+
if (!value) {
|
|
323
|
+
if (!internalInitValue) {
|
|
324
|
+
if (!isNil(initialValue)) {
|
|
325
|
+
console.log("On init data", initialValue);
|
|
326
|
+
onInitData(initialValue);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
onChange(internalInitValue);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}, [internalInitValue]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
if (!listener) {
|
|
336
|
+
setListener({
|
|
337
|
+
onChange,
|
|
338
|
+
emitFocus,
|
|
339
|
+
isTouched: false,
|
|
340
|
+
isDirty: false,
|
|
341
|
+
name,
|
|
342
|
+
formName: formName || form?.formName || contextForm?.formName,
|
|
343
|
+
formItemId,
|
|
344
|
+
onReset,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
// return () => {
|
|
348
|
+
// console.log("Revoke listener", listener);
|
|
349
|
+
// };
|
|
350
|
+
}, []);
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (listener) {
|
|
353
|
+
setListener({
|
|
354
|
+
formItemId,
|
|
355
|
+
onChange,
|
|
356
|
+
name,
|
|
357
|
+
formName: formName || form?.formName || contextForm?.formName,
|
|
358
|
+
onReset,
|
|
359
|
+
isTouched: listener?.isTouched,
|
|
360
|
+
isDirty: listener?.isDirty,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const cacheData = getCacheData(formName || form?.formName || contextForm?.formName);
|
|
364
|
+
// console.log("Get cache Data after list change: ", cacheData);
|
|
365
|
+
if (cacheData) {
|
|
366
|
+
const getNewDataFromCache = get(cacheData, name);
|
|
367
|
+
// console.log("Init data when change form ite: ", name, cacheData);
|
|
368
|
+
if (!getNewDataFromCache) {
|
|
369
|
+
onChange(initialValue);
|
|
370
|
+
}
|
|
371
|
+
else
|
|
372
|
+
onChange(getNewDataFromCache);
|
|
373
|
+
}
|
|
374
|
+
}, [name, formName || form?.formName || contextForm?.formName]);
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
return () => {
|
|
377
|
+
setCleanUpStack({
|
|
378
|
+
itemKey: formItemId,
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
}, []);
|
|
382
|
+
return {
|
|
383
|
+
value: value,
|
|
384
|
+
onChange,
|
|
385
|
+
state,
|
|
386
|
+
errors,
|
|
387
|
+
onFocus,
|
|
388
|
+
isDirty: listener?.isDirty,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { cloneDeep, get, isNil } from "lodash";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { useShallow } from "zustand/react/shallow";
|
|
5
|
+
import { useFormContext } from "../providers/Form";
|
|
6
|
+
import { useFormCleanUp, useFormStore } from "../stores/formStore";
|
|
7
|
+
export default function useFormListControl({ name, form, initialValues, formName, }) {
|
|
8
|
+
const contextForm = useFormContext();
|
|
9
|
+
const getFormValues = useFormStore((state) => state.getFormValues);
|
|
10
|
+
const [listFormInitValues, setListFormInitValues] = useState(undefined);
|
|
11
|
+
const { clearCacheData, setCacheData } = useFormStore(useShallow((state) => ({
|
|
12
|
+
cacheData: state.cacheData,
|
|
13
|
+
clearCacheData: state.clearCacheData,
|
|
14
|
+
setCacheData: state.setCacheData,
|
|
15
|
+
})));
|
|
16
|
+
const { setCleanUpStack } = useFormCleanUp(useShallow((state) => ({
|
|
17
|
+
setCleanUpStack: state.setCleanUpStack,
|
|
18
|
+
})));
|
|
19
|
+
const { initValue: internalInitValue, formState } = useFormStore(useShallow((state) => {
|
|
20
|
+
// console.log(
|
|
21
|
+
// "Shallow get initValue: ",
|
|
22
|
+
// formName || form?.formName || contextForm?.formName,
|
|
23
|
+
// name,
|
|
24
|
+
// get(
|
|
25
|
+
// state.initialValues,
|
|
26
|
+
// `${formName || form?.formName || contextForm?.formName}.${name}`
|
|
27
|
+
// )
|
|
28
|
+
// );
|
|
29
|
+
return {
|
|
30
|
+
initValue: get(state.initialValues, `${formName || form?.formName || contextForm?.formName}.${name}`),
|
|
31
|
+
formState: get(state.formStates, formName || form?.formName || contextForm?.formName),
|
|
32
|
+
setInitData: state.setInitData,
|
|
33
|
+
getFormState: state.getFormState,
|
|
34
|
+
};
|
|
35
|
+
}));
|
|
36
|
+
const [listFields, setListFields] = useState([]);
|
|
37
|
+
const handleCacheListField = (prev, cur) => {
|
|
38
|
+
const currentFormValues = cloneDeep(getFormValues(formName || form?.formName || contextForm?.formName));
|
|
39
|
+
const formDataBeforeChange = get(currentFormValues, name);
|
|
40
|
+
const mapPrevWithKey = formDataBeforeChange
|
|
41
|
+
.map((d, index) => {
|
|
42
|
+
const findPrev = prev.find((p, pIndex) => pIndex === index);
|
|
43
|
+
if (!findPrev)
|
|
44
|
+
return undefined;
|
|
45
|
+
return {
|
|
46
|
+
key: findPrev.key,
|
|
47
|
+
value: d,
|
|
48
|
+
};
|
|
49
|
+
})
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
const mapCurWithKey = cur.map((c) => mapPrevWithKey.find((m) => m.key === c.key) || c);
|
|
52
|
+
const getNewValueCache = mapCurWithKey.filter(Boolean).map((c) => c.value);
|
|
53
|
+
const startRemoveIndex = formDataBeforeChange.length - getNewValueCache.length;
|
|
54
|
+
// console.log("Before list fields: ", prev);
|
|
55
|
+
// console.log("After list fields: ", cur);
|
|
56
|
+
// console.log("Before change arr value: ", formDataBeforeChange);
|
|
57
|
+
// console.log("Mapping prev value with prev fields: ", mapPrevWithKey);
|
|
58
|
+
// console.log("Mapping Cur value with prev fields: ", mapCurWithKey);
|
|
59
|
+
// console.log("After change arr value: ", getNewValueCache);
|
|
60
|
+
// Nếu số phần tử trước khi thay đổi mảng lớn hơn thì đẩy 2 phần tử còn lại vào clean up stack để clear
|
|
61
|
+
if (startRemoveIndex > 0) {
|
|
62
|
+
Array.from(Array(startRemoveIndex))
|
|
63
|
+
.map((_, index) => {
|
|
64
|
+
return getNewValueCache.length + index;
|
|
65
|
+
})
|
|
66
|
+
.forEach((index) => {
|
|
67
|
+
// console.log("Data for cleanup arr element: ", {
|
|
68
|
+
// formName: formName || form?.formName || contextForm?.formName,
|
|
69
|
+
// name: `${name}.${index}`,
|
|
70
|
+
// type: "array",
|
|
71
|
+
// });
|
|
72
|
+
setCleanUpStack({
|
|
73
|
+
formName: formName || form?.formName || contextForm?.formName,
|
|
74
|
+
name: `${name}.${index}`,
|
|
75
|
+
type: "array",
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// console.log({ getNewValueCache });
|
|
80
|
+
setCacheData(formName || form?.formName || contextForm?.formName, name, getNewValueCache);
|
|
81
|
+
};
|
|
82
|
+
const add = (index) => {
|
|
83
|
+
setListFields((prev) => {
|
|
84
|
+
if (index > prev.length)
|
|
85
|
+
return prev;
|
|
86
|
+
if (index < 0)
|
|
87
|
+
return prev;
|
|
88
|
+
if (index === prev.length) {
|
|
89
|
+
const newName = `${name}.${prev.length}`;
|
|
90
|
+
const newKey = v4();
|
|
91
|
+
const result = [
|
|
92
|
+
...prev,
|
|
93
|
+
{
|
|
94
|
+
name: newName,
|
|
95
|
+
key: newKey,
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
const clonePrev = [...prev];
|
|
101
|
+
const result = clonePrev
|
|
102
|
+
.reduce((prev, cur, curIndex) => {
|
|
103
|
+
const newKey = v4();
|
|
104
|
+
if (curIndex === index)
|
|
105
|
+
return [
|
|
106
|
+
...prev,
|
|
107
|
+
{
|
|
108
|
+
key: newKey,
|
|
109
|
+
},
|
|
110
|
+
cur,
|
|
111
|
+
];
|
|
112
|
+
return [...prev, cur];
|
|
113
|
+
}, [])
|
|
114
|
+
.map((r, index) => ({
|
|
115
|
+
name: `${name}.${index}`,
|
|
116
|
+
key: r.key,
|
|
117
|
+
}));
|
|
118
|
+
handleCacheListField(prev, result);
|
|
119
|
+
return result;
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
const remove = ({ index, key }) => {
|
|
123
|
+
setListFields((prev) => {
|
|
124
|
+
if (!isNil(key)) {
|
|
125
|
+
const clonePrev = [...prev];
|
|
126
|
+
const removedItem = clonePrev.filter((d) => d.key !== key);
|
|
127
|
+
console.log("Remove data: ", removedItem);
|
|
128
|
+
const result = removedItem.map((r, index) => ({
|
|
129
|
+
name: `${name}.${index}`,
|
|
130
|
+
key: r.key,
|
|
131
|
+
}));
|
|
132
|
+
handleCacheListField(prev, result);
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
if (!isNil(index)) {
|
|
136
|
+
if (index > prev.length - 1 || index < 0)
|
|
137
|
+
return prev;
|
|
138
|
+
const clonePrev = [...prev];
|
|
139
|
+
clonePrev.splice(index, 1);
|
|
140
|
+
const result = clonePrev.map((r, index) => ({
|
|
141
|
+
name: `${name}.${index}`,
|
|
142
|
+
key: r.key,
|
|
143
|
+
}));
|
|
144
|
+
handleCacheListField(prev, result);
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
return prev;
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
const move = ({ from, fromKey, to }) => {
|
|
151
|
+
setListFields((prev) => {
|
|
152
|
+
console.log("move list item: ", { from, to });
|
|
153
|
+
if (from >= listFields.length ||
|
|
154
|
+
from < 0 ||
|
|
155
|
+
to >= listFields.length ||
|
|
156
|
+
to < 0 ||
|
|
157
|
+
from === to)
|
|
158
|
+
return prev;
|
|
159
|
+
console.log("Trigger move item: ");
|
|
160
|
+
if (!isNil(fromKey)) {
|
|
161
|
+
const findItemIndex = prev.findIndex((p) => p.key === fromKey);
|
|
162
|
+
if (findItemIndex < 0)
|
|
163
|
+
return prev;
|
|
164
|
+
const moveItem = cloneDeep(listFields[findItemIndex]);
|
|
165
|
+
const result = prev
|
|
166
|
+
.reduce((prev, cur, curIndex) => {
|
|
167
|
+
if (findItemIndex === curIndex)
|
|
168
|
+
return [...prev];
|
|
169
|
+
if (to === curIndex) {
|
|
170
|
+
if (findItemIndex > to)
|
|
171
|
+
return [...prev, moveItem, cur];
|
|
172
|
+
else
|
|
173
|
+
return [...prev, cur, moveItem];
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return [...prev, cur];
|
|
177
|
+
}
|
|
178
|
+
}, [])
|
|
179
|
+
.map((i, index) => ({
|
|
180
|
+
key: i.key,
|
|
181
|
+
name: `${name}.${index}`,
|
|
182
|
+
}));
|
|
183
|
+
handleCacheListField(prev, result);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
else if (!isNil(from)) {
|
|
187
|
+
console.log("Trigger from index: ");
|
|
188
|
+
const moveItem = cloneDeep(listFields[from]);
|
|
189
|
+
const result = prev
|
|
190
|
+
.reduce((prev, cur, curIndex) => {
|
|
191
|
+
if (from === curIndex)
|
|
192
|
+
return [...prev];
|
|
193
|
+
if (to === curIndex) {
|
|
194
|
+
if (from > to)
|
|
195
|
+
return [...prev, moveItem, cur];
|
|
196
|
+
else
|
|
197
|
+
return [...prev, cur, moveItem];
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
return [...prev, cur];
|
|
201
|
+
}
|
|
202
|
+
}, [])
|
|
203
|
+
.map((i, index) => ({
|
|
204
|
+
key: i.key,
|
|
205
|
+
name: `${name}.${index}`,
|
|
206
|
+
}));
|
|
207
|
+
console.log({ result });
|
|
208
|
+
handleCacheListField(prev, result);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
return prev;
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
console.log("Trigger init", formState?.isInitied, internalInitValue);
|
|
216
|
+
if (formState?.isInitied) {
|
|
217
|
+
if (Array.isArray(listFormInitValues)) {
|
|
218
|
+
const result = listFormInitValues?.map((_, i) => {
|
|
219
|
+
const itemName = `${name}.${i}`;
|
|
220
|
+
const key = v4();
|
|
221
|
+
return {
|
|
222
|
+
key,
|
|
223
|
+
name: itemName,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
console.log("Get local init values: ", listFormInitValues, result);
|
|
227
|
+
setListFields(result);
|
|
228
|
+
setCacheData(formName || form?.formName || contextForm?.formName, name, listFormInitValues);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(internalInitValue)) {
|
|
232
|
+
const result = internalInitValue.map((_, i) => {
|
|
233
|
+
const itemName = `${name}.${i}`;
|
|
234
|
+
const key = v4();
|
|
235
|
+
return {
|
|
236
|
+
key,
|
|
237
|
+
name: itemName,
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
// console.log("Get form init values: ", internalInitValue, result);
|
|
241
|
+
setListFields(result);
|
|
242
|
+
setListFormInitValues(internalInitValue);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// console.log({ initialValues });
|
|
246
|
+
// console.log("After init: ", { result: cloneDeep(result) });
|
|
247
|
+
if (Array.isArray(initialValues)) {
|
|
248
|
+
const result = initialValues.map((_, i) => {
|
|
249
|
+
const itemName = `${name}.${i}`;
|
|
250
|
+
const key = v4();
|
|
251
|
+
return {
|
|
252
|
+
key,
|
|
253
|
+
name: itemName,
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
if (initialValues) {
|
|
257
|
+
setCacheData(formName || form?.formName || contextForm?.formName, name, initialValues);
|
|
258
|
+
}
|
|
259
|
+
setListFields(result);
|
|
260
|
+
setListFormInitValues(initialValues);
|
|
261
|
+
// console.log("Get form init values: ", internalInitValue, result);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}, [formState?.isInitied]);
|
|
266
|
+
// useEffect(() => {
|
|
267
|
+
// console.log("Show form list init values: ", { internalInitValue });
|
|
268
|
+
// }, [internalInitValue]);
|
|
269
|
+
// useEffect(() => {
|
|
270
|
+
// console.log("List fields update: ", listFields);
|
|
271
|
+
// }, [listFields]);
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
// console.log({ listFields });
|
|
274
|
+
return () => {
|
|
275
|
+
// console.log("Cleanup listfield change");
|
|
276
|
+
clearCacheData();
|
|
277
|
+
};
|
|
278
|
+
}, [listFields]);
|
|
279
|
+
return { listFields, move, add, remove };
|
|
280
|
+
}
|