react-form-manage 1.0.8-beta.22 → 1.0.8-beta.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/src/App.tsx CHANGED
@@ -1,12 +1,9 @@
1
- import { Checkbox } from "@mui/material";
2
- import { Button, Input } from "antd";
1
+ import { Input } from "antd";
3
2
  import { motion } from "framer-motion";
4
- import { useEffect } from "react";
5
- import FormItem from "./components/Form/FormItem";
6
- import FormList from "./components/Form/FormList";
7
- import InputWrapper from "./components/Form/InputWrapper";
8
- import Form, { useForm } from "./providers/Form";
9
- import TestDialog from "./test/TestDialog";
3
+ import Form from "./providers/Form";
4
+ import CommonTest from "./test/CommonTest";
5
+ import TestListener from "./test/TestListener";
6
+ import TestWrapperFormItem from "./test/TestWrapperFormItem";
10
7
 
11
8
  import { Form as AntdForm } from "antd";
12
9
 
@@ -17,17 +14,6 @@ function TestFormWatch() {
17
14
  }
18
15
 
19
16
  const App = () => {
20
- const [form] = useForm("form1");
21
-
22
- const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
23
-
24
- useEffect(() => {
25
- if (form) {
26
- // setTimeout(() => {
27
- // form.setFieldValue("TestData", "HelloWorld");
28
- // }, 500);
29
- }
30
- }, [form]);
31
17
  return (
32
18
  <div>
33
19
  <AntdForm>
@@ -44,164 +30,13 @@ const App = () => {
44
30
  </AntdForm>
45
31
 
46
32
  {/* Hidden Test */}
47
- <Form
48
- initialValues={{
49
- TestData: "",
50
- numericCode: "",
51
- // arr: [{ el: "Item 1" }, { el: "Item 2" }],
52
- }}
53
- onFinish={(values) => {
54
- console.log(values);
55
- }}
56
- formName={"form1"}
57
- // hidden
58
- >
59
- <FormItem
60
- name={"username"}
61
- rules={[
62
- {
63
- required: true,
64
- message: "Test",
65
- },
66
- ]}
67
- initialValue={"283746"}
68
- // hidden
69
- >
70
- <InputWrapper>
71
- <Input />
72
- </InputWrapper>
73
- </FormItem>
74
-
75
- {/* Numberic test */}
76
- <FormItem
77
- name={"numericCode"}
78
- rules={[
79
- { required: true, message: "Vui lòng nhập mã" },
80
- {
81
- pattern: /^\d+$/,
82
- message: "Chỉ cho phép chuỗi số",
83
- },
84
- ]}
85
- >
86
- <InputWrapper>
87
- <Input placeholder="Mã chỉ gồm số" style={{ width: 200 }} />
88
- </InputWrapper>
89
- </FormItem>
33
+ <CommonTest />
34
+ {/* <Form formName="TestDialog" >
35
+ <TestDialog />
36
+ </Form> */}
90
37
 
91
- {/* Motion Test */}
92
- <motion.div
93
- initial={{ opacity: 0 }}
94
- animate={{ opacity: 1 }}
95
- exit={{ opacity: 0 }}
96
- transition={{ duration: 1 }}
97
- >
98
- <FormItem name="motionTest" controlAfterInit initialValue={"1234134"}>
99
- <InputWrapper>
100
- <Input placeholder="Motion Test" style={{ width: 200 }} />
101
- </InputWrapper>
102
- </FormItem>
103
- </motion.div>
104
- <FormList
105
- initialValues={[
106
- {
107
- el: "sdfsdf",
108
- d: { child: "Test Child" },
109
- },
110
- ]}
111
- name="arr"
112
- >
113
- {(fields, { add, remove, move }) => (
114
- <div>
115
- {fields.map((field, index) => (
116
- <div key={field.key} style={{ marginBottom: 8 }}>
117
- <FormItem name={`${field.name}.el`} initialValue={"Chém gió"}>
118
- <InputWrapper>
119
- <Input placeholder="Item value" style={{ width: 200 }} />
120
- </InputWrapper>
121
- </FormItem>
122
- <FormItem
123
- name={`${field.name}.d.child`}
124
- initialValue={"Con của item"}
125
- >
126
- <InputWrapper>
127
- <Input placeholder="Item value" style={{ width: 200 }} />
128
- </InputWrapper>
129
- </FormItem>
130
- <Button
131
- onClick={() => remove({ index })}
132
- style={{ marginLeft: 8 }}
133
- >
134
- Remove
135
- </Button>
136
- {index > 0 && (
137
- <Button
138
- onClick={() => move({ from: index, to: index - 1 })}
139
- style={{ marginLeft: 8 }}
140
- >
141
- Move Up
142
- </Button>
143
- )}
144
- {index < fields.length - 1 && (
145
- <Button
146
- onClick={() => move({ from: index, to: index + 1 })}
147
- style={{ marginLeft: 8 }}
148
- >
149
- Move Down
150
- </Button>
151
- )}
152
- </div>
153
- ))}
154
- <Button onClick={() => add(fields.length)}>Add Item</Button>
155
- </div>
156
- )}
157
- </FormList>
158
- <TestDialog />
159
- <Button
160
- onClick={() => {
161
- form?.setFieldValue("arr", [
162
- { el: "Set Item 1" },
163
- { el: "Set Item 2" },
164
- { el: "Set Item 3" },
165
- ]);
166
- }}
167
- >
168
- Test set array list value
169
- </Button>
170
- <motion.div
171
- initial={{ opacity: 0 }}
172
- animate={{ opacity: 1 }}
173
- exit={{ opacity: 0 }}
174
- transition={{ duration: 1.5 }}
175
- >
176
- <FormItem
177
- valuePropName="checked"
178
- getValueFromEvent={(_, checked) => checked}
179
- name="checkControlledAfterInit"
180
- controlAfterInit={true}
181
- initialValue={true}
182
- >
183
- <Checkbox />
184
- </FormItem>
185
- </motion.div>
186
- <TestFormWatch />
187
- <Button
188
- onClick={() => {
189
- const current = form?.getFieldValue("checkControlledAfterInit");
190
- console.log("Toggle controlled after init: ", current);
191
- form?.setFieldValue("checkControlledAfterInit", !current);
192
- }}
193
- >
194
- Toggle
195
- </Button>
196
- <Button htmlType="submit">Submit</Button>
197
- <Button
198
- onClick={() => {
199
- form?.resetFields?.();
200
- }}
201
- >
202
- Reset
203
- </Button>
204
- </Form>
38
+ <TestListener />
39
+ <TestWrapperFormItem />
205
40
  </div>
206
41
  );
207
42
  };
@@ -38,7 +38,7 @@ const FormCleanUp = () => {
38
38
  });
