@ketvietlab/design-system 0.1.5 → 0.1.7

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 (58) hide show
  1. package/README.md +111 -11
  2. package/dist/catalogue/catalogue.css +41 -34
  3. package/dist/catalogue/index.d.ts.map +1 -1
  4. package/dist/catalogue/index.js +415 -56
  5. package/dist/catalogue/index.js.map +1 -1
  6. package/dist/catalogue/page-surfaces.d.ts.map +1 -1
  7. package/dist/catalogue/page-surfaces.js +8 -6
  8. package/dist/catalogue/page-surfaces.js.map +1 -1
  9. package/dist/contract/index.d.ts.map +1 -1
  10. package/dist/contract/index.js +4 -0
  11. package/dist/contract/index.js.map +1 -1
  12. package/dist/foundations/tokens.css +2 -1
  13. package/dist/index.d.ts +5 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/layouts/flat.css +74 -0
  18. package/dist/layouts/grouped.css +126 -0
  19. package/dist/layouts/index.d.ts +3 -1
  20. package/dist/layouts/index.d.ts.map +1 -1
  21. package/dist/layouts/index.js +5 -2
  22. package/dist/layouts/index.js.map +1 -1
  23. package/dist/layouts/layouts.css +44 -7
  24. package/dist/layouts/shell.css +16 -2
  25. package/dist/layouts/shell.d.ts.map +1 -1
  26. package/dist/layouts/shell.js +1 -1
  27. package/dist/layouts/shell.js.map +1 -1
  28. package/dist/patterns/data-table.d.ts +50 -2
  29. package/dist/patterns/data-table.d.ts.map +1 -1
  30. package/dist/patterns/data-table.js +57 -3
  31. package/dist/patterns/data-table.js.map +1 -1
  32. package/dist/patterns/list-chrome.d.ts +80 -0
  33. package/dist/patterns/list-chrome.d.ts.map +1 -0
  34. package/dist/patterns/list-chrome.js +45 -0
  35. package/dist/patterns/list-chrome.js.map +1 -0
  36. package/dist/patterns/modal-sheet.d.ts +6 -1
  37. package/dist/patterns/modal-sheet.d.ts.map +1 -1
  38. package/dist/patterns/modal-sheet.js +5 -1
  39. package/dist/patterns/modal-sheet.js.map +1 -1
  40. package/dist/patterns/patterns.css +943 -131
  41. package/dist/patterns/record-page.d.ts +3 -2
  42. package/dist/patterns/record-page.d.ts.map +1 -1
  43. package/dist/patterns/record-page.js +2 -2
  44. package/dist/patterns/record-page.js.map +1 -1
  45. package/dist/primitives/actions.d.ts +6 -0
  46. package/dist/primitives/actions.d.ts.map +1 -1
  47. package/dist/primitives/actions.js +4 -2
  48. package/dist/primitives/actions.js.map +1 -1
  49. package/dist/primitives/field.d.ts +16 -3
  50. package/dist/primitives/field.d.ts.map +1 -1
  51. package/dist/primitives/field.js +18 -5
  52. package/dist/primitives/field.js.map +1 -1
  53. package/dist/primitives/navigation.css +1 -1
  54. package/dist/primitives/primitives.css +87 -6
  55. package/dist/primitives/progress.js +1 -1
  56. package/dist/primitives/progress.js.map +1 -1
  57. package/dist/styles.css +2 -0
  58. package/package.json +2 -2
@@ -1,18 +1,21 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@ketvietlab/ketjs-view/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@ketvietlab/ketjs-view/jsx-runtime";
2
2
  import { each, html } from '@ketvietlab/ketjs-view';
3
- import { ActionGroup, Button, LinkButton } from '../primitives/actions.js';
3
+ import { ActionGroup, Button, IconButton, LinkButton } from '../primitives/actions.js';
4
4
  import { Avatar, Badge, Code, CountBadge, Tag } from '../primitives/status.js';
5
5
  import { EmptyState, LoadingState, Notice } from '../primitives/feedback.js';
6
6
  import { Field } from '../primitives/field.js';
7
7
  import { NavList, Tabs } from '../primitives/navigation.js';
8
8
  import { Progress } from '../primitives/progress.js';
9
9
  import { ContentCard, Disclosure, Grid, Inline, Metric, Section, Stack, Surface } from '../layouts/index.js';
10
- import { AppShell, Page, PageHeader, RecordCanvas, RecordSection } from '../layouts/shell.js';
10
+ import { AppShell } from '../layouts/shell.js';
11
11
  import { DataTable } from '../patterns/data-table.js';
12
+ import { ListChrome } from '../patterns/list-chrome.js';
12
13
  import { BoardPage } from '../patterns/board-page.js';
13
14
  import { DashboardPage } from '../patterns/dashboard-page.js';
14
15
  import { ListPage } from '../patterns/list-page.js';
15
16
  import { FormPage } from '../patterns/form-page.js';
17
+ import { RecordPage } from '../patterns/record-page.js';
18
+ import { WorkspacePage } from '../patterns/workspace-page.js';
16
19
  import { ModalSheet } from '../patterns/modal-sheet.js';
17
20
  import { Pipeline } from '../patterns/pipeline.js';
18
21
  import { RecordForm } from '../patterns/record-form.js';
@@ -23,6 +26,83 @@ const orders = [
23
26
  { id: 'SO-1039', customer: 'Nguyễn Minh Châu', total: '2.150.000 ₫', state: 'Blocked' },
24
27
  ];
25
28
  const toneOf = (state) => state === 'Ready' ? 'positive' : state === 'Review' ? 'warning' : 'danger';
