react-form-manage 1.0.8 → 1.1.0-beta.1

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/providers/Form.d.ts +30 -15
  3. package/dist/providers/Form.js +142 -25
  4. package/dist/stores/formStore.js +3 -0
  5. package/dist/test/testFormInstance/TestCase1_FormWithInstance.d.ts +14 -0
  6. package/dist/test/testFormInstance/TestCase1_FormWithInstance.js +21 -0
  7. package/dist/test/testFormInstance/TestCase2_AutoGenerateFormName.d.ts +14 -0
  8. package/dist/test/testFormInstance/TestCase2_AutoGenerateFormName.js +23 -0
  9. package/dist/test/testFormInstance/TestCase3_SafeFormMethods.d.ts +14 -0
  10. package/dist/test/testFormInstance/TestCase3_SafeFormMethods.js +66 -0
  11. package/dist/test/testFormInstance/TestCase4_UseFormStatusFlag.d.ts +15 -0
  12. package/dist/test/testFormInstance/TestCase4_UseFormStatusFlag.js +59 -0
  13. package/dist/test/testFormInstance/TestCase5_UseFormUtils.d.ts +1 -0
  14. package/dist/test/testFormInstance/TestCase5_UseFormUtils.js +65 -0
  15. package/dist/test/testFormInstance/TestCase6_FormWithoutWrapper.d.ts +15 -0
  16. package/dist/test/testFormInstance/TestCase6_FormWithoutWrapper.js +45 -0
  17. package/dist/test/testFormInstance/index.d.ts +12 -0
  18. package/dist/test/testFormInstance/index.js +49 -0
  19. package/dist/test/testSetValue/TestSetValueInEffect.d.ts +3 -0
  20. package/dist/test/testSetValue/TestSetValueInEffect.js +30 -0
  21. package/dist/types/public.d.ts +1 -1
  22. package/package.json +2 -1
  23. package/src/App.tsx +7 -7
  24. package/src/providers/Form.tsx +247 -28
  25. package/src/stores/formStore.ts +3 -1
  26. package/src/test/testFormInstance/TestCase1_FormWithInstance.tsx +112 -0
  27. package/src/test/testFormInstance/TestCase2_AutoGenerateFormName.tsx +139 -0
  28. package/src/test/testFormInstance/TestCase3_SafeFormMethods.tsx +203 -0
  29. package/src/test/testFormInstance/TestCase4_UseFormStatusFlag.tsx +252 -0
  30. package/src/test/testFormInstance/TestCase5_UseFormUtils.tsx +224 -0
  31. package/src/test/testFormInstance/TestCase6_FormWithoutWrapper.tsx +204 -0
  32. package/src/test/testFormInstance/index.tsx +116 -0
  33. package/src/test/testSetValue/TestSetValueInEffect.tsx +54 -0
  34. package/src/types/public.ts +1 -1