39
39
  } else {
40
40
  if (c.type === "array") {
41
- console.log(c);
41
+ // console.log(c);
42
42
  clearArrItem(c.formName, c.name);
43
43
  } else {
44
44
  }
@@ -1,10 +1,11 @@
1
- import type { ReactElement } from "react";
2
- import { cloneElement, Fragment, useRef, useState } from "react";
1
+ import { isNil } from "lodash";
2
+ import type { ComponentProps, FC, ReactElement } from "react";
3
+ import { cloneElement, Fragment, useEffect, useRef, useState } from "react";
3
4
  import { v4 } from "uuid";
4
5
  import useFormItemControl from "../../hooks/useFormItemControl";
5
6
  import type { ValidationRule } from "../../types/public";
6
7
 
7
- export interface FormItemProps {
8
+ export type BaseFormItemProps = {
8
9
  children: ReactElement<any>;
9
10
  name: string;
10
11
  formName?: string;
@@ -15,10 +16,130 @@ export interface FormItemProps {
15
16
  getValueFromEvent?: (...args: any[]) => any;
16
17
  controlAfterInit?: boolean;
17
18
  hidden?: boolean;
19
+ collectOnHidden?: boolean;
20
+ };
21
+
22
+ export type FormItemProps<TCustomWrapper extends FC<any> | undefined> =
23
+ BaseFormItemProps &
24
+ (
25
+ | {
26
+ Component?: undefined;
27
+ }
28
+ | ({
29
+ Component: TCustomWrapper;
30
+ } & Omit<ComponentProps<TCustomWrapper>, keyof BaseFormItemProps>)
31
+ );
32
+
33
+ function BaseFormItem({
34
+ children,
35
+
36
+ name,
37
+ elementRef: elRef,
38
+
39
+ valuePropName,
40
+ getValueFromEvent,
41
+ onChange,
42
+ value,
43
+ isDirty,
44
+ errors,
45
+ onFocus,
46
+ state,
47
+ submitState,
48
+ isTouched,
49
+ }: any) {
50
+ useEffect(() => {
51
+ console.log("Base Form Item: ", { name, value });
52
+ }, [value]);
53
+ return cloneElement(children, {
54
+ name,
55
+ ref: elRef,
56
+ [valuePropName]: value,
57
+ onChange: (...args: any[]) => {
58
+ let val = args[0];
59
+ if (getValueFromEvent && typeof getValueFromEvent === "function") {
60
+ val = getValueFromEvent(...args);
61
+ } else {
62
+ const e = args[0];
63
+ if (e && e.target) {
64
+ val = e.target.value;
65
+ }
66
+ }
67
+ onChange(val);
68
+ },
69
+ // onFocus: () => {
70
+ // setIsTouched(true);
71
+ // },
72
+ // isTouched: isTouched,
73
+ isDirty: isDirty,
74
+ // errors: errors,
75
+ // formState,
76
+
77
+ errors,
78
+ onFocus,
79
+ validateState: state,
80
+ submitState,
81
+ isTouched: isTouched,
82
+ } as any);
18
83
  }
19
84
 
20
- export default function FormItem({
85
+ function FormItemWithWrapper<TComponent extends FC<any>>({
21
86
  children,
87
+
88
+ name,
89
+ elementRef: elRef,
90
+
91
+ valuePropName,
92
+ getValueFromEvent,
93
+ onChange,
94
+ value,
95
+ isDirty,
96
+ errors,
97
+ onFocus,
98
+ state,
99
+ submitState,
100
+ isTouched,
101
+ Component,
102
+ ...props
103
+ }: any) {
104
+ return (
105
+ <Component {...props}>
106
+ {cloneElement(children, {
107
+ name,
108
+ ref: elRef,
109
+ [valuePropName]: value,
110
+ onChange: (...args: any[]) => {
111
+ let val = args[0];
112
+ if (getValueFromEvent && typeof getValueFromEvent === "function") {
113
+ val = getValueFromEvent(...args);
114
+ } else {
115
+ const e = args[0];
116
+ if (e && e.target) {
117
+ val = e.target.value;
118
+ }
119
+ }
120
+ onChange(val);
121
+ },
122
+ // onFocus: () => {
123
+ // setIsTouched(true);
124
+ // },
125
+ // isTouched: isTouched,
126
+ isDirty: isDirty,
127
+ // errors: errors,
128
+ // formState,
129
+
130
+ errors,
131
+ onFocus,
132
+ validateState: state,
133
+ submitState,
134
+ isTouched: isTouched,
135
+ } as any)}
136
+ </Component>
137
+ );
138
+ }
139
+
140
+ export default function FormItem<TCustomWrapper extends FC<any> | undefined>({
141
+ children,
142
+ Component,
22
143
  name,
23
144
  formName,
24
145
  initialValue,
@@ -28,10 +149,13 @@ export default function FormItem({
28
149
  getValueFromEvent,
29
150
  controlAfterInit = false,
30
151
  hidden,
31
- }: FormItemProps) {
152
+ collectOnHidden,
153
+ ...props
154
+ }: FormItemProps<TCustomWrapper>) {
155
+ const [formItemId] = useState(externalFormItemId ?? v4());
156
+
32
157
  const elRef = useRef<any>(null);
33
158
 
34
- const [formItemId] = useState(externalFormItemId ?? v4());
35
159
  const {
36
160
  value,
37
161
  onChange,
@@ -49,61 +173,55 @@ export default function FormItem({
49
173
  formItemId,
50
174
  rules,
51
175
  elementRef: elRef,
176
+ hidden,
177
+ collectOnHidden,
52
178
  });
53
- // console.log("re-render", formName, name);
54
-
55
- // useEffect(() => {
56
- // console.log({ value });
57
- // }, [value]);
58
179
 
59
- // useEffect(() => {
60
- // console.log("isInitied changed: ", {
61
- // isInitied,
62
- // name,
63
- // value,
64
- // controlAfterInit,
65
- // });
66
- // }, [isInitied]);
67
180
  return (
68
181
  <Fragment
69
- key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`}
182
+ key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${externalFormItemId}`}
70
183
  >
71
- {!hidden && children
72
- ? cloneElement(children, {
73
- name,
74
- // ref: inputRef,
75
- [valuePropName]: value,
76
- onChange: (...args: any[]) => {
77
- let val = args[0];
78
- if (
79
- getValueFromEvent &&
80
- typeof getValueFromEvent === "function"
81
- ) {
82
- val = getValueFromEvent(...args);
83
- } else {
84
- const e = args[0];
85
- if (e && e.target) {
86
- val = e.target.value;
87
- }
88
- }
89
- onChange(val);
90
- },
91
- // onFocus: () => {
92
- // setIsTouched(true);
93
- // },
94
- // isTouched: isTouched,
95
- isDirty: isDirty,
96
- // errors: errors,
97
- // formState,
98
-
99
- errors,
100
- onFocus,
101
- validateState: state,
102
- ref: elRef,
103
- submitState,
104
- isTouched: isTouched,
105
- } as any)
106
- : null}
184
+ {!hidden && children ? (
185
+ isNil(Component) ? (
186
+ <BaseFormItem
187
+ elementRef={elRef}
188
+ name={name}
189
+ valuePropName={valuePropName}
190
+ getValueFromEvent={getValueFromEvent}
191
+ value={value}
192
+ onChange={onChange}
193
+ errors={errors}
194
+ state={state}
195
+ onFocus={onFocus}
196
+ isDirty={isDirty}
197
+ submitState={submitState}
198
+ isTouched={isTouched}
199
+ formItemId={formItemId}
200
+ >
201
+ {children}
202
+ </BaseFormItem>
203
+ ) : (
204
+ <FormItemWithWrapper
205
+ elementRef={elRef}
206
+ name={name}
207
+ valuePropName={valuePropName}
208
+ getValueFromEvent={getValueFromEvent}
209
+ value={value}
210
+ onChange={onChange}
211
+ errors={errors}
212
+ state={state}
213
+ onFocus={onFocus}
214
+ isDirty={isDirty}
215
+ submitState={submitState}
216
+ isTouched={isTouched}
217
+ formItemId={formItemId}
218
+ Component={Component}
219
+ {...props}
220
+ >
221
+ {children}
222
+ </FormItemWithWrapper>
223
+ )
224
+ ) : null}
107
225
  </Fragment>
108
226
  );
109
227
  }
@@ -1,6 +1,6 @@
1
1
  import { get, has, isNil } from "lodash";
2
2
  import { useTaskEffect } from "minh-custom-hooks-release";
3
- import { useEffect, useMemo, type RefObject } from "react";
3
+ import { useEffect, useLayoutEffect, useMemo, type RefObject } from "react";
4
4
  import { useShallow } from "zustand/react/shallow"; // Import useShallow
5
5
  import {
6
6
  IS_ALPHABET_STRING_AND_NUMBER_REGEX,
@@ -37,6 +37,8 @@ interface UseFormItemControlProps {
37
37
  formItemId?: string;
38
38
  rules?: ValidationRule[];
39
39
  elementRef?: RefObject<any> | null;
40
+ hidden?: boolean;
41
+ collectOnHidden?: boolean;
40
42
  }
41
43
 
42
44
  export interface UseFormItemControlReturn {
@@ -61,6 +63,8 @@ export default function useFormItemControl<T = any>({
61
63
  formItemId,
62
64
  rules,
63
65
  elementRef,
66
+ hidden,
67
+ collectOnHidden,
64
68
  }: UseFormItemControlProps): UseFormItemControlReturn {
65
69
  const contextForm = useFormContext();
66
70
  const {
@@ -71,6 +75,9 @@ export default function useFormItemControl<T = any>({
71
75
  // getFormState,
72
76
  isStateInitied,
73
77
  submitState,
78
+ getListener,
79
+ revokeListener,
80
+ clearObjKeyItem,
74
81
  } = useFormStore(
75
82
  useShallow((state) => {
76
83
  return {
@@ -90,6 +97,9 @@ export default function useFormItemControl<T = any>({
90
97
  state.formStates?.[
91
98
  formName || form?.formName || contextForm?.formName
92
99
  ]?.submitState,
100
+ getListener: state.getListener,
101
+ revokeListener: state.revokeListener,
102
+ clearObjKeyItem: state.clearObjKeyItem,
93
103
  };
94
104
  }),
95
105
  );
@@ -216,9 +226,9 @@ export default function useFormItemControl<T = any>({
216
226
  return rules || [];
217
227
  }, [rules]);
218
228
 
219
- useEffect(() => {
220
- console.log("Rules changed: ", { name, listener });
221
- }, [value, listener]);
229
+ // useEffect(() => {
230
+ // console.log("Rules changed: ", { name, listener });
231
+ // }, [value, listener]);
222
232
 
223
233
  const { data: errors, state } = useTaskEffect({
224
234
  async task() {
@@ -562,28 +572,31 @@ export default function useFormItemControl<T = any>({
562
572
  }
563
573
  }, [isStateInitied]);
564
574
 
565
- useEffect(() => {
575
+ useLayoutEffect(() => {
576
+ console.log("Component mount: ", name, formItemId, listener);
566
577
  if (!listener) {
567
578
  setListener({
568
579
  onChange,
569
580
  emitFocus,
570
581
  isTouched: false,
571
582
  isDirty: false,
572
- isInitied: true,
583
+ isInitied: false,
573
584
  name,
574
585
  formName: formName || form?.formName || contextForm?.formName,
575
586
  formItemId,
576
587
  onReset,
588
+ hidden,
589
+ collectOnHidden,
577
590
  });
578
591
  }
579
592
 
580
593
  return () => {
581
- if (listener) {
582
- setCleanUpStack({
583
- name: listener.name,
584
- itemKey: listener.formItemId,
585
- });
586
- }
594
+ revokeListener(formItemId, (listener, same) => {
595
+ if (!same.length) {
596
+ console.log("Trigger after revoke: ", listener, same);
597
+ clearObjKeyItem(listener.formName, listener.name);
598
+ }
599
+ });
587
600
  };
588
601
 
589
602
  // return () => {
@@ -592,6 +605,16 @@ export default function useFormItemControl<T = any>({
592
605
  // };
593
606
  }, []);
594
607
 
608
+ useEffect(() => {
609
+ if (listener) {
610
+ setListener({
611
+ formItemId,
612
+ hidden,
613
+ collectOnHidden,
614
+ });
615
+ }
616
+ }, [hidden, collectOnHidden]);
617
+
595
618
  useEffect(() => {
596
619
  if (listener) {
597
620
  setListener({
@@ -337,7 +337,12 @@ export default function useFormListControl<T = any>({
337
337
  name,
338
338
  listFormInitValues,
339
339
  );
340
-
340
+ if (getListener(formItemId)) {
341
+ setListener({
342
+ formItemId,
343
+ isInitied: true,
344
+ });
345
+ }
341
346
  return;
342
347
  }
343
348
 
@@ -355,7 +360,12 @@ export default function useFormListControl<T = any>({
355
360
 
356
361
  setListFields(result);
357
362
  setListFormInitValues(internalInitValue as any);
358
-
363
+ if (getListener(formItemId)) {
364
+ setListener({
365
+ formItemId,
366
+ isInitied: true,
367
+ });
368
+ }
359
369
  return;
360
370
  }
361
371
 
@@ -383,7 +393,12 @@ export default function useFormListControl<T = any>({
383
393
 
384
394
  setListFields(result);
385
395
  setListFormInitValues(initialValues as any);
386
-
396
+ if (getListener(formItemId)) {
397
+ setListener({
398
+ formItemId,
399
+ isInitied: true,
400
+ });
401
+ }
387
402
  // console.log("Get form init values: ", internalInitValue, result);
388
403
 
389
404
  return;