@ketvietlab/design-system 0.1.6 → 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 (57) 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 +413 -55
  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 +3 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  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 +937 -131
  41. package/dist/patterns/record-page.d.ts +2 -2
  42. package/dist/patterns/record-page.d.ts.map +1 -1
  43. package/dist/patterns/record-page.js +1 -1
  44. package/dist/patterns/record-page.js.map +1 -1
  45. package/dist/primitives/actions.d.ts.map +1 -1
  46. package/dist/primitives/actions.js +1 -1
  47. package/dist/primitives/actions.js.map +1 -1
  48. package/dist/primitives/field.d.ts +16 -3
  49. package/dist/primitives/field.d.ts.map +1 -1
  50. package/dist/primitives/field.js +18 -5
  51. package/dist/primitives/field.js.map +1 -1
  52. package/dist/primitives/navigation.css +1 -1
  53. package/dist/primitives/primitives.css +82 -6
  54. package/dist/primitives/progress.js +1 -1
  55. package/dist/primitives/progress.js.map +1 -1
  56. package/dist/styles.css +2 -0
  57. package/package.json +2 -2
@@ -1,4 +1,4 @@
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
3
  import { ActionGroup, Button, IconButton, LinkButton } from '../primitives/actions.js';
4
4
  import { Avatar, Badge, Code, CountBadge, Tag } from '../primitives/status.js';
@@ -7,12 +7,15 @@ 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,7 @@ 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 }),
43
124
  _jsx(IconButton, { label: "Toggle theme", icon: "\u263E" }),
44
125
  ] })),
45
126
  },
@@ -51,6 +132,8 @@ export const componentGroups = [
51
132
  _jsx(Button, { label: "Compact", size: "compact" }),
52
133
  _jsx(Button, { label: "Default" }),
53
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" }),
54
137
  _jsx(LinkButton, { label: "Open record", href: "#data-table", variant: "tertiary", leading: "\u2197" }),
55
138
  ] })),
56
139
  },
@@ -123,16 +206,115 @@ export const componentGroups = [
123
206
  id: 'field',
124
207
  name: 'Field',
125
208
  description: 'Text, select, checkbox and error states without application-owned markup.',
126
- render: () => (_jsx(Grid, { columns: 2, items: [
127
- _jsx(Field, { id: "company-name", name: "company", label: "Company name", value: "K\u00E9t Vi\u1EC7t", required: true }),
128
- _jsx(Field, { id: "deployment", name: "deployment", label: "Deployment", type: "select", value: "commerce", options: [
129
- { value: 'commerce', label: 'Commerce' },
130
- { value: 'cosmetic', label: 'Cosmetic' },
131
- { value: 'hospitality', label: 'Hospitality' },
132
- ], help: "A tenant belongs to exactly one deployment." }),
133
- _jsx(Field, { id: "database-key", name: "databaseKey", label: "Database key", value: "Invalid Key", error: "Use lowercase letters, numbers and hyphens only." }),
134
- _jsx(Field, { id: "active", name: "active", label: "Active for new orders", type: "checkbox", value: true }),
135
- ] })),
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
+ ] }) })),
136
318
  },
137
319
  ],
138
320
  },
@@ -176,27 +358,111 @@ export const componentGroups = [
176
358
  {
177
359
  id: 'application-structure',
178
360
  name: 'Application structure',
179
- 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.',
180
362
  examples: [
181
363
  {
182
364
  id: 'app-shell',
183
- name: 'App shell',
184
- description: 'The sidebar and right rail stay square while independent KPI objects use a 7px radius.',
185
- render: () => (_jsx(AppShell, { mode: "embedded", sidebar: _jsxs("div", { "data-ui": "shell-demo-sidebar", children: [_jsx("strong", { children: "K\u00E9tSuite" }), _jsx(NavList, { label: "Workspace", items: [
186
- { label: 'Overview', href: '#app-shell', leading: '⌂', active: true },
187
- { label: 'Orders', href: '#data-table', leading: '□', count: 148 },
188
- { label: 'Products', href: '#record-page', leading: '◇' },
189
- ] })] }), 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: [
190
- _jsx(Metric, { label: "Orders", value: "148", detail: "+12 today", tone: "positive" }),
191
- _jsx(Metric, { label: "Review queue", value: "7", detail: "Oldest 42 min", tone: "warning" }),
192
- _jsx(Metric, { label: "Blocked", value: "2", detail: "Credential required", tone: "danger" }),
193
- ] }) }), 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
+ ] })),
194
371
  },