@@ -0,0 +1,252 @@
1
+ import { Badge, Button, Space, Tag, Typography } from "antd";
2
+ import { useEffect, useState } from "react";
3
+ import Form from "../../providers/Form";
4
+
5
+ const { Title, Paragraph, Text } = Typography;
6
+
7
+ /**
8
+ * TestCase4: useForm Returns Status Flag
9
+ *
10
+ * Mô tả:
11
+ * - Test breaking change: useForm() trả về tuple [instance, isReady]
12
+ * - isReady = true khi Form đã mount và bind methods
13
+ * - isReady = false khi Form chưa mount hoặc đã unmount
14
+ *
15
+ * Expected Behavior:
16
+ * - Trước khi mount: isReady = false
17
+ * - Sau khi mount: isReady = true
18
+ * - Sau khi unmount: isReady = false
19
+ * - Có thể dùng isReady để conditionally render UI hoặc enable/disable buttons
20
+ */
21
+
22
+ export default function TestCase4_UseFormStatusFlag() {
23
+ const [form, isFormReady] = Form.useForm<{ email: string; message: string }>();
24
+ const [formMounted, setFormMounted] = useState(false);
25
+ const [statusHistory, setStatusHistory] = useState<
26
+ { time: string; status: boolean; event: string }[]
27
+ >([]);
28
+
29
+ useEffect(() => {
30
+ setStatusHistory((prev) => [
31
+ ...prev,
32
+ {
33
+ time: new Date().toLocaleTimeString(),
34
+ status: isFormReady,
35
+ event: isFormReady ? "Form Ready" : "Form Not Ready",
36
+ },
37
+ ]);
38
+ }, [isFormReady]);
39
+
40
+ const handleMountForm = () => {
41
+ setFormMounted(true);
42
+ };
43
+
44
+ const handleUnmountForm = () => {
45
+ setFormMounted(false);
46
+ };
47
+
48
+ const handleSetValue = () => {
49
+ if (isFormReady) {
50
+ form.setFieldValue("email", "test@example.com");
51
+ form.setFieldValue("message", "Hello from ready form!");
52
+ } else {
53
+ console.warn("Form is not ready yet!");
54
+ }
55
+ };
56
+
57
+ const clearHistory = () => {
58
+ setStatusHistory([]);
59
+ };
60
+
61
+ return (
62
+ <div style={{ padding: 24 }}>
63
+ <Title level={3}>TestCase4: useForm Returns Status Flag</Title>
64
+
65
+ <Paragraph>
66
+ <Text strong>Breaking Change:</Text> useForm() bây giờ trả về tuple [instance, isReady]
67
+ </Paragraph>
68
+
69
+ <Paragraph>
70
+ <Text type="secondary">
71
+ isReady flag cho biết Form đã mount và sẵn sàng nhận calls hay chưa.
72
+ Có thể dùng để conditionally enable/disable features dựa trên Form state.
73
+ </Text>
74
+ </Paragraph>
75
+
76
+ {/* Status Indicator */}
77
+ <div style={{ marginBottom: 24, padding: 16, background: "#fafafa", border: "1px solid #d9d9d9" }}>
78
+ <Space size="large">
79
+ <div>
80
+ <Text strong>Form Ready Status: </Text>
81
+ <Badge
82
+ status={isFormReady ? "success" : "error"}
83
+ text={
84
+ <Text strong style={{ color: isFormReady ? "#52c41a" : "#ff4d4f" }}>
85
+ {isFormReady ? "READY" : "NOT READY"}
86
+ </Text>
87
+ }
88
+ />
89
+ </div>
90
+ <div>
91
+ <Text strong>Form Mounted: </Text>
92
+ <Tag color={formMounted ? "green" : "red"}>
93
+ {formMounted ? "YES" : "NO"}
94
+ </Tag>
95
+ </div>
96
+ <div>
97
+ <Text strong>FormName: </Text>
98
+ <Text code>{form.formName}</Text>
99
+ </div>
100
+ </Space>
101
+ </div>
102
+
103
+ {/* Control Buttons */}
104
+ <Space style={{ marginBottom: 24 }} wrap>
105
+ {!formMounted ? (
106
+ <Button onClick={handleMountForm} type="primary" size="large">
107
+ Mount Form
108
+ </Button>
109
+ ) : (
110
+ <Button onClick={handleUnmountForm} danger size="large">
111
+ Unmount Form
112
+ </Button>
113
+ )}
114
+ <Button
115
+ onClick={handleSetValue}
116
+ disabled={!isFormReady}
117
+ type={isFormReady ? "primary" : "default"}
118
+ >
119
+ Set Values {!isFormReady && "(Disabled - Form Not Ready)"}
120
+ </Button>
121
+ <Button onClick={clearHistory}>Clear History</Button>
122
+ </Space>
123
+
124
+ <div style={{ display: "flex", gap: 24 }}>
125
+ {/* Status History */}
126
+ <div style={{ flex: 1 }}>
127
+ <Title level={5}>Status Change History:</Title>
128
+ <div
129
+ style={{
130
+ border: "1px solid #d9d9d9",
131
+ padding: 16,
132
+ height: 300,
133
+ overflow: "auto",
134
+ background: "#ffffff",
135
+ }}
136
+ >
137
+ {statusHistory.length === 0 ? (
138
+ <Text type="secondary">No status changes yet...</Text>
139
+ ) : (
140
+ <table style={{ width: "100%", borderCollapse: "collapse" }}>
141
+ <thead>
142
+ <tr style={{ borderBottom: "2px solid #d9d9d9" }}>
143
+ <th style={{ padding: 8, textAlign: "left" }}>Time</th>
144
+ <th style={{ padding: 8, textAlign: "left" }}>Status</th>
145
+ <th style={{ padding: 8, textAlign: "left" }}>Event</th>
146
+ </tr>
147
+ </thead>
148
+ <tbody>
149
+ {statusHistory.map((entry, index) => (
150
+ <tr
151
+ key={index}
152
+ style={{
153
+ borderBottom: "1px solid #f0f0f0",
154
+ background: index % 2 === 0 ? "#fafafa" : "#ffffff",
155
+ }}
156
+ >
157
+ <td style={{ padding: 8, fontFamily: "monospace", fontSize: 12 }}>
158
+ {entry.time}
159
+ </td>
160
+ <td style={{ padding: 8 }}>
161
+ <Tag color={entry.status ? "green" : "red"}>
162
+ {entry.status ? "TRUE" : "FALSE"}
163
+ </Tag>
164
+ </td>
165
+ <td style={{ padding: 8 }}>{entry.event}</td>
166
+ </tr>
167
+ ))}
168
+ </tbody>
169
+ </table>
170
+ )}
171
+ </div>
172
+ </div>
173
+
174
+ {/* Form Panel */}
175
+ <div style={{ flex: 1 }}>
176
+ <Title level={5}>Form Panel:</Title>
177
+ {formMounted ? (
178
+ <div style={{ border: "2px solid #52c41a", padding: 16 }}>
179
+ <Form
180
+ form={form}
181
+ initialValues={{ email: "", message: "" }}
182
+ onFinish={(values) => {
183
+ console.log("Form submitted:", values);
184
+ alert(`Submitted: ${JSON.stringify(values, null, 2)}`);
185
+ }}
186
+ >
187
+ <Form.Item name="email" label="Email">
188
+ <input
189
+ type="email"
190
+ placeholder="Enter email"
191
+ style={{ padding: 8, width: "100%" }}
192
+ />
193
+ </Form.Item>
194
+
195
+ <Form.Item name="message" label="Message">
196
+ <textarea
197
+ placeholder="Enter message"
198
+ rows={4}
199
+ style={{ padding: 8, width: "100%" }}
200
+ />
201
+ </Form.Item>
202
+
203
+ <Button type="primary" htmlType="submit">
204
+ Submit
205
+ </Button>
206
+ </Form>
207
+ </div>
208
+ ) : (
209
+ <div
210
+ style={{
211
+ border: "2px dashed #d9d9d9",
212
+ padding: 16,
213
+ height: 300,
214
+ display: "flex",
215
+ alignItems: "center",
216
+ justifyContent: "center",
217
+ background: "#fafafa",
218
+ }}
219
+ >
220
+ <div style={{ textAlign: "center" }}>
221
+ <Text type="secondary" style={{ fontSize: 16 }}>
222
+ Form is not mounted
223
+ </Text>
224
+ <br />
225
+ <Button
226
+ type="primary"
227
+ onClick={handleMountForm}
228
+ style={{ marginTop: 16 }}
229
+ >
230
+ Mount Form
231
+ </Button>
232
+ </div>
233
+ </div>
234
+ )}
235
+ </div>
236
+ </div>
237
+
238
+ <div style={{ marginTop: 24, padding: 16, background: "#f5f5f5" }}>
239
+ <Title level={5}>Expected Results:</Title>
240
+ <ul>
241
+ <li>✅ Initial state: isReady = false (logged in history)</li>
242
+ <li>✅ Click "Mount Form" → isReady changes to true (logged in history)</li>
243
+ <li>✅ "Set Values" button becomes enabled when isReady = true</li>
244
+ <li>✅ Click "Set Values" → Form values update successfully</li>
245
+ <li>✅ Click "Unmount Form" → isReady changes back to false (logged in history)</li>
246
+ <li>✅ "Set Values" button becomes disabled again</li>
247
+ <li>✅ Status badge and tag update in real-time</li>
248
+ </ul>
249
+ </div>
250
+ </div>
251
+ );
252
+ }
@@ -0,0 +1,224 @@
1
+ import { Button, Space, Typography, Card, Input, message } from "antd";
2
+ import { useState } from "react";
3
+ import Form from "../../providers/Form";
4
+
5
+ const { Title, Paragraph, Text } = Typography;
6
+
7
+ /**
8
+ * TestCase5: useFormUtils Hook
9
+ *
10
+ * Mô tả:
11
+ * - Test new hook: Form.useFormUtils()
12
+ * - Cho phép thao tác với form từ bên ngoài component, không cần context
13
+ * - Cung cấp: getFormInstance, setFieldValue, setFieldValues
14
+ *
15
+ * Expected Behavior:
16
+ * - Có thể get form instance từ bất kỳ đâu bằng formName
17
+ * - Có thể set values cho form từ bên ngoài form component
18
+ * - Multiple forms có thể được điều khiển từ 1 control panel
19
+ */
20
+
21
+ function ControlPanel() {
22
+ const { getFormInstance, setFieldValue, setFieldValues } = Form.useFormUtils();
23
+ const [targetFormName, setTargetFormName] = useState("form1");
24
+
25
+ const handleSetSingleValue = () => {
26
+ try {
27
+ setFieldValue(targetFormName, "username", "admin");
28
+ message.success(`Set username='admin' for ${targetFormName}`);
29
+ } catch (error: any) {
30
+ message.error(error.message);
31
+ }
32
+ };
33
+
34
+ const handleSetMultipleValues = () => {
35
+ try {
36
+ setFieldValues(targetFormName, {
37
+ username: "john_doe",
38
+ email: "john@example.com",
39
+ });
40
+ message.success(`Set multiple values for ${targetFormName}`);
41
+ } catch (error: any) {
42
+ message.error(error.message);
43
+ }
44
+ };
45
+
46
+ const handleGetFormInstance = () => {
47
+ try {
48
+ const formInstance = getFormInstance(targetFormName);
49
+ console.log("Form Instance:", formInstance);
50
+ console.log("Current Values:", {
51
+ username: formInstance?.getFieldValue("username"),
52
+ email: formInstance?.getFieldValue("email"),
53
+ });
54
+ message.info("Check console for form instance details");
55
+ } catch (error: any) {
56
+ message.error(error.message);
57
+ }
58
+ };
59
+
60
+ const handleReset = () => {
61
+ try {
62
+ const formInstance = getFormInstance(targetFormName);
63
+ formInstance?.resetFields();
64
+ message.success(`Reset ${targetFormName}`);
65
+ } catch (error: any) {
66
+ message.error(error.message);
67
+ }
68
+ };
69
+
70
+ const handleSubmit = () => {
71
+ try {
72
+ const formInstance = getFormInstance(targetFormName);
73
+ formInstance?.submit();
74
+ message.info(`Submitting ${targetFormName}`);
75
+ } catch (error: any) {
76
+ message.error(error.message);
77
+ }
78
+ };
79
+
80
+ return (
81
+ <Card
82
+ title="🎮 Control Panel (useFormUtils)"
83
+ style={{ marginBottom: 24 }}
84
+ extra={
85
+ <Space>
86
+ <Text>Target Form:</Text>
87
+ <Input
88
+ value={targetFormName}
89
+ onChange={(e) => setTargetFormName(e.target.value)}
90
+ placeholder="Enter form name"
91
+ style={{ width: 150 }}
92
+ />
93
+ </Space>
94
+ }
95
+ >
96
+ <Space wrap>
97
+ <Button onClick={handleSetSingleValue} type="primary">
98
+ Set Single Value
99
+ </Button>
100
+ <Button onClick={handleSetMultipleValues} type="primary">
101
+ Set Multiple Values
102
+ </Button>
103
+ <Button onClick={handleGetFormInstance}>
104
+ Get Form Instance
105
+ </Button>
106
+ <Button onClick={handleReset} danger>
107
+ Reset Form
108
+ </Button>
109
+ <Button onClick={handleSubmit} type="dashed">
110
+ Submit Form
111
+ </Button>
112
+ </Space>
113
+ <div style={{ marginTop: 16, padding: 12, background: "#f0f0f0" }}>
114
+ <Text type="secondary">
115
+ 💡 Tip: Change the target form name to "form1" or "form2" to control different forms
116
+ </Text>
117
+ </div>
118
+ </Card>
119
+ );
120
+ }
121
+
122
+ export default function TestCase5_UseFormUtils() {
123
+ return (
124
+ <div style={{ padding: 24 }}>
125
+ <Title level={3}>TestCase5: useFormUtils Hook</Title>
126
+
127
+ <Paragraph>
128
+ <Text strong>New Hook:</Text> Form.useFormUtils() cho phép thao tác với form từ bên ngoài
129
+ </Paragraph>
130
+
131
+ <Paragraph>
132
+ <Text type="secondary">
133
+ Hook này cung cấp các utility methods để get/set form values từ bất kỳ đâu
134
+ trong app, không cần phải ở trong Form context. Hữu ích cho global control panels,
135
+ external triggers, hoặc cross-component form manipulation.
136
+ </Text>
137
+ </Paragraph>
138
+
139
+ {/* Control Panel */}
140
+ <ControlPanel />
141
+
142
+ {/* Forms */}
143
+ <div style={{ display: "flex", gap: 24 }}>
144
+ {/* Form 1 */}
145
+ <div style={{ flex: 1 }}>
146
+ <Card title="Form 1 (formName='form1')">
147
+ <Form
148
+ formName="form1"
149
+ initialValues={{ username: "", email: "" }}
150
+ onFinish={(values) => {
151
+ console.log("Form 1 submitted:", values);
152
+ message.success(`Form 1 submitted: ${JSON.stringify(values)}`);
153
+ }}
154
+ >
155
+ <Form.Item name="username" label="Username">
156
+ <Input placeholder="Username" />
157
+ </Form.Item>
158
+
159
+ <Form.Item name="email" label="Email">
160
+ <Input type="email" placeholder="Email" />
161
+ </Form.Item>
162
+
163
+ <Button type="primary" htmlType="submit">
164
+ Submit Form 1
165
+ </Button>
166
+ </Form>
167
+ </Card>
168
+ </div>
169
+
170
+ {/* Form 2 */}
171
+ <div style={{ flex: 1 }}>
172
+ <Card title="Form 2 (formName='form2')">
173
+ <Form
174
+ formName="form2"
175
+ initialValues={{ username: "", email: "" }}
176
+ onFinish={(values) => {
177
+ console.log("Form 2 submitted:", values);
178
+ message.success(`Form 2 submitted: ${JSON.stringify(values)}`);
179
+ }}
180
+ >
181
+ <Form.Item name="username" label="Username">
182
+ <Input placeholder="Username" />
183
+ </Form.Item>
184
+
185
+ <Form.Item name="email" label="Email">
186
+ <Input type="email" placeholder="Email" />
187
+ </Form.Item>
188
+
189
+ <Button type="primary" htmlType="submit">
190
+ Submit Form 2
191
+ </Button>
192
+ </Form>
193
+ </Card>
194
+ </div>
195
+ </div>
196
+
197
+ <div style={{ marginTop: 24, padding: 16, background: "#f5f5f5" }}>
198
+ <Title level={5}>Expected Results:</Title>
199
+ <ul>
200
+ <li>✅ Control panel starts with target "form1"</li>
201
+ <li>✅ Click "Set Single Value" → Form 1's username field updates to "admin"</li>
202
+ <li>✅ Click "Set Multiple Values" → Form 1's both fields update</li>
203
+ <li>✅ Click "Get Form Instance" → Console logs form instance and current values</li>
204
+ <li>✅ Change target to "form2" in input</li>
205
+ <li>✅ Click "Set Single Value" → Form 2's username field updates</li>
206
+ <li>✅ Click "Reset Form" → Target form resets to initialValues</li>
207
+ <li>✅ Click "Submit Form" → Target form submits programmatically</li>
208
+ <li>⚠️ Enter invalid formName → Error message shows "Form instance not found"</li>
209
+ </ul>
210
+ </div>
211
+
212
+ <div style={{ marginTop: 16, padding: 16, background: "#e6f7ff", border: "1px solid #91d5ff" }}>
213
+ <Title level={5}>Use Cases:</Title>
214
+ <ul style={{ marginBottom: 0 }}>
215
+ <li>Global control panels để quản lý multiple forms</li>
216
+ <li>External triggers (từ API response, WebSocket, events...)</li>
217
+ <li>Cross-component form manipulation</li>
218
+ <li>Testing và debugging tools</li>
219
+ <li>Admin dashboards với form management</li>
220
+ </ul>
221
+ </div>
222
+ </div>
223
+ );
224
+ }
@@ -0,0 +1,204 @@
1
+ import { Button, Card, Input, Space, Typography } from "antd";
2
+ import { useState } from "react";
3
+ import Form from "../../providers/Form";
4
+
5
+ const { Title, Paragraph, Text } = Typography;
6
+
7
+ /**
8
+ * TestCase6: Form Without Wrapper Component
9
+ *
10
+ * Mô tả:
11
+ * - Test use case: Sử dụng form methods mà không cần render Form component
12
+ * - Form instance có thể được sử dụng độc lập để quản lý state
13
+ * - Hữu ích cho: custom form implementations, headless forms, non-standard UI
14
+ *
15
+ * Expected Behavior:
16
+ * - Form instance hoạt động như một state manager
17
+ * - Có thể set/get values mà không cần Form component
18
+ * - Có thể dùng Form.useWatch để reactive update
19
+ * - Warning được log vì không có Form component để validate
20
+ */
21
+
22
+ export default function TestCase6_FormWithoutWrapper() {
23
+ const [form] = Form.useForm<{
24
+ firstName: string;
25
+ lastName: string;
26
+ age: number;
27
+ }>();
28
+
29
+ // Watch individual fields
30
+ const firstName = Form.useWatch("firstName", form);
31
+ const lastName = Form.useWatch("lastName", form);
32
+ const age = Form.useWatch("age", form);
33
+
34
+ const [manualFirstName, setManualFirstName] = useState("");
35
+ const [manualLastName, setManualLastName] = useState("");
36
+ const [manualAge, setManualAge] = useState("");
37
+
38
+ const handleSetFromManualInputs = () => {
39
+ form.setFieldValue("firstName", manualFirstName);
40
+ form.setFieldValue("lastName", manualLastName);
41
+ form.setFieldValue("age", Number(manualAge) || 0);
42
+ };
43
+
44
+ const handleSetBatch = () => {
45
+ form.setFieldValues({
46
+ firstName: "John",
47
+ lastName: "Doe",
48
+ age: 30,
49
+ });
50
+ };
51
+
52
+ const handleGetValues = () => {
53
+ const values = {
54
+ firstName: form.getFieldValue("firstName"),
55
+ lastName: form.getFieldValue("lastName"),
56
+ age: form.getFieldValue("age"),
57
+ };
58
+ console.log("Current values:", values);
59
+ alert(JSON.stringify(values, null, 2));
60
+ };
61
+
62
+ const handleReset = () => {
63
+ form.resetFields();
64
+ };
65
+
66
+ const handleSubmit = () => {
67
+ // Note: Submit sẽ log warning vì không có Form component
68
+ form.submit();
69
+ console.log("Submit called (check warning in console)");
70
+ };
71
+
72
+ const fullName = `${firstName || ""} ${lastName || ""}`.trim() || "(empty)";
73
+
74
+ return (
75
+ <div style={{ padding: 24 }}>
76
+ <Title level={3}>TestCase6: Form Without Wrapper Component</Title>
77
+
78
+ <Paragraph>
79
+ <Text strong>Use Case:</Text> Sử dụng form instance như state manager mà không cần Form component
80
+ </Paragraph>
81
+
82
+ <Paragraph>
83
+ <Text type="secondary">
84
+ Form instance có thể hoạt động độc lập như một state manager.
85
+ Hữu ích cho custom form implementations, headless forms, hoặc khi bạn muốn
86
+ tự build UI mà không dùng Form component wrapper.
87
+ </Text>
88
+ </Paragraph>
89
+
90
+ <div style={{ display: "flex", gap: 24 }}>
91
+ {/* Manual Input Panel */}
92
+ <Card title="📝 Manual Inputs (Not Connected to Form)" style={{ flex: 1 }}>
93
+ <Space direction="vertical" style={{ width: "100%" }}>
94
+ <div>
95
+ <Text strong>First Name:</Text>
96
+ <Input
97
+ value={manualFirstName}
98
+ onChange={(e) => setManualFirstName(e.target.value)}
99
+ placeholder="Enter first name"
100
+ />
101
+ </div>
102
+ <div>
103
+ <Text strong>Last Name:</Text>
104
+ <Input
105
+ value={manualLastName}
106
+ onChange={(e) => setManualLastName(e.target.value)}
107
+ placeholder="Enter last name"
108
+ />
109
+ </div>
110
+ <div>
111
+ <Text strong>Age:</Text>
112
+ <Input
113
+ type="number"
114
+ value={manualAge}
115
+ onChange={(e) => setManualAge(e.target.value)}
116
+ placeholder="Enter age"
117
+ />
118
+ </div>
119
+ <Button
120
+ type="primary"
121
+ onClick={handleSetFromManualInputs}
122
+ block
123
+ >
124
+ Set Form Values
125
+ </Button>
126
+ </Space>
127
+ </Card>
128
+
129
+ {/* Display Panel */}
130
+ <Card title="👁️ Form State (via useWatch)" style={{ flex: 1 }}>
131
+ <Space direction="vertical" style={{ width: "100%" }}>
132
+ <div>
133
+ <Text strong>First Name: </Text>
134
+ <Text code>{firstName || "(empty)"}</Text>
135
+ </div>
136
+ <div>
137
+ <Text strong>Last Name: </Text>
138
+ <Text code>{lastName || "(empty)"}</Text>
139
+ </div>
140
+ <div>
141
+ <Text strong>Age: </Text>
142
+ <Text code>{age ?? "(empty)"}</Text>
143
+ </div>
144
+ <div style={{ marginTop: 16, padding: 12, background: "#f0f0f0" }}>
145
+ <Text strong>Computed Full Name: </Text>
146
+ <Text style={{ fontSize: 16 }}>{fullName}</Text>
147
+ </div>
148
+ </Space>
149
+ </Card>
150
+ </div>
151
+
152
+ {/* Control Buttons */}
153
+ <Card title="🎮 Form Controls" style={{ marginTop: 24 }}>
154
+ <Space wrap>
155
+ <Button onClick={handleSetBatch} type="primary">
156
+ Set Batch Values (John Doe, 30)
157
+ </Button>
158
+ <Button onClick={handleGetValues}>
159
+ Get All Values (Alert)
160
+ </Button>
161
+ <Button onClick={handleReset} danger>
162
+ Reset Form
163
+ </Button>
164
+ <Button onClick={handleSubmit} type="dashed">
165
+ Submit Form (Will Log Warning)
166
+ </Button>
167
+ </Space>
168
+
169
+ <div style={{ marginTop: 16, padding: 12, background: "#fff7e6", border: "1px solid #ffd591" }}>
170
+ <Text strong>⚠️ Note: </Text>
171
+ <Text>
172
+ Không có Form component được render. Form instance hoạt động như pure state manager.
173
+ Submit sẽ log warning vì không có onFinish handler.
174
+ </Text>
175
+ </div>
176
+ </Card>
177
+
178
+ <div style={{ marginTop: 24, padding: 16, background: "#f5f5f5" }}>
179
+ <Title level={5}>Expected Results:</Title>
180
+ <ul>
181
+ <li>✅ Type in manual inputs → Values NOT auto-sync to form state</li>
182
+ <li>✅ Click "Set Form Values" → Form state updates, display panel shows new values</li>
183
+ <li>✅ Display panel updates reactively via useWatch</li>
184
+ <li>✅ Click "Set Batch Values" → All fields update at once</li>
185
+ <li>✅ Click "Get All Values" → Alert shows current form values</li>
186
+ <li>✅ Click "Reset Form" → All fields reset (empty)</li>
187
+ <li>✅ Computed Full Name updates based on firstName + lastName</li>
188
+ <li>⚠️ Click "Submit Form" → Console shows warning (no Form component mounted)</li>
189
+ </ul>
190
+ </div>
191
+
192
+ <div style={{ marginTop: 16, padding: 16, background: "#e6f7ff", border: "1px solid #91d5ff" }}>
193
+ <Title level={5}>Use Cases:</Title>
194
+ <ul style={{ marginBottom: 0 }}>
195
+ <li><strong>Headless Forms:</strong> Build custom UI mà không bị constraint bởi Form component</li>
196
+ <li><strong>State Manager:</strong> Sử dụng như lightweight state manager với validation support</li>
197
+ <li><strong>Multi-step Forms:</strong> Quản lý form state across multiple steps/pages</li>
198
+ <li><strong>Form Wizards:</strong> Share form instance between different wizard steps</li>
199
+ <li><strong>Custom Integrations:</strong> Integrate với third-party UI libraries</li>
200
+ </ul>
201
+ </div>
202
+ </div>
203
+ );
204
+ }