29
+ const demoNavItems = (active) => [
30
+ { label: 'Collection', href: '#app-shell', leading: '≡', active: active === 'collection', count: 148 },
31
+ { label: 'Record', href: '#record-page', leading: '◇', active: active === 'record' },
32
+ { label: 'Flow', href: '#workspace-flow', leading: '▦', active: active === 'flow', count: 7 },
33
+ { label: 'Canvas', href: '#workspace-canvas', leading: '□', active: active === 'canvas' },
34
+ ];
35
+ const DemoSidebar = (props) => (_jsxs("div", { "data-ui": "shell-demo-sidebar", children: [_jsx("strong", { children: "K\u00E9tSuite" }), _jsx(NavList, { label: "Workspace layouts", items: demoNavItems(props.active) })] }));
36
+ const DemoRail = (props) => (_jsxs("div", { "data-ui": "shell-demo-rail", children: [_jsx("p", { "data-ui": "catalogue-kicker", children: "Context" }), _jsx("strong", { children: props.title }), _jsx("p", { children: props.detail }), props.progress !== undefined && _jsx(Progress, { label: props.title, value: props.progress }), props.items !== undefined && _jsx(Stack, { gap: "compact", items: props.items })] }));
37
+ const DemoShell = (props) => (_jsx(AppShell, { mode: "embedded", sidebar: _jsx(DemoSidebar, { active: props.active }), main: props.main, rightRail: props.railTitle !== undefined ? (_jsx(DemoRail, { title: props.railTitle, detail: props.railDetail ?? '', progress: props.railProgress, items: props.railItems })) : undefined }));
38
+ const OrdersTable = (props = {}) => (_jsx(DataTable, { title: props.title, rows: props.grouped ? [] : orders, id: (row) => row.id, rowHref: (row) => `#${row.id}`, responsive: "stack", selection: { selectedIds: ['SO-1042'] }, groups: props.grouped
39
+ ? [
40
+ {
41
+ id: 'ready',
42
+ label: 'Ready to invoice',
43
+ count: 2,
44
+ rows: orders.slice(0, 2),
45
+ pager: { label: '2 shown in Ready', nextHref: '#data-table' },
46
+ },
47
+ {
48
+ id: 'blocked',
49
+ label: 'Needs decision',
50
+ count: 1,
51
+ rows: orders.slice(2),
52
+ },
53
+ ]
54
+ : undefined, columns: [
55
+ {
56
+ key: 'id',
57
+ label: 'Order',
58
+ cell: (row) => row.id,
59
+ priority: 'primary',
60
+ kind: 'identifier',
61
+ sort: { href: '#data-table', direction: 'descending' },
62
+ },
63
+ { key: 'customer', label: 'Customer', cell: (row) => row.customer },
64
+ { key: 'total', label: 'Total', cell: (row) => row.total, align: 'end', kind: 'currency' },
65
+ {
66
+ key: 'state',
67
+ label: 'State',
68
+ cell: (row) => _jsx(Badge, { label: row.state, tone: toneOf(row.state) }),
69
+ kind: 'status',
70
+ },
71
+ ] }));
72
+ const ListDemoChrome = (props) => (_jsx(ListChrome, { search: {
73
+ id: `${props.id}-query`,
74
+ action: `#${props.id}`,
75
+ value: props.id === 'list-page' ? 'Mùa Hạ' : '',
76
+ placeholder: 'Search records',
77
+ hidden: { view: 'table' },
78
+ }, facets: [
79
+ { id: 'all', label: 'All', href: `#${props.id}`, active: true, count: 148 },
80
+ { id: 'ready', label: 'Ready', href: `#${props.id}`, count: 91 },
81
+ { id: 'review', label: 'Review', href: `#${props.id}`, count: 7 },
82
+ ], views: [
83
+ { id: 'table', label: 'Table', href: `#${props.id}`, active: true },
84
+ { id: 'kanban', label: 'Kanban', href: `#${props.id}` },
85
+ ], sort: {
86
+ id: `${props.id}-sort`,
87
+ action: `#${props.id}`,
88
+ choices: [
89
+ { value: 'date-desc', label: 'Newest first', selected: true },
90
+ { value: 'total-desc', label: 'Largest total' },
91
+ ],
92
+ }, bulk: {
93
+ selectedCount: props.selected ?? 1,
94
+ summary: `${props.selected ?? 1} selected`,
95
+ clearHref: `#${props.id}`,
96
+ actions: [{ id: 'approve', label: 'Approve', name: 'intent', value: 'approve', variant: 'primary' }],
97
+ }, pager: {
98
+ summary: 'Showing 1-3 of 148',
99
+ previousHref: null,
100
+ nextHref: `#${props.id}`,
101
+ pages: [
102
+ { label: '1', href: `#${props.id}`, active: true },
103
+ { label: '2', href: `#${props.id}` },
104
+ ],
105
+ } }));
26
106
  export const componentGroups = [
27
107
  {
28
108
  id: 'actions',
@@ -40,6 +120,8 @@ export const componentGroups = [
40
120
  _jsx(Button, { label: "Terminate", variant: "destructive" }),
41
121
  _jsx(Button, { label: "Saving", loading: true }),
42
122
  _jsx(Button, { label: "Unavailable", disabled: true }),
123
+ _jsx(LinkButton, { label: "Opening record", href: "#record-page", loading: true }),
124
+ _jsx(IconButton, { label: "Toggle theme", icon: "\u263E" }),
43
125
  ] })),
44
126
  },
45
127
  {
@@ -50,6 +132,8 @@ export const componentGroups = [
50
132
  _jsx(Button, { label: "Compact", size: "compact" }),
51
133
  _jsx(Button, { label: "Default" }),
52
134
  _jsx(Button, { label: "Prominent decision", size: "prominent", variant: "primary" }),
135
+ _jsx(IconButton, { label: "Compact theme toggle", icon: "\u263E", size: "compact" }),
136
+ _jsx(IconButton, { label: "Prominent theme toggle", icon: "\u263E", size: "prominent" }),
53
137
  _jsx(LinkButton, { label: "Open record", href: "#data-table", variant: "tertiary", leading: "\u2197" }),
54
138
  ] })),
55
139
  },