195
372
  {
196
373
  id: 'record-page',
197
- name: 'Record page',
198
- description: 'Sections use separators and spacing instead of a rounded container around the whole record.',
199
- 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
+ ] })),
200
466
  },
201
467
  ],
202
468
  },
@@ -240,21 +506,91 @@ export const componentGroups = [
240
506
  name: 'Patterns',
241
507
  description: 'Generic workflows assembled from the same primitives.',
242
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
+ },
243
544
  {
244
545
  id: 'list-page',
245
546
  name: 'List page',
246
547
  description: 'The canonical collection hierarchy: identity, actions, URL-driven controls, result context and records.',
247
- 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: [
248
- _jsx(LinkButton, { label: "All orders", href: "#list-page", size: "compact" }),
249
- _jsx(LinkButton, { label: "Ready", href: "#list-page", size: "compact", variant: "tertiary" }),
250
- _jsx(LinkButton, { label: "Needs review", href: "#list-page", size: "compact", variant: "tertiary" }),
251
- ] }), 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: [
252
587
  {
253
588
  key: 'id',
254
589
  label: 'Order',
255
590
  cell: (row) => row.id,
256
591
  priority: 'primary',
257
592
  kind: 'identifier',
593
+ sort: { href: '#list-page', direction: 'descending' },
258
594
  },
259
595
  { key: 'customer', label: 'Customer', cell: (row) => row.customer },
260
596
  { key: 'total', label: 'Total', cell: (row) => row.total, align: 'end', kind: 'currency' },
@@ -277,7 +613,7 @@ export const componentGroups = [
277
613
  _jsx(Metric, { label: "Confirmed", value: 18, detail: "82,000,000 \u20AB", tone: "positive" }),
278
614
  _jsx(Metric, { label: "To invoice", value: 6, detail: "Needs attention", tone: "warning" }),
279
615
  ] }),
280
- _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: [
281
617
  { id: 'draft', label: 'Quotation', value: 24 },
282
618
  { id: 'sent', label: 'Sent', value: 9, tone: 'info' },
283
619
  { id: 'confirmed', label: 'Confirmed', value: 18, tone: 'positive' },
@@ -313,8 +649,30 @@ export const componentGroups = [
313
649
  id: 'data-table',
314
650
  name: 'Data table',
315
651
  description: 'Columns are data; rows remain semantic and horizontally contained.',
316
- render: () => (_jsx(DataTable, { caption: "Recent sales orders", rows: orders, id: (row) => row.id, rowHref: (row) => `#${row.id}`, columns: [
317
- { 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
+ },
318
676
  { key: 'customer', label: 'Customer', cell: (row) => row.customer },
319
677
  { key: 'total', label: 'Total', cell: (row) => row.total, align: 'end', kind: 'currency' },
320
678
  {
@@ -329,30 +687,30 @@ export const componentGroups = [
329
687
  id: 'form-page',
330
688
  name: 'Form page',
331
689
  description: 'The canonical form hierarchy: record identity and decision first, a stable form column, then durable context.',
332
- 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: [
333
- { id: 'partner-name', name: 'name', label: 'Name', value: 'Mùa Hạ Riverside' },
334
- { id: 'partner-ref', name: 'ref', label: 'Reference', value: 'CUS-0042' },
335
- {
336
- id: 'partner-email',
337
- name: 'email',
338
- label: 'Email',
339
- type: 'email',
340
- value: 'hello@muaha.example',
341
- },
342
- {
343
- id: 'partner-phone',
344
- name: 'phone',
345
- label: 'Phone',
346
- type: 'tel',
347
- value: '+84 28 3822 0042',
348
- },
349
- ], 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" })),
350
708
  },
351
709
  {
352
710
  id: 'record-form',
353
711
  name: 'Record form',
354
712
  description: 'Application supplies translated labels and values, not form markup.',
355
- render: () => (_jsx(Surface, { body: _jsx(RecordForm, { action: "#record-form", fields: [
713
+ render: () => (_jsx(Surface, { title: "Main information", body: _jsx(RecordForm, { action: "#record-form", fields: [
356
714
  { id: 'record-name', name: 'name', label: 'Display name', value: 'Mùa Hạ Riverside' },
357
715
  {
358
716
  id: 'record-status',
@@ -417,6 +775,6 @@ export const CataloguePage = (props = {}) => {
417
775
  const theme = props.theme ?? 'system';
418
776
  const density = props.density ?? 'default';
419
777
  const count = componentGroups.reduce((total, group) => total + group.examples.length, 0);
420
- 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() })] })))] })))] })] }));
421
779
  };
422
780
  //# sourceMappingURL=index.js.map