react-form-manage 1.0.8-beta.23 → 1.0.8-beta.26

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 CHANGED
@@ -2,9 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.8-beta.25] - 2026-02-04
6
+
7
+ ### Changes
8
+
9
+ - Remove cleanup stack execution for clearing form items and array values
10
+
11
+ ## [1.0.8-beta.24] - 2026-02-04
12
+
13
+ ### Fixes
14
+
15
+ - Fix cleanup item execution for proper FormItem unmounting
16
+
17
+ ### Features
18
+
19
+ - Add `Component` prop to FormItem for wrapping input with custom elements
20
+ - Support custom wrapper components in FormItem
21
+
5
22
  ## [1.0.8-beta.23] - 2026-02-04
6
23
 
7
24
  ### Features
25
+
8
26
  - Add `hidden` prop to FormItem for conditional visibility control
9
27
  - Add `collectHiddenValue` option to control whether hidden form items contribute to form data collection
10
28
  - FormItem can now be retrieved from Form instance
@@ -13,26 +31,31 @@ All notable changes to this project will be documented in this file.
13
31
  ## [1.0.8-beta.22] - 2026-02-02
14
32
 
15
33
  ### Changes
34
+
16
35
  - Refactor: Adjust useFormItemControl and main.tsx for better performance
17
36
 
18
37
  ## [1.0.8-beta.21] - 2026-02-02
19
38
 
20
39
  ### Fixes
40
+
21
41
  - Patch: Minor adjustments in useFormItemControl
22
42
 
23
43
  ## [1.0.8-beta.20] - 2026-02-02
24
44
 
25
45
  ### Features
46
+
26
47
  - Trigger `initied` flag for onChange in `useFormItemControl` to properly mark form items as initialized
27
48
 
28
49
  ## [1.0.8-beta.19] - 2026-02-02
29
50
 
30
51
  ### Features
52
+
31
53
  - Trigger `initied` flag for onChange in `useFormItemControl` to properly mark form items as initialized
32
54
 
33
55
  ## [1.0.8-beta.18] - 2026-02-01
34
56
 
35
57
  ### Features
58
+
36
59
  - Avoid deep traversal into class instances/functions in path collection
37
60
  - `getAllNoneObjStringPath` now skips non-plain objects and functions, only traverses arrays and plain objects
38
61
  - Improves performance and prevents accidental property access on class instances
@@ -1,6 +1,6 @@
1
- import type { ReactElement } from "react";
1
+ import type { ComponentProps, FC, ReactElement } from "react";
2
2
  import type { ValidationRule } from "../../types/public";
3
- export interface FormItemProps {
3
+ export type BaseFormItemProps = {
4
4
  children: ReactElement<any>;
5
5
  name: string;
6
6
  formName?: string;
@@ -12,5 +12,10 @@ export interface FormItemProps {
12
12
  controlAfterInit?: boolean;
13
13
  hidden?: boolean;
14
14
  collectOnHidden?: boolean;
15
- }
16
- export default function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName, getValueFromEvent, controlAfterInit, hidden, collectOnHidden, }: FormItemProps): import("react/jsx-runtime").JSX.Element;
15
+ };
16
+ export type FormItemProps<TCustomWrapper extends FC<any> | undefined> = BaseFormItemProps & ({
17
+ Component?: undefined;
18
+ } | ({
19
+ Component: TCustomWrapper;
20
+ } & Omit<ComponentProps<TCustomWrapper>, keyof BaseFormItemProps>));
21
+ export default function FormItem<TCustomWrapper extends FC<any> | undefined>({ children, Component, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName, getValueFromEvent, controlAfterInit, hidden, collectOnHidden, ...props }: FormItemProps<TCustomWrapper>): import("react/jsx-runtime").JSX.Element;
@@ -1,23 +1,45 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cloneElement, Fragment, useRef, useState } from "react";
2
+ import { isNil } from "lodash";
3
+ import { cloneElement, Fragment, useEffect, useRef, useState } from "react";
3
4
  import { v4 } from "uuid";
4
5
  import useFormItemControl from "../../hooks/useFormItemControl";
