react-form-manage 1.0.8-beta.7 → 1.0.8
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 +173 -4
- package/README.md +8 -4
- package/dist/components/Form/FormCleanUp.js +3 -3
- package/dist/components/Form/FormItem.d.ts +10 -4
- package/dist/components/Form/FormItem.js +52 -14
- package/dist/components/Form/FormList.d.ts +2 -2
- package/dist/components/Form/FormList.js +2 -2
- package/dist/constants/form.d.ts +1 -1
- package/dist/hooks/useFormItemControl.d.ts +8 -3
- package/dist/hooks/useFormItemControl.js +64 -28
- package/dist/hooks/useFormListControl.d.ts +2 -1
- package/dist/hooks/useFormListControl.js +85 -19
- package/dist/index.cjs.d.ts +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.esm.d.ts +1 -0
- package/dist/index.js +4 -2
- package/dist/providers/Form.d.ts +15 -2
- package/dist/providers/Form.js +226 -41
- package/dist/stores/formStore.d.ts +44 -4
- package/dist/stores/formStore.js +42 -7
- package/dist/test/CommonTest.d.ts +3 -0
- package/dist/test/CommonTest.js +49 -0
- package/dist/test/TestDialog.d.ts +3 -0
- package/dist/test/TestDialog.js +21 -0
- package/dist/test/TestListener.d.ts +3 -0
- package/dist/test/TestListener.js +17 -0
- package/dist/test/TestNotFormWrapper.d.ts +3 -0
- package/dist/test/TestNotFormWrapper.js +15 -0
- package/dist/test/TestSelect.d.ts +6 -0
- package/dist/test/TestSelect.js +24 -0
- package/dist/test/TestWatchNormalize.d.ts +3 -0
- package/dist/test/TestWatchNormalize.js +23 -0
- package/dist/test/TestWrapperFormItem.d.ts +3 -0
- package/dist/test/TestWrapperFormItem.js +13 -0
- package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.d.ts +21 -0
- package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.js +61 -0
- package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.d.ts +16 -0
- package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.js +18 -0
- package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.d.ts +21 -0
- package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.js +33 -0
- package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.d.ts +21 -0
- package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.js +26 -0
- package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.d.ts +20 -0
- package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.js +32 -0
- package/dist/test/testSetValue/TestCase5_FormListRemovedItems.d.ts +22 -0
- package/dist/test/testSetValue/TestCase5_FormListRemovedItems.js +29 -0
- package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.d.ts +28 -0
- package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.js +36 -0
- package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.d.ts +17 -0
- package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.js +33 -0
- package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.d.ts +27 -0
- package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.js +57 -0
- package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.d.ts +25 -0
- package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.js +46 -0
- package/dist/test/testSetValue/index.d.ts +2 -0
- package/dist/test/testSetValue/index.js +28 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/public.d.ts +1 -1
- package/dist/utils/obj.util.d.ts +29 -1
- package/dist/utils/obj.util.js +59 -5
- package/package.json +2 -1
- package/src/App.tsx +39 -156
- package/src/DEEP_TRIGGER_LOGIC.md +573 -0
- package/src/components/Form/FormCleanUp.tsx +4 -8
- package/src/components/Form/FormItem.tsx +174 -57
- package/src/components/Form/FormList.tsx +17 -4
- package/src/constants/form.ts +1 -1
- package/src/hooks/useFormItemControl.ts +78 -32
- package/src/hooks/useFormListControl.ts +133 -43
- package/src/index.ts +25 -13
- package/src/main.tsx +6 -1
- package/src/providers/Form.tsx +454 -26
- package/src/stores/formStore.ts +363 -283
- package/src/test/CommonTest.tsx +177 -0
- package/src/test/TestDialog.tsx +52 -0
- package/src/test/TestListener.tsx +21 -0
- package/src/test/TestNotFormWrapper.tsx +43 -0
- package/src/test/TestSelect.tsx +38 -0
- package/src/test/TestWatchNormalize.tsx +32 -0
- package/src/test/TestWrapperFormItem.tsx +34 -0
- package/src/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.tsx +203 -0
- package/src/test/testSetValue/TestCase1_PlainObjectToPrimitives.tsx +72 -0
- package/src/test/testSetValue/TestCase2_PlainObjectToFormList.tsx +114 -0
- package/src/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.tsx +99 -0
- package/src/test/testSetValue/TestCase4_PlainObjectRemovedFields.tsx +112 -0
- package/src/test/testSetValue/TestCase5_FormListRemovedItems.tsx +119 -0
- package/src/test/testSetValue/TestCase6_NestedFormListRemoved.tsx +185 -0
- package/src/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.tsx +110 -0
- package/src/test/testSetValue/TestCase8_SetFieldValues_NestedObject.tsx +162 -0
- package/src/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.tsx +169 -0
- package/src/test/testSetValue/index.tsx +100 -0
- package/src/types/index.ts +1 -1
- package/src/types/public.ts +1 -1
- package/src/utils/obj.util.ts +153 -13
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@mui/material";
|
|
3
|
+
import { Button, Input, Typography } from "antd";
|
|
4
|
+
import FormList from "../../components/Form/FormList";
|
|
5
|
+
import Form from "../../providers/Form";
|
|
6
|
+
function TestCase5_FormListRemovedItems({}) {
|
|
7
|
+
const [form] = Form.useForm("testCase5");
|
|
8
|
+
const handleSetInitial = () => {
|
|
9
|
+
form == null ? void 0 : form.setFieldValue("items", [{ name: "Item A" }, { name: "Item B" }, { name: "Item C" }], { deepTrigger: true });
|
|
10
|
+
};
|
|
11
|
+
const handleRemoveItems = () => {
|
|
12
|
+
form == null ? void 0 : form.setFieldValue("items", [{ name: "Item A" }], { deepTrigger: true });
|
|
13
|
+
};
|
|
14
|
+
const handleRestoreItems = () => {
|
|
15
|
+
form == null ? void 0 : form.setFieldValue("items", [{ name: "Item A" }, { name: "Item B" }], {
|
|
16
|
+
deepTrigger: true
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
return _jsxs(Box, { sx: { p: 2 }, children: [_jsx(Typography.Title, { level: 4, children: "Test Case 5: FormList - Removed Items" }), _jsx(Typography.Paragraph, { children: "Test edge case: khi FormList items b\u1ECB x\xF3a, listeners ph\u1EA3i \u0111\u01B0\u1EE3c trigger v\u1EDBi undefined" }), _jsxs(Form, { formName: "testCase5", children: [_jsx(FormList, { name: "items", children: (fields, { add, remove }) => _jsxs(Box, { children: [fields.map((field, index) => _jsxs(Box, { sx: {
|
|
20
|
+
border: "1px solid #d9d9d9",
|
|
21
|
+
p: 2,
|
|
22
|
+
mb: 2,
|
|
23
|
+
borderRadius: 1
|
|
24
|
+
}, children: [_jsxs(Typography.Text, { strong: true, children: ["Item ", index + 1] }), _jsx(Form.Item, { name: `items.${index}.name`, label: "Name", children: _jsx(Input, { placeholder: `Item ${index + 1} name` }) }), _jsx(Button, { danger: true, onClick: () => remove({ key: field.key }), children: "Remove" })] }, field.key)), _jsx(Button, { type: "dashed", onClick: () => add(), children: "Add Item" })] }) }), _jsxs(Box, { sx: { mt: 2, display: "flex", gap: 2, flexDirection: "column" }, children: [_jsx(Button, { type: "primary", onClick: handleSetInitial, children: "1. Set Initial (3 Items)" }), _jsx(Button, { danger: true, onClick: handleRemoveItems, children: "2. Remove Items (Keep 1)" }), _jsx(Button, { onClick: handleRestoreItems, children: "3. Restore Items (2 Items)" })] }), _jsxs(Box, { sx: { mt: 2, p: 2, bgcolor: "#f0f0f0", borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Expected Behavior:" }), _jsx(Typography.Paragraph, { children: 'Khi click "Remove Items", items.1 v\xE0 items.2 listeners ph\u1EA3i nh\u1EADn undefined' })] })] })] });
|
|
25
|
+
}
|
|
26
|
+
var stdin_default = TestCase5_FormListRemovedItems;
|
|
27
|
+
export {
|
|
28
|
+
stdin_default as default
|
|
29
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type Props = {};
|
|
2
|
+
/**
|
|
3
|
+
* Test Case 6: Array Non-Listener → Nested FormList Removed (Complex Edge Case)
|
|
4
|
+
*
|
|
5
|
+
* Cấu trúc:
|
|
6
|
+
* - data (array - NON-LISTENER)
|
|
7
|
+
* - data.0.name (string)
|
|
8
|
+
* - data.0.tags (FormList)
|
|
9
|
+
* - data.0.tags.0 (string)
|
|
10
|
+
* - data.0.tags.1 (string)
|
|
11
|
+
* - data.1.name (string)
|
|
12
|
+
* - data.1.tags (FormList)
|
|
13
|
+
*
|
|
14
|
+
* Test:
|
|
15
|
+
* 1. Set initial: [
|
|
16
|
+
* {name: "A", tags: ["tag1", "tag2"]},
|
|
17
|
+
* {name: "B", tags: ["tag3"]}
|
|
18
|
+
* ]
|
|
19
|
+
* 2. Set removed: [{name: "A", tags: ["tag1"]}] (data.1 bị xóa hoàn toàn)
|
|
20
|
+
*
|
|
21
|
+
* Kỳ vọng:
|
|
22
|
+
* - Trigger "data.0.tags" onArrayChange
|
|
23
|
+
* - Trigger "data.1" với undefined
|
|
24
|
+
* - Trigger "data.1.tags" với undefined (nested FormList cleanup!)
|
|
25
|
+
* - Trigger "data.1.tags.0" với undefined
|
|
26
|
+
*/
|
|
27
|
+
declare function TestCase6_NestedFormListRemoved({}: Props): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export default TestCase6_NestedFormListRemoved;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@mui/material";
|
|
3
|
+
import { Button, Input, Typography } from "antd";
|
|
4
|
+
import FormList from "../../components/Form/FormList";
|
|
5
|
+
import Form from "../../providers/Form";
|
|
6
|
+
function TestCase6_NestedFormListRemoved({}) {
|
|
7
|
+
const [form] = Form.useForm("testCase6");
|
|
8
|
+
const handleSetInitial = () => {
|
|
9
|
+
form == null ? void 0 : form.setFieldValue("data", [
|
|
10
|
+
{ name: "Group A", tags: ["tag1", "tag2"] },
|
|
11
|
+
{ name: "Group B", tags: ["tag3", "tag4"] }
|
|
12
|
+
], { deepTrigger: true });
|
|
13
|
+
};
|
|
14
|
+
const handleRemoveNestedArray = () => {
|
|
15
|
+
form == null ? void 0 : form.setFieldValue("data", [{ name: "Group A", tags: ["tag1"] }], {
|
|
16
|
+
deepTrigger: true
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const handleRestoreNested = () => {
|
|
20
|
+
form == null ? void 0 : form.setFieldValue("data", [
|
|
21
|
+
{ name: "Group A", tags: ["tag1", "tag2"] },
|
|
22
|
+
{ name: "Group B", tags: ["tag3"] }
|
|
23
|
+
], { deepTrigger: true });
|
|
24
|
+
};
|
|
25
|
+
return _jsxs(Box, { sx: { p: 2 }, children: [_jsx(Typography.Title, { level: 4, children: "Test Case 6: Nested FormList - Removed Items" }), _jsx(Typography.Paragraph, { children: "Test complex edge case: array non-listener v\u1EDBi nested FormList b\u1ECB x\xF3a" }), _jsxs(Form, { formName: "testCase6", children: [_jsxs(Box, { sx: { border: "2px solid #1890ff", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, style: { fontSize: 16 }, children: "Group 0" }), _jsx(Form.Item, { name: "data.0.name", label: "Group Name", children: _jsx(Input, { placeholder: "Group 0 name" }) }), _jsx(Typography.Text, { children: "Tags (FormList):" }), _jsx(FormList, { name: "data.0.tags", children: (fields, { add, remove }) => _jsxs(Box, { children: [fields.map((field, index) => _jsxs(Box, { sx: { display: "flex", gap: 1, mb: 1 }, children: [_jsx(Form.Item, { name: `data.0.tags.${index}`, children: _jsx(Input, { placeholder: `Tag ${index + 1}` }) }), _jsx(Button, { danger: true, size: "small", onClick: () => remove({ key: field.key }), children: "Remove" })] }, field.key)), _jsx(Button, { type: "dashed", size: "small", onClick: () => add(), children: "Add Tag" })] }) })] }), _jsxs(Box, { sx: { border: "2px solid #52c41a", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, style: { fontSize: 16 }, children: "Group 1" }), _jsx(Form.Item, { name: "data.1.name", label: "Group Name", children: _jsx(Input, { placeholder: "Group 1 name" }) }), _jsx(Typography.Text, { children: "Tags (FormList):" }), _jsx(FormList, { name: "data.1.tags", children: (fields, { add, remove }) => _jsxs(Box, { children: [fields.map((field, index) => _jsxs(Box, { sx: { display: "flex", gap: 1, mb: 1 }, children: [_jsx(Form.Item, { name: `data.1.tags.${index}`, children: _jsx(Input, { placeholder: `Tag ${index + 1}` }) }), _jsx(Button, { danger: true, size: "small", onClick: () => remove({ key: field.key }), children: "Remove" })] }, field.key)), _jsx(Button, { type: "dashed", size: "small", onClick: () => add(), children: "Add Tag" })] }) })] }), _jsxs(Box, { sx: { mt: 2, display: "flex", gap: 2, flexDirection: "column" }, children: [_jsx(Button, { type: "primary", onClick: handleSetInitial, children: "1. Set Initial (2 Groups with Tags)" }), _jsx(Button, { danger: true, onClick: handleRemoveNestedArray, children: "2. Remove Group 1 (Nested FormList)" }), _jsx(Button, { onClick: handleRestoreNested, children: "3. Restore Nested Structure" })] }), _jsxs(Box, { sx: {
|
|
26
|
+
mt: 2,
|
|
27
|
+
p: 2,
|
|
28
|
+
bgcolor: "#fff7e6",
|
|
29
|
+
border: "1px solid #ffa940",
|
|
30
|
+
borderRadius: 1
|
|
31
|
+
}, children: [_jsx(Typography.Text, { strong: true, children: "Expected Behavior:" }), _jsxs(Typography.Paragraph, { children: ['Khi click "Remove Group 1":', _jsx("br", {}), "- data.1 \u2192 undefined", _jsx("br", {}), "- data.1.tags (FormList) \u2192 undefined", _jsx("br", {}), "- data.1.tags.0, data.1.tags.1, ... \u2192 undefined"] })] })] })] });
|
|
32
|
+
}
|
|
33
|
+
var stdin_default = TestCase6_NestedFormListRemoved;
|
|
34
|
+
export {
|
|
35
|
+
stdin_default as default
|
|
36
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type Props = {};
|
|
2
|
+
/**
|
|
3
|
+
* Test Case 7: setFieldValues với Mixed Structure
|
|
4
|
+
*
|
|
5
|
+
* Cấu trúc:
|
|
6
|
+
* - name (string) - primitive
|
|
7
|
+
* - age (number) - primitive
|
|
8
|
+
* - tags (array) - sẽ trigger deepTrigger
|
|
9
|
+
*
|
|
10
|
+
* Test: setFieldValues({name: "John", age: 30, tags: ["tag1", "tag2"]})
|
|
11
|
+
* Kỳ vọng:
|
|
12
|
+
* - "name" listener.onChange("John")
|
|
13
|
+
* - "age" listener.onChange(30)
|
|
14
|
+
* - "tags" → handleDeepTriggerSet(["tag1", "tag2"])
|
|
15
|
+
*/
|
|
16
|
+
declare function TestCase7_SetFieldValues_MixedStructure({}: Props): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export default TestCase7_SetFieldValues_MixedStructure;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@mui/material";
|
|
3
|
+
import { Button, Input, Typography } from "antd";
|
|
4
|
+
import Form from "../../providers/Form";
|
|
5
|
+
function TestCase7_SetFieldValues_MixedStructure({}) {
|
|
6
|
+
const [form] = Form.useForm("testCase7");
|
|
7
|
+
const handleTestSetFieldValues = () => {
|
|
8
|
+
form == null ? void 0 : form.setFieldValues({
|
|
9
|
+
name: "John Doe",
|
|
10
|
+
age: 30,
|
|
11
|
+
tags: ["javascript", "typescript", "react"]
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
const handleTestWithOptions = () => {
|
|
15
|
+
form == null ? void 0 : form.setFieldValues({
|
|
16
|
+
name: "Jane Doe",
|
|
17
|
+
age: 25,
|
|
18
|
+
tags: ["vue", "angular"]
|
|
19
|
+
}, { notTriggerDirty: true });
|
|
20
|
+
};
|
|
21
|
+
const handleClear = () => {
|
|
22
|
+
form == null ? void 0 : form.setFieldValues({
|
|
23
|
+
name: "",
|
|
24
|
+
age: void 0,
|
|
25
|
+
tags: []
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
return _jsxs(Box, { sx: { p: 2 }, children: [_jsx(Typography.Title, { level: 4, children: "Test Case 7: setFieldValues - Mixed Structure" }), _jsx(Typography.Paragraph, { children: "Test setFieldValues v\u1EDBi primitives + array (array s\u1EBD d\xF9ng deepTrigger)" }), _jsxs(Form, { formName: "testCase7", children: [_jsx(Form.Item, { name: "name", label: "Name", children: _jsx(Input, { placeholder: "Enter name" }) }), _jsx(Form.Item, { name: "age", label: "Age", children: _jsx(Input, { type: "number", placeholder: "Enter age" }) }), _jsxs(Box, { sx: { border: "1px solid #d9d9d9", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Tags (Array - Deep Trigger)" }), _jsx(Form.Item, { name: "tags.0", children: _jsx(Input, { placeholder: "Tag 1" }) }), _jsx(Form.Item, { name: "tags.1", children: _jsx(Input, { placeholder: "Tag 2" }) }), _jsx(Form.Item, { name: "tags.2", children: _jsx(Input, { placeholder: "Tag 3" }) })] }), _jsxs(Box, { sx: { mt: 2, display: "flex", gap: 2 }, children: [_jsx(Button, { type: "primary", onClick: handleTestSetFieldValues, children: "Test setFieldValues" }), _jsx(Button, { onClick: handleTestWithOptions, children: "Test with Options" }), _jsx(Button, { danger: true, onClick: handleClear, children: "Clear All" })] }), _jsxs(Box, { sx: { mt: 2, p: 2, bgcolor: "#f0f0f0", borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Expected Behavior:" }), _jsxs(Typography.Paragraph, { children: ["- name, age: trigger onChange tr\u1EF1c ti\u1EBFp", _jsx("br", {}), "- tags: g\u1ECDi handleDeepTriggerSet (\u0111\u1EC7 quy trigger t\u1EA5t c\u1EA3 tags items)"] })] })] })] });
|
|
29
|
+
}
|
|
30
|
+
var stdin_default = TestCase7_SetFieldValues_MixedStructure;
|
|
31
|
+
export {
|
|
32
|
+
stdin_default as default
|
|
33
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type Props = {};
|
|
2
|
+
/**
|
|
3
|
+
* Test Case 8: setFieldValues với Nested Object + Array
|
|
4
|
+
*
|
|
5
|
+
* Cấu trúc:
|
|
6
|
+
* - user.name (string)
|
|
7
|
+
* - user.email (string)
|
|
8
|
+
* - user.preferences.theme (string)
|
|
9
|
+
* - user.preferences.language (string)
|
|
10
|
+
* - user.hobbies (array) - sẽ trigger deepTrigger
|
|
11
|
+
*
|
|
12
|
+
* Test: setFieldValues({
|
|
13
|
+
* user: {
|
|
14
|
+
* name: "John",
|
|
15
|
+
* email: "john@example.com",
|
|
16
|
+
* preferences: { theme: "dark", language: "en" },
|
|
17
|
+
* hobbies: ["reading", "coding"]
|
|
18
|
+
* }
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* Kỳ vọng:
|
|
22
|
+
* - getAllPathsStopAtArray sẽ dừng tại array "user.hobbies"
|
|
23
|
+
* - Tất cả primitive paths được trigger onChange
|
|
24
|
+
* - "user.hobbies" được gọi handleDeepTriggerSet
|
|
25
|
+
*/
|
|
26
|
+
declare function TestCase8_SetFieldValues_NestedObject({}: Props): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export default TestCase8_SetFieldValues_NestedObject;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@mui/material";
|
|
3
|
+
import { Button, Input, Typography } from "antd";
|
|
4
|
+
import Form from "../../providers/Form";
|
|
5
|
+
function TestCase8_SetFieldValues_NestedObject({}) {
|
|
6
|
+
const [form] = Form.useForm("testCase8");
|
|
7
|
+
const handleTestSetFieldValues = () => {
|
|
8
|
+
form == null ? void 0 : form.setFieldValues({
|
|
9
|
+
user: {
|
|
10
|
+
name: "John Doe",
|
|
11
|
+
email: "john@example.com",
|
|
12
|
+
preferences: {
|
|
13
|
+
theme: "dark",
|
|
14
|
+
language: "en"
|
|
15
|
+
},
|
|
16
|
+
hobbies: ["reading", "coding", "gaming"]
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const handleTestPartialUpdate = () => {
|
|
21
|
+
form == null ? void 0 : form.setFieldValues({
|
|
22
|
+
user: {
|
|
23
|
+
name: "Jane Doe",
|
|
24
|
+
email: "jane@example.com",
|
|
25
|
+
preferences: {
|
|
26
|
+
theme: "light",
|
|
27
|
+
language: "vi"
|
|
28
|
+
},
|
|
29
|
+
hobbies: ["painting"]
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const handleClear = () => {
|
|
34
|
+
form == null ? void 0 : form.setFieldValues({
|
|
35
|
+
user: {
|
|
36
|
+
name: "",
|
|
37
|
+
email: "",
|
|
38
|
+
preferences: {
|
|
39
|
+
theme: "",
|
|
40
|
+
language: ""
|
|
41
|
+
},
|
|
42
|
+
hobbies: []
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
return _jsxs(Box, { sx: { p: 2 }, children: [_jsx(Typography.Title, { level: 4, children: "Test Case 8: setFieldValues - Nested Object + Array" }), _jsx(Typography.Paragraph, { children: "Test setFieldValues v\u1EDBi deep nested object v\xE0 array" }), _jsxs(Form, { formName: "testCase8", children: [_jsxs(Box, { sx: { border: "1px solid #1890ff", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "User Info" }), _jsx(Form.Item, { name: "user.name", label: "Name", children: _jsx(Input, { placeholder: "Enter name" }) }), _jsx(Form.Item, { name: "user.email", label: "Email", children: _jsx(Input, { placeholder: "Enter email" }) })] }), _jsxs(Box, { sx: { border: "1px solid #52c41a", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Preferences" }), _jsx(Form.Item, { name: "user.preferences.theme", label: "Theme", children: _jsx(Input, { placeholder: "Enter theme" }) }), _jsx(Form.Item, { name: "user.preferences.language", label: "Language", children: _jsx(Input, { placeholder: "Enter language" }) })] }), _jsxs(Box, { sx: { border: "1px solid #faad14", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Hobbies (Array - Deep Trigger)" }), _jsx(Form.Item, { name: "user.hobbies.0", children: _jsx(Input, { placeholder: "Hobby 1" }) }), _jsx(Form.Item, { name: "user.hobbies.1", children: _jsx(Input, { placeholder: "Hobby 2" }) }), _jsx(Form.Item, { name: "user.hobbies.2", children: _jsx(Input, { placeholder: "Hobby 3" }) })] }), _jsxs(Box, { sx: { mt: 2, display: "flex", gap: 2, flexDirection: "column" }, children: [_jsx(Button, { type: "primary", onClick: handleTestSetFieldValues, children: "1. Set Full Structure (3 hobbies)" }), _jsx(Button, { onClick: handleTestPartialUpdate, children: "2. Partial Update (1 hobby - cleanup test)" }), _jsx(Button, { danger: true, onClick: handleClear, children: "3. Clear All" })] }), _jsxs(Box, { sx: {
|
|
47
|
+
mt: 2,
|
|
48
|
+
p: 2,
|
|
49
|
+
bgcolor: "#e6f7ff",
|
|
50
|
+
border: "1px solid #1890ff",
|
|
51
|
+
borderRadius: 1
|
|
52
|
+
}, children: [_jsx(Typography.Text, { strong: true, children: "Expected Behavior:" }), _jsxs(Typography.Paragraph, { children: ["- Primitives (name, email, theme, language): trigger onChange", _jsx("br", {}), "- user.hobbies: g\u1ECDi handleDeepTriggerSet", _jsx("br", {}), "- Khi update v\u1EDBi 1 hobby, hobbies.1 v\xE0 hobbies.2 nh\u1EADn undefined"] })] })] })] });
|
|
53
|
+
}
|
|
54
|
+
var stdin_default = TestCase8_SetFieldValues_NestedObject;
|
|
55
|
+
export {
|
|
56
|
+
stdin_default as default
|
|
57
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type Props = {};
|
|
2
|
+
/**
|
|
3
|
+
* Test Case 9: setFieldValues với Multiple Arrays
|
|
4
|
+
*
|
|
5
|
+
* Cấu trúc:
|
|
6
|
+
* - title (string)
|
|
7
|
+
* - tags (array) - deepTrigger
|
|
8
|
+
* - items (FormList array) - deepTrigger
|
|
9
|
+
* - categories (array) - deepTrigger
|
|
10
|
+
*
|
|
11
|
+
* Test: setFieldValues({
|
|
12
|
+
* title: "My Post",
|
|
13
|
+
* tags: ["js", "react"],
|
|
14
|
+
* items: [{name: "Item 1"}],
|
|
15
|
+
* categories: ["tech", "web"]
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* Kỳ vọng:
|
|
19
|
+
* - "title" trigger onChange
|
|
20
|
+
* - "tags" gọi handleDeepTriggerSet
|
|
21
|
+
* - "items" gọi handleDeepTriggerSet (FormList)
|
|
22
|
+
* - "categories" gọi handleDeepTriggerSet
|
|
23
|
+
*/
|
|
24
|
+
declare function TestCase9_SetFieldValues_MultipleArrays({}: Props): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export default TestCase9_SetFieldValues_MultipleArrays;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@mui/material";
|
|
3
|
+
import { Button, Input, Typography } from "antd";
|
|
4
|
+
import FormList from "../../components/Form/FormList";
|
|
5
|
+
import Form from "../../providers/Form";
|
|
6
|
+
function TestCase9_SetFieldValues_MultipleArrays({}) {
|
|
7
|
+
const [form] = Form.useForm("testCase9");
|
|
8
|
+
const handleTestSetFieldValues = () => {
|
|
9
|
+
form == null ? void 0 : form.setFieldValues({
|
|
10
|
+
title: "My Blog Post",
|
|
11
|
+
tags: ["javascript", "react"],
|
|
12
|
+
items: [
|
|
13
|
+
{ name: "Item 1", value: 100 },
|
|
14
|
+
{ name: "Item 2", value: 200 }
|
|
15
|
+
],
|
|
16
|
+
categories: ["technology", "web development"]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const handleTestReduceArrays = () => {
|
|
20
|
+
form == null ? void 0 : form.setFieldValues({
|
|
21
|
+
title: "Updated Post",
|
|
22
|
+
tags: ["vue"],
|
|
23
|
+
items: [{ name: "Item 1", value: 150 }],
|
|
24
|
+
categories: ["tech"]
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const handleClear = () => {
|
|
28
|
+
form == null ? void 0 : form.setFieldValues({
|
|
29
|
+
title: "",
|
|
30
|
+
tags: [],
|
|
31
|
+
items: [],
|
|
32
|
+
categories: []
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
return _jsxs(Box, { sx: { p: 2 }, children: [_jsx(Typography.Title, { level: 4, children: "Test Case 9: setFieldValues - Multiple Arrays" }), _jsx(Typography.Paragraph, { children: "Test setFieldValues v\u1EDBi nhi\u1EC1u arrays kh\xE1c nhau (FormList v\xE0 non-FormList)" }), _jsxs(Form, { formName: "testCase9", children: [_jsx(Form.Item, { name: "title", label: "Title", children: _jsx(Input, { placeholder: "Enter title" }) }), _jsxs(Box, { sx: { border: "1px solid #1890ff", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Tags (Simple Array)" }), _jsx(Form.Item, { name: "tags.0", children: _jsx(Input, { placeholder: "Tag 1" }) }), _jsx(Form.Item, { name: "tags.1", children: _jsx(Input, { placeholder: "Tag 2" }) })] }), _jsxs(Box, { sx: { border: "1px solid #52c41a", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Items (FormList)" }), _jsx(FormList, { name: "items", children: (fields, { add, remove }) => _jsxs(Box, { children: [fields.map((field, index) => _jsxs(Box, { sx: { display: "flex", gap: 1, mb: 1 }, children: [_jsx(Form.Item, { name: `items.${index}.name`, children: _jsx(Input, { placeholder: "Name" }) }), _jsx(Form.Item, { name: `items.${index}.value`, children: _jsx(Input, { type: "number", placeholder: "Value" }) }), _jsx(Button, { danger: true, size: "small", onClick: () => remove({ key: field.key }), children: "Remove" })] }, field.key)), _jsx(Button, { type: "dashed", size: "small", onClick: () => add(), children: "Add Item" })] }) })] }), _jsxs(Box, { sx: { border: "1px solid #faad14", p: 2, mb: 2, borderRadius: 1 }, children: [_jsx(Typography.Text, { strong: true, children: "Categories (Simple Array)" }), _jsx(Form.Item, { name: "categories.0", children: _jsx(Input, { placeholder: "Category 1" }) }), _jsx(Form.Item, { name: "categories.1", children: _jsx(Input, { placeholder: "Category 2" }) })] }), _jsxs(Box, { sx: { mt: 2, display: "flex", gap: 2, flexDirection: "column" }, children: [_jsx(Button, { type: "primary", onClick: handleTestSetFieldValues, children: "1. Set All Arrays (2 items each)" }), _jsx(Button, { onClick: handleTestReduceArrays, children: "2. Reduce Arrays (1 item each)" }), _jsx(Button, { danger: true, onClick: handleClear, children: "3. Clear All" })] }), _jsxs(Box, { sx: {
|
|
36
|
+
mt: 2,
|
|
37
|
+
p: 2,
|
|
38
|
+
bgcolor: "#fffbe6",
|
|
39
|
+
border: "1px solid #faad14",
|
|
40
|
+
borderRadius: 1
|
|
41
|
+
}, children: [_jsx(Typography.Text, { strong: true, children: "Expected Behavior:" }), _jsxs(Typography.Paragraph, { children: ["- title: trigger onChange", _jsx("br", {}), "- tags, items, categories: t\u1EA5t c\u1EA3 g\u1ECDi handleDeepTriggerSet", _jsx("br", {}), "- Khi reduce arrays: items b\u1ECB x\xF3a nh\u1EADn undefined (cleanup)"] })] })] })] });
|
|
42
|
+
}
|
|
43
|
+
var stdin_default = TestCase9_SetFieldValues_MultipleArrays;
|
|
44
|
+
export {
|
|
45
|
+
stdin_default as default
|
|
46
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Tab, Tabs } from "@mui/material";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import TestCase10_SetFieldValues_ComplexNested from "./TestCase10_SetFieldValues_ComplexNested";
|
|
5
|
+
import TestCase1_PlainObjectToPrimitives from "./TestCase1_PlainObjectToPrimitives";
|
|
6
|
+
import TestCase2_PlainObjectToFormList from "./TestCase2_PlainObjectToFormList";
|
|
7
|
+
import TestCase3_ArrayNonListenerToPrimitives from "./TestCase3_ArrayNonListenerToPrimitives";
|
|
8
|
+
import TestCase4_PlainObjectRemovedFields from "./TestCase4_PlainObjectRemovedFields";
|
|
9
|
+
import TestCase5_FormListRemovedItems from "./TestCase5_FormListRemovedItems";
|
|
10
|
+
import TestCase6_NestedFormListRemoved from "./TestCase6_NestedFormListRemoved";
|
|
11
|
+
import TestCase7_SetFieldValues_MixedStructure from "./TestCase7_SetFieldValues_MixedStructure";
|
|
12
|
+
import TestCase8_SetFieldValues_NestedObject from "./TestCase8_SetFieldValues_NestedObject";
|
|
13
|
+
import TestCase9_SetFieldValues_MultipleArrays from "./TestCase9_SetFieldValues_MultipleArrays";
|
|
14
|
+
function TabPanel(props) {
|
|
15
|
+
const { children, value, index, ...other } = props;
|
|
16
|
+
return _jsx("div", { role: "tabpanel", hidden: value !== index, id: `test-tabpanel-${index}`, "aria-labelledby": `test-tab-${index}`, ...other, children: value === index && _jsx(Box, { sx: { p: 3 }, children }) });
|
|
17
|
+
}
|
|
18
|
+
function TestSetValueIndex() {
|
|
19
|
+
const [value, setValue] = useState(0);
|
|
20
|
+
const handleChange = (event, newValue) => {
|
|
21
|
+
setValue(newValue);
|
|
22
|
+
};
|
|
23
|
+
return _jsxs(Box, { sx: { width: "100%" }, children: [_jsx(Box, { sx: { borderBottom: 1, borderColor: "divider" }, children: _jsxs(Tabs, { value, onChange: handleChange, "aria-label": "test cases tabs", variant: "scrollable", scrollButtons: "auto", children: [_jsx(Tab, { label: "Case 1: Plain Object" }), _jsx(Tab, { label: "Case 2: FormList Nested" }), _jsx(Tab, { label: "Case 3: Array Non-Listener" }), _jsx(Tab, { label: "Case 4: Removed Fields" }), _jsx(Tab, { label: "Case 5: FormList Removed" }), _jsx(Tab, { label: "Case 6: Nested Removed" }), _jsx(Tab, { label: "Case 7: setFieldValues Mixed" }), _jsx(Tab, { label: "Case 8: setFieldValues Nested" }), _jsx(Tab, { label: "Case 9: setFieldValues Arrays" }), _jsx(Tab, { label: "Case 10: setFieldValues Complex" })] }) }), _jsx(TabPanel, { value, index: 0, children: _jsx(TestCase1_PlainObjectToPrimitives, {}) }), _jsx(TabPanel, { value, index: 1, children: _jsx(TestCase2_PlainObjectToFormList, {}) }), _jsx(TabPanel, { value, index: 2, children: _jsx(TestCase3_ArrayNonListenerToPrimitives, {}) }), _jsx(TabPanel, { value, index: 3, children: _jsx(TestCase4_PlainObjectRemovedFields, {}) }), _jsx(TabPanel, { value, index: 4, children: _jsx(TestCase5_FormListRemovedItems, {}) }), _jsx(TabPanel, { value, index: 5, children: _jsx(TestCase6_NestedFormListRemoved, {}) }), _jsx(TabPanel, { value, index: 6, children: _jsx(TestCase7_SetFieldValues_MixedStructure, {}) }), _jsx(TabPanel, { value, index: 7, children: _jsx(TestCase8_SetFieldValues_NestedObject, {}) }), _jsx(TabPanel, { value, index: 8, children: _jsx(TestCase9_SetFieldValues_MultipleArrays, {}) }), _jsx(TabPanel, { value, index: 9, children: _jsx(TestCase10_SetFieldValues_ComplexNested, {}) })] });
|
|
24
|
+
}
|
|
25
|
+
var stdin_default = TestSetValueIndex;
|
|
26
|
+
export {
|
|
27
|
+
stdin_default as default
|
|
28
|
+
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./public";
|
package/dist/types/public.d.ts
CHANGED
package/dist/utils/obj.util.d.ts
CHANGED
|
@@ -1,2 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Lấy tất cả các path đến các leaf nodes (primitive values)
|
|
3
|
+
* Không bao gồm intermediate object/array paths
|
|
4
|
+
*
|
|
5
|
+
* Ví dụ: {user: {name: "John", age: 30}}
|
|
6
|
+
* Kết quả: ["user.name", "user.age"]
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAllNoneObjStringPath(value: any, prevPath?: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Lấy tất cả các path bao gồm cả intermediate containers (object/array level)
|
|
11
|
+
* Dùng để detect listener được đăng ký trên plain object, array, hoặc array item level
|
|
12
|
+
*
|
|
13
|
+
* Ví dụ: {user: {name: "John", age: 30}, items: [{id: 1}]}
|
|
14
|
+
* Kết quả: [
|
|
15
|
+
* "user", "user.name", "user.age",
|
|
16
|
+
* "items", "items[0]", "items[0].id"
|
|
17
|
+
* ]
|
|
18
|
+
*/
|
|
19
|
+
export declare function getAllPathsIncludingContainers(value: any, prevPath?: string): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Lấy tất cả paths từ object, dừng khi gặp array
|
|
22
|
+
* Kết quả bao gồm:
|
|
23
|
+
* - Paths đến primitive values (leaf nodes)
|
|
24
|
+
* - Paths đến array (dừng tại array, không đi sâu vào)
|
|
25
|
+
*
|
|
26
|
+
* Ví dụ: {user: {name: "John", items: [1,2,3]}}
|
|
27
|
+
* Kết quả: ["user.name", "user.items"]
|
|
28
|
+
*/
|
|
29
|
+
export declare function getAllPathsStopAtArray(value: any, prevPath?: string): string[];
|
|
2
30
|
export declare function getAllStringPath(value: any, prevPath?: string): any;
|
package/dist/utils/obj.util.js
CHANGED
|
@@ -1,14 +1,66 @@
|
|
|
1
|
-
import { filter, isNil, join } from "lodash";
|
|
1
|
+
import { filter, isNil, isPlainObject, join } from "lodash";
|
|
2
2
|
function getAllNoneObjStringPath(value, prevPath = "") {
|
|
3
|
-
if (
|
|
4
|
-
return
|
|
3
|
+
if (value === null || value === void 0 || typeof value !== "object" || typeof value === "function") {
|
|
4
|
+
return [prevPath];
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.reduce((prev, item, index) => {
|
|
5
8
|
return [
|
|
6
9
|
...prev,
|
|
7
|
-
...getAllNoneObjStringPath(
|
|
10
|
+
...getAllNoneObjStringPath(item, join(filter([prevPath, String(index)], (v) => !isNil(v) && v !== ""), "."))
|
|
8
11
|
];
|
|
9
12
|
}, []);
|
|
10
13
|
}
|
|
11
|
-
|
|
14
|
+
if (!isPlainObject(value)) {
|
|
15
|
+
return [prevPath];
|
|
16
|
+
}
|
|
17
|
+
return Object.keys(value).reduce((prev, cur) => {
|
|
18
|
+
return [
|
|
19
|
+
...prev,
|
|
20
|
+
...getAllNoneObjStringPath(value[cur], join(filter([prevPath, cur], (v) => !isNil(v) && v !== ""), "."))
|
|
21
|
+
];
|
|
22
|
+
}, []);
|
|
23
|
+
}
|
|
24
|
+
function getAllPathsIncludingContainers(value, prevPath = "") {
|
|
25
|
+
const results = [];
|
|
26
|
+
if (prevPath !== "") {
|
|
27
|
+
results.push(prevPath);
|
|
28
|
+
}
|
|
29
|
+
if (value === null || value === void 0 || typeof value !== "object" || typeof value === "function") {
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
if (!isPlainObject(value) && !Array.isArray(value)) {
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
value.forEach((item, index) => {
|
|
37
|
+
const itemPath = join(filter([prevPath, String(index)], (v) => !isNil(v) && v !== ""), ".");
|
|
38
|
+
const nestedPaths = getAllPathsIncludingContainers(item, itemPath);
|
|
39
|
+
results.push(...nestedPaths);
|
|
40
|
+
});
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
Object.keys(value).forEach((key) => {
|
|
44
|
+
const nestedPath = join(filter([prevPath, key], (v) => !isNil(v) && v !== ""), ".");
|
|
45
|
+
const nestedPaths = getAllPathsIncludingContainers(value[key], nestedPath);
|
|
46
|
+
results.push(...nestedPaths);
|
|
47
|
+
});
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
function getAllPathsStopAtArray(value, prevPath = "") {
|
|
51
|
+
if (value === null || value === void 0 || typeof value !== "object" || typeof value === "function") {
|
|
52
|
+
return [prevPath];
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
return [prevPath];
|
|
56
|
+
}
|
|
57
|
+
if (!isPlainObject(value)) {
|
|
58
|
+
return [prevPath];
|
|
59
|
+
}
|
|
60
|
+
return Object.keys(value).reduce((prev, key) => {
|
|
61
|
+
const fullPath = prevPath ? `${prevPath}.${key}` : key;
|
|
62
|
+
return [...prev, ...getAllPathsStopAtArray(value[key], fullPath)];
|
|
63
|
+
}, []);
|
|
12
64
|
}
|
|
13
65
|
function getAllStringPath(value, prevPath = "") {
|
|
14
66
|
if (typeof value === "object") {
|
|
@@ -24,5 +76,7 @@ function getAllStringPath(value, prevPath = "") {
|
|
|
24
76
|
}
|
|
25
77
|
export {
|
|
26
78
|
getAllNoneObjStringPath,
|
|
79
|
+
getAllPathsIncludingContainers,
|
|
80
|
+
getAllPathsStopAtArray,
|
|
27
81
|
getAllStringPath
|
|
28
82
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-form-manage",
|
|
3
|
-
"version": "1.0.8
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Lightweight React form management with list and listener support.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@emotion/styled": "^11.14.1",
|
|
37
37
|
"@eslint/js": "^9.14.0",
|
|
38
38
|
"@mui/material": "^7.3.7",
|
|
39
|
+
"@types/lodash": "^4.17.23",
|
|
39
40
|
"@types/node": "^22.9.3",
|
|
40
41
|
"@types/react": "^19.2.9",
|
|
41
42
|
"@types/react-dom": "^19.2.3",
|