@loykin/designkit 0.0.3 → 0.0.5
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 +15 -5
- package/cli/designkit.mjs +12 -4
- package/dist/index.cjs +215 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -25
- package/dist/index.d.ts +152 -25
- package/dist/index.js +209 -50
- package/dist/index.js.map +1 -1
- package/docs/guides/README.md +9 -5
- package/docs/guides/ai-ui-implementation-contract.md +52 -0
- package/docs/guides/commerce-workflow.md +3 -0
- package/docs/guides/form-workflow.md +202 -0
- package/docs/guides/kubernetes-workspace.md +169 -0
- package/docs/guides/managed-table.md +66 -55
- package/docs/guides/manifest.json +26 -0
- package/docs/guides/publishing-workflow.md +17 -0
- package/package.json +2 -1
|
@@ -4,6 +4,8 @@ This is the normative implementation contract for a tabbed administrative table
|
|
|
4
4
|
|
|
5
5
|
The Playground's **Guides / Resource Management / Managed Table** screen is the executable end-to-end reference. Its records are sample data; the pattern is defined by the table responsibilities and template composition below, not by that data domain. If an implementation differs from this contract, revise the implementation rather than inventing a local layout.
|
|
6
6
|
|
|
7
|
+
Read [AI UI Implementation Contract](./ai-ui-implementation-contract.md) first.
|
|
8
|
+
|
|
7
9
|
## Pattern identity and reference registry
|
|
8
10
|
|
|
9
11
|
- Pattern ID: `managed-table`
|
|
@@ -16,10 +18,10 @@ The Playground's **Guides / Resource Management / Managed Table** screen is the
|
|
|
16
18
|
|
|
17
19
|
| Status | Playground reference | Role in this pattern |
|
|
18
20
|
| -------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
19
|
-
| Executable pattern | `Guides / Resource Management / Managed Table` | Complete list, tabs, queries, create route, form, pagination, and concise detail
|
|
21
|
+
| Executable pattern | `Guides / Resource Management / Managed Table` | Complete list, tabs, queries, create route, form, pagination, and concise detail SidePanel |
|
|
20
22
|
| Supporting reference | `DataBodyTemplate / Table / Standard` | Base GridKit table composition and table sizing |
|
|
21
|
-
| Supporting
|
|
22
|
-
| Supporting reference | `DetailBodyTemplate / Detail / Record` | Full-page destination when detail outgrows a
|
|
23
|
+
| Supporting contract | `Guides / Forms / Stacked Form` | Create/edit form spacing, modular Groups, and action alignment |
|
|
24
|
+
| Supporting reference | `DetailBodyTemplate / Detail / Record` | Full-page destination when detail outgrows a SidePanel |
|
|
23
25
|
| Supporting reference | `FormWizardBodyTemplate / Wizard` | Multi-step destination when create/edit outgrows a stacked form |
|
|
24
26
|
| Counterexample | `DashboardBodyTemplate / Dashboard` | Monitoring panels are not an administrative table |
|
|
25
27
|
| Counterexample | `BrowseBodyTemplate / Browse` | Consumer discovery is not an administrative resource list |
|
|
@@ -32,8 +34,8 @@ Only the entry marked **Executable pattern** implements this pattern end to end.
|
|
|
32
34
|
| -------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
|
|
33
35
|
| Collection list, tabs, search, filters, pagination | `DataBodyTemplate` + `DataBodyTemplate.Resource` | `Guides / Resource Management / Managed Table` |
|
|
34
36
|
| Base table behavior | GridKit `DataGrid` | `DataBodyTemplate / Table / Standard` |
|
|
35
|
-
| Simple create/edit route | stacked `DataBodyTemplate.Group` | `
|
|
36
|
-
| Concise read-only inspection | `
|
|
37
|
+
| Simple create/edit route | stacked `DataBodyTemplate.Group` | `Guides / Forms / Stacked Form` |
|
|
38
|
+
| Concise read-only inspection | BaseKit `SidePanel` (`@loykin/side-panel`) | `Managed Table` row detail |
|
|
37
39
|
| Complex full-page detail route | `DetailBodyTemplate` | `DetailBodyTemplate / Detail / Record` |
|
|
38
40
|
| Multi-step create/edit route | `FormWizardBodyTemplate` | `FormWizardBodyTemplate / Wizard` |
|
|
39
41
|
| Destructive confirmation | `AlertDialog` | UI primitive contract |
|
|
@@ -72,7 +74,7 @@ This guide covers three destinations for any managed resource:
|
|
|
72
74
|
|
|
73
75
|
```text
|
|
74
76
|
/<resources>
|
|
75
|
-
├─ select row → concise read-only detail
|
|
77
|
+
├─ select row → concise read-only detail SidePanel
|
|
76
78
|
├─ create action → /<resources>/new create page
|
|
77
79
|
└─ edit action → /<resources>/:resourceId/edit page
|
|
78
80
|
```
|
|
@@ -84,12 +86,12 @@ It does not define every `DataBodyTemplate` use case. Settings, dashboards, long
|
|
|
84
86
|
| User intent | Default destination | Reason |
|
|
85
87
|
| ------------------------------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------- |
|
|
86
88
|
| Browse, search, filter, paginate | Page | Stable, linkable working context |
|
|
87
|
-
| Inspect concise read-only row information |
|
|
89
|
+
| Inspect concise read-only row information | SidePanel | Preserves list context |
|
|
88
90
|
| Create or edit an entity | Page with its own URL | Space for validation, permissions, responsive layout, and future fields |
|
|
89
|
-
| Inspect long, editable, multi-section, permission-sensitive, or linkable detail | Page with its own URL |
|
|
91
|
+
| Inspect long, editable, multi-section, permission-sensitive, or linkable detail | Page with its own URL | SidePanel constraints are no longer appropriate |
|
|
90
92
|
| Confirm a destructive action or collect one narrowly scoped value | Modal | Short, blocking decision |
|
|
91
93
|
|
|
92
|
-
A
|
|
94
|
+
A SidePanel is not the default form container. Do not put general create or edit forms in a SidePanel merely because the trigger originates in a table.
|
|
93
95
|
|
|
94
96
|
## Route and page hierarchy
|
|
95
97
|
|
|
@@ -156,7 +158,7 @@ Each tab must be a separate React component and own all state that affects its r
|
|
|
156
158
|
- TanStack Query and query key
|
|
157
159
|
- Search and filters
|
|
158
160
|
- Pagination
|
|
159
|
-
- Row selection and concise detail
|
|
161
|
+
- Row selection and concise detail SidePanel
|
|
160
162
|
- Mutations that belong to the list itself
|
|
161
163
|
|
|
162
164
|
Query keys include every server-state input:
|
|
@@ -302,7 +304,7 @@ Grid rules:
|
|
|
302
304
|
|
|
303
305
|
## Create and edit form page
|
|
304
306
|
|
|
305
|
-
Create and edit forms
|
|
307
|
+
Create and edit forms follow the canonical `form-workflow` contract in **Guides / Forms / Stacked Form**. Preserve the template-owned page width and padding, and split semantic Groups into named section components.
|
|
306
308
|
|
|
307
309
|
```tsx
|
|
308
310
|
function UserCreatePage() {
|
|
@@ -332,43 +334,21 @@ function UserCreatePage() {
|
|
|
332
334
|
description="Identity and access settings for the new account."
|
|
333
335
|
>
|
|
334
336
|
<form className="space-y-3" onSubmit={handleSubmit}>
|
|
335
|
-
<
|
|
336
|
-
<Label htmlFor="user-name" className="text-xs">
|
|
337
|
-
Name
|
|
338
|
-
</Label>
|
|
337
|
+
<FormField label="Name" htmlFor="user-name">
|
|
339
338
|
<Input id="user-name" className="h-8 text-sm" required />
|
|
340
|
-
</
|
|
341
|
-
<
|
|
342
|
-
<Label htmlFor="user-email" className="text-xs">
|
|
343
|
-
Email
|
|
344
|
-
</Label>
|
|
339
|
+
</FormField>
|
|
340
|
+
<FormField label="Email" htmlFor="user-email">
|
|
345
341
|
<Input id="user-email" type="email" className="h-8 text-sm" required />
|
|
346
|
-
</
|
|
347
|
-
<
|
|
348
|
-
<Label htmlFor="user-role" className="text-xs">
|
|
349
|
-
Role
|
|
350
|
-
</Label>
|
|
342
|
+
</FormField>
|
|
343
|
+
<FormField label="Role" htmlFor="user-role">
|
|
351
344
|
<Select defaultValue="Viewer">
|
|
352
345
|
<SelectTrigger id="user-role" className="h-8 text-sm">
|
|
353
346
|
<SelectValue />
|
|
354
347
|
</SelectTrigger>
|
|
355
348
|
<SelectContent>{/* roles */}</SelectContent>
|
|
356
349
|
</Select>
|
|
357
|
-
</
|
|
358
|
-
<
|
|
359
|
-
<Button
|
|
360
|
-
type="button"
|
|
361
|
-
variant="outline"
|
|
362
|
-
size="sm"
|
|
363
|
-
className="h-8 text-xs"
|
|
364
|
-
onClick={() => navigate('/users')}
|
|
365
|
-
>
|
|
366
|
-
Cancel
|
|
367
|
-
</Button>
|
|
368
|
-
<Button type="submit" size="sm" className="h-8 text-xs">
|
|
369
|
-
Create user
|
|
370
|
-
</Button>
|
|
371
|
-
</div>
|
|
350
|
+
</FormField>
|
|
351
|
+
<FormActions onCancel={() => navigate('/users')} submitLabel="Create user" />
|
|
372
352
|
</form>
|
|
373
353
|
</DataBodyTemplate.Group>
|
|
374
354
|
</DataBodyTemplate>
|
|
@@ -379,28 +359,57 @@ function UserCreatePage() {
|
|
|
379
359
|
Form rules:
|
|
380
360
|
|
|
381
361
|
- Form controls and buttons are `32px` (`h-8`); this differs from the table toolbar's `28px` controls.
|
|
382
|
-
-
|
|
362
|
+
- Wrap each field in `FormField`; use `space-y-3` between fields inside the form.
|
|
363
|
+
- End the form in `FormActions`; it already renders the divider and Cancel-before-submit order — do not add a second divider around it.
|
|
383
364
|
- Use the full content width supplied by the stacked group.
|
|
384
|
-
- Do not add `mx-auto`, an arbitrary `max-w-*`, extra horizontal padding, a nested card
|
|
365
|
+
- Do not add `mx-auto`, an arbitrary `max-w-*`, extra horizontal padding, or a nested card.
|
|
385
366
|
- The form page owns its mutation. After success, invalidate the narrowest affected queries and navigate to the list or created entity.
|
|
386
367
|
|
|
387
|
-
## Concise detail
|
|
368
|
+
## Concise detail SidePanel
|
|
388
369
|
|
|
389
|
-
Keep the list mounted while showing concise, mostly read-only row information:
|
|
370
|
+
Keep the list mounted while showing concise, mostly read-only row information. Use BaseKit `SidePanelProvider`/`useSidePanel` (`@loykin/side-panel`) — the same package the Kubernetes Workspace guide uses — with DesignKit `PanelTemplate` for the panel body: `title`/`eyebrow` for the header, `PanelTemplate.Section` + `PanelTemplate.Row` for read-only fields, and a `Close` button in `footer` calling `close()`. Do not use DesignKit's own `Sheet` primitive for this, and do not hand-roll a label/value grid with raw `div`/`p` markup.
|
|
390
371
|
|
|
391
372
|
```tsx
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
373
|
+
function UserPanel({ user }: { user: SelectedUser }) {
|
|
374
|
+
const { close } = useSidePanel()
|
|
375
|
+
return (
|
|
376
|
+
<PanelTemplate
|
|
377
|
+
eyebrow="User"
|
|
378
|
+
title={user.name}
|
|
379
|
+
footer={
|
|
380
|
+
<div className="flex justify-end">
|
|
381
|
+
<Button variant="outline" size="sm" onClick={() => void close()}>
|
|
382
|
+
Close
|
|
383
|
+
</Button>
|
|
384
|
+
</div>
|
|
385
|
+
}
|
|
386
|
+
>
|
|
387
|
+
<PanelTemplate.Section title="Details">
|
|
388
|
+
<dl className="space-y-2">
|
|
389
|
+
<PanelTemplate.Row label="Email">{user.email}</PanelTemplate.Row>
|
|
390
|
+
<PanelTemplate.Row label="Role">{user.role}</PanelTemplate.Row>
|
|
391
|
+
</dl>
|
|
392
|
+
</PanelTemplate.Section>
|
|
393
|
+
</PanelTemplate>
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function UsersListPage() {
|
|
398
|
+
const { open } = useSidePanel()
|
|
399
|
+
return (
|
|
400
|
+
<SidePanelProvider className="h-full min-h-0">
|
|
401
|
+
<DataBodyTemplate>
|
|
402
|
+
<DataGrid
|
|
403
|
+
/* … */
|
|
404
|
+
onRowClick={(user) => open(<UserPanel user={user} />, { side: 'right', size: 420, resizable: true })}
|
|
405
|
+
/>
|
|
406
|
+
</DataBodyTemplate>
|
|
407
|
+
</SidePanelProvider>
|
|
408
|
+
)
|
|
409
|
+
}
|
|
401
410
|
```
|
|
402
411
|
|
|
403
|
-
If the detail gains editing, multiple sections, complex permissions, a long history, or a need for a shareable URL, replace the
|
|
412
|
+
If the detail gains editing, multiple sections, complex permissions, a long history, or a need for a shareable URL, replace the SidePanel with a detail page. Do not gradually turn the SidePanel into a full page inside an overlay.
|
|
404
413
|
|
|
405
414
|
## Public API boundary
|
|
406
415
|
|
|
@@ -429,7 +438,9 @@ Do not import package-internal source paths. Do not add TanStack Query, React Ro
|
|
|
429
438
|
- [ ] Table toolbar and pagination controls are `28px` high.
|
|
430
439
|
- [ ] Form controls and buttons are `32px` high.
|
|
431
440
|
- [ ] The stacked form has no arbitrary max-width wrapper or extra action divider.
|
|
432
|
-
- [ ]
|
|
441
|
+
- [ ] The create/edit form uses `FormField` and `FormActions` (see the Stacked Form guide), not hand-rolled label/input or action-row markup.
|
|
442
|
+
- [ ] Create and edit use pages; concise read-only detail uses a SidePanel.
|
|
443
|
+
- [ ] The SidePanel body uses `PanelTemplate` (`title`/`eyebrow` + `PanelTemplate.Section`/`PanelTemplate.Row`), not hand-rolled label/value markup, and DesignKit's own `Sheet` primitive is not used for this.
|
|
433
444
|
- [ ] Complex detail uses a page.
|
|
434
445
|
- [ ] Only public package entry points are imported.
|
|
435
446
|
- [ ] The experience is visually checked in both Sidebar and Header shells.
|
|
@@ -14,6 +14,32 @@
|
|
|
14
14
|
],
|
|
15
15
|
"contract": "managed-table.md"
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
"id": "kubernetes-workspace",
|
|
19
|
+
"title": "Operations / Kubernetes Workspace",
|
|
20
|
+
"summary": "Operational resource collection with quick SidePanel inspection and persistent logs, shell, and events tools in a bottom dock.",
|
|
21
|
+
"useWhen": [
|
|
22
|
+
"Kubernetes operations",
|
|
23
|
+
"resource inspection",
|
|
24
|
+
"logs and shell",
|
|
25
|
+
"persistent tool dock"
|
|
26
|
+
],
|
|
27
|
+
"templates": ["DataBodyTemplate", "PanelTemplate"],
|
|
28
|
+
"playgroundPaths": [
|
|
29
|
+
"/sidebar/kubernetes-workspace-guide",
|
|
30
|
+
"/header/kubernetes-workspace-guide"
|
|
31
|
+
],
|
|
32
|
+
"contract": "kubernetes-workspace.md"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "form-workflow",
|
|
36
|
+
"title": "Forms / Stacked Form",
|
|
37
|
+
"summary": "Canonical stacked page structure and modular section boundaries for create, edit, and settings forms.",
|
|
38
|
+
"useWhen": ["create form", "edit form", "settings form", "multiple form sections"],
|
|
39
|
+
"templates": ["DataBodyTemplate"],
|
|
40
|
+
"playgroundPaths": ["/sidebar/form-workflow-guide", "/header/form-workflow-guide"],
|
|
41
|
+
"contract": "form-workflow.md"
|
|
42
|
+
},
|
|
17
43
|
{
|
|
18
44
|
"id": "publishing-workflow",
|
|
19
45
|
"title": "Publishing / Blog → Article",
|
|
@@ -12,6 +12,8 @@ Use this guide to build a publishing experience where a content collection leads
|
|
|
12
12
|
|
|
13
13
|
The existing **Blog Feed** and **Article** template demos are visual references only. This Guide is the supported example of how those page shapes connect.
|
|
14
14
|
|
|
15
|
+
Read [AI UI Implementation Contract](./ai-ui-implementation-contract.md) first. It governs how this Guide's card grid and article content are built.
|
|
16
|
+
|
|
15
17
|
## Route contract
|
|
16
18
|
|
|
17
19
|
```text
|
|
@@ -37,6 +39,18 @@ Card selection uses React Router navigation. Browser Back returns to the collect
|
|
|
37
39
|
- Article actions such as Save or Share belong in the article header.
|
|
38
40
|
- Render one page-level template per route. Never put `DetailBodyTemplate` inside `DataBodyTemplate.Body`.
|
|
39
41
|
|
|
42
|
+
## Card and article presentation
|
|
43
|
+
|
|
44
|
+
Do not hand-roll the card grid or article body with raw `<div>`/Tailwind. Use these DesignKit components:
|
|
45
|
+
|
|
46
|
+
- `ArticleCardPreview` — the whole grid card: cover, category, title, excerpt, byline, read time. Pass `coverContent` for per-listing decoration (an icon, an overlay); do not reassemble the card shape from `InteractiveCard` + `ArticleCover` + `ArticleByline` by hand — that duplication is exactly why this component exists (it was copy-pasted near-verbatim between this guide and the Blog Feed demo before being promoted).
|
|
47
|
+
- `ArticleCover` / `ArticleByline` — the cover and byline building blocks `ArticleCardPreview` uses internally. Reach for them directly only outside the card shape, e.g. the article page's hero (`ArticleCover`) and header (`ArticleByline`). `tone` is a semantic value (`'violet' | 'emerald' | 'amber' | 'rose' | 'sky' | 'slate'`), not a raw gradient class string — sample data stores `accent: ArticleTone`, never a Tailwind string.
|
|
48
|
+
- `ArticleBody` — reading-width container with article typography for the article's prose.
|
|
49
|
+
- `ArticleBodySkeleton` — loading placeholder matching `ArticleBody`'s width.
|
|
50
|
+
- `ArticleToc` — the article aside's table of contents.
|
|
51
|
+
|
|
52
|
+
Article title, excerpt, and category stay as plain content passed into these components; only the reusable shell moves into DesignKit.
|
|
53
|
+
|
|
40
54
|
## Deterministic ordering
|
|
41
55
|
|
|
42
56
|
The mock API returns articles newest first and the GridKit collection declares the same initial published-date sort. In a real API, send sort parameters to the server and include them in the query key. Never sort humanized labels such as “2 hours ago”.
|
|
@@ -51,3 +65,6 @@ The mock API returns articles newest first and the GridKit collection declares t
|
|
|
51
65
|
- [ ] Initial loading is local to the destination content
|
|
52
66
|
- [ ] Existing content remains visible during background refetch
|
|
53
67
|
- [ ] No nested page templates and no article Sheet
|
|
68
|
+
- [ ] The grid card uses `ArticleCardPreview`, not a hand-assembled `InteractiveCard` + `ArticleCover` + `ArticleByline` composition
|
|
69
|
+
- [ ] Article page presentation uses `ArticleBody`/`ArticleBodySkeleton` and `ArticleToc`, not hand-rolled `className` blocks
|
|
70
|
+
- [ ] Sample data stores a semantic `accent`/tone value, never a Tailwind gradient string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loykin/designkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "A React UI component library with theming support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@eslint/js": "^10.0.1",
|
|
51
51
|
"@loykin/gridkit": "^0.2.2",
|
|
52
|
+
"@tanstack/react-table": "^8.21.3",
|
|
52
53
|
"@typescript/native": "npm:typescript@~7.0.2",
|
|
53
54
|
"@types/node": "^26.0.0",
|
|
54
55
|
"@types/react": "^19.2.14",
|