react-form-manage 1.0.8-beta.7 → 1.0.8

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.
Files changed (94) hide show
  1. package/CHANGELOG.md +173 -4
  2. package/README.md +8 -4
  3. package/dist/components/Form/FormCleanUp.js +3 -3
  4. package/dist/components/Form/FormItem.d.ts +10 -4
  5. package/dist/components/Form/FormItem.js +52 -14
  6. package/dist/components/Form/FormList.d.ts +2 -2
  7. package/dist/components/Form/FormList.js +2 -2
  8. package/dist/constants/form.d.ts +1 -1
  9. package/dist/hooks/useFormItemControl.d.ts +8 -3
  10. package/dist/hooks/useFormItemControl.js +64 -28
  11. package/dist/hooks/useFormListControl.d.ts +2 -1
  12. package/dist/hooks/useFormListControl.js +85 -19
  13. package/dist/index.cjs.d.ts +1 -0
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.esm.d.ts +1 -0
  16. package/dist/index.js +4 -2
  17. package/dist/providers/Form.d.ts +15 -2
  18. package/dist/providers/Form.js +226 -41
  19. package/dist/stores/formStore.d.ts +44 -4
  20. package/dist/stores/formStore.js +42 -7
  21. package/dist/test/CommonTest.d.ts +3 -0
  22. package/dist/test/CommonTest.js +49 -0
  23. package/dist/test/TestDialog.d.ts +3 -0
  24. package/dist/test/TestDialog.js +21 -0
  25. package/dist/test/TestListener.d.ts +3 -0
  26. package/dist/test/TestListener.js +17 -0
  27. package/dist/test/TestNotFormWrapper.d.ts +3 -0
  28. package/dist/test/TestNotFormWrapper.js +15 -0
  29. package/dist/test/TestSelect.d.ts +6 -0
  30. package/dist/test/TestSelect.js +24 -0
  31. package/dist/test/TestWatchNormalize.d.ts +3 -0
  32. package/dist/test/TestWatchNormalize.js +23 -0
  33. package/dist/test/TestWrapperFormItem.d.ts +3 -0
  34. package/dist/test/TestWrapperFormItem.js +13 -0
  35. package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.d.ts +21 -0
  36. package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.js +61 -0
  37. package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.d.ts +16 -0
  38. package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.js +18 -0
  39. package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.d.ts +21 -0
  40. package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.js +33 -0
  41. package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.d.ts +21 -0
  42. package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.js +26 -0
  43. package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.d.ts +20 -0
  44. package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.js +32 -0
  45. package/dist/test/testSetValue/TestCase5_FormListRemovedItems.d.ts +22 -0
  46. package/dist/test/testSetValue/TestCase5_FormListRemovedItems.js +29 -0
  47. package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.d.ts +28 -0
  48. package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.js +36 -0
  49. package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.d.ts +17 -0
  50. package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.js +33 -0
  51. package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.d.ts +27 -0
  52. package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.js +57 -0
  53. package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.d.ts +25 -0
  54. package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.js +46 -0
  55. package/dist/test/testSetValue/index.d.ts +2 -0
  56. package/dist/test/testSetValue/index.js +28 -0
  57. package/dist/types/index.d.ts +1 -1
  58. package/dist/types/public.d.ts +1 -1
  59. package/dist/utils/obj.util.d.ts +29 -1
  60. package/dist/utils/obj.util.js +59 -5
  61. package/package.json +2 -1
  62. package/src/App.tsx +39 -156
  63. package/src/DEEP_TRIGGER_LOGIC.md +573 -0
  64. package/src/components/Form/FormCleanUp.tsx +4 -8
  65. package/src/components/Form/FormItem.tsx +174 -57
  66. package/src/components/Form/FormList.tsx +17 -4
  67. package/src/constants/form.ts +1 -1
  68. package/src/hooks/useFormItemControl.ts +78 -32
  69. package/src/hooks/useFormListControl.ts +133 -43
  70. package/src/index.ts +25 -13
  71. package/src/main.tsx +6 -1
  72. package/src/providers/Form.tsx +454 -26
  73. package/src/stores/formStore.ts +363 -283
  74. package/src/test/CommonTest.tsx +177 -0
  75. package/src/test/TestDialog.tsx +52 -0
  76. package/src/test/TestListener.tsx +21 -0
  77. package/src/test/TestNotFormWrapper.tsx +43 -0
  78. package/src/test/TestSelect.tsx +38 -0
  79. package/src/test/TestWatchNormalize.tsx +32 -0
  80. package/src/test/TestWrapperFormItem.tsx +34 -0
  81. package/src/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.tsx +203 -0
  82. package/src/test/testSetValue/TestCase1_PlainObjectToPrimitives.tsx +72 -0
  83. package/src/test/testSetValue/TestCase2_PlainObjectToFormList.tsx +114 -0
  84. package/src/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.tsx +99 -0
  85. package/src/test/testSetValue/TestCase4_PlainObjectRemovedFields.tsx +112 -0
  86. package/src/test/testSetValue/TestCase5_FormListRemovedItems.tsx +119 -0
  87. package/src/test/testSetValue/TestCase6_NestedFormListRemoved.tsx +185 -0
  88. package/src/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.tsx +110 -0
  89. package/src/test/testSetValue/TestCase8_SetFieldValues_NestedObject.tsx +162 -0
  90. package/src/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.tsx +169 -0
  91. package/src/test/testSetValue/index.tsx +100 -0
  92. package/src/types/index.ts +1 -1
  93. package/src/types/public.ts +1 -1
  94. package/src/utils/obj.util.ts +153 -13