5
- function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName = "value", getValueFromEvent, controlAfterInit = false, hidden, collectOnHidden }) {
6
- const elRef = useRef(null);
7
- const [formItemId] = useState(externalFormItemId != null ? externalFormItemId : v4());
8
- const { value, onChange, errors, state, onFocus, isDirty, submitState, isTouched, isInitied } = useFormItemControl({
9
- formName,
6
+ function BaseFormItem({ children, name, elementRef: elRef, valuePropName, getValueFromEvent, onChange, value, isDirty, errors, onFocus, state, submitState, isTouched }) {
7
+ useEffect(() => {
8
+ }, [value]);
9
+ return cloneElement(children, {
10
10
  name,
11
- initialValue,
12
- formItemId,
13
- rules,
14
- elementRef: elRef,
15
- hidden,
16
- collectOnHidden
11
+ ref: elRef,
12
+ [valuePropName]: value,
13
+ onChange: (...args) => {
14
+ let val = args[0];
15
+ if (getValueFromEvent && typeof getValueFromEvent === "function") {
16
+ val = getValueFromEvent(...args);
17
+ } else {
18
+ const e = args[0];
19
+ if (e && e.target) {
20
+ val = e.target.value;
21
+ }
22
+ }
23
+ onChange(val);
24
+ },
25
+ // onFocus: () => {
26
+ // setIsTouched(true);
27
+ // },
28
+ // isTouched: isTouched,
29
+ isDirty,
30
+ // errors: errors,
31
+ // formState,
32
+ errors,
33
+ onFocus,
34
+ validateState: state,
35
+ submitState,
36
+ isTouched
17
37
  });
18
- return _jsx(Fragment, { children: !hidden && children ? cloneElement(children, {
38
+ }
39
+ function FormItemWithWrapper({ children, name, elementRef: elRef, valuePropName, getValueFromEvent, onChange, value, isDirty, errors, onFocus, state, submitState, isTouched, Component, ...props }) {
40
+ return _jsx(Component, { ...props, children: cloneElement(children, {
19
41
  name,
20
- // ref: inputRef,
42
+ ref: elRef,
21
43
  [valuePropName]: value,
22
44
  onChange: (...args) => {
23
45
  let val = args[0];
@@ -41,10 +63,24 @@ function FormItem({ children, name, formName, initialValue, formItemId: external
41
63
  errors,
42
64
  onFocus,
43
65
  validateState: state,
44
- ref: elRef,
45
66
  submitState,
46
67
  isTouched
47
- }) : null }, `control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`);
68
+ }) });
69
+ }
70
+ function FormItem({ children, Component, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName = "value", getValueFromEvent, controlAfterInit = false, hidden, collectOnHidden, ...props }) {
71
+ const [formItemId] = useState(externalFormItemId != null ? externalFormItemId : v4());
72
+ const elRef = useRef(null);
73
+ const { value, onChange, errors, state, onFocus, isDirty, submitState, isTouched, isInitied } = useFormItemControl({
74
+ formName,
75
+ name,
76
+ initialValue,
77
+ formItemId,
78
+ rules,
79
+ elementRef: elRef,
80
+ hidden,
81
+ collectOnHidden
82
+ });
83
+ return _jsx(Fragment, { children: !hidden && children ? isNil(Component) ? _jsx(BaseFormItem, { elementRef: elRef, name, valuePropName, getValueFromEvent, value, onChange, errors, state, onFocus, isDirty, submitState, isTouched, formItemId, children }) : _jsx(FormItemWithWrapper, { elementRef: elRef, name, valuePropName, getValueFromEvent, value, onChange, errors, state, onFocus, isDirty, submitState, isTouched, formItemId, Component, ...props, children }) : null }, `control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${externalFormItemId}`);
48
84
  }
49
85
  export {
50
86
  FormItem as default
@@ -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 } from "react";
3
+ import { useEffect, useLayoutEffect, useMemo } from "react";
4
4
  import { useShallow } from "zustand/react/shallow";
5
5
  import { IS_ALPHABET_STRING_AND_NUMBER_REGEX, IS_EMAIL_REGEX, IS_NAME_REGEX, IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX, IS_NO_SPACE_ALPHABET_STRING_REGEX, IS_NOSPACE_STRING_AND_NUMBER_REGEX, IS_PASSWORD_REGEX, IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX, IS_POSITIVE_STRING_NUMBER_REGEX, IS_STRING_AND_NUMBER_REGEX, IS_STRING_NUMBER_REGEX, IS_USERNAME_REGEX, IS_VIETNAMESE_PHONE_NUMBER_REGEX } from "../constants/validation";
6
6
  import { useFormContext } from "../providers/Form";
@@ -15,7 +15,10 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
15
15
  getFormValues,
16
16
  // getFormState,
17
17
  isStateInitied,
18
- submitState
18
+ submitState,
19
+ getListener,
20
+ revokeListener,
21
+ clearObjKeyItem
19
22
  } = useFormStore(useShallow((state2) => {
20
23
  var _a, _b, _c, _d;
21
24
  return {
@@ -25,7 +28,10 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
25
28
  getFormValues: state2.getFormValues,
26
29
  // getFormState: state.getFormState,
27
30
  isStateInitied: (_b = (_a = state2.formStates) == null ? void 0 : _a[formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]) == null ? void 0 : _b.isInitied,
28
- submitState: (_d = (_c = state2.formStates) == null ? void 0 : _c[formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]) == null ? void 0 : _d.submitState
31
+ submitState: (_d = (_c = state2.formStates) == null ? void 0 : _c[formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]) == null ? void 0 : _d.submitState,
32
+ getListener: state2.getListener,
33
+ revokeListener: state2.revokeListener,
34
+ clearObjKeyItem: state2.clearObjKeyItem
29
35
  };
30
36
  }));
31
37
  const { setCleanUpStack } = useFormStore(useShallow((state2) => ({
@@ -295,7 +301,7 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
295
301
  return;
296
302
  }
297
303
  }, [isStateInitied]);
298
- useEffect(() => {
304
+ useLayoutEffect(() => {
299
305
  if (!listener) {
300
306
  setListener({
301
307
  onChange,
@@ -312,14 +318,22 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
312
318
  });
313
319
  }
314
320
  return () => {
315
- if (listener) {
316
- setCleanUpStack({
317
- name: listener.name,
318
- itemKey: listener.formItemId
319
- });
320
- }
321
+ revokeListener(formItemId, (listener2, same) => {
322
+ if (!same.length) {
323
+ clearObjKeyItem(listener2.formName, listener2.name);
324
+ }
325
+ });
321
326
  };
322
327
  }, []);
328
+ useEffect(() => {
329
+ if (listener) {
330
+ setListener({
331
+ formItemId,
332
+ hidden,
333
+ collectOnHidden
334
+ });
335
+ }
336
+ }, [hidden, collectOnHidden]);
323
337
  useEffect(() => {
324
338
  if (listener) {
325
339
  setListener({
@@ -9,17 +9,16 @@ function useFormListControl({ name, form, initialValues, formName }) {
9
9
  const contextForm = useFormContext();
10
10
  const getFormValues = useFormStore((state) => state.getFormValues);
11
11
  const [listFormInitValues, setListFormInitValues] = useState(void 0);
12
- const { clearCacheData, setCacheData, setListener, getListener } = useFormStore(useShallow((state) => ({
12
+ const { clearCacheData, setCacheData, setListener, getListener, clearArrItems } = useFormStore(useShallow((state) => ({
13
13
  // Cache
14
14
  cacheData: state.cacheData,
15
15
  clearCacheData: state.clearCacheData,
16
16
  setCacheData: state.setCacheData,
17
17
  // Listener
18
18
  setListener: state.setListener,
19
- getListener: state.getListener
20
- })));
21
- const { setCleanUpStack } = useFormStore(useShallow((state) => ({
22
- setCleanUpStack: state.setCleanUpStack
19
+ getListener: state.getListener,
20
+ // Clear Arr Items
21
+ clearArrItems: state.clearArrItems
23
22
  })));
24
23
  const { initValue: internalInitValue, formState } = useFormStore(useShallow((state) => {
25
24
  return {
@@ -55,15 +54,14 @@ function useFormListControl({ name, form, initialValues, formName }) {
55
54
  const getNewValueCache = mapCurWithKey.filter(Boolean).map((c) => c.value);
56
55
  const startRemoveIndex = formDataBeforeChange.length - getNewValueCache.length;
57
56
  if (startRemoveIndex > 0) {
58
- Array.from(Array(startRemoveIndex)).map((_, index) => {
59
- return getNewValueCache.length + index;
60
- }).forEach((index) => {
61
- setCleanUpStack({
57
+ const clearItems = Array.from(Array(startRemoveIndex)).map((_, index) => {
58
+ const clearIndex = getNewValueCache.length + index;
59
+ return {
62
60
  formName: formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName),
63
- name: `${name}.${index}`,
64
- type: "array"
65
- });
61
+ name: `${name}.${clearIndex}`
62
+ };
66
63
  });
64
+ clearArrItems(clearItems);
67
65
  }
68
66
  setCacheData(formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName), name, getNewValueCache);
69
67
  };
@@ -1,10 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cloneDeep, filter, get, isEqual, isNil, isPlainObject, last, set, uniqBy } from "lodash";
3
3
  import { useTask } from "minh-custom-hooks-release";
4
4
  import { createContext, useContext, useEffect, useState } from "react";
5
5
  import { flushSync } from "react-dom";
6
6
  import { useShallow } from "zustand/react/shallow";
7
- import FormCleanUp from "../components/Form/FormCleanUp";
8
7
  import FormItem from "../components/Form/FormItem";
9
8
  import { SUBMIT_STATE } from "../constants/form";
10
9
  import { useFormStore } from "../stores/formStore";
@@ -300,9 +299,9 @@ function Form({ children, formName, initialValues, onFinish, onReject, onFinally
300
299
  clearFormState(formName);
301
300
  };
302
301
  }, []);
303
- return _jsxs(FormContext.Provider, { value: {
302
+ return _jsx(FormContext.Provider, { value: {
304
303
  formName
305
- }, children: [FormElement ? _jsx(FormElement, { onSubmit: (e) => {
304
+ }, children: FormElement ? _jsx(FormElement, { onSubmit: (e) => {
306
305
  e.preventDefault();
307
306
  e.stopPropagation();
308
307
  runSubmit(void 0);
@@ -310,7 +309,7 @@ function Form({ children, formName, initialValues, onFinish, onReject, onFinally
310
309
  e.preventDefault();
311
310
  e.stopPropagation();
312
311
  runSubmit(void 0);
313
- }, ...props, children }), _jsx(FormCleanUp, {})] });
312
+ }, ...props, children }) });
314
313
  }
315
314
  function useFormContext() {
316
315
  const c = useContext(FormContext);
@@ -72,6 +72,19 @@ const createFormStoreSlice = (storeSet, storeGet, api) => ({
72
72
  }
73
73
  }));
74
74
  },
75
+ clearArrItems(clearItems) {
76
+ return storeSet(produce((state) => {
77
+ const oldValues = state.forms;
78
+ clearItems.forEach(({ formName, name }) => {
79
+ const arrPath = name.split(".").slice(0, -1).join(".");
80
+ const elPath = Number(name.split(".").slice(-1).join(""));
81
+ const getArrItem = get(oldValues, `${formName}.${arrPath}`);
82
+ if (isNumber(elPath)) {
83
+ getArrItem.splice(elPath, 1);
84
+ }
85
+ });
86
+ }));
87
+ },
75
88
  setFormState({ formName, isInitied, submitState }) {
76
89
  return storeSet(produce((state) => {
77
90
  const oldValues = state.formStates;
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function CommonTest({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default CommonTest;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Checkbox, Input } from "antd";
3
+ import { motion } from "framer-motion";
4
+ import FormItem from "../components/Form/FormItem";
5
+ import FormList from "../components/Form/FormList";
6
+ import InputWrapper from "../components/Form/InputWrapper";
7
+ import Form from "../providers/Form";
8
+ function CommonTest({}) {
9
+ const [form] = Form.useForm("form1");
10
+ const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
11
+ return _jsxs(Form, { initialValues: {
12
+ TestData: "",
13
+ numericCode: ""
14
+ // arr: [{ el: "Item 1" }, { el: "Item 2" }],
15
+ }, onFinish: (values) => {
16
+ }, formName: "form1", children: [_jsx(FormItem, { name: "username", rules: [
17
+ {
18
+ required: true,
19
+ message: "Test"
20
+ }
21
+ ], initialValue: "283746", children: _jsx(InputWrapper, { children: _jsx(Input, {}) }) }), _jsx(FormItem, { name: "numericCode", rules: [
22
+ { required: true, message: "Vui l\xF2ng nh\u1EADp m\xE3" },
23
+ {
24
+ pattern: /^\d+$/,
25
+ message: "Ch\u1EC9 cho ph\xE9p chu\u1ED7i s\u1ED1"
26
+ }
27
+ ], children: _jsx(InputWrapper, { children: _jsx(Input, { placeholder: "M\xE3 ch\u1EC9 g\u1ED3m s\u1ED1", style: { width: 200 } }) }) }), _jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 1 }, children: _jsx(FormItem, { name: "motionTest", controlAfterInit: true, initialValue: "1234134", children: _jsx(InputWrapper, { children: _jsx(Input, { placeholder: "Motion Test", style: { width: 200 } }) }) }) }), _jsx(FormList, { initialValues: [
28
+ {
29
+ el: "sdfsdf",
30
+ d: { child: "Test Child" }
31
+ }
32
+ ], name: "arr", children: (fields, { add, remove, move }) => _jsxs("div", { children: [fields.map((field, index) => _jsxs("div", { style: { marginBottom: 8 }, children: [_jsx(FormItem, { name: `${field.name}.el`, initialValue: "Ch\xE9m gi\xF3", children: _jsx(InputWrapper, { children: _jsx(Input, { placeholder: "Item value", style: { width: 200 } }) }) }), _jsx(FormItem, { name: `${field.name}.d.child`, initialValue: "Con c\u1EE7a item", children: _jsx(InputWrapper, { children: _jsx(Input, { placeholder: "Item value", style: { width: 200 } }) }) }), _jsx(Button, { onClick: () => remove({ index }), style: { marginLeft: 8 }, children: "Remove" }), index > 0 && _jsx(Button, { onClick: () => move({ from: index, to: index - 1 }), style: { marginLeft: 8 }, children: "Move Up" }), index < fields.length - 1 && _jsx(Button, { onClick: () => move({ from: index, to: index + 1 }), style: { marginLeft: 8 }, children: "Move Down" })] }, field.key)), _jsx(Button, { onClick: () => add(fields.length), children: "Add Item" })] }) }), _jsx(Button, { onClick: () => {
33
+ form == null ? void 0 : form.setFieldValue("arr", [
34
+ { el: "Set Item 1" },
35
+ { el: "Set Item 2" },
36
+ { el: "Set Item 3" }
37
+ ]);
38
+ }, children: "Test set array list value" }), _jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 1.5 }, children: _jsx(Form.Item, { valuePropName: "checked", getValueFromEvent: (_, checked) => checked, name: "checkControlledAfterInit", controlAfterInit: true, initialValue: true, hidden: true, children: _jsx(Checkbox, {}) }) }), _jsx(Button, { onClick: () => {
39
+ const current = form == null ? void 0 : form.getFieldValue("checkControlledAfterInit");
40
+ form == null ? void 0 : form.setFieldValue("checkControlledAfterInit", !current);
41
+ }, children: "Toggle" }), _jsx(Button, { htmlType: "submit", children: "Submit" }), _jsx(Button, { onClick: () => {
42
+ var _a;
43
+ (_a = form == null ? void 0 : form.resetFields) == null ? void 0 : _a.call(form);
44
+ }, children: "Reset" })] });
45
+ }
46
+ var stdin_default = CommonTest;
47
+ export {
48
+ stdin_default as default
49
+ };
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function TestWrapperFormItem({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default TestWrapperFormItem;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Col, Input, Row } from "antd";
3
+ import { useToggle } from "minh-custom-hooks-release";
4
+ import Form from "../providers/Form";
5
+ function TestWrapperFormItem({}) {
6
+ const { state, toggle } = useToggle(false);
7
+ return _jsxs(Form, { onFinish: (values, all) => {
8
+ }, formName: "testWrapper", children: [_jsx(Row, { children: _jsx(Form.Item, { hidden: !state, name: "test", collectOnHidden: false, initialValue: "test", Component: Col, xs: 12, children: _jsx(Input, {}) }) }), _jsx(Button, { htmlType: "submit", children: "Submit" }), _jsx(Button, { onClick: toggle, children: state ? "Off" : "On" })] });
9
+ }
10
+ var stdin_default = TestWrapperFormItem;
11
+ export {
12
+ stdin_default as default
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-manage",
3
- "version": "1.0.8-beta.23",
3
+ "version": "1.0.8-beta.26",
4
4
  "description": "Lightweight React form management with list and listener support.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/App.tsx CHANGED
@@ -1,13 +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";
10
5
  import TestListener from "./test/TestListener";
6
+ import TestWrapperFormItem from "./test/TestWrapperFormItem";
11
7
 
12
8
  import { Form as AntdForm } from "antd";
13
9
 
@@ -18,17 +14,6 @@ function TestFormWatch() {
18
14
  }
19
15
 
20
16
  const App = () => {
21
- const [form] = useForm("form1");
22
-
23
- const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
24
-
25
- useEffect(() => {
26
- if (form) {
27
- // setTimeout(() => {
28
- // form.setFieldValue("TestData", "HelloWorld");
29
- // }, 500);
30
- }
31
- }, [form]);
32
17
  return (
33
18
  <div>
34
19
  <AntdForm>
@@ -45,167 +30,13 @@ const App = () => {
45
30
  </AntdForm>
46
31
 
47
32
  {/* Hidden Test */}
48
- <Form
49
- initialValues={{
50
- TestData: "",
51
- numericCode: "",
52
- // arr: [{ el: "Item 1" }, { el: "Item 2" }],
53
- }}
54
- onFinish={(values) => {
55
- console.log(values);
56
- }}
57
- formName={"form1"}
58
- // hidden
59
- >
60
- <FormItem
61
- name={"username"}
62
- rules={[
63
- {
64
- required: true,
65
- message: "Test",
66
- },
67
- ]}
68
- initialValue={"283746"}
69
- // hidden
70
- >
71
- <InputWrapper>
72
- <Input />
73
- </InputWrapper>
74
- </FormItem>
75
-
76
- {/* Numberic test */}
77
- <FormItem
78
- name={"numericCode"}
79
- rules={[
80
- { required: true, message: "Vui lòng nhập mã" },
81
- {
82
- pattern: /^\d+$/,
83
- message: "Chỉ cho phép chuỗi số",
84
- },
85
- ]}
86
- >
87
- <InputWrapper>
88
- <Input placeholder="Mã chỉ gồm số" style={{ width: 200 }} />
89
- </InputWrapper>
90
- </FormItem>
91
-
92
- {/* Motion Test */}
93
- <motion.div
94
- initial={{ opacity: 0 }}
95
- animate={{ opacity: 1 }}
96
- exit={{ opacity: 0 }}
97
- transition={{ duration: 1 }}
98
- >
99
- <FormItem name="motionTest" controlAfterInit initialValue={"1234134"}>
100
- <InputWrapper>
101
- <Input placeholder="Motion Test" style={{ width: 200 }} />
102
- </InputWrapper>
103
- </FormItem>
104
- </motion.div>
105
- <FormList
106
- initialValues={[
107
- {
108
- el: "sdfsdf",
109
- d: { child: "Test Child" },
110
- },
111
- ]}
112
- name="arr"
113
- >
114
- {(fields, { add, remove, move }) => (
115
- <div>
116
- {fields.map((field, index) => (
117
- <div key={field.key} style={{ marginBottom: 8 }}>
118
- <FormItem name={`${field.name}.el`} initialValue={"Chém gió"}>
119
- <InputWrapper>
120
- <Input placeholder="Item value" style={{ width: 200 }} />
121
- </InputWrapper>
122
- </FormItem>
123
- <FormItem
124
- name={`${field.name}.d.child`}
125
- initialValue={"Con của item"}
126
- >
127
- <InputWrapper>
128
- <Input placeholder="Item value" style={{ width: 200 }} />
129
- </InputWrapper>
130
- </FormItem>
131
- <Button
132
- onClick={() => remove({ index })}
133
- style={{ marginLeft: 8 }}
134
- >
135
- Remove
136
- </Button>
137
- {index > 0 && (
138
- <Button
139
- onClick={() => move({ from: index, to: index - 1 })}
140
- style={{ marginLeft: 8 }}
141
- >
142
- Move Up
143
- </Button>
144
- )}
145
- {index < fields.length - 1 && (
146
- <Button
147
- onClick={() => move({ from: index, to: index + 1 })}
148
- style={{ marginLeft: 8 }}
149
- >
150
- Move Down
151
- </Button>
152
- )}
153
- </div>
154
- ))}
155
- <Button onClick={() => add(fields.length)}>Add Item</Button>
156
- </div>
157
- )}
158
- </FormList>
159
- <TestDialog />
160
- <Button
161
- onClick={() => {
162
- form?.setFieldValue("arr", [
163
- { el: "Set Item 1" },
164
- { el: "Set Item 2" },
165
- { el: "Set Item 3" },
166
- ]);
167
- }}
168
- >
169
- Test set array list value
170
- </Button>
171
- <motion.div
172
- initial={{ opacity: 0 }}
173
- animate={{ opacity: 1 }}
174
- exit={{ opacity: 0 }}
175
- transition={{ duration: 1.5 }}
176
- >
177
- <Form.Item
178
- valuePropName="checked"
179
- getValueFromEvent={(_, checked) => checked}
180
- name="checkControlledAfterInit"
181
- controlAfterInit={true}
182
- initialValue={true}
183
- hidden
184
- >
185
- <Checkbox />
186
- </Form.Item>
187
- </motion.div>
188
- <TestFormWatch />
189
- <Button
190
- onClick={() => {
191
- const current = form?.getFieldValue("checkControlledAfterInit");
192
- console.log("Toggle controlled after init: ", current);
193
- form?.setFieldValue("checkControlledAfterInit", !current);
194
- }}
195
- >
196
- Toggle
197
- </Button>
198
- <Button htmlType="submit">Submit</Button>
199
- <Button
200
- onClick={() => {
201
- form?.resetFields?.();
202
- }}
203
- >
204
- Reset
205
- </Button>
206
- </Form>
33
+ <CommonTest />
34
+ {/* <Form formName="TestDialog" >
35
+ <TestDialog />
36
+ </Form> */}
207
37
 
208
38
  <TestListener />
39
+ <TestWrapperFormItem />
209
40
  </div>
210
41
  );
211
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;
@@ -16,10 +17,129 @@ export interface FormItemProps {
16
17
  controlAfterInit?: boolean;
17
18
  hidden?: boolean;
18
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);
19
83
  }
20
84
 
21
- export default function FormItem({
85
+ function FormItemWithWrapper<TComponent extends FC<any>>({
22
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,
23
143
  name,
24
144
  formName,
25
145
  initialValue,
@@ -30,10 +150,12 @@ export default function FormItem({
30
150
  controlAfterInit = false,
31
151
  hidden,
32
152
  collectOnHidden,
33
- }: FormItemProps) {
153
+ ...props
154
+ }: FormItemProps<TCustomWrapper>) {
155
+ const [formItemId] = useState(externalFormItemId ?? v4());
156
+
34
157
  const elRef = useRef<any>(null);
35
158
 
36
- const [formItemId] = useState(externalFormItemId ?? v4());
37
159
  const {
38
160
  value,
39
161
  onChange,
@@ -54,60 +176,52 @@ export default function FormItem({
54
176
  hidden,
55
177
  collectOnHidden,
56
178
  });
57
- // console.log("re-render", formName, name);
58
-
59
- // useEffect(() => {
60
- // console.log({ value });
61
- // }, [value]);
62
179
 
63
- // useEffect(() => {
64
- // console.log("isInitied changed: ", {
65
- // isInitied,
66
- // name,
67
- // value,
68
- // controlAfterInit,
69
- // });
70
- // }, [isInitied]);
71
180
  return (
72
181
  <Fragment
73
- key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`}
182
+ key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${externalFormItemId}`}
74
183
  >
75
- {!hidden && children
76
- ? cloneElement(children, {
77
- name,
78
- // ref: inputRef,
79
- [valuePropName]: value,
80
- onChange: (...args: any[]) => {
81
- let val = args[0];
82
- if (
83
- getValueFromEvent &&
84
- typeof getValueFromEvent === "function"
85
- ) {
86
- val = getValueFromEvent(...args);
87
- } else {
88
- const e = args[0];
89
- if (e && e.target) {
90
- val = e.target.value;
91
- }
92
- }
93
- onChange(val);
94
- },
95
- // onFocus: () => {
96
- // setIsTouched(true);
97
- // },
98
- // isTouched: isTouched,
99
- isDirty: isDirty,
100
- // errors: errors,
101
- // formState,
102
-
103
- errors,
104
- onFocus,
105
- validateState: state,
106
- ref: elRef,
107
- submitState,
108
- isTouched: isTouched,
109
- } as any)
110
- : 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}
111
225
  </Fragment>
112
226
  );
113
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,
@@ -75,6 +75,9 @@ export default function useFormItemControl<T = any>({
75
75
  // getFormState,
76
76
  isStateInitied,
77
77
  submitState,
78
+ getListener,
79
+ revokeListener,
80
+ clearObjKeyItem,
78
81
  } = useFormStore(
79
82
  useShallow((state) => {
80
83
  return {
@@ -94,6 +97,9 @@ export default function useFormItemControl<T = any>({
94
97
  state.formStates?.[
95
98
  formName || form?.formName || contextForm?.formName
96
99
  ]?.submitState,
100
+ getListener: state.getListener,
101
+ revokeListener: state.revokeListener,
102
+ clearObjKeyItem: state.clearObjKeyItem,
97
103
  };
98
104
  }),
99
105
  );
@@ -566,7 +572,8 @@ export default function useFormItemControl<T = any>({
566
572
  }
567
573
  }, [isStateInitied]);
568
574
 
569
- useEffect(() => {
575
+ useLayoutEffect(() => {
576
+ console.log("Component mount: ", name, formItemId, listener);
570
577
  if (!listener) {
571
578
  setListener({
572
579
  onChange,
@@ -584,12 +591,12 @@ export default function useFormItemControl<T = any>({
584
591
  }
585
592
 
586
593
  return () => {
587
- if (listener) {
588
- setCleanUpStack({
589
- name: listener.name,
590
- itemKey: listener.formItemId,
591
- });
592
- }
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
+ });
593
600
  };
594
601
 
595
602
  // return () => {
@@ -598,15 +605,15 @@ export default function useFormItemControl<T = any>({
598
605
  // };
599
606
  }, []);
600
607
 
601
- // useEffect(() => {
602
- // if (listener) {
603
- // setListener({
604
- // formItemId,
605
- // hidden,
606
- // collectOnHidden,
607
- // });
608
- // }
609
- // }, [hidden, collectOnHidden]);
608
+ useEffect(() => {
609
+ if (listener) {
610
+ setListener({
611
+ formItemId,
612
+ hidden,
613
+ collectOnHidden,
614
+ });
615
+ }
616
+ }, [hidden, collectOnHidden]);
610
617
 
611
618
  useEffect(() => {
612
619
  if (listener) {
@@ -35,24 +35,28 @@ export default function useFormListControl<T = any>({
35
35
  const [listFormInitValues, setListFormInitValues] = useState<
36
36
  any[] | undefined
37
37
  >(undefined);
38
- const { clearCacheData, setCacheData, setListener, getListener } =
39
- useFormStore(
40
- useShallow((state) => ({
41
- // Cache
42
- cacheData: state.cacheData,
43
- clearCacheData: state.clearCacheData,
44
- setCacheData: state.setCacheData,
45
-
46
- // Listener
47
- setListener: state.setListener,
48
- getListener: state.getListener,
49
- })),
50
- );
51
- const { setCleanUpStack } = useFormStore(
38
+ const {
39
+ clearCacheData,
40
+ setCacheData,
41
+ setListener,
42
+ getListener,
43
+ clearArrItems,
44
+ } = useFormStore(
52
45
  useShallow((state) => ({
53
- setCleanUpStack: state.setCleanUpStack,
46
+ // Cache
47
+ cacheData: state.cacheData,
48
+ clearCacheData: state.clearCacheData,
49
+ setCacheData: state.setCacheData,
50
+
51
+ // Listener
52
+ setListener: state.setListener,
53
+ getListener: state.getListener,
54
+
55
+ // Clear Arr Items
56
+ clearArrItems: state.clearArrItems,
54
57
  })),
55
58
  );
59
+
56
60
  const { initValue: internalInitValue, formState } = useFormStore(
57
61
  useShallow((state) => {
58
62
  // console.log(
@@ -129,22 +133,15 @@ export default function useFormListControl<T = any>({
129
133
 
130
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
131
135
  if (startRemoveIndex > 0) {
132
- Array.from(Array(startRemoveIndex))
133
- .map((_, index) => {
134
- return getNewValueCache.length + index;
135
- })
136
- .forEach((index) => {
137
- // console.log("Data for cleanup arr element: ", {
138
- // formName: formName || form?.formName || contextForm?.formName,
139
- // name: `${name}.${index}`,
140
- // type: "array",
141
- // });
142
- setCleanUpStack({
143
- formName: formName || form?.formName || contextForm?.formName,
144
- name: `${name}.${index}`,
145
- type: "array",
146
- });
147
- });
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);
148
145
  }
149
146
 
150
147
  console.log("Set cache data for form list: ", {
@@ -14,7 +14,6 @@ import type { ComponentType, FormHTMLAttributes, ReactNode } from "react";
14
14
  import { createContext, useContext, useEffect, useState } from "react";
15
15
  import { flushSync } from "react-dom";
16
16
  import { useShallow } from "zustand/react/shallow"; // Import useShallow
17
- import FormCleanUp from "../components/Form/FormCleanUp";
18
17
  import FormItem from "../components/Form/FormItem";
19
18
  import { SUBMIT_STATE } from "../constants/form";
20
19
  import { ListenerItem, SubmitProps, useFormStore } from "../stores/formStore";
@@ -492,7 +491,6 @@ export default function Form<T = any>({
492
491
  {children}
493
492
  </form>
494
493
  )}
495
- <FormCleanUp />
496
494
  </FormContext.Provider>
497
495
  );
498
496
  }
@@ -185,6 +185,30 @@ const createFormStoreSlice = (storeSet: any, storeGet: any, api: any) => ({
185
185
  );
186
186
  },
187
187
 
188
+ clearArrItems(clearItems: { formName: string; name: string }[]) {
189
+ return storeSet(
190
+ produce<any>((state: any) => {
191
+ const oldValues = state.forms;
192
+
193
+ console.log("Clear item array values: ", clearItems);
194
+
195
+ clearItems.forEach(({ formName, name }) => {
196
+ const arrPath = name.split(".").slice(0, -1).join(".");
197
+
198
+ const elPath = Number(name.split(".").slice(-1).join(""));
199
+
200
+ const getArrItem = get(oldValues, `${formName}.${arrPath}`);
201
+
202
+ if (isNumber(elPath)) {
203
+ getArrItem.splice(elPath, 1);
204
+ }
205
+ });
206
+
207
+ // console.log(JSON.parse(JSON.stringify(oldValues)));
208
+ }),
209
+ );
210
+ },
211
+
188
212
  setFormState({ formName, isInitied, submitState }) {
189
213
  return storeSet(
190
214
  produce<any>((state: any) => {
@@ -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;