@wangs-ui/skills 1.1.0-alpha.7 → 1.1.0-alpha.9
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/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-
|
|
2
|
+
import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-BMVvfgtq.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
//#region bin.ts
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as getAgentSkillDirs, c as isSkillInstalled, d as loadAllSkills, i as listSkills, l as removeSkill, n as updateSkills, o as getInstalledSkills, r as addSkills, s as installSkill, t as removeSkills, u as getSkill } from "./src-
|
|
1
|
+
import { a as getAgentSkillDirs, c as isSkillInstalled, d as loadAllSkills, i as listSkills, l as removeSkill, n as updateSkills, o as getInstalledSkills, r as addSkills, s as installSkill, t as removeSkills, u as getSkill } from "./src-BMVvfgtq.js";
|
|
2
2
|
export { addSkills, getAgentSkillDirs, getInstalledSkills, getSkill, installSkill, isSkillInstalled, listSkills, loadAllSkills, removeSkill, removeSkills, updateSkills };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-form
|
|
3
|
-
description:
|
|
3
|
+
description: Form architecture, validation workflows, strongly-typed forms (useForm, useDialogForm, useWatchField), initialValues/reset lifecycle, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Skill: Form Architecture & Validation Workflows
|
|
@@ -9,11 +9,11 @@ Use this skill when building forms, data entry panels, modal forms, settings pag
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
## 1. MCP
|
|
12
|
+
## 1. MCP Protocol & Component Rules (Mandatory Single Source of Truth)
|
|
13
13
|
|
|
14
|
-
Do **NOT** hardcode or guess prop names,
|
|
14
|
+
Do **NOT** hardcode or guess prop names, component options, preset variations, or Storybook patterns in this document. Always retrieve component definitions, active props, and live Storybook implementations directly via MCP:
|
|
15
15
|
|
|
16
|
-
###
|
|
16
|
+
### Component & Form Documentation Protocol:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
19
|
get-documentation({ "id": "form" })
|
|
@@ -27,17 +27,17 @@ get-documentation({ "id": "datepicker" })
|
|
|
27
27
|
get-documentation({ "id": "fileupload" })
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Live Storybook & Interactive Behavior Protocol:
|
|
31
31
|
|
|
32
32
|
```json
|
|
33
33
|
get-documentation-for-story({ "id": "form", "storyName": "Default" })
|
|
34
|
-
get-documentation-for-story({ "id": "
|
|
35
|
-
get-documentation-for-story({ "id": "
|
|
36
|
-
get-documentation-for-story({ "id": "
|
|
37
|
-
get-documentation-for-story({ "id": "
|
|
34
|
+
get-documentation-for-story({ "id": "form", "storyName": "AsyncInitialValues" })
|
|
35
|
+
get-documentation-for-story({ "id": "form", "storyName": "ConditionalFields" })
|
|
36
|
+
get-documentation-for-story({ "id": "form", "storyName": "CascadingOptions" })
|
|
37
|
+
get-documentation-for-story({ "id": "dialogform", "storyName": "Default" })
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
###
|
|
40
|
+
### Knowledge Graph & Symbol Usages:
|
|
41
41
|
|
|
42
42
|
```json
|
|
43
43
|
query_graph({ "query": "useForm" })
|
|
@@ -47,61 +47,88 @@ query_graph({ "query": "useWatchField" })
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
-
## 2. Form
|
|
50
|
+
## 2. Core Form Concepts & Lifecycle Mechanics
|
|
51
51
|
|
|
52
|
-
###
|
|
52
|
+
### A. Strongly Typed Form Instance (`useForm<TForm>()`)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
`useForm<TForm>()` instantiates a `FormControl` natively bound to model type `TForm`.
|
|
55
|
+
|
|
56
|
+
- `Field`: `name` is strictly typed to `Path<TForm>` dot-paths.
|
|
57
|
+
- `useWatchField`: `name` is strictly typed to `Path<TForm>`.
|
|
58
|
+
- `control`: Provides `setInitialValues`, `setValues`, `setFieldError`, `setErrors`, and `reset`.
|
|
59
|
+
|
|
60
|
+
### B. Dynamic Initial Values & Baseline Reset (`setInitialValues` vs `setValues`)
|
|
61
|
+
|
|
62
|
+
1. **Async Initial Values (`control.setInitialValues(values)`)**:
|
|
63
|
+
- Accepts a `Partial<TForm>` JSON object (e.g. fetched from an API).
|
|
64
|
+
- Establishes an **immutable baseline** for registered fields. Once set for a field path, subsequent calls to `setInitialValues` for that path are ignored.
|
|
65
|
+
2. **Batch Value Updates (`control.setValues(values)`)**:
|
|
66
|
+
- Accepts a `Partial<TForm>` JSON object to update current input values without altering the initial baseline.
|
|
67
|
+
3. **Reset Behavior (`control.reset()`)**:
|
|
68
|
+
- Restores all fields back to their registered initial baseline values (set via `setInitialValues` or field `initialValue`) and clears all field-level validation errors.
|
|
69
|
+
|
|
70
|
+
### C. Primitive Component Integration Architecture
|
|
71
|
+
|
|
72
|
+
`Field` serves as the form integration wrapper for primitive UI input components (`Input`, `Select`, `MultiSelect`, `DatePicker`, `NumberInput`, `FileUpload`, `Calendar`, etc.):
|
|
73
|
+
|
|
74
|
+
- **Children Render Callback**: `Field` yields `{ fieldProps, fieldState }`.
|
|
75
|
+
- **`fieldProps`**: Pass directly to primitive inputs (`<Input {...fieldProps} />`). Contains `name`, `value`, `ref`, `onChange`.
|
|
76
|
+
- **`fieldState`**: Provides `invalid`, `error`, `isDirty`, `isPending`. Pass `invalid={fieldState.invalid}` to primitive components for accessibility and validation styling.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. High-Level Form Architecture & Usage Patterns
|
|
81
|
+
|
|
82
|
+
### Pattern 1: Page Forms (`useForm<T>()`)
|
|
55
83
|
|
|
56
84
|
```tsx
|
|
57
85
|
import Button from '@wangs-ui/react-core/primitive/button';
|
|
58
86
|
import { useForm } from '@wangs-ui/react-core/primitive/form';
|
|
59
87
|
import Input from '@wangs-ui/react-core/primitive/input';
|
|
60
88
|
import { useI18n } from '@wangs-ui/react-i18n';
|
|
89
|
+
import { useEffect } from 'react';
|
|
61
90
|
|
|
62
|
-
interface
|
|
91
|
+
interface UserProfile {
|
|
63
92
|
name: string;
|
|
64
93
|
email: string;
|
|
65
|
-
address: {
|
|
66
|
-
city: string;
|
|
67
|
-
};
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
export function
|
|
96
|
+
export function UserProfilePage({ userId }: { userId: string }) {
|
|
71
97
|
const { t } = useI18n();
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
const { Form, Field, control } = useForm<UserProfile>();
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
async function loadData() {
|
|
102
|
+
const data = await fetchUserData(userId);
|
|
103
|
+
// Establish immutable initial baseline from async response
|
|
104
|
+
control.setInitialValues(data);
|
|
105
|
+
}
|
|
106
|
+
loadData();
|
|
107
|
+
}, [userId, control]);
|
|
74
108
|
|
|
75
109
|
return (
|
|
76
|
-
<Form control={control} onSubmit={(values) =>
|
|
110
|
+
<Form control={control} onSubmit={(values) => saveUserData(values)}>
|
|
77
111
|
<Field required label={t('Full Name')} name="name">
|
|
78
|
-
{({ fieldProps }) =>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{/* Autocompletes nested dot-path keys */}
|
|
82
|
-
<Field required label={t('City')} name="address.city">
|
|
83
|
-
{({ fieldProps }) => <Input {...fieldProps} placeholder={t('Enter city')} />}
|
|
112
|
+
{({ fieldProps, fieldState }) => (
|
|
113
|
+
<Input {...fieldProps} invalid={fieldState.invalid} placeholder={t('Enter full name')} />
|
|
114
|
+
)}
|
|
84
115
|
</Field>
|
|
85
116
|
|
|
86
117
|
<div className="flex gap-2">
|
|
87
118
|
<Button
|
|
88
119
|
label={t('Reset')}
|
|
89
120
|
type="button"
|
|
90
|
-
|
|
121
|
+
variant="outlined"
|
|
91
122
|
onClick={() => control.reset()}
|
|
92
123
|
/>
|
|
93
|
-
<Button label={t('
|
|
124
|
+
<Button label={t('Save')} type="submit" />
|
|
94
125
|
</div>
|
|
95
126
|
</Form>
|
|
96
127
|
);
|
|
97
128
|
}
|
|
98
129
|
```
|
|
99
130
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
### Pattern B: Modal Forms with `useDialogForm<T>()`
|
|
103
|
-
|
|
104
|
-
For modal/dialog forms, use `useDialogForm<T>()` from `@wangs-ui/react-core/primitive/dialogform`. It combines a modal `Dialog` with form provider lifecycle, providing automatic modal close on success (`closeOnSubmit`), validation interception, and native Enter-key submission.
|
|
131
|
+
### Pattern 2: Modal Forms (`useDialogForm<T>()`)
|
|
105
132
|
|
|
106
133
|
```tsx
|
|
107
134
|
import Button from '@wangs-ui/react-core/primitive/button';
|
|
@@ -110,50 +137,29 @@ import Input from '@wangs-ui/react-core/primitive/input';
|
|
|
110
137
|
import { useI18n } from '@wangs-ui/react-i18n';
|
|
111
138
|
import { useState } from 'react';
|
|
112
139
|
|
|
113
|
-
interface
|
|
114
|
-
|
|
115
|
-
email: string;
|
|
140
|
+
interface EditUserForm {
|
|
141
|
+
name: string;
|
|
116
142
|
}
|
|
117
143
|
|
|
118
|
-
export function
|
|
144
|
+
export function EditUserModal() {
|
|
119
145
|
const { t } = useI18n();
|
|
120
146
|
const [open, setOpen] = useState(false);
|
|
121
|
-
const { DialogForm, Field, control } = useDialogForm<
|
|
147
|
+
const { DialogForm, Field, control } = useDialogForm<EditUserForm>();
|
|
122
148
|
|
|
123
149
|
return (
|
|
124
150
|
<>
|
|
125
|
-
<Button label={t('Edit
|
|
126
|
-
|
|
151
|
+
<Button label={t('Edit')} onClick={() => setOpen(true)} />
|
|
127
152
|
<DialogForm
|
|
128
153
|
closeOnSubmit
|
|
129
154
|
control={control}
|
|
130
|
-
header={t('Edit
|
|
155
|
+
header={t('Edit User')}
|
|
131
156
|
open={open}
|
|
132
157
|
onOpenChange={setOpen}
|
|
133
|
-
onSubmit={(values) =>
|
|
134
|
-
footer={
|
|
135
|
-
<div className="flex w-full justify-end gap-2">
|
|
136
|
-
<Button
|
|
137
|
-
label={t('Cancel')}
|
|
138
|
-
severity="secondary"
|
|
139
|
-
type="button"
|
|
140
|
-
variant="outline"
|
|
141
|
-
onClick={() => setOpen(false)}
|
|
142
|
-
/>
|
|
143
|
-
<Button label={t('Save Changes')} type="submit" />
|
|
144
|
-
</div>
|
|
145
|
-
}
|
|
158
|
+
onSubmit={(values) => handleSave(values)}
|
|
146
159
|
>
|
|
147
|
-
<
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
</Field>
|
|
151
|
-
<Field required label={t('Email')} name="email">
|
|
152
|
-
{({ fieldProps }) => (
|
|
153
|
-
<Input {...fieldProps} type="email" placeholder="you@example.com" />
|
|
154
|
-
)}
|
|
155
|
-
</Field>
|
|
156
|
-
</div>
|
|
160
|
+
<Field required label={t('Full Name')} name="name">
|
|
161
|
+
{({ fieldProps, fieldState }) => <Input {...fieldProps} invalid={fieldState.invalid} />}
|
|
162
|
+
</Field>
|
|
157
163
|
</DialogForm>
|
|
158
164
|
</>
|
|
159
165
|
);
|
|
@@ -162,35 +168,9 @@ export function EditProfileModal() {
|
|
|
162
168
|
|
|
163
169
|
---
|
|
164
170
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
To observe real-time field changes without causing the entire form to re-render, use `useWatchField` from `@wangs-ui/form/react`.
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
import { useWatchField } from '@wangs-ui/form/react';
|
|
171
|
-
|
|
172
|
-
function FormSummaryWatcher({ control }) {
|
|
173
|
-
// Subscribes only to 'username' value changes
|
|
174
|
-
const username = useWatchField({ control, name: 'username' });
|
|
175
|
-
|
|
176
|
-
return <div>Current Username: {username}</div>;
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## 3. Submission Types
|
|
183
|
-
|
|
184
|
-
| Config | Use case |
|
|
185
|
-
| -------------------------------------------------------------------- | ------------------------- |
|
|
186
|
-
| `useForm<T>()` / `useDialogForm<T>()` | Default JSON submission |
|
|
187
|
-
| `useForm<T, 'multipart/form-data'>({ type: 'multipart/form-data' })` | File uploads (`FormData`) |
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## 4. Mandatory Implementation Rules
|
|
171
|
+
## 4. Mandatory Implementation Guidelines
|
|
192
172
|
|
|
193
|
-
1. **
|
|
194
|
-
2. **
|
|
195
|
-
3. **
|
|
196
|
-
4. **Server
|
|
173
|
+
1. **Query MCP First**: Never guess component props or story examples — inspect via MCP tools.
|
|
174
|
+
2. **Granular Primitive Subpaths**: Import primitives via exact subpath modules (`@wangs-ui/react-core/primitive/form`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/input`).
|
|
175
|
+
3. **i18n Localization**: Wrap all user-visible labels, placeholders, and error strings in `t('...')` from `@wangs-ui/react-i18n`.
|
|
176
|
+
4. **Server Error Mapping**: Map HTTP validation errors (e.g. 422 response) into the form using `control.setErrors(apiErrors)`.
|
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { cancel, intro, isCancel, multiselect, outro } from "@clack/prompts";
|
|
5
5
|
//#region skills/create-form/SKILL.md?raw
|
|
6
|
-
var SKILL_default$7 = "---\nname: create-form\ndescription:
|
|
6
|
+
var SKILL_default$7 = "---\nname: create-form\ndescription: Form architecture, validation workflows, strongly-typed forms (useForm, useDialogForm, useWatchField), initialValues/reset lifecycle, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.\n---\n\n# Skill: Form Architecture & Validation Workflows\n\nUse this skill when building forms, data entry panels, modal forms, settings pages, or multipart forms in Wangs UI applications.\n\n---\n\n## 1. MCP Protocol & Component Rules (Mandatory Single Source of Truth)\n\nDo **NOT** hardcode or guess prop names, component options, preset variations, or Storybook patterns in this document. Always retrieve component definitions, active props, and live Storybook implementations directly via MCP:\n\n### Component & Form Documentation Protocol:\n\n```json\nget-documentation({ \"id\": \"form\" })\nget-documentation({ \"id\": \"field\" })\nget-documentation({ \"id\": \"dialogform\" })\nget-documentation({ \"id\": \"input\" })\nget-documentation({ \"id\": \"numberinput\" })\nget-documentation({ \"id\": \"select\" })\nget-documentation({ \"id\": \"multiselect\" })\nget-documentation({ \"id\": \"datepicker\" })\nget-documentation({ \"id\": \"fileupload\" })\n```\n\n### Live Storybook & Interactive Behavior Protocol:\n\n```json\nget-documentation-for-story({ \"id\": \"form\", \"storyName\": \"Default\" })\nget-documentation-for-story({ \"id\": \"form\", \"storyName\": \"AsyncInitialValues\" })\nget-documentation-for-story({ \"id\": \"form\", \"storyName\": \"ConditionalFields\" })\nget-documentation-for-story({ \"id\": \"form\", \"storyName\": \"CascadingOptions\" })\nget-documentation-for-story({ \"id\": \"dialogform\", \"storyName\": \"Default\" })\n```\n\n### Knowledge Graph & Symbol Usages:\n\n```json\nquery_graph({ \"query\": \"useForm\" })\nquery_graph({ \"query\": \"useDialogForm\" })\nquery_graph({ \"query\": \"useWatchField\" })\n```\n\n---\n\n## 2. Core Form Concepts & Lifecycle Mechanics\n\n### A. Strongly Typed Form Instance (`useForm<TForm>()`)\n\n`useForm<TForm>()` instantiates a `FormControl` natively bound to model type `TForm`.\n\n- `Field`: `name` is strictly typed to `Path<TForm>` dot-paths.\n- `useWatchField`: `name` is strictly typed to `Path<TForm>`.\n- `control`: Provides `setInitialValues`, `setValues`, `setFieldError`, `setErrors`, and `reset`.\n\n### B. Dynamic Initial Values & Baseline Reset (`setInitialValues` vs `setValues`)\n\n1. **Async Initial Values (`control.setInitialValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object (e.g. fetched from an API).\n - Establishes an **immutable baseline** for registered fields. Once set for a field path, subsequent calls to `setInitialValues` for that path are ignored.\n2. **Batch Value Updates (`control.setValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object to update current input values without altering the initial baseline.\n3. **Reset Behavior (`control.reset()`)**:\n - Restores all fields back to their registered initial baseline values (set via `setInitialValues` or field `initialValue`) and clears all field-level validation errors.\n\n### C. Primitive Component Integration Architecture\n\n`Field` serves as the form integration wrapper for primitive UI input components (`Input`, `Select`, `MultiSelect`, `DatePicker`, `NumberInput`, `FileUpload`, `Calendar`, etc.):\n\n- **Children Render Callback**: `Field` yields `{ fieldProps, fieldState }`.\n- **`fieldProps`**: Pass directly to primitive inputs (`<Input {...fieldProps} />`). Contains `name`, `value`, `ref`, `onChange`.\n- **`fieldState`**: Provides `invalid`, `error`, `isDirty`, `isPending`. Pass `invalid={fieldState.invalid}` to primitive components for accessibility and validation styling.\n\n---\n\n## 3. High-Level Form Architecture & Usage Patterns\n\n### Pattern 1: Page Forms (`useForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useForm } from '@wangs-ui/react-core/primitive/form';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useEffect } from 'react';\n\ninterface UserProfile {\n name: string;\n email: string;\n}\n\nexport function UserProfilePage({ userId }: { userId: string }) {\n const { t } = useI18n();\n const { Form, Field, control } = useForm<UserProfile>();\n\n useEffect(() => {\n async function loadData() {\n const data = await fetchUserData(userId);\n // Establish immutable initial baseline from async response\n control.setInitialValues(data);\n }\n loadData();\n }, [userId, control]);\n\n return (\n <Form control={control} onSubmit={(values) => saveUserData(values)}>\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => (\n <Input {...fieldProps} invalid={fieldState.invalid} placeholder={t('Enter full name')} />\n )}\n </Field>\n\n <div className=\"flex gap-2\">\n <Button\n label={t('Reset')}\n type=\"button\"\n variant=\"outlined\"\n onClick={() => control.reset()}\n />\n <Button label={t('Save')} type=\"submit\" />\n </div>\n </Form>\n );\n}\n```\n\n### Pattern 2: Modal Forms (`useDialogForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useDialogForm } from '@wangs-ui/react-core/primitive/dialogform';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useState } from 'react';\n\ninterface EditUserForm {\n name: string;\n}\n\nexport function EditUserModal() {\n const { t } = useI18n();\n const [open, setOpen] = useState(false);\n const { DialogForm, Field, control } = useDialogForm<EditUserForm>();\n\n return (\n <>\n <Button label={t('Edit')} onClick={() => setOpen(true)} />\n <DialogForm\n closeOnSubmit\n control={control}\n header={t('Edit User')}\n open={open}\n onOpenChange={setOpen}\n onSubmit={(values) => handleSave(values)}\n >\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => <Input {...fieldProps} invalid={fieldState.invalid} />}\n </Field>\n </DialogForm>\n </>\n );\n}\n```\n\n---\n\n## 4. Mandatory Implementation Guidelines\n\n1. **Query MCP First**: Never guess component props or story examples — inspect via MCP tools.\n2. **Granular Primitive Subpaths**: Import primitives via exact subpath modules (`@wangs-ui/react-core/primitive/form`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/input`).\n3. **i18n Localization**: Wrap all user-visible labels, placeholders, and error strings in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Server Error Mapping**: Map HTTP validation errors (e.g. 422 response) into the form using `control.setErrors(apiErrors)`.\n";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region skills/data-table/SKILL.md?raw
|
|
9
9
|
var SKILL_default$6 = "---\nname: data-table\ndescription: Architecture, workflows, and MCP discovery protocol for building DataTables with sorting, pagination, filtering, selection, and export.\n---\n\n# Skill: DataTable Architecture & Integration Workflows\n\nUse this skill when implementing data grids, server-paginated tables, filterable listing views, or batch management interfaces with `@wangs-ui/react-core`.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess table prop names or hardcode table structures. Query the MCP server dynamically to inspect exact TypeScript signatures, live story implementations, and companion controls:\n\n### Inspect Component Contracts:\n\n```json\nget-documentation({ \"id\": \"datatable\" })\nget-documentation({ \"id\": \"exportbutton\" })\nget-documentation({ \"id\": \"filtercontainer\" })\nget-documentation({ \"id\": \"bulkactionbutton\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"Basic\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"ServerPagination\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"Sortable\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"MultipleSelection\" })\nget-documentation-for-story({ \"id\": \"datatable\", \"storyName\": \"CustomColumn\" })\nget-documentation-for-story({ \"id\": \"exportbutton\", \"storyName\": \"WithTable\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"DataTable\" })\nquery_graph({ \"query\": \"useDataTableFetch\" })\n```\n\n---\n\n## 2. Core Architecture & Mental Model\n\nThe Wangs UI `DataTable` is built on a modular, headless-first architecture:\n\n1. **Declarative Column Definitions (`TableColumn<T>[]`)**:\n Columns are configured as typed array objects, not as JSX children. Check `get-documentation({ \"id\": \"datatable\" })` for column field types.\n2. **Table Instance Hook (`useDataTable`)**:\n Coordinates table state (sorting, pagination, selection, column ordering, pinning, visibility).\n3. **Data Fetching Hook (`useDataTableFetch`)**:\n Feeds server-side data, handles loading indicators, manages query parameters (`search`, `filter`, `sort`, `page`, `limit`), and debounces requests automatically.\n4. **Ecosystem Companions**:\n - `FilterContainer` & `FilterToggleButton`: Filter popovers and faceted search.\n - `ExportButton`: Client/server export to Excel, CSV, PDF, or Print.\n - `BulkActionButton`: Contextual batch actions triggered when rows are selected.\n - `CustomColumn`: User-controlled column ordering, visibility toggling, and pinning.\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Query MCP for Current Code Patterns**: Always run `get-documentation-for-story` for `datatable` before drafting code.\n2. **Strict Subpath Imports**: Import via `@wangs-ui/react-core/primitive/datatable` and companion primitive paths.\n3. **Always Translate Visible Copy**: All column header labels, empty state messages, and action button labels must be wrapped in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Stable Row Identity**: Always configure a unique key identifier for stable selection and row identity.\n";
|
|
@@ -144,7 +144,7 @@ function removeSkill(skillId, baseDir = process.cwd()) {
|
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region src/commands/list.ts
|
|
146
146
|
function listSkills(baseDir = process.cwd()) {
|
|
147
|
-
intro(`\x1b[1m\x1b[36m📦 Wangs UI Consumer Skills Registry\x1b[0m [2m(v1.1.0-alpha.
|
|
147
|
+
intro(`\x1b[1m\x1b[36m📦 Wangs UI Consumer Skills Registry\x1b[0m [2m(v1.1.0-alpha.8)[0m`);
|
|
148
148
|
const allSkills = loadAllSkills();
|
|
149
149
|
const targetDirs = getAgentSkillDirs(baseDir);
|
|
150
150
|
if (allSkills.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-form
|
|
3
|
-
description:
|
|
3
|
+
description: Form architecture, validation workflows, strongly-typed forms (useForm, useDialogForm, useWatchField), initialValues/reset lifecycle, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Skill: Form Architecture & Validation Workflows
|
|
@@ -9,11 +9,11 @@ Use this skill when building forms, data entry panels, modal forms, settings pag
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
## 1. MCP
|
|
12
|
+
## 1. MCP Protocol & Component Rules (Mandatory Single Source of Truth)
|
|
13
13
|
|
|
14
|
-
Do **NOT** hardcode or guess prop names,
|
|
14
|
+
Do **NOT** hardcode or guess prop names, component options, preset variations, or Storybook patterns in this document. Always retrieve component definitions, active props, and live Storybook implementations directly via MCP:
|
|
15
15
|
|
|
16
|
-
###
|
|
16
|
+
### Component & Form Documentation Protocol:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
19
|
get-documentation({ "id": "form" })
|
|
@@ -27,17 +27,17 @@ get-documentation({ "id": "datepicker" })
|
|
|
27
27
|
get-documentation({ "id": "fileupload" })
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Live Storybook & Interactive Behavior Protocol:
|
|
31
31
|
|
|
32
32
|
```json
|
|
33
33
|
get-documentation-for-story({ "id": "form", "storyName": "Default" })
|
|
34
|
-
get-documentation-for-story({ "id": "
|
|
35
|
-
get-documentation-for-story({ "id": "
|
|
36
|
-
get-documentation-for-story({ "id": "
|
|
37
|
-
get-documentation-for-story({ "id": "
|
|
34
|
+
get-documentation-for-story({ "id": "form", "storyName": "AsyncInitialValues" })
|
|
35
|
+
get-documentation-for-story({ "id": "form", "storyName": "ConditionalFields" })
|
|
36
|
+
get-documentation-for-story({ "id": "form", "storyName": "CascadingOptions" })
|
|
37
|
+
get-documentation-for-story({ "id": "dialogform", "storyName": "Default" })
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
###
|
|
40
|
+
### Knowledge Graph & Symbol Usages:
|
|
41
41
|
|
|
42
42
|
```json
|
|
43
43
|
query_graph({ "query": "useForm" })
|
|
@@ -47,61 +47,88 @@ query_graph({ "query": "useWatchField" })
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
-
## 2. Form
|
|
50
|
+
## 2. Core Form Concepts & Lifecycle Mechanics
|
|
51
51
|
|
|
52
|
-
###
|
|
52
|
+
### A. Strongly Typed Form Instance (`useForm<TForm>()`)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
`useForm<TForm>()` instantiates a `FormControl` natively bound to model type `TForm`.
|
|
55
|
+
|
|
56
|
+
- `Field`: `name` is strictly typed to `Path<TForm>` dot-paths.
|
|
57
|
+
- `useWatchField`: `name` is strictly typed to `Path<TForm>`.
|
|
58
|
+
- `control`: Provides `setInitialValues`, `setValues`, `setFieldError`, `setErrors`, and `reset`.
|
|
59
|
+
|
|
60
|
+
### B. Dynamic Initial Values & Baseline Reset (`setInitialValues` vs `setValues`)
|
|
61
|
+
|
|
62
|
+
1. **Async Initial Values (`control.setInitialValues(values)`)**:
|
|
63
|
+
- Accepts a `Partial<TForm>` JSON object (e.g. fetched from an API).
|
|
64
|
+
- Establishes an **immutable baseline** for registered fields. Once set for a field path, subsequent calls to `setInitialValues` for that path are ignored.
|
|
65
|
+
2. **Batch Value Updates (`control.setValues(values)`)**:
|
|
66
|
+
- Accepts a `Partial<TForm>` JSON object to update current input values without altering the initial baseline.
|
|
67
|
+
3. **Reset Behavior (`control.reset()`)**:
|
|
68
|
+
- Restores all fields back to their registered initial baseline values (set via `setInitialValues` or field `initialValue`) and clears all field-level validation errors.
|
|
69
|
+
|
|
70
|
+
### C. Primitive Component Integration Architecture
|
|
71
|
+
|
|
72
|
+
`Field` serves as the form integration wrapper for primitive UI input components (`Input`, `Select`, `MultiSelect`, `DatePicker`, `NumberInput`, `FileUpload`, `Calendar`, etc.):
|
|
73
|
+
|
|
74
|
+
- **Children Render Callback**: `Field` yields `{ fieldProps, fieldState }`.
|
|
75
|
+
- **`fieldProps`**: Pass directly to primitive inputs (`<Input {...fieldProps} />`). Contains `name`, `value`, `ref`, `onChange`.
|
|
76
|
+
- **`fieldState`**: Provides `invalid`, `error`, `isDirty`, `isPending`. Pass `invalid={fieldState.invalid}` to primitive components for accessibility and validation styling.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. High-Level Form Architecture & Usage Patterns
|
|
81
|
+
|
|
82
|
+
### Pattern 1: Page Forms (`useForm<T>()`)
|
|
55
83
|
|
|
56
84
|
```tsx
|
|
57
85
|
import Button from '@wangs-ui/react-core/primitive/button';
|
|
58
86
|
import { useForm } from '@wangs-ui/react-core/primitive/form';
|
|
59
87
|
import Input from '@wangs-ui/react-core/primitive/input';
|
|
60
88
|
import { useI18n } from '@wangs-ui/react-i18n';
|
|
89
|
+
import { useEffect } from 'react';
|
|
61
90
|
|
|
62
|
-
interface
|
|
91
|
+
interface UserProfile {
|
|
63
92
|
name: string;
|
|
64
93
|
email: string;
|
|
65
|
-
address: {
|
|
66
|
-
city: string;
|
|
67
|
-
};
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
export function
|
|
96
|
+
export function UserProfilePage({ userId }: { userId: string }) {
|
|
71
97
|
const { t } = useI18n();
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
const { Form, Field, control } = useForm<UserProfile>();
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
async function loadData() {
|
|
102
|
+
const data = await fetchUserData(userId);
|
|
103
|
+
// Establish immutable initial baseline from async response
|
|
104
|
+
control.setInitialValues(data);
|
|
105
|
+
}
|
|
106
|
+
loadData();
|
|
107
|
+
}, [userId, control]);
|
|
74
108
|
|
|
75
109
|
return (
|
|
76
|
-
<Form control={control} onSubmit={(values) =>
|
|
110
|
+
<Form control={control} onSubmit={(values) => saveUserData(values)}>
|
|
77
111
|
<Field required label={t('Full Name')} name="name">
|
|
78
|
-
{({ fieldProps }) =>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{/* Autocompletes nested dot-path keys */}
|
|
82
|
-
<Field required label={t('City')} name="address.city">
|
|
83
|
-
{({ fieldProps }) => <Input {...fieldProps} placeholder={t('Enter city')} />}
|
|
112
|
+
{({ fieldProps, fieldState }) => (
|
|
113
|
+
<Input {...fieldProps} invalid={fieldState.invalid} placeholder={t('Enter full name')} />
|
|
114
|
+
)}
|
|
84
115
|
</Field>
|
|
85
116
|
|
|
86
117
|
<div className="flex gap-2">
|
|
87
118
|
<Button
|
|
88
119
|
label={t('Reset')}
|
|
89
120
|
type="button"
|
|
90
|
-
|
|
121
|
+
variant="outlined"
|
|
91
122
|
onClick={() => control.reset()}
|
|
92
123
|
/>
|
|
93
|
-
<Button label={t('
|
|
124
|
+
<Button label={t('Save')} type="submit" />
|
|
94
125
|
</div>
|
|
95
126
|
</Form>
|
|
96
127
|
);
|
|
97
128
|
}
|
|
98
129
|
```
|
|
99
130
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
### Pattern B: Modal Forms with `useDialogForm<T>()`
|
|
103
|
-
|
|
104
|
-
For modal/dialog forms, use `useDialogForm<T>()` from `@wangs-ui/react-core/primitive/dialogform`. It combines a modal `Dialog` with form provider lifecycle, providing automatic modal close on success (`closeOnSubmit`), validation interception, and native Enter-key submission.
|
|
131
|
+
### Pattern 2: Modal Forms (`useDialogForm<T>()`)
|
|
105
132
|
|
|
106
133
|
```tsx
|
|
107
134
|
import Button from '@wangs-ui/react-core/primitive/button';
|
|
@@ -110,50 +137,29 @@ import Input from '@wangs-ui/react-core/primitive/input';
|
|
|
110
137
|
import { useI18n } from '@wangs-ui/react-i18n';
|
|
111
138
|
import { useState } from 'react';
|
|
112
139
|
|
|
113
|
-
interface
|
|
114
|
-
|
|
115
|
-
email: string;
|
|
140
|
+
interface EditUserForm {
|
|
141
|
+
name: string;
|
|
116
142
|
}
|
|
117
143
|
|
|
118
|
-
export function
|
|
144
|
+
export function EditUserModal() {
|
|
119
145
|
const { t } = useI18n();
|
|
120
146
|
const [open, setOpen] = useState(false);
|
|
121
|
-
const { DialogForm, Field, control } = useDialogForm<
|
|
147
|
+
const { DialogForm, Field, control } = useDialogForm<EditUserForm>();
|
|
122
148
|
|
|
123
149
|
return (
|
|
124
150
|
<>
|
|
125
|
-
<Button label={t('Edit
|
|
126
|
-
|
|
151
|
+
<Button label={t('Edit')} onClick={() => setOpen(true)} />
|
|
127
152
|
<DialogForm
|
|
128
153
|
closeOnSubmit
|
|
129
154
|
control={control}
|
|
130
|
-
header={t('Edit
|
|
155
|
+
header={t('Edit User')}
|
|
131
156
|
open={open}
|
|
132
157
|
onOpenChange={setOpen}
|
|
133
|
-
onSubmit={(values) =>
|
|
134
|
-
footer={
|
|
135
|
-
<div className="flex w-full justify-end gap-2">
|
|
136
|
-
<Button
|
|
137
|
-
label={t('Cancel')}
|
|
138
|
-
severity="secondary"
|
|
139
|
-
type="button"
|
|
140
|
-
variant="outline"
|
|
141
|
-
onClick={() => setOpen(false)}
|
|
142
|
-
/>
|
|
143
|
-
<Button label={t('Save Changes')} type="submit" />
|
|
144
|
-
</div>
|
|
145
|
-
}
|
|
158
|
+
onSubmit={(values) => handleSave(values)}
|
|
146
159
|
>
|
|
147
|
-
<
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
</Field>
|
|
151
|
-
<Field required label={t('Email')} name="email">
|
|
152
|
-
{({ fieldProps }) => (
|
|
153
|
-
<Input {...fieldProps} type="email" placeholder="you@example.com" />
|
|
154
|
-
)}
|
|
155
|
-
</Field>
|
|
156
|
-
</div>
|
|
160
|
+
<Field required label={t('Full Name')} name="name">
|
|
161
|
+
{({ fieldProps, fieldState }) => <Input {...fieldProps} invalid={fieldState.invalid} />}
|
|
162
|
+
</Field>
|
|
157
163
|
</DialogForm>
|
|
158
164
|
</>
|
|
159
165
|
);
|
|
@@ -162,35 +168,9 @@ export function EditProfileModal() {
|
|
|
162
168
|
|
|
163
169
|
---
|
|
164
170
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
To observe real-time field changes without causing the entire form to re-render, use `useWatchField` from `@wangs-ui/form/react`.
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
import { useWatchField } from '@wangs-ui/form/react';
|
|
171
|
-
|
|
172
|
-
function FormSummaryWatcher({ control }) {
|
|
173
|
-
// Subscribes only to 'username' value changes
|
|
174
|
-
const username = useWatchField({ control, name: 'username' });
|
|
175
|
-
|
|
176
|
-
return <div>Current Username: {username}</div>;
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## 3. Submission Types
|
|
183
|
-
|
|
184
|
-
| Config | Use case |
|
|
185
|
-
| -------------------------------------------------------------------- | ------------------------- |
|
|
186
|
-
| `useForm<T>()` / `useDialogForm<T>()` | Default JSON submission |
|
|
187
|
-
| `useForm<T, 'multipart/form-data'>({ type: 'multipart/form-data' })` | File uploads (`FormData`) |
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## 4. Mandatory Implementation Rules
|
|
171
|
+
## 4. Mandatory Implementation Guidelines
|
|
192
172
|
|
|
193
|
-
1. **
|
|
194
|
-
2. **
|
|
195
|
-
3. **
|
|
196
|
-
4. **Server
|
|
173
|
+
1. **Query MCP First**: Never guess component props or story examples — inspect via MCP tools.
|
|
174
|
+
2. **Granular Primitive Subpaths**: Import primitives via exact subpath modules (`@wangs-ui/react-core/primitive/form`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/input`).
|
|
175
|
+
3. **i18n Localization**: Wrap all user-visible labels, placeholders, and error strings in `t('...')` from `@wangs-ui/react-i18n`.
|
|
176
|
+
4. **Server Error Mapping**: Map HTTP validation errors (e.g. 422 response) into the form using `control.setErrors(apiErrors)`.
|