@loykin/designkit 0.0.2 → 0.0.4
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/README.md +208 -125
- package/cli/designkit.mjs +78 -0
- package/dist/index.cjs +65 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +65 -5
- package/dist/index.js.map +1 -1
- package/docs/guides/README.md +20 -0
- package/docs/guides/commerce-workflow.md +47 -0
- package/docs/guides/form-workflow.md +212 -0
- package/docs/guides/managed-table.md +435 -0
- package/docs/guides/manifest.json +48 -0
- package/docs/guides/publishing-workflow.md +53 -0
- package/package.json +17 -5
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# DesignKit Implementation Guides
|
|
2
|
+
|
|
3
|
+
The Playground's **Guides** group contains complete product flows. Existing template groups remain visual API references; they do not receive an AI guide merely because a workflow uses that template.
|
|
4
|
+
|
|
5
|
+
| Workflow | Pattern ID | Connected destinations | Contract |
|
|
6
|
+
| ------------------- | --------------------- | -------------------------------------- | ----------------------------------------------- |
|
|
7
|
+
| Resource Management | `managed-table` | table → detail Sheet; create/edit page | [Managed Table](./managed-table.md) |
|
|
8
|
+
| Forms | `form-workflow` | create, edit, and settings pages | [Stacked Form](./form-workflow.md) |
|
|
9
|
+
| Publishing | `publishing-workflow` | blog collection → article route | [Publishing Workflow](./publishing-workflow.md) |
|
|
10
|
+
| Commerce | `commerce-workflow` | filtered catalog → product route | [Commerce Workflow](./commerce-workflow.md) |
|
|
11
|
+
|
|
12
|
+
## How to use these guides
|
|
13
|
+
|
|
14
|
+
1. Choose the guide by the product workflow, not by a component name or sample entity.
|
|
15
|
+
2. Give the complete guide Markdown and its Playground source to the AI.
|
|
16
|
+
3. Preserve route, query, action-placement, loading, and page-template boundaries.
|
|
17
|
+
4. Replace mock API functions and sample records with application code.
|
|
18
|
+
5. Use existing template demos only to compare supported visual variants.
|
|
19
|
+
|
|
20
|
+
Each route renders exactly one page-level template. A workflow may connect different templates across routes, but must never nest one page-level template inside another.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Commerce Workflow Contract for AI
|
|
2
|
+
|
|
3
|
+
Use this guide to build a discovery-to-decision commerce experience. The executable reference is **Guides / Commerce / Catalog → Product**.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- Pattern ID: `commerce-workflow`
|
|
8
|
+
- Catalog route template: `BrowseBodyTemplate`
|
|
9
|
+
- Product route template: `DetailBodyTemplate`
|
|
10
|
+
- Server state: TanStack Query
|
|
11
|
+
- Executable source: `playground/src/templates/demos/guides/CommerceWorkflowGuide.tsx`
|
|
12
|
+
|
|
13
|
+
The existing **Browse / Catalog** and product detail template demos remain visual references. This Guide defines their behavioral connection.
|
|
14
|
+
|
|
15
|
+
## Route contract
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
/products → filterable and sortable catalog
|
|
19
|
+
/products/:productSlug → linkable product destination
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Selecting a product card navigates to the product route. Browser Back and the product breadcrumb return to the catalog. A product decision page is not a Sheet and must not be rendered inside the catalog template.
|
|
23
|
+
|
|
24
|
+
## Query and filter ownership
|
|
25
|
+
|
|
26
|
+
- `CatalogPage` owns category, search, sort, pagination, and `['commerce', 'products', ...inputs]`.
|
|
27
|
+
- Every server-state input belongs in the query key.
|
|
28
|
+
- `ProductPage` owns `['commerce', 'product', slug]`.
|
|
29
|
+
- Filters stay in `BrowseBodyTemplate.sidebar`; sort and result count stay in `toolbar`.
|
|
30
|
+
- Initial loading replaces only catalog cards. Background refetch keeps current cards mounted.
|
|
31
|
+
|
|
32
|
+
## Layout and action rules
|
|
33
|
+
|
|
34
|
+
- Catalog discovery actions and filters remain on the catalog route.
|
|
35
|
+
- Product-specific status, price, variants, purchase action, and delivery information belong on the detail route.
|
|
36
|
+
- Use `DetailBodyTemplate` media layout when product media is a primary decision input.
|
|
37
|
+
- Render exactly one page-level template per route.
|
|
38
|
+
|
|
39
|
+
## AI reconstruction checklist
|
|
40
|
+
|
|
41
|
+
- [ ] Real catalog and product routes
|
|
42
|
+
- [ ] `BrowseBodyTemplate` catalog and `DetailBodyTemplate` product
|
|
43
|
+
- [ ] Filters and sort included in the catalog query key
|
|
44
|
+
- [ ] Product card click navigates to a slug route
|
|
45
|
+
- [ ] Product breadcrumb returns to catalog
|
|
46
|
+
- [ ] Purchase action exists only on the product route
|
|
47
|
+
- [ ] No nested page templates and no product Sheet
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Stacked Form Workflow Contract for AI
|
|
2
|
+
|
|
3
|
+
Use this contract for create, edit, and settings forms. It standardizes the visual structure and component boundaries. It does not prescribe a form-state, validation, routing, or data-fetching library.
|
|
4
|
+
|
|
5
|
+
## Canonical reference
|
|
6
|
+
|
|
7
|
+
- Pattern ID: `form-workflow`
|
|
8
|
+
- Playground: **Guides / Forms / Stacked Form**
|
|
9
|
+
- Page template: `DataBodyTemplate`
|
|
10
|
+
- Section primitive: `DataBodyTemplate.Group layout="stacked"`
|
|
11
|
+
|
|
12
|
+
The existing **DataBodyTemplate / Form / Horizontal**, **Stacked**, and **Inline** entries are visual API references. When generating a product form, follow this guide instead of selecting among those demos.
|
|
13
|
+
|
|
14
|
+
The executable Playground example uses React Hook Form to demonstrate shared state across modular section components, controlled DesignKit inputs, and field-level errors. React Hook Form is a Playground implementation choice, not a DesignKit dependency or part of this visual contract.
|
|
15
|
+
|
|
16
|
+
## Non-negotiable visual contract
|
|
17
|
+
|
|
18
|
+
1. Render the form as a full route page by default. Do not use a Sheet for ordinary create, edit, or settings work.
|
|
19
|
+
2. Keep the route hierarchy in `PageTopBar`: for example, `Resources / Members / Add member`.
|
|
20
|
+
3. Use one `DataBodyTemplate` page root.
|
|
21
|
+
4. Use the stacked form shape for create, edit, and settings pages. Do not switch to `horizontal` or `inline` because the domain changed.
|
|
22
|
+
5. Divide the form into one or more `DataBodyTemplate.Group layout="stacked"` sections according to meaning, not visual preference.
|
|
23
|
+
6. Keep field spacing at `space-y-3`; keep each label/control/help block at `space-y-1.5`.
|
|
24
|
+
7. Use DesignKit controls and the established compact control sizing: `h-8 text-sm` for inputs and selects, `size="sm"` with `h-8 text-xs` for actions.
|
|
25
|
+
8. Put Cancel before the primary submit action. Keep the action cluster right-aligned at the bottom of its save boundary.
|
|
26
|
+
9. Show validation text directly below its field. Show form-level failure above the bottom actions, inside the same form boundary.
|
|
27
|
+
10. A submitting, refreshing, or validation state must not replace or flash the page header or unrelated groups.
|
|
28
|
+
|
|
29
|
+
## Module boundary contract
|
|
30
|
+
|
|
31
|
+
Treat each semantic group like tab-scoped content: implement it as a named component instead of one large page function.
|
|
32
|
+
|
|
33
|
+
- The route component owns only `DataBodyTemplate`, breadcrumb, title, and route-level description.
|
|
34
|
+
- A form component owns the submit boundary and bottom actions.
|
|
35
|
+
- Each section component returns one stacked `DataBodyTemplate.Group`.
|
|
36
|
+
- Section-only state and asynchronous work stay in that section component when possible.
|
|
37
|
+
- Shared form state may be provided by the form component through props, context, React Hook Form, or another application-selected mechanism.
|
|
38
|
+
- Do not lift section-only pending, error, or refresh state into the page header.
|
|
39
|
+
- Do not nest another page-level template inside the form or a Group.
|
|
40
|
+
|
|
41
|
+
Separating components is a behavior boundary, not permission to change their layout. Every section still uses the same stacked Group contract.
|
|
42
|
+
|
|
43
|
+
## Save boundaries
|
|
44
|
+
|
|
45
|
+
The visual system remains the same; only form ownership changes.
|
|
46
|
+
|
|
47
|
+
| Workflow | Form ownership | Actions |
|
|
48
|
+
| -------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------- |
|
|
49
|
+
| Create or edit one record | One form owns all stacked sections | One bottom action row after the final section |
|
|
50
|
+
| Settings saved as one document | One form owns all stacked sections | One bottom action row after the final section |
|
|
51
|
+
| Settings with independently saved categories | Each section component owns its own form | Each stacked section ends with its own right-aligned action row |
|
|
52
|
+
|
|
53
|
+
Do not create separate forms merely because the screen contains multiple Groups. Split forms only when the server-side save boundaries are genuinely independent.
|
|
54
|
+
|
|
55
|
+
## Canonical composition
|
|
56
|
+
|
|
57
|
+
The following is the concrete React Hook Form composition used by the Playground. Applications may replace React Hook Form, but must preserve the same component and visual boundaries.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form'
|
|
61
|
+
|
|
62
|
+
interface MemberFormValues {
|
|
63
|
+
name: string
|
|
64
|
+
email: string
|
|
65
|
+
role: string
|
|
66
|
+
active: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function IdentitySection() {
|
|
70
|
+
const {
|
|
71
|
+
register,
|
|
72
|
+
formState: { errors },
|
|
73
|
+
} = useFormContext<MemberFormValues>()
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<DataBodyTemplate.Group
|
|
77
|
+
layout="stacked"
|
|
78
|
+
title="Identity"
|
|
79
|
+
description="Basic account information."
|
|
80
|
+
>
|
|
81
|
+
<div className="space-y-3">
|
|
82
|
+
<div className="space-y-1.5">
|
|
83
|
+
<Label htmlFor="member-name" className="text-xs">
|
|
84
|
+
Name
|
|
85
|
+
</Label>
|
|
86
|
+
<Input
|
|
87
|
+
id="member-name"
|
|
88
|
+
{...register('name', { required: 'Enter a member name.' })}
|
|
89
|
+
aria-invalid={Boolean(errors.name)}
|
|
90
|
+
className="h-8 text-sm"
|
|
91
|
+
/>
|
|
92
|
+
{errors.name && <p className="text-xs text-destructive">{errors.name.message}</p>}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</DataBodyTemplate.Group>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function AccessSection() {
|
|
100
|
+
const { control } = useFormContext<MemberFormValues>()
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<DataBodyTemplate.Group
|
|
104
|
+
layout="stacked"
|
|
105
|
+
title="Role & access"
|
|
106
|
+
description="Default workspace permissions."
|
|
107
|
+
>
|
|
108
|
+
<div className="space-y-3">
|
|
109
|
+
<div className="space-y-1.5">
|
|
110
|
+
<Label htmlFor="member-role" className="text-xs">
|
|
111
|
+
Role
|
|
112
|
+
</Label>
|
|
113
|
+
<Controller
|
|
114
|
+
control={control}
|
|
115
|
+
name="role"
|
|
116
|
+
render={({ field }) => (
|
|
117
|
+
<Select value={field.value} onValueChange={field.onChange}>
|
|
118
|
+
<SelectTrigger id="member-role" className="h-8 text-sm">
|
|
119
|
+
<SelectValue />
|
|
120
|
+
</SelectTrigger>
|
|
121
|
+
<SelectContent>{/* roles */}</SelectContent>
|
|
122
|
+
</Select>
|
|
123
|
+
)}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
<Controller
|
|
127
|
+
control={control}
|
|
128
|
+
name="active"
|
|
129
|
+
render={({ field }) => <Switch checked={field.value} onCheckedChange={field.onChange} />}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
</DataBodyTemplate.Group>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function MemberForm() {
|
|
137
|
+
const form = useForm<MemberFormValues>({
|
|
138
|
+
defaultValues: {
|
|
139
|
+
name: '',
|
|
140
|
+
email: '',
|
|
141
|
+
role: 'viewer',
|
|
142
|
+
active: true,
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const submitMember = (values: MemberFormValues) => {
|
|
147
|
+
// Application-owned mutation or submit behavior.
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<FormProvider {...form}>
|
|
152
|
+
<form className="contents" onSubmit={form.handleSubmit(submitMember)}>
|
|
153
|
+
<IdentitySection />
|
|
154
|
+
<AccessSection />
|
|
155
|
+
<div className="flex justify-end gap-2 border-t border-border pt-(--designkit-panel-gap)">
|
|
156
|
+
<Button type="button" variant="outline" size="sm" className="h-8 text-xs">
|
|
157
|
+
Cancel
|
|
158
|
+
</Button>
|
|
159
|
+
<Button type="submit" size="sm" className="h-8 text-xs">
|
|
160
|
+
Save
|
|
161
|
+
</Button>
|
|
162
|
+
</div>
|
|
163
|
+
</form>
|
|
164
|
+
</FormProvider>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function MemberCreatePage() {
|
|
169
|
+
return (
|
|
170
|
+
<DataBodyTemplate
|
|
171
|
+
topBar={<PageTopBar left="Resources / Members / Add member" />}
|
|
172
|
+
title="Add member"
|
|
173
|
+
description="Create a workspace member."
|
|
174
|
+
>
|
|
175
|
+
<MemberForm />
|
|
176
|
+
</DataBodyTemplate>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
React Hook Form is used here to prove that independently implemented Group components can share one form boundary. It remains a Playground-only dependency. Replacing it with ordinary React state, Formik, or another form tool must not change the rendered structure.
|
|
182
|
+
|
|
183
|
+
## Settings with independent saves
|
|
184
|
+
|
|
185
|
+
When settings categories save independently, retain the same stacked appearance and move each form boundary into its section component.
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
function ProfileSettingsSection() {
|
|
189
|
+
return (
|
|
190
|
+
<DataBodyTemplate.Group layout="stacked" title="Profile">
|
|
191
|
+
<form className="space-y-3" onSubmit={saveProfile}>
|
|
192
|
+
{/* profile fields */}
|
|
193
|
+
<div className="flex justify-end">
|
|
194
|
+
<Button type="submit" size="sm" className="h-8 text-xs">
|
|
195
|
+
Save profile
|
|
196
|
+
</Button>
|
|
197
|
+
</div>
|
|
198
|
+
</form>
|
|
199
|
+
</DataBodyTemplate.Group>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Review checklist
|
|
205
|
+
|
|
206
|
+
- Create, edit, and settings screens all use the stacked shape.
|
|
207
|
+
- The page is a route rather than a Sheet unless the task explicitly requires a constrained secondary edit.
|
|
208
|
+
- Breadcrumb, title, Groups, fields, and actions follow the same vertical order.
|
|
209
|
+
- Groups are named components and unrelated section state is not owned by the route header.
|
|
210
|
+
- Group count follows the information model; it does not select a different layout.
|
|
211
|
+
- Form and validation libraries remain application choices.
|
|
212
|
+
- Bottom actions belong to the form that they submit.
|