react-form-manage 1.0.8-beta.2 → 1.0.8-beta.21

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 (46) hide show
  1. package/CHANGELOG.md +85 -5
  2. package/README.md +8 -4
  3. package/dist/components/Form/FormCleanUp.js +3 -3
  4. package/dist/components/Form/FormItem.d.ts +4 -2
  5. package/dist/components/Form/FormItem.js +7 -5
  6. package/dist/components/Form/FormList.d.ts +2 -2
  7. package/dist/components/Form/FormList.js +2 -2
  8. package/dist/hooks/useFormItemControl.d.ts +1 -0
  9. package/dist/hooks/useFormItemControl.js +60 -23
  10. package/dist/hooks/useFormListControl.d.ts +2 -1
  11. package/dist/hooks/useFormListControl.js +60 -10
  12. package/dist/index.cjs.d.ts +1 -0
  13. package/dist/index.d.ts +4 -3
  14. package/dist/index.esm.d.ts +1 -0
  15. package/dist/index.js +4 -2
  16. package/dist/providers/Form.d.ts +4 -2
  17. package/dist/providers/Form.js +98 -28
  18. package/dist/stores/formStore.d.ts +27 -2
  19. package/dist/stores/formStore.js +25 -9
  20. package/dist/test/TestDialog.d.ts +3 -0
  21. package/dist/test/TestDialog.js +21 -0
  22. package/dist/test/TestListener.d.ts +3 -0
  23. package/dist/test/TestListener.js +17 -0
  24. package/dist/test/TestSelect.d.ts +6 -0
  25. package/dist/test/TestSelect.js +24 -0
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/public.d.ts +6 -1
  28. package/dist/utils/obj.util.d.ts +1 -1
  29. package/dist/utils/obj.util.js +16 -5
  30. package/package.json +3 -1
  31. package/src/App.tsx +98 -4
  32. package/src/components/Form/FormCleanUp.tsx +3 -7
  33. package/src/components/Form/FormItem.tsx +56 -31
  34. package/src/components/Form/FormList.tsx +17 -4
  35. package/src/components/Form/InputWrapper.tsx +5 -0
  36. package/src/hooks/useFormItemControl.ts +70 -29
  37. package/src/hooks/useFormListControl.ts +104 -26
  38. package/src/index.ts +27 -13
  39. package/src/providers/Form.tsx +126 -17
  40. package/src/stores/formStore.ts +319 -288
  41. package/src/test/TestDialog.tsx +52 -0
  42. package/src/test/TestListener.tsx +21 -0
  43. package/src/test/TestSelect.tsx +38 -0
  44. package/src/types/index.ts +1 -1
  45. package/src/types/public.ts +7 -1
  46. package/src/utils/obj.util.ts +44 -13
package/src/index.ts CHANGED
@@ -1,22 +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
+ type UseFormItemStateWatchReturn,
11
+ type ValidationRule,
10
12
  } from "./providers/Form";
11
- import { SUBMIT_STATE } from "./constants/form";
12
13
 
13
14
  import FormItem, { type FormItemProps } from "./components/Form/FormItem";
14
15
  import FormList, { type FormListProps } from "./components/Form/FormList";
16
+ import InputWrapper, {
17
+ type InputWrapperProps,
18
+ } from "./components/Form/InputWrapper";
15
19
  import Input from "./components/Input";
16
- import InputWrapper, { type InputWrapperProps } from "./components/Form/InputWrapper";
17
20
 
18
21
  import useFormItemControl from "./hooks/useFormItemControl";
19
22
  import useFormListControl from "./hooks/useFormListControl";
23
+ import {
24
+ useFormStore,
25
+ type CleanUpItem,
26
+ type FormInstance,
27
+ type ListenerItem,
28
+ } from "./stores/formStore";
20
29
 