@@ -122,16 +206,115 @@ export const componentGroups = [
122
206
  id: 'field',
123
207
  name: 'Field',
124
208
  description: 'Text, select, checkbox and error states without application-owned markup.',
125
- render: () => (_jsx(Grid, { columns: 2, items: [
126
- _jsx(Field, { id: "company-name", name: "company", label: "Company name", value: "K\u00E9t Vi\u1EC7t", required: true }),
127
- _jsx(Field, { id: "deployment", name: "deployment", label: "Deployment", type: "select", value: "commerce", options: [
128
- { value: 'commerce', label: 'Commerce' },
129
- { value: 'cosmetic', label: 'Cosmetic' },
130
- { value: 'hospitality', label: 'Hospitality' },
131
- ], help: "A tenant belongs to exactly one deployment." }),
132
- _jsx(Field, { id: "database-key", name: "databaseKey", label: "Database key", value: "Invalid Key", error: "Use lowercase letters, numbers and hyphens only." }),
133
- _jsx(Field, { id: "active", name: "active", label: "Active for new orders", type: "checkbox", value: true }),
134
- ] })),
209
+ render: () => (_jsx(Surface, { body: _jsx(Grid, { columns: 2, items: [
210
+ _jsx(Field, { id: "company-name", name: "company", label: "Company name", value: "K\u00E9t Vi\u1EC7t", required: true }),
211
+ _jsx(Field, { id: "deployment", name: "deployment", label: "Deployment", type: "select", value: "commerce", options: [
212
+ { value: 'commerce', label: 'Commerce' },
213
+ { value: 'cosmetic', label: 'Cosmetic' },
214
+ { value: 'hospitality', label: 'Hospitality' },
215
+ ], help: "A tenant belongs to exactly one deployment." }),
216
+ _jsx(Field, { id: "database-key", name: "databaseKey", label: "Database key", value: "Invalid Key", error: "Use lowercase letters, numbers and hyphens only." }),
217
+ _jsx(Field, { id: "active", name: "active", label: "Active for new orders", type: "checkbox", value: true, help: "New orders can be assigned to this company." }),
218
+ ] }) })),
219
+ },
220
+ ],
221
+ },
222
+ {
223
+ id: 'field-states',
224
+ name: 'Field states',
225
+ description: 'Native input types, validation, read-only values and nested groups.',
226
+ examples: [
227
+ {
228
+ id: 'advanced-fields',
229
+ name: 'Operational fields',
230
+ description: 'Numeric constraints, dates, options and nested validation use the same field contract.',
231
+ render: () => (_jsx(Surface, { title: "Settings", body: _jsx(RecordForm, { action: "#advanced-fields", submitLabel: "Save settings", fields: [
232
+ {
233
+ id: 'settings-reference',
234
+ name: 'reference',
235
+ label: 'Reference',
236
+ value: 'CUS-0042',
237
+ readOnly: true,
238
+ help: 'Assigned when the account was created.',
239
+ },
240
+ {
241
+ id: 'settings-locked',
242
+ name: 'locked',
243
+ label: 'External reference',
244
+ value: 'Pending verification',
245
+ disabled: true,
246
+ },
247
+ {
248
+ id: 'settings-limit',
249
+ name: 'limit',
250
+ label: 'Credit limit',
251
+ type: 'decimal',
252
+ value: 80000000,
253
+ min: 0,
254
+ step: '1000',
255
+ },
256
+ {
257
+ id: 'settings-date',
258
+ name: 'date',
259
+ label: 'Effective date',
260
+ type: 'date',
261
+ value: '2026-09-08',
262
+ },
263
+ { id: 'settings-time', name: 'time', label: 'Delivery time', type: 'time', value: '09:00' },
264
+ {
265
+ id: 'settings-color',
266
+ name: 'color',
267
+ label: 'Calendar colour',
268
+ type: 'color',
269
+ value: '#5167c4',
270
+ },
271
+ {
272
+ id: 'settings-billing',
273
+ name: 'billing',
274
+ label: 'Billing frequency',
275
+ type: 'radio',
276
+ value: 'monthly',
277
+ required: true,
278
+ options: [
279
+ { value: 'monthly', label: 'Monthly' },
280
+ { value: 'quarterly', label: 'Quarterly' },
281
+ ],
282
+ },
283
+ {
284
+ id: 'settings-channels',
285
+ name: 'channels',
286
+ label: 'Notifications',
287
+ type: 'checkbox-group',
288
+ options: [
289
+ { value: 'email', label: 'Email', checked: true },
290
+ { value: 'sms', label: 'SMS' },
291
+ ],
292
+ },
293
+ {
294
+ id: 'settings-address',
295
+ name: 'address',
296
+ label: 'Delivery address',
297
+ error: 'Check the delivery address.',
298
+ fields: [
299
+ {
300
+ id: 'settings-street',
301
+ name: 'street',
302
+ label: 'Street',
303
+ value: '',
304
+ required: true,
305
+ error: 'Enter a street address.',
306
+ span: 'full',
307
+ },
308
+ {
309
+ id: 'settings-city',
310
+ name: 'city',
311
+ label: 'City',
312
+ value: 'Hồ Chí Minh',
313
+ autocomplete: 'address-level2',
314
+ },
315
+ ],
316
+ },
317
+ ] }) })),
135
318
  },
136
319
  ],
137
320
  },