@@ -3,11 +3,11 @@ import { useEffect, useState } from "react";
3
3
  import { v4 } from "uuid";
4
4
  import { useShallow } from "zustand/react/shallow";
5
5
  import { useFormContext } from "../providers/Form";
6
- import { useFormCleanUp, useFormStore } from "../stores/formStore";
6
+ import { useFormStore } from "../stores/formStore";
7
7
 
8
8
  import type { FormInstance } from "../stores/formStore";
9
9
 
10
- type ListField = { name: string; key: string };
10
+ type ListField = { name: string; key: string; value?: any };
11
11
 
12
12
  interface UseFormListControlProps {
13
13
  name?: string;
@@ -19,7 +19,7 @@ interface UseFormListControlProps {
19
19
  interface UseFormListControlReturn {
20
20
  listFields: ListField[];
21
21
  move: (opts: { from?: number; fromKey?: string; to: number }) => void;
22
- add: (index: number) => void;
22
+ add: (index?: number) => void;
23
23
  remove: (opts: { index?: number; key?: string }) => void;
24
24
  }
25
25
 
@@ -29,21 +29,34 @@ export default function useFormListControl<T = any>({
29
29
  initialValues,
30
30
  formName,
31
31
  }: UseFormListControlProps): UseFormListControlReturn {
32
+ const [formItemId] = useState<string>(v4());
32
33
  const contextForm = useFormContext();
33
34
  const getFormValues = useFormStore((state) => state.getFormValues);
34
- const [listFormInitValues, setListFormInitValues] = useState<any[] | undefined>(undefined);
35
- const { clearCacheData, setCacheData } = useFormStore(
35
+ const [listFormInitValues, setListFormInitValues] = useState<
36
+ any[] | undefined
37
+ >(undefined);
38
+ const {
39
+ clearCacheData,
40
+ setCacheData,
41
+ setListener,
42
+ getListener,
43
+ clearArrItems,
44
+ } = useFormStore(
36
45
  useShallow((state) => ({
46
+ // Cache
37
47
  cacheData: state.cacheData,
38
48
  clearCacheData: state.clearCacheData,
39
49
  setCacheData: state.setCacheData,
50
+
51
+ // Listener
52
+ setListener: state.setListener,
53
+ getListener: state.getListener,
54
+
55
+ // Clear Arr Items
56
+ clearArrItems: state.clearArrItems,
40
57
  })),
41
58
  );
42
- const { setCleanUpStack } = useFormCleanUp(
43
- useShallow((state) => ({
44
- setCleanUpStack: state.setCleanUpStack,
45
- })),
46
- );
59
+
47
60
  const { initValue: internalInitValue, formState } = useFormStore(
48
61
  useShallow((state) => {
49
62
  // console.log(
@@ -92,9 +105,20 @@ export default function useFormListControl<T = any>({
92
105
  })
93
106
  .filter(Boolean);
94
107
 
95
- const mapCurWithKey = cur.map(
96
- (c) => mapPrevWithKey.find((m) => m.key === c.key) || c,
97
- );
108
+ const mapCurWithKey = cur.map((c) => {
109
+ const find = mapPrevWithKey.find((m) => m.key === c.key);
110
+
111
+ if (find) {
112
+ return {
113
+ key: find.key,
114
+ value: isNil(c.value) ? find.value : c.value,
115
+ };
116
+ }
117
+
118
+ return c;
119
+ });
120
+
121
+ console.log("compare prev cur", { prev, cur });
98
122
 
99
123
  const getNewValueCache = mapCurWithKey.filter(Boolean).map((c) => c.value);
100
124
 
@@ -107,26 +131,25 @@ export default function useFormListControl<T = any>({
107
131
  // console.log("Mapping Cur value with prev fields: ", mapCurWithKey);
108
132
  // console.log("After change arr value: ", getNewValueCache);
109
133
 
110
- // 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
134
+ // Nếu số phần tử trước khi thay đổi mảng lớn hơn thì đẩy các phần tử còn lại vào clean up stack để clear
111
135
  if (startRemoveIndex > 0) {
112
- Array.from(Array(startRemoveIndex))
113
- .map((_, index) => {
114
- return getNewValueCache.length + index;
115
- })
116
- .forEach((index) => {
117
- // console.log("Data for cleanup arr element: ", {
118
- // formName: formName || form?.formName || contextForm?.formName,
119
- // name: `${name}.${index}`,
120
- // type: "array",
121
- // });
122
- setCleanUpStack({
123
- formName: formName || form?.formName || contextForm?.formName,
124
- name: `${name}.${index}`,
125
- type: "array",
126
- });
127
- });
136
+ const clearItems = Array.from(Array(startRemoveIndex)).map((_, index) => {
137
+ const clearIndex = getNewValueCache.length + index;
138
+
139
+ return {
140
+ formName: formName || form?.formName || contextForm?.formName,
141
+ name: `${name}.${clearIndex}`,
142
+ };
143
+ });
144
+ clearArrItems(clearItems);
128
145
  }
129
146
 
147
+ console.log("Set cache data for form list: ", {
148
+ formName: formName || form?.formName || contextForm?.formName,
149
+ name,
150
+ getNewValueCache,
151
+ });
152
+
130
153
  // console.log({ getNewValueCache });
131
154
  setCacheData(
132
155
  formName || form?.formName || contextForm?.formName,
@@ -135,13 +158,9 @@ export default function useFormListControl<T = any>({
135
158
  );
136
159
  };
137
160
 
138
- const add = (index: number) => {
161
+ const add = (index?: number) => {
139
162
  setListFields((prev) => {
140
- if (index > prev.length) return prev;
141
-
142
- if (index < 0) return prev;
143
-
144
- if (index === prev.length) {
163
+ if (isNil(index) || index === prev.length) {
145
164
  const newName = `${name}.${prev.length}`;
146
165
  const newKey = v4();
147
166
  const result = [
@@ -154,6 +173,9 @@ export default function useFormListControl<T = any>({
154
173
 
155
174
  return result;
156
175
  }
176
+ if (index > prev.length) return prev;
177
+
178
+ if (index < 0) return prev;
157
179
 
158
180
  const clonePrev = [...prev];
159
181
 
@@ -217,9 +239,17 @@ export default function useFormListControl<T = any>({
217
239
  });
218
240
  };
219
241
 
220
- const move = ({ from, fromKey, to }: { from?: number; fromKey?: string; to: number }) => {
242
+ const move = ({
243
+ from,
244
+ fromKey,
245
+ to,
246
+ }: {
247
+ from?: number;
248
+ fromKey?: string;
249
+ to: number;
250
+ }) => {
221
251
  setListFields((prev) => {
222
- console.log("move list item: ", { from, to });
252
+ // console.log("move list item: ", { from, to });
223
253
  if (
224
254
  from >= listFields.length ||
225
255
  from < 0 ||
@@ -228,7 +258,7 @@ export default function useFormListControl<T = any>({
228
258
  from === to
229
259
  )
230
260
  return prev;
231
- console.log("Trigger move item: ");
261
+ // console.log("Trigger move item: ");
232
262
 
233
263
  if (!isNil(fromKey)) {
234
264
  const findItemIndex = prev.findIndex((p) => p.key === fromKey);
@@ -286,7 +316,7 @@ export default function useFormListControl<T = any>({
286
316
  useEffect(() => {
287
317
  console.log("Trigger init", formState?.isInitied, internalInitValue);
288
318
  if (formState?.isInitied) {
289
- if (Array.isArray(listFormInitValues)) {
319
+ if (Array.isArray(listFormInitValues)) {
290
320
  const result = listFormInitValues?.map((_, i) => {
291
321
  const itemName = `${name}.${i}`;
292
322
  const key = v4();
@@ -304,7 +334,12 @@ export default function useFormListControl<T = any>({
304
334
  name,
305
335
  listFormInitValues,
306
336
  );
307
-
337
+ if (getListener(formItemId)) {
338
+ setListener({
339
+ formItemId,
340
+ isInitied: true,
341
+ });
342
+ }
308
343
  return;
309
344
  }
310
345
 
@@ -322,7 +357,12 @@ export default function useFormListControl<T = any>({
322
357
 
323
358
  setListFields(result);
324
359
  setListFormInitValues(internalInitValue as any);
325
-
360
+ if (getListener(formItemId)) {
361
+ setListener({
362
+ formItemId,
363
+ isInitied: true,
364
+ });
365
+ }
326
366
  return;
327
367
  }
328
368
 
@@ -350,7 +390,12 @@ export default function useFormListControl<T = any>({
350
390
 
351
391
  setListFields(result);
352
392
  setListFormInitValues(initialValues as any);
353
-
393
+ if (getListener(formItemId)) {
394
+ setListener({
395
+ formItemId,
396
+ isInitied: true,
397
+ });
398
+ }
354
399
  // console.log("Get form init values: ", internalInitValue, result);
355
400
 
356
401
  return;
@@ -374,5 +419,50 @@ export default function useFormListControl<T = any>({
374
419
  };
375
420
  }, [listFields]);
376
421
 
422
+ useEffect(() => {
423
+ if (!getListener(formItemId)) {
424
+ setListener({
425
+ formName: formName || form?.formName || contextForm?.formName,
426
+ name: name || "",
427
+ formItemId: formItemId,
428
+ type: "array",
429
+ onArrayChange: (newArr) => {
430
+ setListFields((prev) => {
431
+ const result = newArr.map((_, i) => {
432
+ const itemName = `${name}.${i}`;
433
+ const existingItem = prev[i];
434
+ return {
435
+ key: existingItem ? existingItem.key : v4(),
436
+ name: itemName,
437
+ };
438
+ });
439
+
440
+ handleCacheListField(
441
+ prev,
442
+ result.map((r, i) => {
443
+ return { ...r, value: newArr[i] };
444
+ }),
445
+ );
446
+ return result;
447
+ });
448
+ },
449
+ });
450
+ }
451
+ return () => {
452
+ // Remove listener on unmount
453
+ if (getListener(formItemId)) {
454
+ console.log("Remove listener for form list: ", {
455
+ formItemId,
456
+ });
457
+ setListener({
458
+ formName: formName || form?.formName || contextForm?.formName,
459
+ name: name || "",
460
+ formItemId: formItemId,
461
+ onArrayChange: undefined,
462
+ });
463
+ }
464
+ };
465
+ }, []);
466
+
377
467
  return { listFields, move, add, remove };
378
468
  }
package/src/index.ts CHANGED
@@ -1,23 +1,31 @@
1
+ import { SUBMIT_STATE } from "./constants/form";
1
2
  import Form, {
2
3
  useForm,
3
- useWatch,
4
- useSubmitDataWatch,
5
4
  useFormStateWatch,
6
- type FormProps,
7
- type ValidationRule,
5
+ useSubmitDataWatch,
6
+ useWatch,
8
7
  type FormFieldError,
8
+ type FormProps,
9
9
  type SubmitState,
10
10
  type UseFormItemStateWatchReturn,
11
+ type ValidationRule,
11
12
  } from "./providers/Form";
12
- import { SUBMIT_STATE } from "./constants/form";
13
13
 
14
14
  import FormItem, { type FormItemProps } from "./components/Form/FormItem";
15
15
  import FormList, { type FormListProps } from "./components/Form/FormList";
16
+ import InputWrapper, {
17
+ type InputWrapperProps,
18
+ } from "./components/Form/InputWrapper";
16
19
  import Input from "./components/Input";
17
- import InputWrapper, { type InputWrapperProps } from "./components/Form/InputWrapper";
18
20
 
19
21
  import useFormItemControl from "./hooks/useFormItemControl";
20
22
  import useFormListControl from "./hooks/useFormListControl";
23
+ import {
24
+ useFormStore,
25
+ type CleanUpItem,
26
+ type FormInstance,
27
+ type ListenerItem,
28
+ } from "./stores/formStore";
21
29
 
22
30
  export {
23
31
  Form,
@@ -25,21 +33,25 @@ export {
25
33
  FormList,
26
34
  Input,
27
35
  InputWrapper,
36
+ SUBMIT_STATE,
37
+ useForm,
28
38
  useFormItemControl,
29
39
  useFormListControl,
30
- useForm,
31
- useWatch,
32
- useSubmitDataWatch,
33
40
  useFormStateWatch,
34
- type FormProps,
41
+ useFormStore,
42
+ useSubmitDataWatch,
43
+ useWatch,
44
+ type CleanUpItem,
45
+ type FormFieldError,
46
+ type FormInstance,
35
47
  type FormItemProps,
36
48
  type FormListProps,
49
+ type FormProps,
37
50
  type InputWrapperProps,
38
- type ValidationRule,
39
- type FormFieldError,
51
+ type ListenerItem,
40
52
  type SubmitState,
41
53
  type UseFormItemStateWatchReturn,
42
- SUBMIT_STATE,
54
+ type ValidationRule,
43
55
  };
44
56
 
45
57
  export default Form;
package/src/main.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import { createRoot } from "react-dom/client";
2
3
  import App from "./App";
3
4
  import "./index.css";
@@ -7,4 +8,8 @@ if (!container) {
7
8
  throw new Error("Root container missing");
8
9
  }
9
10
 
10
- createRoot(container).render(<App />);
11
+ createRoot(container).render(
12
+ <React.StrictMode>
13
+ <App />
14
+ </React.StrictMode>,
15
+ );