21
30
  export {
22
31
  Form,
@@ -24,20 +33,25 @@ export {
24
33
  FormList,
25
34
  Input,
26
35
  InputWrapper,
36
+ SUBMIT_STATE,
37
+ useForm,
27
38
  useFormItemControl,
28
39
  useFormListControl,
29
- useForm,
30
- useWatch,
31
- useSubmitDataWatch,
32
40
  useFormStateWatch,
33
- type FormProps,
41
+ useFormStore,
42
+ useSubmitDataWatch,
43
+ useWatch,
44
+ type CleanUpItem,
45
+ type FormFieldError,
46
+ type FormInstance,
34
47
  type FormItemProps,
35
48
  type FormListProps,
49
+ type FormProps,
36
50
  type InputWrapperProps,
37
- type ValidationRule,
38
- type FormFieldError,
51
+ type ListenerItem,
39
52
  type SubmitState,
40
- SUBMIT_STATE,
53
+ type UseFormItemStateWatchReturn,
54
+ type ValidationRule,
41
55
  };
42
56
 
43
57
  export default Form;
@@ -1,14 +1,32 @@
1
- import { cloneDeep, get, isEqual, last, set, uniq } from "lodash";
1
+ import {
2
+ cloneDeep,
3
+ get,
4
+ isEqual,
5
+ isNil,
6
+ isPlainObject,
7
+ last,
8
+ set,
9
+ uniqBy,
10
+ } from "lodash";
2
11
  import { useTask } from "minh-custom-hooks-release";
3
12
  import type { ComponentType, FormHTMLAttributes, ReactNode } from "react";
4
13
  import { createContext, useContext, useEffect, useState } from "react";
5
14
  import { flushSync } from "react-dom";
6
15
  import { useShallow } from "zustand/react/shallow"; // Import useShallow
7
16
  import FormCleanUp from "../components/Form/FormCleanUp";
8
- import { useFormListeners, useFormStore } from "../stores/formStore";
9
- import type { PublicFormInstance } from "../types/public";
17
+ import { SUBMIT_STATE } from "../constants/form";
18
+ import { ListenerItem, useFormStore } from "../stores/formStore";
19
+ import type {
20
+ PublicFormInstance,
21
+ UseFormItemStateWatchReturn,
22
+ } from "../types/public";
10
23
  import { getAllNoneObjStringPath } from "../utils/obj.util";
11
- export type { ValidationRule, FormFieldError, SubmitState } from "../types/public";
24
+ export type {
25
+ FormFieldError,
26
+ SubmitState,
27
+ UseFormItemStateWatchReturn,
28
+ ValidationRule,
29
+ } from "../types/public";
12
30
 
13
31
  export const FormContext = createContext(null);
14
32
 
@@ -40,7 +58,7 @@ export default function Form<T = any>({
40
58
  ...props
41
59
  }: FormProps<T>) {
42
60
  const {
43
- appInitValue,
61
+ // appInitValue,
44
62
  getFormItemValue,
45
63
  setInitData,
46
64
  setData,
@@ -54,7 +72,7 @@ export default function Form<T = any>({
54
72
  clearFormState,
55
73
  } = useFormStore(
56
74
  useShallow((state) => ({
57
- appInitValue: state.initialValues,
75
+ // appInitValue: state.initialValues,
58
76
  setInitData: state.setInitData,
59
77
  getFormValues: state.getFormValues,
60
78
  setFormState: state.setFormState,
@@ -68,11 +86,11 @@ export default function Form<T = any>({
68
86
  clearFormState: state.clearFormState,
69
87
 
70
88
  // Test, nhớ xóa sau khi xong
71
- formStates: state.formStates?.[formName],
89
+ // formStates: state.formStates?.[formName],
72
90
  })),
73
91
  );
74
92
 
75
- const { getListeners } = useFormListeners(
93
+ const { getListeners } = useFormStore(
76
94
  useShallow((state) => {
77
95
  return {
78
96
  getListeners: state.getListeners,
@@ -81,13 +99,51 @@ export default function Form<T = any>({
81
99
  );
82
100
 
83
101
  const setFieldValue = (name, value, options) => {
84
- const listener = getListeners().find(
102
+ const listener: ListenerItem | null = getListeners().find(
85
103
  (l) => l.name === name && l.formName === formName,
86
104
  );
87
105
  if (listener) {
88
- listener.onChange(value, options);
106
+ // Nếu loại listener là array thì gọi onArrayChange
107
+ if (listener.type === "array") {
108
+ // Do nothing if the value is the same for array item to prevent unnecessary re-renders
109
+ if (!isEqual(getFormItemValue(formName, name), value)) {
110
+ listener.onArrayChange(value, options);
111
+
112
+ // Kiểm tra từng path con của array item xem có listener nào không, nếu có thì gọi onChange của listener đó
113
+ const allStringPath = getAllNoneObjStringPath(value);
114
+ allStringPath.forEach((p) => {
115
+ const findListener = getListeners().find(
116
+ (l) => l.name === `${name}.${p}` && l.formName === formName,
117
+ );
118
+ if (findListener) {
119
+ findListener.onChange(get(value, p), options);
120
+ } else {
121
+ setData(formName, `${name}.${p}`, get(value, p));
122
+ }
123
+ });
124
+ }
125
+ } else {
126
+ listener.onChange(value, options);
127
+ }
89
128
  } else {
90
- setData(formName, name, value);
129
+ // set data for non-listener field
130
+ if (isPlainObject(value) || Array.isArray(value)) {
131
+ // Nếu là object hoặc array thì set từng path con
132
+ const allStringPath = getAllNoneObjStringPath(value);
133
+
134
+ allStringPath.forEach((p) => {
135
+ const findListener = getListeners().find(
136
+ (l) => l.name === `${name}.${p}` && l.formName === formName,
137
+ );
138
+ if (findListener) {
139
+ findListener.onChange(get(value, p), options);
140
+ } else {
141
+ setData(formName, `${name}.${p}`, get(value, p));
142
+ }
143
+ });
144
+ } else {
145
+ setData(formName, name, value);
146
+ }
91
147
  }
92
148
  };
93
149
 
@@ -99,7 +155,31 @@ export default function Form<T = any>({
99
155
  (l) => l.name === p && l.formName === formName,
100
156
  );
101
157
  if (listener) {
102
- listener.onChange(get(values, listener.name), options);
158
+ if (listener.type === "array") {
159
+ // Do nothing if the value is the same for array item to prevent unnecessary re-renders
160
+ if (!isEqual(getFormItemValue(formName, p), get(values, p))) {
161
+ listener.onArrayChange(get(values, listener.name), options);
162
+
163
+ // Kiểm tra từng path con của array item xem có listener nào không, nếu có thì gọi onChange của listener đó
164
+ const nestedAllStringPath: string[] = getAllNoneObjStringPath(
165
+ get(values, p),
166
+ );
167
+ nestedAllStringPath.forEach((np) => {
168
+ {
169
+ const findListener = getListeners().find(
170
+ (l) => l.name === `${p}.${np}` && l.formName === formName,
171
+ );
172
+ if (findListener) {
173
+ findListener.onChange(get(values, `${p}.${np}`), options);
174
+ } else {
175
+ setData(formName, `${p}.${np}`, get(values, `${p}.${np}`));
176
+ }
177
+ }
178
+ });
179
+ }
180
+ } else {
181
+ listener.onChange(get(values, listener.name), options);
182
+ }
103
183
  } else {
104
184
  setData(formName, p, get(values, p));
105
185
  }
@@ -165,8 +245,8 @@ export default function Form<T = any>({
165
245
 
166
246
  const resultValues = cloneDeep(formValues);
167
247
  const cleanValues = {} as T;
168
- uniq(listeners, (l) => l.name).forEach((l) => {
169
- set(cleanValues, l.name, get(resultValues, l.name));
248
+ uniqBy(listeners, (l: any) => l.name).forEach((l) => {
249
+ set(cleanValues as any, l.name, get(resultValues, l.name));
170
250
  });
171
251
 
172
252
  const handleIsolateCase = async () => {
@@ -271,6 +351,7 @@ export default function Form<T = any>({
271
351
  setFormState({
272
352
  formName,
273
353
  isInitied: false,
354
+ submitState: SUBMIT_STATE.IDLE,
274
355
  }),
275
356
  );
276
357
  const totalListenerFields = getListeners();
@@ -403,9 +484,11 @@ export function useFormContext() {
403
484
  export function useForm<T = any>(
404
485
  formNameOrFormInstance?: string | PublicFormInstance<T>,
405
486
  ) {
406
- const targetFormName =
407
- typeof formNameOrFormInstance === "object" &&
408
- formNameOrFormInstance !== null
487
+ const formContext = useContext(FormContext);
488
+ const targetFormName = isNil(formNameOrFormInstance)
489
+ ? formContext?.formName
490
+ : typeof formNameOrFormInstance === "object" &&
491
+ formNameOrFormInstance !== null
409
492
  ? (formNameOrFormInstance as PublicFormInstance<T>).formName
410
493
  : (formNameOrFormInstance as string | undefined);
411
494
 
@@ -472,7 +555,33 @@ export const useFormStateWatch = <T = any,>(
472
555
  return formState as any;
473
556
  };
474
557
 
558
+ // Get Form Item State Using name (make sure no same name listener before using this) or formItemId, formNameOrFormInstance
559
+ // reutrn formItem state like isTouched, isDirty, errors
560
+ export const useFormItemStateWatch = <T = any,>(
561
+ nameOrFormItemId: string,
562
+ formNameOrFormInstance?: string | PublicFormInstance<T>,
563
+ ): UseFormItemStateWatchReturn => {
564
+ const [formInstance] = useForm<T>(formNameOrFormInstance as any);
565
+
566
+ const listener = useFormStore((state) => {
567
+ return state
568
+ .getListeners()
569
+ .find(
570
+ (l) =>
571
+ l.formName === formInstance?.formName &&
572
+ (l.name === nameOrFormItemId || l.formItemId === nameOrFormItemId),
573
+ );
574
+ });
575
+
576
+ return {
577
+ isTouched: listener?.isTouched,
578
+ isDirty: listener?.isDirty,
579
+ errors: listener?.internalErrors || [],
580
+ };
581
+ };
582
+
475
583
  Form.useForm = useForm;
476
584
  Form.useWatch = useWatch;
477
585
  Form.useSubmitDataWatch = useSubmitDataWatch;
478
586
  Form.useFormStateWatch = useFormStateWatch;
587
+ Form.useFormItemStateWatch = useFormItemStateWatch;