react-form-manage 1.0.8-beta.0 → 1.0.8-beta.10
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 +16 -0
- package/dist/components/Form/FormItem.d.ts +4 -2
- package/dist/components/Form/FormItem.js +9 -6
- package/dist/hooks/useFormItemControl.d.ts +5 -3
- package/dist/hooks/useFormItemControl.js +20 -4
- package/dist/index.d.ts +2 -2
- package/dist/providers/Form.d.ts +4 -2
- package/dist/providers/Form.js +45 -20
- package/dist/stores/formStore.js +8 -4
- package/dist/types/public.d.ts +5 -0
- package/package.json +2 -1
- package/src/App.tsx +76 -2
- package/src/components/Form/FormItem.tsx +74 -39
- package/src/components/Form/InputWrapper.tsx +5 -0
- package/src/hooks/useFormItemControl.ts +35 -6
- package/src/index.ts +2 -0
- package/src/providers/Form.tsx +41 -5
- package/src/stores/formStore.ts +8 -2
- package/src/types/public.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.8-beta.3] - 2026-01-22
|
|
6
|
+
|
|
7
|
+
## [1.0.8-beta.7] - 2026-01-24
|
|
8
|
+
- Add control flag for FormItem to support rendering MUI uncontrolled components when initial value is undefined (control on init).
|
|
9
|
+
- Fix: `onReset` did not restore listener state to init.
|
|
10
|
+
- Fix: reset did not return Form submit state to `idle`.
|
|
11
|
+
- Add `hidden` prop to allow hiding components while still assigning a value.
|
|
12
|
+
|
|
13
|
+
Docs: Update usage notes for `FormItem` control-on-init flag and `hidden` prop.
|
|
14
|
+
- Add `UseFormItemStateWatchReturn` type export for `useFormItemStateWatch` hook
|
|
15
|
+
|
|
16
|
+
- Add isTouched field to FormItem for tracking user interaction state
|
|
17
|
+
|
|
18
|
+
## [1.0.8-beta.1] - 2026-01-22
|
|
19
|
+
|
|
20
|
+
- Fix UseFormItemControlReturn errors type to properly export FormFieldError[]
|
|
5
21
|
## [1.0.7-beta.1] - 2026-01-22
|
|
6
22
|
|
|
7
23
|
- Add `FormFieldError` type export for typed error handling
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
import type { ValidationRule } from "../../types/public";
|
|
3
3
|
export interface FormItemProps {
|
|
4
|
-
children: ReactElement
|
|
4
|
+
children: ReactElement<any>;
|
|
5
5
|
name: string;
|
|
6
6
|
formName?: string;
|
|
7
7
|
initialValue?: any;
|
|
@@ -9,5 +9,7 @@ export interface FormItemProps {
|
|
|
9
9
|
rules?: ValidationRule[];
|
|
10
10
|
valuePropName?: string;
|
|
11
11
|
getValueFromEvent?: (...args: any[]) => any;
|
|
12
|
+
controlAfterInit?: boolean;
|
|
13
|
+
hidden?: boolean;
|
|
12
14
|
}
|
|
13
|
-
export default function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName, getValueFromEvent, }: FormItemProps):
|
|
15
|
+
export default function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName, getValueFromEvent, controlAfterInit, hidden, }: FormItemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cloneElement, Fragment, useRef, useState } from "react";
|
|
2
3
|
import { v4 } from "uuid";
|
|
3
4
|
import useFormItemControl from "../../hooks/useFormItemControl";
|
|
4
|
-
function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName = "value", getValueFromEvent }) {
|
|
5
|
+
function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName = "value", getValueFromEvent, controlAfterInit = false, hidden }) {
|
|
5
6
|
const elRef = useRef(null);
|
|
6
7
|
const [formItemId] = useState(externalFormItemId != null ? externalFormItemId : v4());
|
|
7
|
-
const { value, onChange, errors, state, onFocus, isDirty, submitState } = useFormItemControl({
|
|
8
|
+
const { value, onChange, errors, state, onFocus, isDirty, submitState, isTouched, isInitied } = useFormItemControl({
|
|
8
9
|
formName,
|
|
9
10
|
name,
|
|
10
11
|
initialValue,
|
|
@@ -12,7 +13,8 @@ function FormItem({ children, name, formName, initialValue, formItemId: external
|
|
|
12
13
|
rules,
|
|
13
14
|
elementRef: elRef
|
|
14
15
|
});
|
|
15
|
-
return cloneElement(children, {
|
|
16
|
+
return _jsx(Fragment, { children: !hidden && children ? cloneElement(children, {
|
|
17
|
+
name,
|
|
16
18
|
// ref: inputRef,
|
|
17
19
|
[valuePropName]: value,
|
|
18
20
|
onChange: (...args) => {
|
|
@@ -38,8 +40,9 @@ function FormItem({ children, name, formName, initialValue, formItemId: external
|
|
|
38
40
|
onFocus,
|
|
39
41
|
validateState: state,
|
|
40
42
|
ref: elRef,
|
|
41
|
-
submitState
|
|
42
|
-
|
|
43
|
+
submitState,
|
|
44
|
+
isTouched
|
|
45
|
+
}) : null }, `control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`);
|
|
43
46
|
}
|
|
44
47
|
export {
|
|
45
48
|
FormItem as default
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RefObject } from "react";
|
|
2
2
|
import type { FormInstance } from "../stores/formStore";
|
|
3
|
-
import type { SubmitState, ValidationRule } from "../types/public";
|
|
3
|
+
import type { FormFieldError, SubmitState, ValidationRule } from "../types/public";
|
|
4
4
|
type AnyObject = Record<string, any>;
|
|
5
5
|
interface UseFormItemControlProps {
|
|
6
6
|
formName?: string;
|
|
@@ -11,14 +11,16 @@ interface UseFormItemControlProps {
|
|
|
11
11
|
rules?: ValidationRule[];
|
|
12
12
|
elementRef?: RefObject<any> | null;
|
|
13
13
|
}
|
|
14
|
-
interface UseFormItemControlReturn {
|
|
14
|
+
export interface UseFormItemControlReturn {
|
|
15
15
|
value: any;
|
|
16
16
|
onChange: (value: any, options?: AnyObject) => void;
|
|
17
17
|
state: any;
|
|
18
|
-
errors:
|
|
18
|
+
errors: FormFieldError[];
|
|
19
19
|
onFocus: () => void;
|
|
20
20
|
isDirty?: boolean;
|
|
21
|
+
isTouched?: boolean;
|
|
21
22
|
submitState?: SubmitState;
|
|
23
|
+
isInitied?: boolean;
|
|
22
24
|
}
|
|
23
25
|
export default function useFormItemControl<T = any>({ formName, form, name, initialValue, formItemId, rules, elementRef, }: UseFormItemControlProps): UseFormItemControlReturn;
|
|
24
26
|
export {};
|
|
@@ -38,7 +38,7 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
|
|
|
38
38
|
}));
|
|
39
39
|
const onInitData = (value2) => {
|
|
40
40
|
setInitData(formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName), name, value2);
|
|
41
|
-
onChange(value2, { notTriggerDirty: true });
|
|
41
|
+
onChange(value2, { notTriggerDirty: true, initiedData: true });
|
|
42
42
|
};
|
|
43
43
|
const onFocus = () => {
|
|
44
44
|
setListener({
|
|
@@ -60,10 +60,21 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
|
|
|
60
60
|
isTouched: listener == null ? void 0 : listener.isTouched
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
+
if ((options == null ? void 0 : options.initiedData) === true) {
|
|
64
|
+
setListener({
|
|
65
|
+
formItemId,
|
|
66
|
+
isInitied: true
|
|
67
|
+
});
|
|
68
|
+
}
|
|
63
69
|
setData(formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName), name, value2);
|
|
64
70
|
};
|
|
65
71
|
const onReset = (value2) => {
|
|
66
|
-
|
|
72
|
+
setListener({
|
|
73
|
+
formItemId,
|
|
74
|
+
isDirty: false,
|
|
75
|
+
isTouched: false
|
|
76
|
+
});
|
|
77
|
+
onChange(isNil(value2) ? getInitData(formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName), name) : value2, { notTriggerDirty: true, initiedData: true });
|
|
67
78
|
};
|
|
68
79
|
const internalRules = useMemo(() => {
|
|
69
80
|
return rules || [];
|
|
@@ -262,7 +273,10 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
|
|
|
262
273
|
onInitData(initialValue);
|
|
263
274
|
}
|
|
264
275
|
} else {
|
|
265
|
-
onChange(internalInitValue, {
|
|
276
|
+
onChange(internalInitValue, {
|
|
277
|
+
notTriggerDirty: true,
|
|
278
|
+
initiedData: true
|
|
279
|
+
});
|
|
266
280
|
}
|
|
267
281
|
}
|
|
268
282
|
return;
|
|
@@ -319,7 +333,9 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
|
|
|
319
333
|
errors,
|
|
320
334
|
onFocus,
|
|
321
335
|
isDirty: listener == null ? void 0 : listener.isDirty,
|
|
322
|
-
|
|
336
|
+
isTouched: listener == null ? void 0 : listener.isTouched,
|
|
337
|
+
submitState,
|
|
338
|
+
isInitied: listener == null ? void 0 : listener.isInitied
|
|
323
339
|
};
|
|
324
340
|
}
|
|
325
341
|
export {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Form, { useForm, useWatch, useSubmitDataWatch, useFormStateWatch, type FormProps, type ValidationRule, type FormFieldError, type SubmitState } from "./providers/Form";
|
|
1
|
+
import Form, { useForm, useWatch, useSubmitDataWatch, useFormStateWatch, type FormProps, type ValidationRule, type FormFieldError, type SubmitState, type UseFormItemStateWatchReturn } from "./providers/Form";
|
|
2
2
|
import { SUBMIT_STATE } from "./constants/form";
|
|
3
3
|
import FormItem, { type FormItemProps } from "./components/Form/FormItem";
|
|
4
4
|
import FormList, { type FormListProps } from "./components/Form/FormList";
|
|
@@ -6,5 +6,5 @@ import Input from "./components/Input";
|
|
|
6
6
|
import InputWrapper, { type InputWrapperProps } from "./components/Form/InputWrapper";
|
|
7
7
|
import useFormItemControl from "./hooks/useFormItemControl";
|
|
8
8
|
import useFormListControl from "./hooks/useFormListControl";
|
|
9
|
-
export { Form, FormItem, FormList, Input, InputWrapper, useFormItemControl, useFormListControl, useForm, useWatch, useSubmitDataWatch, useFormStateWatch, type FormProps, type FormItemProps, type FormListProps, type InputWrapperProps, type ValidationRule, type FormFieldError, type SubmitState, SUBMIT_STATE, };
|
|
9
|
+
export { Form, FormItem, FormList, Input, InputWrapper, useFormItemControl, useFormListControl, useForm, useWatch, useSubmitDataWatch, useFormStateWatch, type FormProps, type FormItemProps, type FormListProps, type InputWrapperProps, type ValidationRule, type FormFieldError, type SubmitState, type UseFormItemStateWatchReturn, SUBMIT_STATE, };
|
|
10
10
|
export default Form;
|
package/dist/providers/Form.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentType, FormHTMLAttributes, ReactNode } from "react";
|
|
2
|
-
import type { PublicFormInstance } from "../types/public";
|
|
3
|
-
export type {
|
|
2
|
+
import type { PublicFormInstance, UseFormItemStateWatchReturn } from "../types/public";
|
|
3
|
+
export type { FormFieldError, SubmitState, UseFormItemStateWatchReturn, ValidationRule, } from "../types/public";
|
|
4
4
|
export declare const FormContext: import("react").Context<any>;
|
|
5
5
|
export interface FormProps<T = any> extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
|
|
6
6
|
children: ReactNode;
|
|
@@ -21,6 +21,7 @@ declare namespace Form {
|
|
|
21
21
|
var useWatch: typeof import("./Form").useWatch;
|
|
22
22
|
var useSubmitDataWatch: typeof import("./Form").useSubmitDataWatch;
|
|
23
23
|
var useFormStateWatch: <T = any>(formNameOrFormInstance?: string | PublicFormInstance<T>) => any;
|
|
24
|
+
var useFormItemStateWatch: <T = any>(nameOrFormItemId: string, formNameOrFormInstance?: string | PublicFormInstance<T>) => UseFormItemStateWatchReturn;
|
|
24
25
|
}
|
|
25
26
|
export default Form;
|
|
26
27
|
export declare function useFormContext(): any;
|
|
@@ -32,3 +33,4 @@ export declare function useSubmitDataWatch<T = any>({ formNameOrFormInstance, tr
|
|
|
32
33
|
mapFn?: (v: any, prev: any) => any;
|
|
33
34
|
}): T | undefined;
|
|
34
35
|
export declare const useFormStateWatch: <T = any>(formNameOrFormInstance?: string | PublicFormInstance<T>) => any;
|
|
36
|
+
export declare const useFormItemStateWatch: <T = any>(nameOrFormItemId: string, formNameOrFormInstance?: string | PublicFormInstance<T>) => UseFormItemStateWatchReturn;
|
package/dist/providers/Form.js
CHANGED
|
@@ -5,29 +5,40 @@ import { createContext, useContext, useEffect, useState } from "react";
|
|
|
5
5
|
import { flushSync } from "react-dom";
|
|
6
6
|
import { useShallow } from "zustand/react/shallow";
|
|
7
7
|
import FormCleanUp from "../components/Form/FormCleanUp";
|
|
8
|
+
import { SUBMIT_STATE } from "../constants/form";
|
|
8
9
|
import { useFormListeners, useFormStore } from "../stores/formStore";
|
|
9
10
|
import { getAllNoneObjStringPath } from "../utils/obj.util";
|
|
10
11
|
const FormContext = createContext(null);
|
|
11
12
|
function Form({ children, formName, initialValues, onFinish, onReject, onFinally, FormElement, ...props }) {
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
const {
|
|
14
|
+
// appInitValue,
|
|
15
|
+
getFormItemValue,
|
|
16
|
+
setInitData,
|
|
17
|
+
setData,
|
|
18
|
+
getFormValues,
|
|
19
|
+
setFormState,
|
|
20
|
+
setFormInstance,
|
|
21
|
+
revokeFormInstance,
|
|
22
|
+
setSubmitHistory,
|
|
23
|
+
clearFormValues,
|
|
24
|
+
clearFormInitialValues,
|
|
25
|
+
clearFormState
|
|
26
|
+
} = useFormStore(useShallow((state) => ({
|
|
27
|
+
// appInitValue: state.initialValues,
|
|
28
|
+
setInitData: state.setInitData,
|
|
29
|
+
getFormValues: state.getFormValues,
|
|
30
|
+
setFormState: state.setFormState,
|
|
31
|
+
setFormInstance: state.setFormInstance,
|
|
32
|
+
revokeFormInstance: state.revokeFormInstance,
|
|
33
|
+
setData: state.setData,
|
|
34
|
+
setSubmitHistory: state.setSubmitHistory,
|
|
35
|
+
getFormItemValue: state.getFormItemValue,
|
|
36
|
+
clearFormValues: state.clearFormValues,
|
|
37
|
+
clearFormInitialValues: state.clearFormInitialValues,
|
|
38
|
+
clearFormState: state.clearFormState
|
|
39
|
+
// Test, nhớ xóa sau khi xong
|
|
40
|
+
// formStates: state.formStates?.[formName],
|
|
41
|
+
})));
|
|
31
42
|
const { getListeners } = useFormListeners(useShallow((state) => {
|
|
32
43
|
return {
|
|
33
44
|
getListeners: state.getListeners
|
|
@@ -167,7 +178,8 @@ function Form({ children, formName, initialValues, onFinish, onReject, onFinally
|
|
|
167
178
|
reset();
|
|
168
179
|
flushSync(setFormState({
|
|
169
180
|
formName,
|
|
170
|
-
isInitied: false
|
|
181
|
+
isInitied: false,
|
|
182
|
+
submitState: SUBMIT_STATE.IDLE
|
|
171
183
|
}));
|
|
172
184
|
const totalListenerFields = getListeners();
|
|
173
185
|
if (Array.isArray(resetOptions)) {
|
|
@@ -287,15 +299,28 @@ const useFormStateWatch = (formNameOrFormInstance) => {
|
|
|
287
299
|
});
|
|
288
300
|
return formState;
|
|
289
301
|
};
|
|
302
|
+
const useFormItemStateWatch = (nameOrFormItemId, formNameOrFormInstance) => {
|
|
303
|
+
const [formInstance] = useForm(formNameOrFormInstance);
|
|
304
|
+
const listener = useFormStore((state) => {
|
|
305
|
+
return state.getListeners().find((l) => l.formName === (formInstance == null ? void 0 : formInstance.formName) && (l.name === nameOrFormItemId || l.formItemId === nameOrFormItemId));
|
|
306
|
+
});
|
|
307
|
+
return {
|
|
308
|
+
isTouched: listener == null ? void 0 : listener.isTouched,
|
|
309
|
+
isDirty: listener == null ? void 0 : listener.isDirty,
|
|
310
|
+
errors: (listener == null ? void 0 : listener.internalErrors) || []
|
|
311
|
+
};
|
|
312
|
+
};
|
|
290
313
|
Form.useForm = useForm;
|
|
291
314
|
Form.useWatch = useWatch;
|
|
292
315
|
Form.useSubmitDataWatch = useSubmitDataWatch;
|
|
293
316
|
Form.useFormStateWatch = useFormStateWatch;
|
|
317
|
+
Form.useFormItemStateWatch = useFormItemStateWatch;
|
|
294
318
|
export {
|
|
295
319
|
FormContext,
|
|
296
320
|
Form as default,
|
|
297
321
|
useForm,
|
|
298
322
|
useFormContext,
|
|
323
|
+
useFormItemStateWatch,
|
|
299
324
|
useFormStateWatch,
|
|
300
325
|
useSubmitDataWatch,
|
|
301
326
|
useWatch
|
package/dist/stores/formStore.js
CHANGED
|
@@ -200,7 +200,7 @@ const useFormListeners = create((storeSet, storeGet) => ({
|
|
|
200
200
|
getListeners() {
|
|
201
201
|
return storeGet().listeners;
|
|
202
202
|
},
|
|
203
|
-
setListener({ formName, name, onChange, onReset, isTouched, isDirty, formItemId, internalErrors, onFocus, emitFocus }) {
|
|
203
|
+
setListener({ formName, name, onChange, onReset, isTouched, isDirty, formItemId, internalErrors, onFocus, emitFocus, isInitied }) {
|
|
204
204
|
return storeSet(produce((state) => {
|
|
205
205
|
const storeListeners = state.listeners;
|
|
206
206
|
const findListenerIndex = state.listeners.findIndex((l) => l.formItemId === formItemId);
|
|
@@ -232,17 +232,21 @@ const useFormListeners = create((storeSet, storeGet) => ({
|
|
|
232
232
|
if (!isNil(emitFocus)) {
|
|
233
233
|
storeListeners[findListenerIndex].emitFocus = emitFocus;
|
|
234
234
|
}
|
|
235
|
+
if (!isNil(isInitied)) {
|
|
236
|
+
storeListeners[findListenerIndex].isInitied = isInitied;
|
|
237
|
+
}
|
|
235
238
|
return;
|
|
236
239
|
}
|
|
237
240
|
storeListeners.push({
|
|
238
241
|
name,
|
|
239
242
|
formName,
|
|
240
|
-
isTouched,
|
|
241
|
-
isDirty,
|
|
243
|
+
isTouched: Boolean(isTouched),
|
|
244
|
+
isDirty: Boolean(isDirty),
|
|
242
245
|
formItemId,
|
|
243
246
|
internalErrors,
|
|
244
247
|
onChange,
|
|
245
|
-
onReset
|
|
248
|
+
onReset,
|
|
249
|
+
isInitied: Boolean(isInitied)
|
|
246
250
|
});
|
|
247
251
|
}));
|
|
248
252
|
},
|
package/dist/types/public.d.ts
CHANGED
|
@@ -61,6 +61,11 @@ export interface UseFormItemReturn<T = any> {
|
|
|
61
61
|
isDirty?: boolean;
|
|
62
62
|
submitState?: SubmitState;
|
|
63
63
|
}
|
|
64
|
+
export interface UseFormItemStateWatchReturn {
|
|
65
|
+
isTouched?: boolean;
|
|
66
|
+
isDirty?: boolean;
|
|
67
|
+
errors: FormFieldError[];
|
|
68
|
+
}
|
|
64
69
|
export interface UseFormListProps<T = any> {
|
|
65
70
|
name?: string;
|
|
66
71
|
form?: PublicFormInstance<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-form-manage",
|
|
3
|
-
"version": "1.0.8-beta.
|
|
3
|
+
"version": "1.0.8-beta.10",
|
|
4
4
|
"description": "Lightweight React form management with list and listener support.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"eslint": "^9.14.0",
|
|
46
46
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
47
47
|
"eslint-plugin-react-refresh": "^0.4.14",
|
|
48
|
+
"framer-motion": "^12.29.0",
|
|
48
49
|
"globals": "^15.12.0",
|
|
49
50
|
"react": "^19.0.0",
|
|
50
51
|
"react-dom": "^19.0.0",
|
package/src/App.tsx
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import { Checkbox } from "@mui/material";
|
|
1
2
|
import { Button, Input } from "antd";
|
|
3
|
+
import { motion } from "framer-motion";
|
|
2
4
|
import { useEffect } from "react";
|
|
3
5
|
import FormItem from "./components/Form/FormItem";
|
|
4
6
|
import FormList from "./components/Form/FormList";
|
|
5
7
|
import InputWrapper from "./components/Form/InputWrapper";
|
|
6
8
|
import Form, { useForm } from "./providers/Form";
|
|
7
9
|
|
|
10
|
+
import { Form as AntdForm } from "antd";
|
|
11
|
+
|
|
8
12
|
const App = () => {
|
|
9
13
|
const [form] = useForm("form1");
|
|
10
14
|
|
|
15
|
+
const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
|
|
16
|
+
|
|
11
17
|
useEffect(() => {
|
|
12
18
|
if (form) {
|
|
13
19
|
// setTimeout(() => {
|
|
@@ -17,6 +23,20 @@ const App = () => {
|
|
|
17
23
|
}, [form]);
|
|
18
24
|
return (
|
|
19
25
|
<div>
|
|
26
|
+
<AntdForm>
|
|
27
|
+
<motion.div
|
|
28
|
+
initial={{ opacity: 0 }}
|
|
29
|
+
animate={{ opacity: 1 }}
|
|
30
|
+
exit={{ opacity: 0 }}
|
|
31
|
+
transition={{ duration: 1 }}
|
|
32
|
+
>
|
|
33
|
+
<AntdForm.Item name={"234"} initialValue={"23432"} label="Antd Input">
|
|
34
|
+
<Input />
|
|
35
|
+
</AntdForm.Item>
|
|
36
|
+
</motion.div>
|
|
37
|
+
</AntdForm>
|
|
38
|
+
|
|
39
|
+
{/* Hidden Test */}
|
|
20
40
|
<Form
|
|
21
41
|
initialValues={{
|
|
22
42
|
TestData: "",
|
|
@@ -27,13 +47,25 @@ const App = () => {
|
|
|
27
47
|
console.log(values);
|
|
28
48
|
}}
|
|
29
49
|
formName={"form1"}
|
|
50
|
+
// hidden
|
|
30
51
|
>
|
|
31
|
-
<FormItem
|
|
52
|
+
<FormItem
|
|
53
|
+
name={"username"}
|
|
54
|
+
rules={[
|
|
55
|
+
{
|
|
56
|
+
required: true,
|
|
57
|
+
message: "Test",
|
|
58
|
+
},
|
|
59
|
+
]}
|
|
60
|
+
initialValue={"283746"}
|
|
61
|
+
// hidden
|
|
62
|
+
>
|
|
32
63
|
<InputWrapper>
|
|
33
64
|
<Input />
|
|
34
65
|
</InputWrapper>
|
|
35
66
|
</FormItem>
|
|
36
67
|
|
|
68
|
+
{/* Numberic test */}
|
|
37
69
|
<FormItem
|
|
38
70
|
name={"numericCode"}
|
|
39
71
|
rules={[
|
|
@@ -48,6 +80,20 @@ const App = () => {
|
|
|
48
80
|
<Input placeholder="Mã chỉ gồm số" style={{ width: 200 }} />
|
|
49
81
|
</InputWrapper>
|
|
50
82
|
</FormItem>
|
|
83
|
+
|
|
84
|
+
{/* Motion Test */}
|
|
85
|
+
<motion.div
|
|
86
|
+
initial={{ opacity: 0 }}
|
|
87
|
+
animate={{ opacity: 1 }}
|
|
88
|
+
exit={{ opacity: 0 }}
|
|
89
|
+
transition={{ duration: 1 }}
|
|
90
|
+
>
|
|
91
|
+
<FormItem name="motionTest" controlAfterInit initialValue={"1234134"}>
|
|
92
|
+
<InputWrapper>
|
|
93
|
+
<Input placeholder="Motion Test" style={{ width: 200 }} />
|
|
94
|
+
</InputWrapper>
|
|
95
|
+
</FormItem>
|
|
96
|
+
</motion.div>
|
|
51
97
|
<FormList
|
|
52
98
|
initialValues={[
|
|
53
99
|
{
|
|
@@ -93,12 +139,40 @@ const App = () => {
|
|
|
93
139
|
</div>
|
|
94
140
|
)}
|
|
95
141
|
</FormList>
|
|
142
|
+
<motion.div
|
|
143
|
+
initial={{ opacity: 0 }}
|
|
144
|
+
animate={{ opacity: 1 }}
|
|
145
|
+
exit={{ opacity: 0 }}
|
|
146
|
+
transition={{ duration: 1.5 }}
|
|
147
|
+
>
|
|
148
|
+
<FormItem
|
|
149
|
+
valuePropName="checked"
|
|
150
|
+
getValueFromEvent={(_, checked) => checked}
|
|
151
|
+
name="checkControlledAfterInit"
|
|
152
|
+
controlAfterInit={true}
|
|
153
|
+
initialValue={true}
|
|
154
|
+
>
|
|
155
|
+
<Checkbox />
|
|
156
|
+
</FormItem>
|
|
157
|
+
</motion.div>
|
|
158
|
+
|
|
159
|
+
<Button
|
|
160
|
+
onClick={() => {
|
|
161
|
+
const current = form?.getFieldValue("checkControlledAfterInit");
|
|
162
|
+
console.log("Toggle controlled after init: ", current);
|
|
163
|
+
form?.setFieldValue("checkControlledAfterInit", !current);
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
Toggle
|
|
167
|
+
</Button>
|
|
96
168
|
<Button htmlType="submit">Submit</Button>
|
|
97
169
|
<Button
|
|
98
170
|
onClick={() => {
|
|
99
171
|
form?.resetFields?.();
|
|
100
172
|
}}
|
|
101
|
-
|
|
173
|
+
>
|
|
174
|
+
Reset
|
|
175
|
+
</Button>
|
|
102
176
|
</Form>
|
|
103
177
|
</div>
|
|
104
178
|
);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import { cloneElement, useRef, useState } from "react";
|
|
2
|
+
import { cloneElement, Fragment, useRef, useState } from "react";
|
|
3
3
|
import { v4 } from "uuid";
|
|
4
4
|
import useFormItemControl from "../../hooks/useFormItemControl";
|
|
5
5
|
import type { ValidationRule } from "../../types/public";
|
|
6
6
|
|
|
7
7
|
export interface FormItemProps {
|
|
8
|
-
children: ReactElement
|
|
8
|
+
children: ReactElement<any>;
|
|
9
9
|
name: string;
|
|
10
10
|
formName?: string;
|
|
11
11
|
initialValue?: any;
|
|
@@ -13,6 +13,8 @@ export interface FormItemProps {
|
|
|
13
13
|
rules?: ValidationRule[];
|
|
14
14
|
valuePropName?: string;
|
|
15
15
|
getValueFromEvent?: (...args: any[]) => any;
|
|
16
|
+
controlAfterInit?: boolean;
|
|
17
|
+
hidden?: boolean;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export default function FormItem({
|
|
@@ -24,52 +26,85 @@ export default function FormItem({
|
|
|
24
26
|
rules,
|
|
25
27
|
valuePropName = "value",
|
|
26
28
|
getValueFromEvent,
|
|
29
|
+
controlAfterInit = false,
|
|
30
|
+
hidden,
|
|
27
31
|
}: FormItemProps) {
|
|
28
32
|
const elRef = useRef<any>(null);
|
|
29
33
|
|
|
30
34
|
const [formItemId] = useState(externalFormItemId ?? v4());
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const {
|
|
36
|
+
value,
|
|
37
|
+
onChange,
|
|
38
|
+
errors,
|
|
39
|
+
state,
|
|
40
|
+
onFocus,
|
|
41
|
+
isDirty,
|
|
42
|
+
submitState,
|
|
43
|
+
isTouched,
|
|
44
|
+
isInitied,
|
|
45
|
+
} = useFormItemControl({
|
|
46
|
+
formName,
|
|
47
|
+
name,
|
|
48
|
+
initialValue,
|
|
49
|
+
formItemId,
|
|
50
|
+
rules,
|
|
51
|
+
elementRef: elRef,
|
|
52
|
+
});
|
|
40
53
|
// console.log("re-render", formName, name);
|
|
41
54
|
|
|
42
55
|
// useEffect(() => {
|
|
43
56
|
// console.log({ value });
|
|
44
57
|
// }, [value]);
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// useEffect(() => {
|
|
60
|
+
// console.log("isInitied changed: ", {
|
|
61
|
+
// isInitied,
|
|
62
|
+
// name,
|
|
63
|
+
// value,
|
|
64
|
+
// controlAfterInit,
|
|
65
|
+
// });
|
|
66
|
+
// }, [isInitied]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Fragment
|
|
70
|
+
key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`}
|
|
71
|
+
>
|
|
72
|
+
{!hidden && children
|
|
73
|
+
? cloneElement(children, {
|
|
74
|
+
name,
|
|
75
|
+
// ref: inputRef,
|
|
76
|
+
[valuePropName]: value,
|
|
77
|
+
onChange: (...args: any[]) => {
|
|
78
|
+
let val = args[0];
|
|
79
|
+
if (
|
|
80
|
+
getValueFromEvent &&
|
|
81
|
+
typeof getValueFromEvent === "function"
|
|
82
|
+
) {
|
|
83
|
+
val = getValueFromEvent(...args);
|
|
84
|
+
} else {
|
|
85
|
+
const e = args[0];
|
|
86
|
+
if (e && e.target) {
|
|
87
|
+
val = e.target.value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
onChange(val);
|
|
91
|
+
},
|
|
92
|
+
// onFocus: () => {
|
|
93
|
+
// setIsTouched(true);
|
|
94
|
+
// },
|
|
95
|
+
// isTouched: isTouched,
|
|
96
|
+
isDirty: isDirty,
|
|
97
|
+
// errors: errors,
|
|
98
|
+
// formState,
|
|
68
99
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
100
|
+
errors,
|
|
101
|
+
onFocus,
|
|
102
|
+
validateState: state,
|
|
103
|
+
ref: elRef,
|
|
104
|
+
submitState,
|
|
105
|
+
isTouched: isTouched,
|
|
106
|
+
} as any)
|
|
107
|
+
: null}
|
|
108
|
+
</Fragment>
|
|
109
|
+
);
|
|
75
110
|
}
|
|
@@ -13,6 +13,11 @@ const InputWrapper = ({
|
|
|
13
13
|
errors = [],
|
|
14
14
|
...props
|
|
15
15
|
}: InputWrapperProps) => {
|
|
16
|
+
// useEffect(() => {
|
|
17
|
+
// console.log("InputWrapper submitState changed: ", {
|
|
18
|
+
// submitState: props.submitState,
|
|
19
|
+
// });
|
|
20
|
+
// }, [props.submitState]);
|
|
16
21
|
return (
|
|
17
22
|
<div>
|
|
18
23
|
{cloneElement(children, props)}
|
|
@@ -25,7 +25,11 @@ import {
|
|
|
25
25
|
} from "../stores/formStore";
|
|
26
26
|
|
|
27
27
|
import type { FormInstance } from "../stores/formStore";
|
|
28
|
-
import type {
|
|
28
|
+
import type {
|
|
29
|
+
FormFieldError,
|
|
30
|
+
SubmitState,
|
|
31
|
+
ValidationRule,
|
|
32
|
+
} from "../types/public";
|
|
29
33
|
|
|
30
34
|
type AnyObject = Record<string, any>;
|
|
31
35
|
|
|
@@ -39,14 +43,16 @@ interface UseFormItemControlProps {
|
|
|
39
43
|
elementRef?: RefObject<any> | null;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
interface UseFormItemControlReturn {
|
|
46
|
+
export interface UseFormItemControlReturn {
|
|
43
47
|
value: any;
|
|
44
48
|
onChange: (value: any, options?: AnyObject) => void;
|
|
45
49
|
state: any;
|
|
46
|
-
errors:
|
|
50
|
+
errors: FormFieldError[];
|
|
47
51
|
onFocus: () => void;
|
|
48
52
|
isDirty?: boolean;
|
|
53
|
+
isTouched?: boolean;
|
|
49
54
|
submitState?: SubmitState;
|
|
55
|
+
isInitied?: boolean;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
|
|
@@ -149,7 +155,7 @@ export default function useFormItemControl<T = any>({
|
|
|
149
155
|
name,
|
|
150
156
|
value,
|
|
151
157
|
);
|
|
152
|
-
onChange(value, { notTriggerDirty: true });
|
|
158
|
+
onChange(value, { notTriggerDirty: true, initiedData: true });
|
|
153
159
|
};
|
|
154
160
|
|
|
155
161
|
const onFocus = () => {
|
|
@@ -166,7 +172,13 @@ export default function useFormItemControl<T = any>({
|
|
|
166
172
|
}
|
|
167
173
|
};
|
|
168
174
|
|
|
169
|
-
const onChange = (
|
|
175
|
+
const onChange = (
|
|
176
|
+
value: T,
|
|
177
|
+
options?: {
|
|
178
|
+
notTriggerDirty?: boolean;
|
|
179
|
+
initiedData?: boolean;
|
|
180
|
+
},
|
|
181
|
+
) => {
|
|
170
182
|
if (options?.notTriggerDirty !== true) {
|
|
171
183
|
setListener({
|
|
172
184
|
formItemId,
|
|
@@ -174,6 +186,12 @@ export default function useFormItemControl<T = any>({
|
|
|
174
186
|
isTouched: listener?.isTouched,
|
|
175
187
|
});
|
|
176
188
|
}
|
|
189
|
+
if (options?.initiedData === true) {
|
|
190
|
+
setListener({
|
|
191
|
+
formItemId,
|
|
192
|
+
isInitied: true,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
177
195
|
setData(formName || form?.formName || contextForm?.formName, name, value);
|
|
178
196
|
};
|
|
179
197
|
|
|
@@ -183,10 +201,16 @@ export default function useFormItemControl<T = any>({
|
|
|
183
201
|
value,
|
|
184
202
|
getInitData(formName || form?.formName || contextForm?.formName, name),
|
|
185
203
|
);
|
|
204
|
+
setListener({
|
|
205
|
+
formItemId,
|
|
206
|
+
isDirty: false,
|
|
207
|
+
isTouched: false,
|
|
208
|
+
});
|
|
186
209
|
onChange(
|
|
187
210
|
isNil(value)
|
|
188
211
|
? getInitData(formName || form?.formName || contextForm?.formName, name)
|
|
189
212
|
: value,
|
|
213
|
+
{ notTriggerDirty: true, initiedData: true },
|
|
190
214
|
);
|
|
191
215
|
};
|
|
192
216
|
|
|
@@ -517,7 +541,10 @@ export default function useFormItemControl<T = any>({
|
|
|
517
541
|
onInitData(initialValue);
|
|
518
542
|
}
|
|
519
543
|
} else {
|
|
520
|
-
onChange(internalInitValue, {
|
|
544
|
+
onChange(internalInitValue, {
|
|
545
|
+
notTriggerDirty: true,
|
|
546
|
+
initiedData: true,
|
|
547
|
+
});
|
|
521
548
|
}
|
|
522
549
|
}
|
|
523
550
|
return;
|
|
@@ -592,6 +619,8 @@ export default function useFormItemControl<T = any>({
|
|
|
592
619
|
errors,
|
|
593
620
|
onFocus,
|
|
594
621
|
isDirty: listener?.isDirty,
|
|
622
|
+
isTouched: listener?.isTouched,
|
|
595
623
|
submitState,
|
|
624
|
+
isInitied: listener?.isInitied,
|
|
596
625
|
};
|
|
597
626
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import Form, {
|
|
|
7
7
|
type ValidationRule,
|
|
8
8
|
type FormFieldError,
|
|
9
9
|
type SubmitState,
|
|
10
|
+
type UseFormItemStateWatchReturn,
|
|
10
11
|
} from "./providers/Form";
|
|
11
12
|
import { SUBMIT_STATE } from "./constants/form";
|
|
12
13
|
|
|
@@ -37,6 +38,7 @@ export {
|
|
|
37
38
|
type ValidationRule,
|
|
38
39
|
type FormFieldError,
|
|
39
40
|
type SubmitState,
|
|
41
|
+
type UseFormItemStateWatchReturn,
|
|
40
42
|
SUBMIT_STATE,
|
|
41
43
|
};
|
|
42
44
|
|
package/src/providers/Form.tsx
CHANGED
|
@@ -5,10 +5,19 @@ import { createContext, useContext, useEffect, useState } from "react";
|
|
|
5
5
|
import { flushSync } from "react-dom";
|
|
6
6
|
import { useShallow } from "zustand/react/shallow"; // Import useShallow
|
|
7
7
|
import FormCleanUp from "../components/Form/FormCleanUp";
|
|
8
|
+
import { SUBMIT_STATE } from "../constants/form";
|
|
8
9
|
import { useFormListeners, useFormStore } from "../stores/formStore";
|
|
9
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
PublicFormInstance,
|
|
12
|
+
UseFormItemStateWatchReturn,
|
|
13
|
+
} from "../types/public";
|
|
10
14
|
import { getAllNoneObjStringPath } from "../utils/obj.util";
|
|
11
|
-
export type {
|
|
15
|
+
export type {
|
|
16
|
+
FormFieldError,
|
|
17
|
+
SubmitState,
|
|
18
|
+
UseFormItemStateWatchReturn,
|
|
19
|
+
ValidationRule,
|
|
20
|
+
} from "../types/public";
|
|
12
21
|
|
|
13
22
|
export const FormContext = createContext(null);
|
|
14
23
|
|
|
@@ -40,7 +49,7 @@ export default function Form<T = any>({
|
|
|
40
49
|
...props
|
|
41
50
|
}: FormProps<T>) {
|
|
42
51
|
const {
|
|
43
|
-
appInitValue,
|
|
52
|
+
// appInitValue,
|
|
44
53
|
getFormItemValue,
|
|
45
54
|
setInitData,
|
|
46
55
|
setData,
|
|
@@ -54,7 +63,7 @@ export default function Form<T = any>({
|
|
|
54
63
|
clearFormState,
|
|
55
64
|
} = useFormStore(
|
|
56
65
|
useShallow((state) => ({
|
|
57
|
-
appInitValue: state.initialValues,
|
|
66
|
+
// appInitValue: state.initialValues,
|
|
58
67
|
setInitData: state.setInitData,
|
|
59
68
|
getFormValues: state.getFormValues,
|
|
60
69
|
setFormState: state.setFormState,
|
|
@@ -68,7 +77,7 @@ export default function Form<T = any>({
|
|
|
68
77
|
clearFormState: state.clearFormState,
|
|
69
78
|
|
|
70
79
|
// Test, nhớ xóa sau khi xong
|
|
71
|
-
formStates: state.formStates?.[formName],
|
|
80
|
+
// formStates: state.formStates?.[formName],
|
|
72
81
|
})),
|
|
73
82
|
);
|
|
74
83
|
|
|
@@ -271,6 +280,7 @@ export default function Form<T = any>({
|
|
|
271
280
|
setFormState({
|
|
272
281
|
formName,
|
|
273
282
|
isInitied: false,
|
|
283
|
+
submitState: SUBMIT_STATE.IDLE,
|
|
274
284
|
}),
|
|
275
285
|
);
|
|
276
286
|
const totalListenerFields = getListeners();
|
|
@@ -472,7 +482,33 @@ export const useFormStateWatch = <T = any,>(
|
|
|
472
482
|
return formState as any;
|
|
473
483
|
};
|
|
474
484
|
|
|
485
|
+
// Get Form Item State Using name (make sure no same name listener before using this) or formItemId, formNameOrFormInstance
|
|
486
|
+
// reutrn formItem state like isTouched, isDirty, errors
|
|
487
|
+
export const useFormItemStateWatch = <T = any,>(
|
|
488
|
+
nameOrFormItemId: string,
|
|
489
|
+
formNameOrFormInstance?: string | PublicFormInstance<T>,
|
|
490
|
+
): UseFormItemStateWatchReturn => {
|
|
491
|
+
const [formInstance] = useForm<T>(formNameOrFormInstance as any);
|
|
492
|
+
|
|
493
|
+
const listener = useFormStore((state) => {
|
|
494
|
+
return state
|
|
495
|
+
.getListeners()
|
|
496
|
+
.find(
|
|
497
|
+
(l) =>
|
|
498
|
+
l.formName === formInstance?.formName &&
|
|
499
|
+
(l.name === nameOrFormItemId || l.formItemId === nameOrFormItemId),
|
|
500
|
+
);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
return {
|
|
504
|
+
isTouched: listener?.isTouched,
|
|
505
|
+
isDirty: listener?.isDirty,
|
|
506
|
+
errors: listener?.internalErrors || [],
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
|
|
475
510
|
Form.useForm = useForm;
|
|
476
511
|
Form.useWatch = useWatch;
|
|
477
512
|
Form.useSubmitDataWatch = useSubmitDataWatch;
|
|
478
513
|
Form.useFormStateWatch = useFormStateWatch;
|
|
514
|
+
Form.useFormItemStateWatch = useFormItemStateWatch;
|
package/src/stores/formStore.ts
CHANGED
|
@@ -375,6 +375,7 @@ export const useFormListeners = create<any>((storeSet: any, storeGet: any) => ({
|
|
|
375
375
|
internalErrors,
|
|
376
376
|
onFocus,
|
|
377
377
|
emitFocus,
|
|
378
|
+
isInitied,
|
|
378
379
|
}) {
|
|
379
380
|
return storeSet(
|
|
380
381
|
produce<any>((state: any) => {
|
|
@@ -412,17 +413,22 @@ export const useFormListeners = create<any>((storeSet: any, storeGet: any) => ({
|
|
|
412
413
|
storeListeners[findListenerIndex].emitFocus = emitFocus;
|
|
413
414
|
}
|
|
414
415
|
|
|
416
|
+
if (!isNil(isInitied)) {
|
|
417
|
+
storeListeners[findListenerIndex].isInitied = isInitied;
|
|
418
|
+
}
|
|
419
|
+
|
|
415
420
|
return;
|
|
416
421
|
}
|
|
417
422
|
storeListeners.push({
|
|
418
423
|
name,
|
|
419
424
|
formName,
|
|
420
|
-
isTouched,
|
|
421
|
-
isDirty,
|
|
425
|
+
isTouched: Boolean(isTouched),
|
|
426
|
+
isDirty: Boolean(isDirty),
|
|
422
427
|
formItemId,
|
|
423
428
|
internalErrors,
|
|
424
429
|
onChange,
|
|
425
430
|
onReset,
|
|
431
|
+
isInitied: Boolean(isInitied),
|
|
426
432
|
});
|
|
427
433
|
}),
|
|
428
434
|
);
|
package/src/types/public.ts
CHANGED
|
@@ -67,6 +67,12 @@ export interface UseFormItemReturn<T = any> {
|
|
|
67
67
|
submitState?: SubmitState;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
export interface UseFormItemStateWatchReturn {
|
|
71
|
+
isTouched?: boolean;
|
|
72
|
+
isDirty?: boolean;
|
|
73
|
+
errors: FormFieldError[];
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
export interface UseFormListProps<T = any> {
|
|
71
77
|
name?: string;
|
|
72
78
|
form?: PublicFormInstance<T>;
|