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/CHANGELOG.md +17 -0
- package/dist/components/Form/FormItem.d.ts +10 -4
- package/dist/components/Form/FormItem.js +52 -14
- package/dist/hooks/useFormItemControl.d.ts +3 -1
- package/dist/hooks/useFormItemControl.js +29 -15
- package/dist/hooks/useFormListControl.js +18 -0
- package/dist/providers/Form.d.ts +4 -1
- package/dist/providers/Form.js +17 -5
- package/dist/stores/formStore.d.ts +17 -2
- package/dist/stores/formStore.js +12 -2
- package/dist/test/CommonTest.d.ts +3 -0
- package/dist/test/CommonTest.js +49 -0
- package/dist/test/TestWrapperFormItem.d.ts +3 -0
- package/dist/test/TestWrapperFormItem.js +13 -0
- package/package.json +1 -1
- package/src/App.tsx +11 -176
- package/src/components/Form/FormCleanUp.tsx +1 -1
- package/src/components/Form/FormItem.tsx +174 -56
- package/src/hooks/useFormItemControl.ts +35 -12
- package/src/hooks/useFormListControl.ts +18 -3
- package/src/providers/Form.tsx +31 -5
- package/src/stores/formStore.ts +34 -3
- package/src/test/CommonTest.tsx +177 -0
- package/src/test/TestWrapperFormItem.tsx +34 -0
package/src/providers/Form.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cloneDeep,
|
|
3
|
+
filter,
|
|
3
4
|
get,
|
|
4
5
|
isEqual,
|
|
5
6
|
isNil,
|
|
@@ -14,8 +15,9 @@ import { createContext, useContext, useEffect, useState } from "react";
|
|
|
14
15
|
import { flushSync } from "react-dom";
|
|
15
16
|
import { useShallow } from "zustand/react/shallow"; // Import useShallow
|
|
16
17
|
import FormCleanUp from "../components/Form/FormCleanUp";
|
|
18
|
+
import FormItem from "../components/Form/FormItem";
|
|
17
19
|
import { SUBMIT_STATE } from "../constants/form";
|
|
18
|
-
import { ListenerItem, useFormStore } from "../stores/formStore";
|
|
20
|
+
import { ListenerItem, SubmitProps, useFormStore } from "../stores/formStore";
|
|
19
21
|
import type {
|
|
20
22
|
PublicFormInstance,
|
|
21
23
|
UseFormItemStateWatchReturn,
|
|
@@ -34,6 +36,7 @@ export interface FormProps<T = any> extends Omit<
|
|
|
34
36
|
FormHTMLAttributes<HTMLFormElement>,
|
|
35
37
|
"onSubmit"
|
|
36
38
|
> {
|
|
39
|
+
collectHiddenFields?: boolean;
|
|
37
40
|
children: ReactNode;
|
|
38
41
|
formName: string;
|
|
39
42
|
initialValues?: T;
|
|
@@ -55,6 +58,7 @@ export default function Form<T = any>({
|
|
|
55
58
|
onReject,
|
|
56
59
|
onFinally,
|
|
57
60
|
FormElement,
|
|
61
|
+
collectHiddenFields: formCollectHiddenFields = true,
|
|
58
62
|
...props
|
|
59
63
|
}: FormProps<T>) {
|
|
60
64
|
const {
|
|
@@ -236,16 +240,36 @@ export default function Form<T = any>({
|
|
|
236
240
|
state: _,
|
|
237
241
|
reset,
|
|
238
242
|
} = useTask({
|
|
239
|
-
async task(props) {
|
|
243
|
+
async task(props?: SubmitProps<T>) {
|
|
240
244
|
try {
|
|
241
245
|
flushSync(setFormState({ formName, submitState: "submitting" }));
|
|
242
246
|
const errorFields = getAllFieldErrors();
|
|
243
|
-
const listeners = getListeners().filter(
|
|
247
|
+
const listeners: ListenerItem[] = getListeners().filter(
|
|
248
|
+
(l) => l.formName === formName,
|
|
249
|
+
);
|
|
244
250
|
const formValues = getFormValues(formName);
|
|
245
251
|
|
|
246
252
|
const resultValues = cloneDeep(formValues);
|
|
247
253
|
const cleanValues = {} as T;
|
|
248
|
-
uniqBy(
|
|
254
|
+
uniqBy(
|
|
255
|
+
filter(listeners, (l: ListenerItem) => {
|
|
256
|
+
// console.log("Check collect field hidden: ", {
|
|
257
|
+
// name: l.name,
|
|
258
|
+
// hidden: l.hidden,
|
|
259
|
+
// collectOnHidden: l.collectOnHidden,
|
|
260
|
+
// collectHiddenFields,
|
|
261
|
+
// });
|
|
262
|
+
if (!l.hidden) return true;
|
|
263
|
+
if (isNil(props?.collectHiddenFields)) {
|
|
264
|
+
if (isNil(l.collectOnHidden)) {
|
|
265
|
+
return formCollectHiddenFields;
|
|
266
|
+
}
|
|
267
|
+
return Boolean(l.collectOnHidden);
|
|
268
|
+
}
|
|
269
|
+
return props.collectHiddenFields;
|
|
270
|
+
}),
|
|
271
|
+
(l) => l.name,
|
|
272
|
+
).forEach((l) => {
|
|
249
273
|
set(cleanValues as any, l.name, get(resultValues, l.name));
|
|
250
274
|
});
|
|
251
275
|
|
|
@@ -443,6 +467,8 @@ export default function Form<T = any>({
|
|
|
443
467
|
formName,
|
|
444
468
|
}}
|
|
445
469
|
>
|
|
470
|
+
<FormCleanUp />
|
|
471
|
+
|
|
446
472
|
{FormElement ? (
|
|
447
473
|
<FormElement
|
|
448
474
|
onSubmit={(e) => {
|
|
@@ -468,7 +494,6 @@ export default function Form<T = any>({
|
|
|
468
494
|
{children}
|
|
469
495
|
</form>
|
|
470
496
|
)}
|
|
471
|
-
<FormCleanUp />
|
|
472
497
|
</FormContext.Provider>
|
|
473
498
|
);
|
|
474
499
|
}
|
|
@@ -581,6 +606,7 @@ export const useFormItemStateWatch = <T = any,>(
|
|
|
581
606
|
};
|
|
582
607
|
|
|
583
608
|
Form.useForm = useForm;
|
|
609
|
+
Form.Item = FormItem;
|
|
584
610
|
Form.useWatch = useWatch;
|
|
585
611
|
Form.useSubmitDataWatch = useSubmitDataWatch;
|
|
586
612
|
Form.useFormStateWatch = useFormStateWatch;
|
package/src/stores/formStore.ts
CHANGED
|
@@ -4,10 +4,25 @@ import { v4 } from "uuid";
|
|
|
4
4
|
import { create } from "zustand";
|
|
5
5
|
import { getAllNoneObjStringPath } from "../utils/obj.util";
|
|
6
6
|
type ListenerFormItemType = "normal" | "array";
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
export interface SubmitProps<T = any> {
|
|
9
|
+
externalFinishCallback?: (values: T, allValues?: any) => void | Promise<void>;
|
|
10
|
+
externalRejectCallback?: (errorFields: any[]) => void | Promise<void>;
|
|
11
|
+
externalFinallyCallback?: (result: {
|
|
12
|
+
errorsField: any[];
|
|
13
|
+
values: T;
|
|
14
|
+
withUnRegisteredValues: any;
|
|
15
|
+
}) => void | Promise<void>;
|
|
16
|
+
callBothFinish?: boolean;
|
|
17
|
+
callBothReject?: boolean;
|
|
18
|
+
callBothFinally?: boolean;
|
|
19
|
+
collectHiddenFields?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FormInstance<T = any> {
|
|
8
23
|
formName: string;
|
|
9
24
|
resetFields: (values?: any) => void;
|
|
10
|
-
submit: (
|
|
25
|
+
submit: (props?: SubmitProps<T>) => void;
|
|
11
26
|
submitAsync: (values?: any) => Promise<any>;
|
|
12
27
|
setFieldValue: (name: string, value: any, options?: any) => void;
|
|
13
28
|
setFieldValues: (values: Record<string, any>, options?: any) => void;
|
|
@@ -31,6 +46,8 @@ export interface ListenerItem {
|
|
|
31
46
|
emitFocus?: any;
|
|
32
47
|
isInitied?: boolean;
|
|
33
48
|
type?: ListenerFormItemType;
|
|
49
|
+
hidden?: boolean;
|
|
50
|
+
collectOnHidden?: boolean;
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
export interface CleanUpItem {
|
|
@@ -362,7 +379,7 @@ const createFormStoreSlice = (storeSet: any, storeGet: any, api: any) => ({
|
|
|
362
379
|
|
|
363
380
|
// Listeners Slice
|
|
364
381
|
const createListenersSlice = (storeSet: any, storeGet: any, api: any) => ({
|
|
365
|
-
listeners: [],
|
|
382
|
+
listeners: [] as ListenerItem[],
|
|
366
383
|
getListener(formItemId) {
|
|
367
384
|
return storeGet().listeners.find((l) => l.formItemId === formItemId);
|
|
368
385
|
},
|
|
@@ -383,6 +400,8 @@ const createListenersSlice = (storeSet: any, storeGet: any, api: any) => ({
|
|
|
383
400
|
isInitied,
|
|
384
401
|
type,
|
|
385
402
|
onArrayChange,
|
|
403
|
+
hidden,
|
|
404
|
+
collectOnHidden,
|
|
386
405
|
}: Partial<ListenerItem> & { formItemId: string }) {
|
|
387
406
|
return storeSet(
|
|
388
407
|
produce<any>((state: any) => {
|
|
@@ -428,6 +447,14 @@ const createListenersSlice = (storeSet: any, storeGet: any, api: any) => ({
|
|
|
428
447
|
storeListeners[findListenerIndex].onArrayChange = onArrayChange;
|
|
429
448
|
}
|
|
430
449
|
|
|
450
|
+
if (!isNil(hidden)) {
|
|
451
|
+
storeListeners[findListenerIndex].hidden = hidden;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (!isNil(collectOnHidden)) {
|
|
455
|
+
storeListeners[findListenerIndex].collectOnHidden = collectOnHidden;
|
|
456
|
+
}
|
|
457
|
+
|
|
431
458
|
return;
|
|
432
459
|
}
|
|
433
460
|
storeListeners.push({
|
|
@@ -442,6 +469,10 @@ const createListenersSlice = (storeSet: any, storeGet: any, api: any) => ({
|
|
|
442
469
|
isInitied: Boolean(isInitied),
|
|
443
470
|
type: type || "normal",
|
|
444
471
|
onArrayChange,
|
|
472
|
+
onFocus,
|
|
473
|
+
emitFocus,
|
|
474
|
+
hidden: Boolean(hidden),
|
|
475
|
+
collectOnHidden,
|
|
445
476
|
});
|
|
446
477
|
}),
|
|
447
478
|
);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { Button, Checkbox, Input } from "antd";
|
|
2
|
+
import { motion } from "framer-motion";
|
|
3
|
+
import FormItem from "../components/Form/FormItem";
|
|
4
|
+
import FormList from "../components/Form/FormList";
|
|
5
|
+
import InputWrapper from "../components/Form/InputWrapper";
|
|
6
|
+
import Form from "../providers/Form";
|
|
7
|
+
|
|
8
|
+
type Props = {};
|
|
9
|
+
|
|
10
|
+
function CommonTest({}: Props) {
|
|
11
|
+
const [form] = Form.useForm("form1");
|
|
12
|
+
|
|
13
|
+
const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Form
|
|
17
|
+
initialValues={{
|
|
18
|
+
TestData: "",
|
|
19
|
+
numericCode: "",
|
|
20
|
+
// arr: [{ el: "Item 1" }, { el: "Item 2" }],
|
|
21
|
+
}}
|
|
22
|
+
onFinish={(values) => {
|
|
23
|
+
console.log(values);
|
|
24
|
+
}}
|
|
25
|
+
formName={"form1"}
|
|
26
|
+
// hidden
|
|
27
|
+
>
|
|
28
|
+
<FormItem
|
|
29
|
+
name={"username"}
|
|
30
|
+
rules={[
|
|
31
|
+
{
|
|
32
|
+
required: true,
|
|
33
|
+
message: "Test",
|
|
34
|
+
},
|
|
35
|
+
]}
|
|
36
|
+
initialValue={"283746"}
|
|
37
|
+
// hidden
|
|
38
|
+
>
|
|
39
|
+
<InputWrapper>
|
|
40
|
+
<Input />
|
|
41
|
+
</InputWrapper>
|
|
42
|
+
</FormItem>
|
|
43
|
+
|
|
44
|
+
{/* Numberic test */}
|
|
45
|
+
<FormItem
|
|
46
|
+
name={"numericCode"}
|
|
47
|
+
rules={[
|
|
48
|
+
{ required: true, message: "Vui lòng nhập mã" },
|
|
49
|
+
{
|
|
50
|
+
pattern: /^\d+$/,
|
|
51
|
+
message: "Chỉ cho phép chuỗi số",
|
|
52
|
+
},
|
|
53
|
+
]}
|
|
54
|
+
>
|
|
55
|
+
<InputWrapper>
|
|
56
|
+
<Input placeholder="Mã chỉ gồm số" style={{ width: 200 }} />
|
|
57
|
+
</InputWrapper>
|
|
58
|
+
</FormItem>
|
|
59
|
+
|
|
60
|
+
{/* Motion Test */}
|
|
61
|
+
<motion.div
|
|
62
|
+
initial={{ opacity: 0 }}
|
|
63
|
+
animate={{ opacity: 1 }}
|
|
64
|
+
exit={{ opacity: 0 }}
|
|
65
|
+
transition={{ duration: 1 }}
|
|
66
|
+
>
|
|
67
|
+
<FormItem name="motionTest" controlAfterInit initialValue={"1234134"}>
|
|
68
|
+
<InputWrapper>
|
|
69
|
+
<Input placeholder="Motion Test" style={{ width: 200 }} />
|
|
70
|
+
</InputWrapper>
|
|
71
|
+
</FormItem>
|
|
72
|
+
</motion.div>
|
|
73
|
+
<FormList
|
|
74
|
+
initialValues={[
|
|
75
|
+
{
|
|
76
|
+
el: "sdfsdf",
|
|
77
|
+
d: { child: "Test Child" },
|
|
78
|
+
},
|
|
79
|
+
]}
|
|
80
|
+
name="arr"
|
|
81
|
+
>
|
|
82
|
+
{(fields, { add, remove, move }) => (
|
|
83
|
+
<div>
|
|
84
|
+
{fields.map((field, index) => (
|
|
85
|
+
<div key={field.key} style={{ marginBottom: 8 }}>
|
|
86
|
+
<FormItem name={`${field.name}.el`} initialValue={"Chém gió"}>
|
|
87
|
+
<InputWrapper>
|
|
88
|
+
<Input placeholder="Item value" style={{ width: 200 }} />
|
|
89
|
+
</InputWrapper>
|
|
90
|
+
</FormItem>
|
|
91
|
+
<FormItem
|
|
92
|
+
name={`${field.name}.d.child`}
|
|
93
|
+
initialValue={"Con của item"}
|
|
94
|
+
>
|
|
95
|
+
<InputWrapper>
|
|
96
|
+
<Input placeholder="Item value" style={{ width: 200 }} />
|
|
97
|
+
</InputWrapper>
|
|
98
|
+
</FormItem>
|
|
99
|
+
<Button
|
|
100
|
+
onClick={() => remove({ index })}
|
|
101
|
+
style={{ marginLeft: 8 }}
|
|
102
|
+
>
|
|
103
|
+
Remove
|
|
104
|
+
</Button>
|
|
105
|
+
{index > 0 && (
|
|
106
|
+
<Button
|
|
107
|
+
onClick={() => move({ from: index, to: index - 1 })}
|
|
108
|
+
style={{ marginLeft: 8 }}
|
|
109
|
+
>
|
|
110
|
+
Move Up
|
|
111
|
+
</Button>
|
|
112
|
+
)}
|
|
113
|
+
{index < fields.length - 1 && (
|
|
114
|
+
<Button
|
|
115
|
+
onClick={() => move({ from: index, to: index + 1 })}
|
|
116
|
+
style={{ marginLeft: 8 }}
|
|
117
|
+
>
|
|
118
|
+
Move Down
|
|
119
|
+
</Button>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
))}
|
|
123
|
+
<Button onClick={() => add(fields.length)}>Add Item</Button>
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
126
|
+
</FormList>
|
|
127
|
+
<Button
|
|
128
|
+
onClick={() => {
|
|
129
|
+
form?.setFieldValue("arr", [
|
|
130
|
+
{ el: "Set Item 1" },
|
|
131
|
+
{ el: "Set Item 2" },
|
|
132
|
+
{ el: "Set Item 3" },
|
|
133
|
+
]);
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
Test set array list value
|
|
137
|
+
</Button>
|
|
138
|
+
<motion.div
|
|
139
|
+
initial={{ opacity: 0 }}
|
|
140
|
+
animate={{ opacity: 1 }}
|
|
141
|
+
exit={{ opacity: 0 }}
|
|
142
|
+
transition={{ duration: 1.5 }}
|
|
143
|
+
>
|
|
144
|
+
<Form.Item
|
|
145
|
+
valuePropName="checked"
|
|
146
|
+
getValueFromEvent={(_, checked) => checked}
|
|
147
|
+
name="checkControlledAfterInit"
|
|
148
|
+
controlAfterInit={true}
|
|
149
|
+
initialValue={true}
|
|
150
|
+
hidden
|
|
151
|
+
>
|
|
152
|
+
<Checkbox />
|
|
153
|
+
</Form.Item>
|
|
154
|
+
</motion.div>
|
|
155
|
+
{/* <TestFormWatch /> */}
|
|
156
|
+
<Button
|
|
157
|
+
onClick={() => {
|
|
158
|
+
const current = form?.getFieldValue("checkControlledAfterInit");
|
|
159
|
+
console.log("Toggle controlled after init: ", current);
|
|
160
|
+
form?.setFieldValue("checkControlledAfterInit", !current);
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
Toggle
|
|
164
|
+
</Button>
|
|
165
|
+
<Button htmlType="submit">Submit</Button>
|
|
166
|
+
<Button
|
|
167
|
+
onClick={() => {
|
|
168
|
+
form?.resetFields?.();
|
|
169
|
+
}}
|
|
170
|
+
>
|
|
171
|
+
Reset
|
|
172
|
+
</Button>
|
|
173
|
+
</Form>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default CommonTest;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Button, Col, Input, Row } from "antd";
|
|
2
|
+
import { useToggle } from "minh-custom-hooks-release";
|
|
3
|
+
import Form from "../providers/Form";
|
|
4
|
+
|
|
5
|
+
type Props = {};
|
|
6
|
+
|
|
7
|
+
function TestWrapperFormItem({}: Props) {
|
|
8
|
+
const { state, toggle } = useToggle(false);
|
|
9
|
+
return (
|
|
10
|
+
<Form
|
|
11
|
+
onFinish={(values, all) => {
|
|
12
|
+
console.log(values, all);
|
|
13
|
+
}}
|
|
14
|
+
formName="testWrapper"
|
|
15
|
+
>
|
|
16
|
+
<Row>
|
|
17
|
+
<Form.Item
|
|
18
|
+
hidden={!state}
|
|
19
|
+
name="test"
|
|
20
|
+
collectOnHidden={false}
|
|
21
|
+
initialValue={"test"}
|
|
22
|
+
Component={Col}
|
|
23
|
+
xs={12}
|
|
24
|
+
>
|
|
25
|
+
<Input />
|
|
26
|
+
</Form.Item>
|
|
27
|
+
</Row>
|
|
28
|
+
<Button htmlType="submit">Submit</Button>
|
|
29
|
+
<Button onClick={toggle}>{state ? "Off" : "On"}</Button>
|
|
30
|
+
</Form>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default TestWrapperFormItem;
|