@@ -175,27 +358,111 @@ export const componentGroups = [
175
358
  {
176
359
  id: 'application-structure',
177
360
  name: 'Application structure',
178
- description: 'Sidebar, content and context rail are peer regions—not cards nested inside a card.',
361
+ description: 'Four practical layouts inside the same shell: collection, record, flow workspace and canvas workspace.',
179
362
  examples: [
180
363
  {
181
364
  id: 'app-shell',
182
- name: 'App shell',
183
- description: 'The sidebar and right rail stay square while independent KPI objects use a 7px radius.',
184
- render: () => (_jsx(AppShell, { mode: "embedded", sidebar: _jsxs("div", { "data-ui": "shell-demo-sidebar", children: [_jsx("strong", { children: "K\u00E9tSuite" }), _jsx(NavList, { label: "Workspace", items: [
185
- { label: 'Overview', href: '#app-shell', leading: '⌂', active: true },
186
- { label: 'Orders', href: '#data-table', leading: '□', count: 148 },
187
- { label: 'Products', href: '#record-page', leading: '◇' },
188
- ] })] }), main: _jsx(Page, { title: "Operations", description: "Today across all active tenants", actions: _jsx(Button, { label: "Create order", variant: "primary" }), body: _jsx(Grid, { columns: 3, items: [
189
- _jsx(Metric, { label: "Orders", value: "148", detail: "+12 today", tone: "positive" }),
190
- _jsx(Metric, { label: "Review queue", value: "7", detail: "Oldest 42 min", tone: "warning" }),
191
- _jsx(Metric, { label: "Blocked", value: "2", detail: "Credential required", tone: "danger" }),
192
- ] }) }), rightRail: _jsxs("div", { "data-ui": "shell-demo-rail", children: [_jsx("p", { "data-ui": "catalogue-kicker", children: "Context" }), _jsx("strong", { children: "Today" }), _jsx("p", { children: "Three items need an operational decision." }), _jsx(Progress, { label: "Daily target", value: 72 })] }) })),
365
+ name: 'Collection shell',
366
+ description: 'A full list workspace with sidebar navigation, URL-owned list chrome, selectable rows and a context rail.',
367
+ render: () => (_jsx(DemoShell, { active: "collection", main: _jsx(ListPage, { variant: "operational", context: "Sales / Sales orders", title: "Sales orders", description: "Review demand, fulfillment and payment state from one operational list.", actions: _jsx(Button, { label: "Create order", variant: "primary" }), controls: _jsx(ListDemoChrome, { id: "app-shell" }), body: _jsx(OrdersTable, { title: "Order list" }) }), railTitle: "List health", railDetail: "Seven orders need a human decision before invoicing.", railProgress: 72, railItems: [
368
+ _jsx(Badge, { label: "91 ready", tone: "positive" }),
369
+ _jsx(Badge, { label: "7 review", tone: "warning" }),
370
+ ] })),
193
371
  },
194
372
  {
195
373
  id: 'record-page',
196
- name: 'Record page',
197
- description: 'Sections use separators and spacing instead of a rounded container around the whole record.',
198
- render: () => (_jsx(RecordCanvas, { body: _jsxs(_Fragment, { children: [_jsx(PageHeader, { title: "M\u00F9a H\u1EA1 Riverside", description: "Customer \u00B7 CUS-0042", actions: _jsx(Button, { label: "Edit", size: "compact" }) }), _jsx(RecordSection, { title: "Overview", body: "Stable customer with six active locations and no overdue invoices." }), _jsx(RecordSection, { title: "Delivery", body: _jsx(Progress, { label: "Onboarding complete", value: 84, tone: "positive" }) })] }) })),
374
+ name: 'Record shell',
375
+ description: 'A durable subject with record identity, tabs, form content and a right-hand facts rail.',
376
+ render: () => (_jsx(DemoShell, { active: "record", main: _jsx(RecordPage, { variant: "operational", context: "Customers / M\u00F9a H\u1EA1 Riverside", title: "M\u00F9a H\u1EA1 Riverside", description: "Customer \u00B7 CUS-0042", status: _jsx(Badge, { label: "Active", tone: "positive" }), actions: _jsx(ActionGroup, { actions: [
377
+ _jsx(Button, { label: "Save", variant: "primary", size: "compact" }),
378
+ _jsx(Button, { label: "Archive", variant: "tertiary", size: "compact" }),
379
+ ] }), navigation: _jsx(Tabs, { label: "Customer record", items: [
380
+ { id: 'details', label: 'Details', href: '#record-page', active: true },
381
+ { id: 'orders', label: 'Orders', href: '#record-page', count: 6 },
382
+ { id: 'activity', label: 'Activity', href: '#record-page', count: 8 },
383
+ ] }), body: _jsx(Stack, { items: [
384
+ _jsx(Surface, { title: "Main information", body: _jsx(RecordForm, { action: "#record-page", fields: [
385
+ {
386
+ id: 'layout-partner-name',
387
+ span: 'full',
388
+ name: 'name',
389
+ label: 'Name',
390
+ value: 'Mùa Hạ Riverside',
391
+ },
392
+ {
393
+ id: 'layout-partner-email',
394
+ span: 'full',
395
+ name: 'email',
396
+ label: 'Email',
397
+ type: 'email',
398
+ value: 'hello@muaha.example',
399
+ },
400
+ {
401
+ id: 'layout-partner-tags',
402
+ name: 'tags',
403
+ label: 'Tags',
404
+ type: 'checkbox-group',
405
+ span: 'full',
406
+ options: [
407
+ { value: 'vip', label: 'VIP', checked: true },
408
+ { value: 'hospitality', label: 'Hospitality', checked: true },
409
+ ],
410
+ },
411
+ ], submitLabel: "Save" }) }),
412
+ _jsx(OrdersTable, { title: "Recent orders" }),
413
+ ] }), aside: _jsx(Stack, { items: [
414
+ _jsx(Metric, { label: "Credit limit", value: "80m \u20AB", detail: "42m \u20AB available" }),
415
+ _jsx(Progress, { label: "Onboarding complete", value: 84, tone: "positive" }),
416
+ ] }), asideLabel: "Customer context" }) })),
417
+ },
418
+ {
419
+ id: 'workspace-flow',
420
+ name: 'Flow workspace',
421
+ description: 'A vertical operations overview where metrics, process state and exception lists sit on one grey canvas.',
422
+ render: () => (_jsx(DemoShell, { active: "flow", main: _jsx(WorkspacePage, { variant: "operational", layout: "flow", context: "Sales / Overview", title: "Revenue operations", description: "Follow confirmed demand, open work and handoffs across the team.", actions: _jsx(Button, { label: "Create quotation", variant: "primary" }), controls: _jsx(ActionGroup, { label: "Flow controls", actions: [
423
+ _jsx(LinkButton, { label: "Today", href: "#workspace-flow", size: "compact" }),
424
+ _jsx(LinkButton, { label: "This week", href: "#workspace-flow", size: "compact", variant: "tertiary" }),
425
+ ] }), body: _jsx(Stack, { gap: "loose", items: [
426
+ _jsx(Grid, { columns: 4, items: [
427
+ _jsx(Metric, { label: "Quotations", value: 24, detail: "5 new today" }),
428
+ _jsx(Metric, { label: "Sent", value: 9, detail: "Awaiting reply", tone: "info" }),
429
+ _jsx(Metric, { label: "Confirmed", value: 18, detail: "82,000,000 \u20AB", tone: "positive" }),
430
+ _jsx(Metric, { label: "Blocked", value: 2, detail: "Needs attention", tone: "danger" }),
431
+ ] }),
432
+ _jsx(Surface, { title: "Sales flow", body: _jsx(Pipeline, { label: "Sales flow", steps: [
433
+ { id: 'draft', label: 'Quotation', value: 24 },
434
+ { id: 'sent', label: 'Sent', value: 9, tone: 'info' },
435
+ { id: 'confirmed', label: 'Confirmed', value: 18, tone: 'positive' },
436
+ { id: 'invoice', label: 'To invoice', value: 6, tone: 'warning' },
437
+ ] }) }),
438
+ _jsx(OrdersTable, { grouped: true, title: "Exceptions" }),
439
+ ] }) }), railTitle: "Daily target", railDetail: "56 of 82 confirmed orders have been invoiced today.", railProgress: 68 })),
440
+ },
441
+ {
442
+ id: 'workspace-canvas',
443
+ name: 'Canvas workspace',
444
+ description: 'A horizontal board-style surface where the main content can scroll without changing the shell.',
445
+ render: () => (_jsx(DemoShell, { active: "canvas", main: _jsx(WorkspacePage, { variant: "operational", layout: "canvas", context: "CRM / Pipeline", title: "Opportunities board", description: "Move active opportunities through a spatial workflow.", actions: _jsx(Button, { label: "Create opportunity", variant: "primary" }), controls: _jsx(Inline, { items: [
446
+ _jsx(LinkButton, { label: "My pipeline", href: "#workspace-canvas", size: "compact" }),
447
+ _jsx(LinkButton, { label: "All teams", href: "#workspace-canvas", size: "compact", variant: "tertiary" }),
448
+ _jsx(Tag, { label: "High value", removeHref: "#workspace-canvas", removeLabel: "Remove filter" }),
449
+ ] }), body: _jsx(Grid, { columns: 3, items: [
450
+ _jsx(Section, { title: "Qualified", body: _jsx(Stack, { gap: "compact", items: [
451
+ _jsx(ContentCard, { title: "M\u00F9a H\u1EA1 Riverside", meta: "42,000,000 \u20AB", body: "Needs proposal" }),
452
+ _jsx(ContentCard, { title: "\u00C1nh D\u01B0\u01A1ng Group", meta: "18,000,000 \u20AB", body: "Demo booked" }),
453
+ ] }) }),
454
+ _jsx(Section, { title: "Proposal", body: _jsx(Stack, { gap: "compact", items: [
455
+ _jsx(ContentCard, { title: "Lotus Hotels", meta: "64,000,000 \u20AB", body: "Awaiting CFO" }),
456
+ _jsx(ContentCard, { title: "B\u00ECnh Minh Retail", meta: "30,000,000 \u20AB", body: "Legal review" }),
457
+ ] }) }),
458
+ _jsx(Section, { title: "Negotiation", body: _jsx(Stack, { gap: "compact", items: [
459
+ _jsx(ContentCard, { title: "S\u00F4ng Xanh", meta: "52,000,000 \u20AB", body: "Discount requested" }),
460
+ _jsx(ContentCard, { title: "Urban Stay", meta: "40,000,000 \u20AB", body: "Close this week" }),
461
+ ] }) }),
462
+ ] }) }), railTitle: "Pipeline risk", railDetail: "Three opportunities need owner follow-up before Friday.", railItems: [
463
+ _jsx(Badge, { label: "2 overdue", tone: "danger" }),
464
+ _jsx(Badge, { label: "6 due this week", tone: "warning" }),
465
+ ] })),
199
466
  },
200
467
  ],
201
468
  },
@@ -239,21 +506,91 @@ export const componentGroups = [
239
506
  name: 'Patterns',
240
507
  description: 'Generic workflows assembled from the same primitives.',
241
508
  examples: [
509
+ {
510
+ id: 'list-chrome',
511
+ name: 'List chrome',
512
+ description: 'Search, filters, view switch, bulk state and paging for URL-driven collection screens.',
513
+ render: () => (_jsx(ListChrome, { search: {
514
+ id: 'customer-query',
515
+ action: '#list-chrome',
516
+ placeholder: 'Search customers',
517
+ }, facets: [
518
+ { id: 'active', label: 'Active', href: '#list-chrome', active: true, count: 42 },
519
+ { id: 'draft', label: 'Draft', href: '#list-chrome', count: 6 },
520
+ ], views: [
521
+ { id: 'table', label: 'Table', href: '#list-chrome', active: true },
522
+ { id: 'cards', label: 'Cards', href: '#list-chrome' },
523
+ ], sort: {
524
+ id: 'customer-sort',
525
+ action: '#list-chrome',
526
+ choices: [
527
+ { value: 'updated-desc', label: 'Recently updated', selected: true },
528
+ { value: 'name-asc', label: 'Name A-Z' },
529
+ ],
530
+ }, status: "48 customers", actions: _jsx(Button, { label: "Create", variant: "primary", size: "compact" }), bulk: {
531
+ selectedCount: 2,
532
+ summary: '2 customers selected',
533
+ clearHref: '#list-chrome',
534
+ actions: [{ id: 'archive', label: 'Archive', name: 'intent', value: 'archive' }],
535
+ }, pager: {
536
+ summary: 'Showing 1-25 of 48',
537
+ nextHref: '#list-chrome',
538
+ pages: [
539
+ { label: '1', href: '#list-chrome', active: true },
540
+ { label: '2', href: '#list-chrome' },
541
+ ],
542
+ } })),
543
+ },
242
544
  {
243
545
  id: 'list-page',
244
546
  name: 'List page',
245
547
  description: 'The canonical collection hierarchy: identity, actions, URL-driven controls, result context and records.',
246
- render: () => (_jsx(ListPage, { variant: "operational", context: "Sales / Sales orders", eyebrow: "Sales", title: "Sales orders", description: "Review demand, fulfillment and payment state from one operational list.", actions: _jsx(Button, { label: "Create order", variant: "primary" }), controls: _jsx(ActionGroup, { label: "List controls", actions: [
247
- _jsx(LinkButton, { label: "All orders", href: "#list-page", size: "compact" }),
248
- _jsx(LinkButton, { label: "Ready", href: "#list-page", size: "compact", variant: "tertiary" }),
249
- _jsx(LinkButton, { label: "Needs review", href: "#list-page", size: "compact", variant: "tertiary" }),
250
- ] }), status: "3 of 148 orders \u00B7 Updated just now", body: _jsx(DataTable, { rows: orders, id: (row) => row.id, rowHref: (row) => `#${row.id}`, columns: [
548
+ render: () => (_jsx(ListPage, { variant: "operational", context: "Sales / Sales orders", eyebrow: "Sales", title: "Sales orders", description: "Review demand, fulfillment and payment state from one operational list.", actions: _jsx(Button, { label: "Create order", variant: "primary" }), controls: _jsx(ListChrome, { search: {
549
+ id: 'orders-query',
550
+ action: '#list-page',
551
+ value: 'Mùa Hạ',
552
+ placeholder: 'Search orders',
553
+ hidden: { view: 'all' },
554
+ }, facets: [
555
+ { id: 'all', label: 'All', href: '#list-page', active: true, count: 148 },
556
+ { id: 'ready', label: 'Ready', href: '#list-page', count: 91 },
557
+ { id: 'review', label: 'Review', href: '#list-page', count: 7 },
558
+ ], views: [
559
+ { id: 'table', label: 'Table', href: '#list-page', active: true },
560
+ { id: 'kanban', label: 'Kanban', href: '#list-page' },
561
+ ], sort: {
562
+ id: 'orders-sort',
563
+ action: '#list-page',
564
+ choices: [
565
+ { value: 'date-desc', label: 'Newest first', selected: true },
566
+ { value: 'total-desc', label: 'Largest total' },
567
+ { value: 'customer-asc', label: 'Customer A-Z' },
568
+ ],
569
+ }, bulk: {
570
+ selectedCount: 1,
571
+ summary: '1 order selected',
572
+ clearHref: '#list-page',
573
+ actions: [
574
+ { id: 'approve', label: 'Approve', name: 'intent', value: 'approve', variant: 'primary' },
575
+ { id: 'export', label: 'Export', name: 'intent', value: 'export' },
576
+ ],
577
+ }, pager: {
578
+ summary: 'Showing 1-3 of 148',
579
+ previousHref: null,
580
+ nextHref: '#list-page',
581
+ pages: [
582
+ { label: '1', href: '#list-page', active: true },
583
+ { label: '2', href: '#list-page' },
584
+ { label: '3', href: '#list-page' },
585
+ ],
586
+ } }), body: _jsx(DataTable, { title: "Order list", rows: orders, id: (row) => row.id, rowHref: (row) => `#${row.id}`, responsive: "stack", selection: { selectedIds: ['SO-1042'] }, columns: [
251
587
  {
252
588
  key: 'id',
253
589
  label: 'Order',
254
590
  cell: (row) => row.id,
255
591
  priority: 'primary',
256
592
  kind: 'identifier',
593
+ sort: { href: '#list-page', direction: 'descending' },
257
594
  },
258
595
  { key: 'customer', label: 'Customer', cell: (row) => row.customer },
259
596
  { key: 'total', label: 'Total', cell: (row) => row.total, align: 'end', kind: 'currency' },
@@ -276,7 +613,7 @@ export const componentGroups = [
276
613
  _jsx(Metric, { label: "Confirmed", value: 18, detail: "82,000,000 \u20AB", tone: "positive" }),
277
614
  _jsx(Metric, { label: "To invoice", value: 6, detail: "Needs attention", tone: "warning" }),
278
615
  ] }),
279
- _jsx(Section, { title: "Sales flow", description: "From quotation through to invoicing", body: _jsx(Pipeline, { label: "Sales flow", steps: [
616
+ _jsx(Surface, { title: "Sales flow", body: _jsx(Pipeline, { label: "Sales flow", steps: [
280
617
  { id: 'draft', label: 'Quotation', value: 24 },
281
618
  { id: 'sent', label: 'Sent', value: 9, tone: 'info' },
282
619
  { id: 'confirmed', label: 'Confirmed', value: 18, tone: 'positive' },
@@ -312,8 +649,30 @@ export const componentGroups = [
312
649
  id: 'data-table',
313
650
  name: 'Data table',
314
651
  description: 'Columns are data; rows remain semantic and horizontally contained.',
315
- render: () => (_jsx(DataTable, { caption: "Recent sales orders", rows: orders, id: (row) => row.id, rowHref: (row) => `#${row.id}`, columns: [
316
- { key: 'id', label: 'Order', cell: (row) => row.id, priority: 'primary', kind: 'identifier' },
652
+ render: () => (_jsx(DataTable, { title: "Recent sales orders", actions: _jsx(LinkButton, { label: "View all orders", href: "#app-shell", variant: "tertiary" }), rows: [], id: (row) => row.id, rowHref: (row) => `#${row.id}`, responsive: "stack", gutter: "compact", selection: { selectedIds: ['SO-1042'] }, groups: [
653
+ {
654
+ id: 'ready',
655
+ label: 'Ready to invoice',
656
+ count: 2,
657
+ href: '#data-table',
658
+ rows: orders.slice(0, 2),
659
+ pager: { label: '2 shown in Ready', nextHref: '#data-table' },
660
+ },
661
+ {
662
+ id: 'blocked',
663
+ label: 'Needs decision',
664
+ count: 1,
665
+ rows: orders.slice(2),
666
+ },
667
+ ], columns: [
668
+ {
669
+ key: 'id',
670
+ label: 'Order',
671
+ cell: (row) => row.id,
672
+ priority: 'primary',
673
+ kind: 'identifier',
674
+ sort: { href: '#data-table', direction: 'descending' },
675
+ },
317
676
  { key: 'customer', label: 'Customer', cell: (row) => row.customer },
318
677
  { key: 'total', label: 'Total', cell: (row) => row.total, align: 'end', kind: 'currency' },
319
678
  {
@@ -328,30 +687,30 @@ export const componentGroups = [
328
687
  id: 'form-page',
329
688
  name: 'Form page',
330
689
  description: 'The canonical form hierarchy: record identity and decision first, a stable form column, then durable context.',
331
- render: () => (_jsx(FormPage, { variant: "operational", context: "Customers / M\u00F9a H\u1EA1 Riverside", title: "M\u00F9a H\u1EA1 Riverside", description: "Customer \u00B7 CUS-0042", status: _jsx(Badge, { label: "Active", tone: "positive" }), actions: _jsx(Button, { label: "Save partner", variant: "primary" }), body: _jsx(Section, { title: "Main information", body: _jsx(Surface, { body: _jsx(RecordForm, { action: "#form-page", fields: [
332
- { id: 'partner-name', name: 'name', label: 'Name', value: 'Mùa Hạ Riverside' },
333
- { id: 'partner-ref', name: 'ref', label: 'Reference', value: 'CUS-0042' },
334
- {
335
- id: 'partner-email',
336
- name: 'email',
337
- label: 'Email',
338
- type: 'email',
339
- value: 'hello@muaha.example',
340
- },
341
- {
342
- id: 'partner-phone',
343
- name: 'phone',
344
- label: 'Phone',
345
- type: 'tel',
346
- value: '+84 28 3822 0042',
347
- },
348
- ], submitLabel: "Save partner" }) }) }), aside: _jsx(Section, { title: "Record context", body: _jsx(Stack, { gap: "compact", items: ['Customer since 2023', '6 delivery addresses'] }) }), asideLabel: "Partner context" })),
690
+ render: () => (_jsx(FormPage, { variant: "operational", context: "Customers / M\u00F9a H\u1EA1 Riverside", title: "M\u00F9a H\u1EA1 Riverside", description: "Customer \u00B7 CUS-0042", status: _jsx(Badge, { label: "Active", tone: "positive" }), actions: _jsx(Button, { label: "Save partner", variant: "primary" }), body: _jsx(Surface, { title: "Main information", body: _jsx(RecordForm, { action: "#form-page", fields: [
691
+ { id: 'partner-name', name: 'name', label: 'Name', value: 'Mùa Hạ Riverside' },
692
+ { id: 'partner-ref', name: 'ref', label: 'Reference', value: 'CUS-0042' },
693
+ {
694
+ id: 'partner-email',
695
+ name: 'email',
696
+ label: 'Email',
697
+ type: 'email',
698
+ value: 'hello@muaha.example',
699
+ },
700
+ {
701
+ id: 'partner-phone',
702
+ name: 'phone',
703
+ label: 'Phone',
704
+ type: 'tel',
705
+ value: '+84 28 3822 0042',
706
+ },
707
+ ], submitLabel: "Save partner" }) }), aside: _jsx(Section, { title: "Record context", body: _jsx(Stack, { gap: "compact", items: ['Customer since 2023', '6 delivery addresses'] }) }), asideLabel: "Partner context" })),
349
708
  },
350
709
  {
351
710
  id: 'record-form',
352
711
  name: 'Record form',
353
712
  description: 'Application supplies translated labels and values, not form markup.',
354
- render: () => (_jsx(Surface, { body: _jsx(RecordForm, { action: "#record-form", fields: [
713
+ render: () => (_jsx(Surface, { title: "Main information", body: _jsx(RecordForm, { action: "#record-form", fields: [
355
714
  { id: 'record-name', name: 'name', label: 'Display name', value: 'Mùa Hạ Riverside' },
356
715
  {
357
716
  id: 'record-status',
@@ -416,6 +775,6 @@ export const CataloguePage = (props = {}) => {
416
775
  const theme = props.theme ?? 'system';
417
776
  const density = props.density ?? 'default';
418
777
  const count = componentGroups.reduce((total, group) => total + group.examples.length, 0);
419
- return (_jsxs("main", { "data-kv-design-system": true, "data-ui": "catalogue", "data-theme": theme === 'system' ? null : theme, "data-density": density, children: [_jsxs("aside", { "data-ui": "catalogue-rail", children: [_jsxs("a", { "data-ui": "catalogue-brand", href: "#top", children: [_jsx("span", { "aria-hidden": "true", children: "K" }), _jsx("strong", { children: "K\u00E9t Vi\u1EC7t" })] }), _jsx("p", { "data-ui": "catalogue-kicker", children: "Design system \u00B7 0.1.3" }), _jsx("nav", { "data-ui": "catalogue-nav", "aria-label": "Component groups", children: each(componentGroups, (group) => group.id, (group) => (_jsxs("a", { "data-ui": "catalogue-nav-group", href: `#${group.id}`, children: [_jsx("span", { children: group.name }), _jsx("span", { "data-ui": "catalogue-nav-count", children: String(group.examples.length) })] }))) })] }), _jsxs("div", { "data-ui": "catalogue-main", id: "top", children: [_jsxs("header", { "data-ui": "catalogue-hero", children: [_jsxs("div", { children: [_jsx("p", { "data-ui": "catalogue-kicker", children: "Public components \u00B7 server rendered" }), _jsx("h1", { "data-ui": "catalogue-title", children: "Operational UI, kept honest." }), _jsxs("p", { "data-ui": "catalogue-intro", children: [String(count), " specimens from one markup, token and state contract. Dense enough for daily work; quiet enough for decisions."] }), _jsx(LinkButton, { label: "Review page surfaces", href: "/surfaces?kind=record&theme=light", variant: "secondary" })] }), _jsxs("div", { "data-ui": "catalogue-controls", role: "group", "aria-label": "Catalogue preferences", children: [_jsx("span", { children: "Theme" }), _jsx("a", { href: `/?theme=light&density=${density}`, "aria-current": theme === 'light' ? 'page' : null, children: "Light" }), _jsx("a", { href: `/?theme=dark&density=${density}`, "aria-current": theme === 'dark' ? 'page' : null, children: "Dark" }), _jsx("a", { href: `/?theme=system&density=${density}`, "aria-current": theme === 'system' ? 'page' : null, children: "System" }), _jsx("span", { children: "Density" }), _jsx("a", { href: `/?theme=${theme}&density=compact`, "aria-current": density === 'compact' ? 'page' : null, children: "Compact" }), _jsx("a", { href: `/?theme=${theme}&density=default`, "aria-current": density === 'default' ? 'page' : null, children: "Default" }), _jsx("a", { href: `/?theme=${theme}&density=comfortable`, "aria-current": density === 'comfortable' ? 'page' : null, children: "Comfort" })] })] }), each(componentGroups, (group) => group.id, (group) => (_jsxs("section", { "data-ui": "catalogue-group", id: group.id, children: [_jsxs("header", { "data-ui": "catalogue-group-head", children: [_jsxs("div", { children: [_jsxs("p", { "data-ui": "catalogue-kicker", children: [String(group.examples.length).padStart(2, '0'), " components"] }), _jsx("h2", { children: group.name })] }), _jsx("p", { children: group.description })] }), each(group.examples, (example) => example.id, (example) => (_jsxs("article", { "data-ui": "catalogue-specimen", id: example.id, children: [_jsxs("header", { "data-ui": "catalogue-specimen-head", children: [_jsxs("div", { children: [_jsx("h3", { "data-ui": "catalogue-specimen-name", children: example.name }), _jsx("p", { "data-ui": "catalogue-specimen-description", children: example.description })] }), _jsx(Code, { value: `@ketvietlab/design-system/${example.name}`, context: "component" })] }), _jsx("div", { "data-ui": "catalogue-stage", children: example.render() })] })))] })))] })] }));
778
+ return (_jsxs("main", { "data-kv-design-system": true, "data-ui": "catalogue", "data-theme": theme === 'system' ? null : theme, "data-density": density, children: [_jsxs("aside", { "data-ui": "catalogue-rail", children: [_jsxs("a", { "data-ui": "catalogue-brand", href: "#top", children: [_jsx("span", { "aria-hidden": "true", children: "K" }), _jsx("strong", { children: "K\u00E9t Vi\u1EC7t" })] }), _jsx("p", { "data-ui": "catalogue-kicker", children: "Design system \u00B7 0.1.7" }), _jsx("nav", { "data-ui": "catalogue-nav", "aria-label": "Component groups", children: each(componentGroups, (group) => group.id, (group) => (_jsxs("a", { "data-ui": "catalogue-nav-group", href: `#${group.id}`, children: [_jsx("span", { children: group.name }), _jsx("span", { "data-ui": "catalogue-nav-count", children: String(group.examples.length) })] }))) })] }), _jsxs("div", { "data-ui": "catalogue-main", id: "top", children: [_jsxs("header", { "data-ui": "catalogue-hero", children: [_jsxs("div", { children: [_jsx("p", { "data-ui": "catalogue-kicker", children: "Public components \u00B7 server rendered" }), _jsx("h1", { "data-ui": "catalogue-title", children: "K\u00E9t Vi\u1EC7t Design System" }), _jsxs("p", { "data-ui": "catalogue-intro", children: [String(count), " specimens from one markup, token and state contract. Dense enough for daily work; quiet enough for decisions."] }), _jsx(LinkButton, { label: "Review page surfaces", href: "/surfaces?kind=record&theme=light", variant: "secondary" })] }), _jsxs("div", { "data-ui": "catalogue-controls", role: "group", "aria-label": "Catalogue preferences", children: [_jsx("span", { children: "Theme" }), _jsx("a", { href: `/?theme=light&density=${density}`, "aria-current": theme === 'light' ? 'page' : null, children: "Light" }), _jsx("a", { href: `/?theme=dark&density=${density}`, "aria-current": theme === 'dark' ? 'page' : null, children: "Dark" }), _jsx("a", { href: `/?theme=system&density=${density}`, "aria-current": theme === 'system' ? 'page' : null, children: "System" }), _jsx("span", { children: "Density" }), _jsx("a", { href: `/?theme=${theme}&density=compact`, "aria-current": density === 'compact' ? 'page' : null, children: "Compact" }), _jsx("a", { href: `/?theme=${theme}&density=default`, "aria-current": density === 'default' ? 'page' : null, children: "Default" }), _jsx("a", { href: `/?theme=${theme}&density=comfortable`, "aria-current": density === 'comfortable' ? 'page' : null, children: "Comfort" })] })] }), each(componentGroups, (group) => group.id, (group) => (_jsxs("section", { "data-ui": "catalogue-group", id: group.id, children: [_jsxs("header", { "data-ui": "catalogue-group-head", children: [_jsxs("div", { children: [_jsxs("p", { "data-ui": "catalogue-kicker", children: [String(group.examples.length).padStart(2, '0'), " components"] }), _jsx("h2", { children: group.name })] }), _jsx("p", { children: group.description })] }), each(group.examples, (example) => example.id, (example) => (_jsxs("article", { "data-ui": "catalogue-specimen", id: example.id, children: [_jsxs("header", { "data-ui": "catalogue-specimen-head", children: [_jsxs("div", { children: [_jsx("h3", { "data-ui": "catalogue-specimen-name", children: example.name }), _jsx("p", { "data-ui": "catalogue-specimen-description", children: example.description })] }), _jsx(Code, { value: "@ketvietlab/design-system", context: "package" })] }), _jsx("div", { "data-ui": "catalogue-stage", children: example.render() })] })))] })))] })] }));
420
779
  };
421
780
  //# sourceMappingURL=index.js.map