@twoux/ui 0.0.0-stage → 0.1.1

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 (325) hide show
  1. package/LICENSE.md +61 -0
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/NOTICE.md +64 -0
  4. package/README.md +211 -2
  5. package/SHADCN-DIFF.md +228 -0
  6. package/agent/AGENTS.md +358 -0
  7. package/agent/docs-md/accessibility.md +49 -0
  8. package/agent/docs-md/action-catalogue.md +81 -0
  9. package/agent/docs-md/actions.md +74 -0
  10. package/agent/docs-md/agent-activity-log.md +70 -0
  11. package/agent/docs-md/agent-permissions.md +82 -0
  12. package/agent/docs-md/agent-run.md +90 -0
  13. package/agent/docs-md/agents-on-behalf.md +74 -0
  14. package/agent/docs-md/ai-column.md +85 -0
  15. package/agent/docs-md/ai-origin-label.md +70 -0
  16. package/agent/docs-md/announcing-features.md +73 -0
  17. package/agent/docs-md/answer-actions.md +81 -0
  18. package/agent/docs-md/app-shell.md +114 -0
  19. package/agent/docs-md/artifact-card.md +89 -0
  20. package/agent/docs-md/ask-on-screen.md +72 -0
  21. package/agent/docs-md/assistant-page.md +100 -0
  22. package/agent/docs-md/assistant-panel.md +142 -0
  23. package/agent/docs-md/avatar.md +82 -0
  24. package/agent/docs-md/banner.md +106 -0
  25. package/agent/docs-md/board.md +163 -0
  26. package/agent/docs-md/breadcrumb.md +89 -0
  27. package/agent/docs-md/breakdown-table.md +97 -0
  28. package/agent/docs-md/bulk-action-bar.md +112 -0
  29. package/agent/docs-md/button.md +293 -0
  30. package/agent/docs-md/card.md +115 -0
  31. package/agent/docs-md/change-list.md +99 -0
  32. package/agent/docs-md/change-request.md +166 -0
  33. package/agent/docs-md/chart-area.md +87 -0
  34. package/agent/docs-md/chart-bar.md +100 -0
  35. package/agent/docs-md/chart-box-plot.md +88 -0
  36. package/agent/docs-md/chart-bullet.md +89 -0
  37. package/agent/docs-md/chart-candlestick.md +112 -0
  38. package/agent/docs-md/chart-card-line.md +120 -0
  39. package/agent/docs-md/chart-donut.md +90 -0
  40. package/agent/docs-md/chart-drill-down.md +72 -0
  41. package/agent/docs-md/chart-funnel.md +71 -0
  42. package/agent/docs-md/chart-heatmap.md +76 -0
  43. package/agent/docs-md/chart-histogram.md +81 -0
  44. package/agent/docs-md/chart-map.md +165 -0
  45. package/agent/docs-md/chart-mixed.md +88 -0
  46. package/agent/docs-md/chart-price.md +92 -0
  47. package/agent/docs-md/chart-radial-bars.md +109 -0
  48. package/agent/docs-md/chart-radial-lines.md +111 -0
  49. package/agent/docs-md/chart-sankey.md +104 -0
  50. package/agent/docs-md/chart-scatter.md +87 -0
  51. package/agent/docs-md/chart-share-bar.md +89 -0
  52. package/agent/docs-md/chart-treemap.md +98 -0
  53. package/agent/docs-md/chart-waterfall.md +93 -0
  54. package/agent/docs-md/checkbox.md +131 -0
  55. package/agent/docs-md/choosing-selection-control.md +71 -0
  56. package/agent/docs-md/citations-sources.md +79 -0
  57. package/agent/docs-md/citing-sources.md +63 -0
  58. package/agent/docs-md/clarify-before-acting.md +69 -0
  59. package/agent/docs-md/clarifying-question.md +92 -0
  60. package/agent/docs-md/code-block.md +174 -0
  61. package/agent/docs-md/colours.md +87 -0
  62. package/agent/docs-md/columns-menu.md +140 -0
  63. package/agent/docs-md/combobox.md +161 -0
  64. package/agent/docs-md/command-palette.md +126 -0
  65. package/agent/docs-md/confidence-note.md +87 -0
  66. package/agent/docs-md/configurable-dashboard.md +76 -0
  67. package/agent/docs-md/confirmation-dialog.md +93 -0
  68. package/agent/docs-md/connected-tools.md +80 -0
  69. package/agent/docs-md/connector-picker.md +127 -0
  70. package/agent/docs-md/consistency.md +53 -0
  71. package/agent/docs-md/content-density.md +61 -0
  72. package/agent/docs-md/conversation-history.md +78 -0
  73. package/agent/docs-md/create-record.md +80 -0
  74. package/agent/docs-md/csv-import.md +88 -0
  75. package/agent/docs-md/dashboard-items.md +78 -0
  76. package/agent/docs-md/data-export.md +61 -0
  77. package/agent/docs-md/data-grid.md +137 -0
  78. package/agent/docs-md/data-vis-colours.md +76 -0
  79. package/agent/docs-md/data-visualisation.md +76 -0
  80. package/agent/docs-md/date-picker.md +178 -0
  81. package/agent/docs-md/delete-record.md +75 -0
  82. package/agent/docs-md/dense-table.md +495 -0
  83. package/agent/docs-md/design-tokens.md +53 -0
  84. package/agent/docs-md/disabled-read-only.md +75 -0
  85. package/agent/docs-md/drag-and-drop.md +74 -0
  86. package/agent/docs-md/dropdown-menu.md +137 -0
  87. package/agent/docs-md/edit-record.md +73 -0
  88. package/agent/docs-md/empty-loading-error.md +78 -0
  89. package/agent/docs-md/empty-state.md +128 -0
  90. package/agent/docs-md/error-messages.md +75 -0
  91. package/agent/docs-md/error-state.md +123 -0
  92. package/agent/docs-md/export-menu.md +86 -0
  93. package/agent/docs-md/feedback-prompt.md +88 -0
  94. package/agent/docs-md/field-mapping.md +78 -0
  95. package/agent/docs-md/file-drop.md +110 -0
  96. package/agent/docs-md/filter-bar.md +138 -0
  97. package/agent/docs-md/filter-panel.md +103 -0
  98. package/agent/docs-md/filtering-search.md +79 -0
  99. package/agent/docs-md/follow-ups-starters.md +79 -0
  100. package/agent/docs-md/form-validation.md +95 -0
  101. package/agent/docs-md/generated-columns.md +65 -0
  102. package/agent/docs-md/generated-drafts-files.md +71 -0
  103. package/agent/docs-md/grouped-list.md +89 -0
  104. package/agent/docs-md/guide-cue.md +170 -0
  105. package/agent/docs-md/help-panel.md +103 -0
  106. package/agent/docs-md/help-system.md +74 -0
  107. package/agent/docs-md/iconography.md +49 -0
  108. package/agent/docs-md/inbox.md +108 -0
  109. package/agent/docs-md/inline-editing-validation.md +80 -0
  110. package/agent/docs-md/inline-input-in-answer.md +70 -0
  111. package/agent/docs-md/items-palette.md +116 -0
  112. package/agent/docs-md/keyboard-model.md +47 -0
  113. package/agent/docs-md/keyboard-navigation.md +68 -0
  114. package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
  115. package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
  116. package/agent/docs-md/labelling-ai-output.md +66 -0
  117. package/agent/docs-md/layout.md +47 -0
  118. package/agent/docs-md/list-table.md +254 -0
  119. package/agent/docs-md/loading-skeleton.md +91 -0
  120. package/agent/docs-md/managing-agents.md +71 -0
  121. package/agent/docs-md/message-composer.md +105 -0
  122. package/agent/docs-md/migrating.md +49 -0
  123. package/agent/docs-md/motion.md +93 -0
  124. package/agent/docs-md/navigation.md +76 -0
  125. package/agent/docs-md/notification-channels.md +65 -0
  126. package/agent/docs-md/notifications-decision.md +100 -0
  127. package/agent/docs-md/onboarding-step.md +114 -0
  128. package/agent/docs-md/order-book.md +108 -0
  129. package/agent/docs-md/page-header.md +96 -0
  130. package/agent/docs-md/pagination.md +88 -0
  131. package/agent/docs-md/pane-layout.md +105 -0
  132. package/agent/docs-md/period-comparison.md +66 -0
  133. package/agent/docs-md/plan-card.md +120 -0
  134. package/agent/docs-md/popover-rules.md +88 -0
  135. package/agent/docs-md/prompt-editor.md +90 -0
  136. package/agent/docs-md/propose-apply.md +65 -0
  137. package/agent/docs-md/provider-logo.md +97 -0
  138. package/agent/docs-md/radio.md +83 -0
  139. package/agent/docs-md/ranked-list.md +102 -0
  140. package/agent/docs-md/reasoning-progress.md +72 -0
  141. package/agent/docs-md/record-detail.md +71 -0
  142. package/agent/docs-md/regenerating-answers.md +70 -0
  143. package/agent/docs-md/run-status-bar.md +94 -0
  144. package/agent/docs-md/saved-tasks-schedules.md +63 -0
  145. package/agent/docs-md/saved-views.md +149 -0
  146. package/agent/docs-md/saving-views.md +68 -0
  147. package/agent/docs-md/scheduler.md +258 -0
  148. package/agent/docs-md/secondary-panels.md +72 -0
  149. package/agent/docs-md/segmented-control.md +88 -0
  150. package/agent/docs-md/selection-bulk-actions.md +70 -0
  151. package/agent/docs-md/separator.md +75 -0
  152. package/agent/docs-md/setup-checklist.md +92 -0
  153. package/agent/docs-md/shape-elevation.md +55 -0
  154. package/agent/docs-md/sheet-grid.md +211 -0
  155. package/agent/docs-md/side-panel.md +134 -0
  156. package/agent/docs-md/sign-in.md +114 -0
  157. package/agent/docs-md/spacing.md +55 -0
  158. package/agent/docs-md/split-button.md +100 -0
  159. package/agent/docs-md/stat-compare.md +82 -0
  160. package/agent/docs-md/static-dashboard.md +75 -0
  161. package/agent/docs-md/status-badge.md +140 -0
  162. package/agent/docs-md/step-card.md +75 -0
  163. package/agent/docs-md/stepper.md +92 -0
  164. package/agent/docs-md/suggested-changes.md +91 -0
  165. package/agent/docs-md/switch.md +78 -0
  166. package/agent/docs-md/table-settings.md +81 -0
  167. package/agent/docs-md/tabs.md +85 -0
  168. package/agent/docs-md/tag-input.md +108 -0
  169. package/agent/docs-md/tag.md +79 -0
  170. package/agent/docs-md/task-builder.md +87 -0
  171. package/agent/docs-md/team-instructions.md +72 -0
  172. package/agent/docs-md/templates-gallery.md +85 -0
  173. package/agent/docs-md/text-input.md +160 -0
  174. package/agent/docs-md/thinking-steps.md +89 -0
  175. package/agent/docs-md/timeline.md +189 -0
  176. package/agent/docs-md/timestamp.md +84 -0
  177. package/agent/docs-md/timestamps.md +70 -0
  178. package/agent/docs-md/toast.md +117 -0
  179. package/agent/docs-md/tooltip.md +90 -0
  180. package/agent/docs-md/typography.md +62 -0
  181. package/agent/docs-md/undo-destructive.md +66 -0
  182. package/agent/docs-md/unsaved-changes.md +71 -0
  183. package/agent/docs-md/usage-meter.md +80 -0
  184. package/agent/docs-md/user-feedback.md +71 -0
  185. package/agent/docs-md/voice-and-tone.md +60 -0
  186. package/agent/docs-md/why-choice.md +93 -0
  187. package/agent/docs-md/workspace-onboarding.md +74 -0
  188. package/agent/docs-md/writing.md +48 -0
  189. package/agent/llms-full.txt +18369 -0
  190. package/agent/llms.txt +198 -0
  191. package/agent/skills/twoux-audit/SKILL.md +49 -0
  192. package/agent/skills/twoux-build/SKILL.md +39 -0
  193. package/agent/skills/twoux-design/SKILL.md +54 -0
  194. package/agent/skills/twoux-write/SKILL.md +32 -0
  195. package/bin/check.mjs +123 -0
  196. package/bin/docs.mjs +24 -0
  197. package/bin/guard.mjs +27 -0
  198. package/bin/init.mjs +113 -0
  199. package/bin/knowledge.mjs +87 -0
  200. package/bin/mcp.mjs +116 -0
  201. package/bin/theme.mjs +168 -0
  202. package/bin/twoux.mjs +25 -0
  203. package/package.json +80 -3
  204. package/registry/jobs.json +1028 -0
  205. package/registry/palette.json +770 -0
  206. package/registry/rules.json +913 -0
  207. package/src/components/agent-review.tsx +364 -0
  208. package/src/components/ai-basics.tsx +212 -0
  209. package/src/components/ai-work.tsx +304 -0
  210. package/src/components/ai.tsx +227 -0
  211. package/src/components/alert-dialog.tsx +203 -0
  212. package/src/components/alert.tsx +86 -0
  213. package/src/components/avatar.tsx +114 -0
  214. package/src/components/badge.tsx +59 -0
  215. package/src/components/board.tsx +397 -0
  216. package/src/components/breadcrumb.tsx +114 -0
  217. package/src/components/button-group.tsx +90 -0
  218. package/src/components/button.tsx +109 -0
  219. package/src/components/calendar.tsx +247 -0
  220. package/src/components/card.tsx +102 -0
  221. package/src/components/chart-map.tsx +336 -0
  222. package/src/components/charts-more.tsx +1331 -0
  223. package/src/components/charts-radial.tsx +328 -0
  224. package/src/components/charts.tsx +390 -0
  225. package/src/components/checkbox.tsx +40 -0
  226. package/src/components/code.tsx +139 -0
  227. package/src/components/collapsible.tsx +37 -0
  228. package/src/components/combobox.tsx +339 -0
  229. package/src/components/command-extras.tsx +11 -0
  230. package/src/components/command.tsx +196 -0
  231. package/src/components/data-grid.tsx +213 -0
  232. package/src/components/data-table.tsx +334 -0
  233. package/src/components/data-views.tsx +740 -0
  234. package/src/components/date-picker.tsx +686 -0
  235. package/src/components/dense-table.tsx +207 -0
  236. package/src/components/dialog.tsx +162 -0
  237. package/src/components/dropdown-menu.tsx +264 -0
  238. package/src/components/empty.tsx +114 -0
  239. package/src/components/feedback-prompt.tsx +80 -0
  240. package/src/components/field.tsx +254 -0
  241. package/src/components/fields.tsx +112 -0
  242. package/src/components/flow.tsx +840 -0
  243. package/src/components/guide-cue.tsx +273 -0
  244. package/src/components/inbox.tsx +113 -0
  245. package/src/components/input-group.tsx +176 -0
  246. package/src/components/input.tsx +26 -0
  247. package/src/components/kbd.tsx +34 -0
  248. package/src/components/label.tsx +27 -0
  249. package/src/components/labels.tsx +176 -0
  250. package/src/components/motion.tsx +136 -0
  251. package/src/components/onboarding.tsx +454 -0
  252. package/src/components/order-book.tsx +185 -0
  253. package/src/components/pane-layout.tsx +417 -0
  254. package/src/components/popover.tsx +96 -0
  255. package/src/components/provider-logos.data.ts +49 -0
  256. package/src/components/radio-group.tsx +52 -0
  257. package/src/components/scheduler.tsx +997 -0
  258. package/src/components/segmented-control.tsx +28 -0
  259. package/src/components/select.tsx +195 -0
  260. package/src/components/separator.tsx +31 -0
  261. package/src/components/sheet.tsx +148 -0
  262. package/src/components/shell.tsx +426 -0
  263. package/src/components/shortcut-sheet.tsx +83 -0
  264. package/src/components/skeleton.tsx +18 -0
  265. package/src/components/sonner.tsx +107 -0
  266. package/src/components/spinner.tsx +13 -0
  267. package/src/components/split-button.tsx +58 -0
  268. package/src/components/stepper.tsx +78 -0
  269. package/src/components/switch.tsx +43 -0
  270. package/src/components/table.tsx +126 -0
  271. package/src/components/tabs.tsx +118 -0
  272. package/src/components/tag-input.tsx +253 -0
  273. package/src/components/textarea.tsx +21 -0
  274. package/src/components/theme.tsx +80 -0
  275. package/src/components/timeline.tsx +408 -0
  276. package/src/components/toggle-group.tsx +87 -0
  277. package/src/components/toggle.tsx +54 -0
  278. package/src/components/tooltip.tsx +65 -0
  279. package/src/components/workspace-actions.tsx +294 -0
  280. package/src/components/workspace-import.tsx +465 -0
  281. package/src/components/workspace.tsx +1884 -0
  282. package/src/index.ts +80 -0
  283. package/src/lib/active-indicator.tsx +53 -0
  284. package/src/lib/chart-kit.tsx +72 -0
  285. package/src/lib/cn.ts +6 -0
  286. package/src/lib/shortcuts.tsx +61 -0
  287. package/src/lib/workspace.tsx +10 -0
  288. package/src/shadcn/alert-dialog.tsx +196 -0
  289. package/src/shadcn/alert.tsx +65 -0
  290. package/src/shadcn/avatar.tsx +108 -0
  291. package/src/shadcn/badge.tsx +47 -0
  292. package/src/shadcn/breadcrumb.tsx +108 -0
  293. package/src/shadcn/button-group.tsx +83 -0
  294. package/src/shadcn/button.tsx +63 -0
  295. package/src/shadcn/calendar.tsx +220 -0
  296. package/src/shadcn/card.tsx +91 -0
  297. package/src/shadcn/checkbox.tsx +31 -0
  298. package/src/shadcn/collapsible.tsx +33 -0
  299. package/src/shadcn/command.tsx +184 -0
  300. package/src/shadcn/dialog.tsx +158 -0
  301. package/src/shadcn/dropdown-menu.tsx +256 -0
  302. package/src/shadcn/empty.tsx +103 -0
  303. package/src/shadcn/field.tsx +248 -0
  304. package/src/shadcn/input-group.tsx +170 -0
  305. package/src/shadcn/input.tsx +20 -0
  306. package/src/shadcn/kbd.tsx +28 -0
  307. package/src/shadcn/label.tsx +23 -0
  308. package/src/shadcn/popover.tsx +88 -0
  309. package/src/shadcn/radio-group.tsx +44 -0
  310. package/src/shadcn/select.tsx +189 -0
  311. package/src/shadcn/separator.tsx +27 -0
  312. package/src/shadcn/sheet.tsx +142 -0
  313. package/src/shadcn/skeleton.tsx +13 -0
  314. package/src/shadcn/sonner.tsx +40 -0
  315. package/src/shadcn/switch.tsx +34 -0
  316. package/src/shadcn/table.tsx +115 -0
  317. package/src/shadcn/tabs.tsx +90 -0
  318. package/src/shadcn/textarea.tsx +17 -0
  319. package/src/shadcn/toggle-group.tsx +83 -0
  320. package/src/shadcn/toggle.tsx +46 -0
  321. package/src/shadcn/tooltip.tsx +56 -0
  322. package/src/styles/palette.css +127 -0
  323. package/src/styles/theme.css +35 -0
  324. package/src/styles/twoux.css +236 -0
  325. package/theme.json +14 -0
@@ -0,0 +1,61 @@
1
+ # Content density
2
+
3
+ TwoUX ships one density: compact. Controls are 28 or 32px high and table rows 36px. Spacing that protects reading and pointing is kept, so compact never means cramped targets. Tool pages that pass three tests use workspace density: the same sizes with the chrome and gaps taken out.
4
+
5
+ Kind: foundation · Group: Visual foundation
6
+
7
+ ## Principles
8
+
9
+ - Compact only. Compact suits data-heavy views such as dashboards, resource lists and details; that is every main view in TwoUX, so a second mode would double the testing for little gain.
10
+ - One density means one set of numbers. People, reviewers and code generators never have to ask which mode a screen is in.
11
+ - Dense data, roomy reading. Informational content such as alerts and help keeps more space even in compact mode; TwoUX keeps 14px text and wider line height in AI answers and descriptions.
12
+ - Workspace density takes away chrome, never size. A trading or operations screen fits every region in view by dropping the canvas margin, card frames and gaps between regions, not by shrinking text or targets.
13
+
14
+ ## Guidelines
15
+
16
+ - Control heights: --h-sm 28px for toolbars, filters and buttons inside rows or panels; --h-md 32px for primary forms and page-level actions; --row 36px for table and list rows.
17
+ - Every pointer target is at least 24 by 24 CSS px, or has enough clear space around it. Icon buttons at --h-sm (28px square) pass outright.
18
+ - Small targets inside dropdowns and pickers keep extra padding, even in compact mode.
19
+ - Never shrink alerts, validation messages or empty states to save space.
20
+ - One variant, for data rather than people: Table and SheetGrid take size="dense" (28px rows, the same 13px text) for spreadsheet-like data such as inventories, ledgers, imports and mapping. It is chosen per table by whoever builds the screen, never a switch people turn on.
21
+ - Use workspace density only when all three are true: people watch and act on several regions at once; experts work in it for long, repeated sessions; the content is mostly numbers or tables scanned across many rows. Trading, operations consoles, data workspaces and editors pass; forms, settings, onboarding, record detail, list pages and glance dashboards do not.
22
+ - Workspace density is a layout, not a size: AppShell frame="flush" with the sidebar as a rail, regions in a Pane layout divided by 1px lines, 8px padding inside panes, charts flush (the default inside a flush shell), and size="dense" for spreadsheet-like tables.
23
+ - Even in a workspace, text stays on TwoUX's type scale (13px body, 12px meta, never smaller), targets at 24px or more, and alerts, validation messages and empty states keep their size.
24
+ - Below 900px a workspace stacks its panes and the page scrolls; it never becomes a separate mobile app.
25
+ - Choose workspace density per page when the page is designed, never as a switch people turn on. Salt's high density (20px controls, 11px text) is the counter-example: Salt itself says it does not meet WCAG 2.2 AA, target size included, and is not recommended as a default.
26
+
27
+ ## Accessibility
28
+
29
+ - Target size minimum is 24 by 24 CSS px at AA, with a spacing exception for smaller targets.
30
+ - Compact rows must still reflow and stay readable at 400% zoom; tables may scroll horizontally inside their own container.
31
+ - Because density is not a user setting, zoom and text-size support carry the job of making things bigger.
32
+
33
+ ## Do
34
+
35
+ - Use --row for every list and table, including the inbox and AI history.
36
+ - Check targets with the 24px circle test when placing icon buttons side by side.
37
+
38
+ ## Don't
39
+
40
+ - Do not add a 'comfortable' variant to a single component.
41
+ - Do not shrink text below --t-sm to fit more in a row.
42
+ - Do not use workspace density to fit more on a page that fails one of the three tests; give it the inset canvas.
43
+
44
+ ## Tokens
45
+
46
+ | Token | Value | Use |
47
+ |---|---|---|
48
+ | `--h-sm` | `28px` | Small controls: toolbars, filters, in-row and in-panel buttons |
49
+ | `--h-md` | `32px` | Default controls: forms, page actions |
50
+ | `--row` | `36px` | Table and list row height: the 32px control plus 2px above and below |
51
+ | `--size-row-dense` | `28px` | Table and SheetGrid size="dense", spreadsheet-like data only |
52
+
53
+ ## Related
54
+
55
+ - Spacing
56
+ - Typography
57
+ - Layout
58
+ - Accessibility
59
+ - Dense table
60
+ - Pane layout
61
+ - App shell and sidebar navigation
@@ -0,0 +1,78 @@
1
+ # Previous conversations
2
+
3
+ A searchable list opened from the clock in the panel bar: scheduled tasks first, then recent conversations, with a dot for a new answer and "Needs you" for one waiting.
4
+
5
+ Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - Returning to an earlier answer or a run waiting for you.
10
+ - Finding conversations by their title, named from the first question.
11
+
12
+ ## When not to use
13
+
14
+ - Handling everything that needs you across the product. Use the Inbox (inbox).
15
+ - Auditing agent actions. Use the Agent run timeline (agent-run) log.
16
+
17
+ ## Structure
18
+
19
+ 1. Search field
20
+ 2. Scheduled tasks
21
+ 3. Recent conversations (title, status dot or Needs you badge, date)
22
+
23
+ ## Guidelines
24
+
25
+ - Name conversations from the first question so they are findable.
26
+ - Mark a new answer with a dot and a waiting run with "Needs you".
27
+ - Selecting a past conversation lets people continue it or start from it.
28
+ - Offer deletion and keep history private by default.
29
+ - Title each conversation from its first question or a short summary, never "New conversation".
30
+ - Group by Today, Yesterday, Previous 7 days, then month; show local time with the full date and time on hover.
31
+ - Delete has Undo. Conversations are never removed automatically without notice; the retention period is stated at the top of the list.
32
+ - History is the person's record of conversations; what the assistant keeps between conversations lives in Team instructions, where it can be seen and edited.
33
+
34
+ ## Content
35
+
36
+ - Dates are short and relative where recent ("Yesterday", "Mon", "2 Nov").
37
+
38
+ ## Accessibility
39
+
40
+ - The search field filters the list; announce the result count (SC 4.1.3).
41
+ - The new-answer dot has a text equivalent ("New answer") (SC 1.4.1, 1.3.1).
42
+ - Esc closes the history and returns focus to the clock button (SC 2.4.3).
43
+
44
+ ## Keyboard
45
+
46
+ | Keys | Action |
47
+ |---|---|
48
+ | Esc | Close the list |
49
+ | Tab / Shift+Tab | Move through conversations |
50
+ | Enter | Open a conversation |
51
+
52
+ ## Examples
53
+
54
+ ### history
55
+
56
+ ```tsx
57
+ import { ConversationHistory, toast } from "@twoux/ui";
58
+
59
+ export const meta = { title: "Find past conversations", description: "Named from the first question, scheduled tasks first, a dot for a new answer and Needs you for a waiting run.", order: 1 };
60
+
61
+ export default function Example() {
62
+ return (
63
+ <div className="w-full max-w-md rounded-sm border border-line bg-surface p-3">
64
+ <ConversationHistory
65
+ scheduled={[{ id: "s1", title: "Weekly listing check", date: "Mon", status: "needs-you" }]}
66
+ recent={[{ id: "r1", title: "Why are these closing with few applicants?", date: "Today", status: "new" }, { id: "r2", title: "Compare Design applicants with last quarter", date: "Tue" }, { id: "r3", title: "Draft a shorter description for LST-1044", date: "3 Nov" }]}
67
+ onOpen={(id) => toast(`Opened ${id}`)}
68
+ />
69
+ </div>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ## Related
75
+
76
+ - Assistant side panel
77
+ - Inbox (triage)
78
+ - Agent run timeline
@@ -0,0 +1,80 @@
1
+ # Creating a record: dialog, single page or steps
2
+
3
+ Pick the creation surface by how much people must decide: a small dialog for a name or two, one page for most records, steps only for long or high-stakes set-ups. Fill everything else with visible defaults.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Each team builds creation differently: a wizard for a three-field listing, a modal stacked on a modal for a new location, forms that ask for things the product could have guessed. People abandon long forms and get lost in short wizards.
8
+
9
+ **Solution:** Count the decisions people must make, not the fields that exist. Ask only for what has no good default; put the rest under Additional settings with the default showing. Use a dialog for up to three short fields, one page for most records, and the Stepper only when steps depend on each other, settings can't be changed after creation, or the result reaches people outside the workspace.
10
+
11
+ **Built from:** Page header, Breadcrumb, Card, Text input, Combobox, Date picker, Form validation, Stepper, Side panel (drawer), Button, Toast, Confirmation dialog
12
+
13
+ ## How it works
14
+
15
+ 1. List every field, then sort it: required with no good default, expected by at least 8 in 10 people (such as salary on a listing), and everything else. Only the first two groups go in the main section.
16
+ 2. Choose the surface. A dialog for one to three short fields, such as a new saved view or a new tag. A single page for most records, such as Create listing. The Stepper for long or dependent set-ups, such as Publish listing to job boards. When fields appear based on earlier answers, count the longest version of the form.
17
+ 3. Show defaults as real values in the fields: Closing date 6 December 2026 (30 days), Hiring manager: you. Values TwoUX decides later say so in the hint: "Reference number is assigned when you save."
18
+ 4. Put the remaining fields in a collapsed Additional settings section at the end, each with its default filled in.
19
+ 5. The primary button stays enabled. On submit, validate, show the error summary and move focus to the first problem.
20
+ 6. On success, open the new record (side panel or page, as Record detail says) and show a toast such as "Listing created".
21
+
22
+ ## When to use
23
+
24
+ - Dialog: one to three short fields that can all be changed later, with nothing to look up elsewhere.
25
+ - Single page: most records, from a draft listing to a new candidate with CV upload, up to about 15 main fields or 5 groups.
26
+ - Stepper: more than about 15 main fields or 5 groups, later steps that change with earlier answers, settings that cannot be changed after creation, or one group complex enough to need its own page, such as application questions with their own rules.
27
+ - If length, complexity and recovery still leave it open, use frequency: one page for records people create often, steps for set-ups done rarely.
28
+
29
+ ## When not to use
30
+
31
+ - Don't use steps to make a short form feel lighter. Three steps of two fields each is slower than one page.
32
+ - Don't use this for changing one value on an existing record. Use inline editing.
33
+ - Don't create records the assistant suggested without the person applying the proposal. Use Propose, then apply.
34
+
35
+ ## Guidelines
36
+
37
+ - Order the main fields the way the list shows them. The field order on Create listing becomes the default column order in the Listings table.
38
+ - Defaults must match what TwoUX will actually save. If the hint says "Closes after 30 days", the listing closes after 30 days.
39
+ - Never ask for something the workspace already knows. Prefill the team, office address and hiring manager from the person and workspace.
40
+ - When a field needs something that does not exist yet, such as a new location, offer "Create Leeds office" inside the select for a name only, or open an overlay side panel for more. When it is saved, select it in the field and return focus there.
41
+ - Never stack a dialog on a dialog, and never run a Stepper inside a side panel or dialog.
42
+ - Leaving with unsaved input asks "Discard new listing?" with Keep editing focused. Leaving an untouched form closes without asking.
43
+ - Stepper flows: three to five steps, seven at most, one page per step, a Review step at the end with Edit links back to each step, in the same order as the steps.
44
+ - In a Stepper, check each step when people press Next and keep them on it until it passes. The Review step checks everything again and links each problem to the step that holds it.
45
+
46
+ ## Content
47
+
48
+ - Page title and button use the same verb and object: page "Create listing", button "Create listing". Cancel is "Cancel".
49
+ - Section headings are nouns: "Role", "Location and pay", "Additional settings". Not "Enter role details".
50
+ - Hints say what happens if the field is left as it is: "If you leave this blank, the listing stays open until you close it."
51
+ - Mark optional fields "(optional)"; don't mark required ones.
52
+
53
+ ## Accessibility
54
+
55
+ - One h1 per page (the page title); each section is an h2 in a labelled group.
56
+ - Additional settings is a disclosure button with aria-expanded. When it opens, focus stays on the button.
57
+ - The Stepper names the current step in text ("Step 2 of 4: Application questions") and moves focus to the step heading on Next and Back.
58
+ - When a sub-create side panel closes, focus returns to the field that opened it.
59
+
60
+ ## Do
61
+
62
+ - Do let people create with one press of Create when the defaults are right.
63
+ - Do show every default in the field or hint, never hidden behind a setting.
64
+ - Do keep the Review step free of editing; it links back to steps instead.
65
+
66
+ ## Don't
67
+
68
+ - Don't disable Create until the form is valid. Validate on submit and say what is missing.
69
+ - Don't add a sidebar item for a create page; it opens from the page header of the list.
70
+ - Don't open a dialog to create something from inside another dialog.
71
+
72
+ ## Related
73
+
74
+ - Editing a record: inline, section or whole page
75
+ - Deleting records
76
+ - Inline editing and validation
77
+ - Record detail: side panel, overlay or full page
78
+ - Stepper
79
+ - Form validation
80
+ - Page header
@@ -0,0 +1,88 @@
1
+ # CSV import
2
+
3
+ A two-step dialog: choose a CSV file, its delimiter and header row; then match columns, read the problems before importing, preview the first 20 rows with problem cells marked, and import with the count on the button.
4
+
5
+ Kind: component · Group: Data · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - Bringing rows from a spreadsheet into a table.
10
+
11
+ ## When not to use
12
+
13
+ - For a single file attached to a record. Use File drop.
14
+ - For syncing a connected service. Use the connector and an automation.
15
+
16
+ ## Structure
17
+
18
+ 1. Step 1: File drop, Use the sample file, Delimiter, First row contains column names
19
+ 2. Step 2: problem summary with what happens to bad rows
20
+ 3. Field mapping
21
+ 4. Preview of the first 20 rows, or only rows with problems, with Fix on problem cells
22
+ 5. Footer: Back, Cancel, Import 1,248 rows
23
+
24
+ ## Guidelines
25
+
26
+ - Show problems before importing, in a summary at the top that says what will happen to bad rows: skipped, or imported with the cells left empty.
27
+ - Mark problem cells in the preview with an icon and a word, and let people fix them in place.
28
+ - Put the number of rows on the Import button, so the result is known before pressing it.
29
+ - Accept the file by drop or by the Upload button, state the type and size limit, and explain a wrong file in words.
30
+ - Detect the delimiter and header row, and let people change both.
31
+ - Read dates day first, as in the UK (02/11/2026 is 2 November), accept ISO 8601 and written months (14 Mar 2026), and mark anything else, including impossible dates such as 31/02/2026, as Not a date. Hand dates to the app as ISO 8601 so it never re-reads them.
32
+
33
+ ## Content
34
+
35
+ - Summary: "3 rows have an invalid email. They will be skipped or you can fix them."
36
+ - Wrong file: "That isn't a CSV file. Save the sheet as CSV and choose it again."
37
+
38
+ ## Accessibility
39
+
40
+ - Progress is written as Step 2 of 2.
41
+ - The summary takes focus after a failed Import; problem cells have the word, not only a tint.
42
+
43
+ ## Keyboard
44
+
45
+ | Keys | Action |
46
+ |---|---|
47
+ | Esc | Close the dialog |
48
+ | Enter (fixing) | Save the fixed value |
49
+ | Esc (fixing) | Cancel the fix |
50
+
51
+ ## Examples
52
+
53
+ ### leads
54
+
55
+ ```tsx
56
+ import { Button, CsvImport, type MappingTarget, toast } from "@twoux/ui";
57
+ import { FileUp } from "lucide-react";
58
+ import { useState } from "react";
59
+
60
+ export const meta = { title: "Import event leads", description: "Use the sample file or choose your own CSV. The summary says what happens to rows with problems before anything is imported, and the button states how many rows will be added.", order: 1 };
61
+
62
+ const TARGETS: MappingTarget[] = [
63
+ { id: "name", label: "Name", kind: "text", required: true },
64
+ { id: "email", label: "Work email", kind: "email" },
65
+ { id: "company", label: "Company", kind: "text" },
66
+ { id: "size", label: "Company size", kind: "number" },
67
+ ];
68
+ const SAMPLE = {
69
+ name: "leads.csv",
70
+ text: "Full name,E-mail,Company,Headcount,Country\nMaya Fischer,maya.fischer@larkfield.energy,Larkfield Energy,3100,UK\nRuairi Doyle,ruairi.doyle@pennine,Pennine Freight,1200,Ireland\nSana Iqbal,sana.iqbal@mosaiclending.co.uk,Mosaic Lending,410,UK\n,info@copperleaf.co.uk,Copperleaf Foods,780,UK\nTheo Clarke,theo.clarke@orrin.dev,Orrin Software,about 130,UK\nGrace Ndlovu,grace.ndlovu@harbourvale.co.uk,Harbour & Vale,240,UK",
71
+ };
72
+
73
+ export default function Example() {
74
+ const [open, setOpen] = useState(false);
75
+ return (
76
+ <>
77
+ <Button variant="outline" onClick={() => setOpen(true)}><FileUp />Import CSV</Button>
78
+ <CsvImport open={open} onOpenChange={setOpen} targets={TARGETS} tableName="People" sample={SAMPLE} aliases={{ email: ["e-mail"], size: ["headcount"] }} onImport={(r) => toast.success(`Imported ${r.rows.length} rows${r.skipped ? `, skipped ${r.skipped}` : ""}`)} />
79
+ </>
80
+ );
81
+ }
82
+ ```
83
+
84
+ ## Related
85
+
86
+ - Field mapping
87
+ - File drop
88
+ - Sheet grid
@@ -0,0 +1,78 @@
1
+ # Dashboard items
2
+
3
+ The catalogue of item types a dashboard is built from, such as key metrics, charts, short lists, alerts, quick links, getting started and feature news, and what each one should contain.
4
+
5
+ Kind: pattern · Group: Patterns: data and dashboards
6
+
7
+ **Problem:** Without a shared catalogue, every new dashboard item invents its own header, actions and amount of data. Items grow into mini reports with their own filters and tables, and the dashboard stops being readable at a glance.
8
+
9
+ **Solution:** Every item is a board item with the same anatomy: title, optional description, at most one header action, a more menu, content, and an optional footer link. The content is one of a small set of types, each with a clear job and size, and each links to the page where the full data lives.
10
+
11
+ **Built from:** Board and board item, Card, Stat card with comparison, Line chart card, Bar chart, Funnel, List table, Status badge, Banner (inline alert), Stepper, Dropdown menu, Empty state, Loading skeleton, Error state (failed to load), AI origin label
12
+
13
+ ## How it works
14
+
15
+ 1. Key metrics (full width): two to four Stat cards with comparison, such as Live listings, Applications this week, Median days to first review, Offers accepted. Each opens its report.
16
+ 2. Chart (medium or large): one chart that explains a key metric, such as applications per week or the views to offers funnel, with the Chart or Table switch and a View report link.
17
+ 3. List or table (medium or large): the top five records that need attention, such as listings expiring in 7 days or candidates waiting over 5 days, with status badges and "View all 23" in the footer.
18
+ 4. Status and alerts (small or full width): what is wrong now and what to do, such as "Indeed sync failed for 2 listings" with Retry sync. It shows "No issues" when all is well rather than disappearing.
19
+ 5. Quick links and resources (small): three to five links to frequent destinations or help, such as Post a listing, Import candidates, Interview scorecard guide.
20
+ 6. Getting started (medium): the workspace setup checklist for new teams, with progress such as "2 of 5 done". People can dismiss it once they are set up.
21
+ 7. Feature news (small): one product update in a sentence with one link. It is dismissible, and there is never more than one on a dashboard.
22
+
23
+ ## When to use
24
+
25
+ - Whenever you add anything to a static or configurable dashboard: pick a type from this catalogue first.
26
+ - When a summary can be read on its own without the other items.
27
+ - When the item has a clear destination for people who need more detail.
28
+
29
+ ## When not to use
30
+
31
+ - For data people need to filter, sort and act on in bulk. Link to the List table.
32
+ - For long text or logs, such as the full activity history of a listing. Put it on the record page.
33
+ - For a single number with no comparison and no action. Fold it into the key metrics row or leave it out.
34
+
35
+ ## Guidelines
36
+
37
+ - One item, one job. Combine at most two visual types in an item, such as a chart with its breakdown table, never a chart, a table and a list.
38
+ - At most one action in the header, for the most common next step. Everything else goes in the more menu: Preferences first, then other actions by how often they are used, then Remove from dashboard last.
39
+ - Show all the content by default. Preferences can hide parts, such as a column or the table under a chart, but only on configurable dashboards.
40
+ - Keep the content light: no inline filters beyond a period or one select, no row selection, no editing. Link out for anything more.
41
+ - Size by content: small (1 of 4 columns) for one number or a few links, medium (2) for a chart or short list, large (3) for a chart that compares several series, full width (4) for key metrics.
42
+ - An AI summary item, if offered, carries the origin label, cites the records it used by ID and only suggests. It never ranks, rejects or advances candidates.
43
+ - Give each item's title heading level 2 (level={2} on ChartFrame, BoardItem and ChangeList) so the page outline runs from the h1 straight to the items and screen reader users can jump between them.
44
+
45
+ ## Content
46
+
47
+ - Title is a noun phrase with scope: "Candidates waiting over 5 days", not "Waiting" or "Widget 3".
48
+ - Footer links say how many: "View all 23 listings", not "See more".
49
+ - Item menu labels are verbs or plain nouns: "Preferences", "View report", "Remove from dashboard".
50
+ - An item with nothing to show explains why and what to do: "No listings expire in the next 7 days."
51
+
52
+ ## Accessibility
53
+
54
+ - Each item is a region labelled by its h2 title.
55
+ - Key metric changes are read as words: "Up 12% vs previous 7 days", not only an arrow and a colour.
56
+ - A table inside a narrow item scrolls inside the item, not the page, and keeps its header row.
57
+ - Alerts that appear after load are announced politely; failures that block work use the error state in the item.
58
+
59
+ ## Do
60
+
61
+ - Do treat each item as an executive summary of its subject.
62
+ - Do give every list and chart item a link to its full page.
63
+ - Do keep loading, empty and error states inside the item's frame so the layout does not jump.
64
+
65
+ ## Don't
66
+
67
+ - Don't add actions that belong on another page, such as Post a listing inside a chart item.
68
+ - Don't use expanding footers or accordions inside items.
69
+ - Don't show more than one feature news item, and never one that cannot be dismissed.
70
+
71
+ ## Related
72
+
73
+ - Static dashboard
74
+ - Configurable dashboard
75
+ - Board and board item
76
+ - Stat card with comparison
77
+ - Line chart card
78
+ - Onboarding to an empty workspace
@@ -0,0 +1,61 @@
1
+ # Data export
2
+
3
+ One Export menu on every table: choose what to include first, then the format, then copy instead of download if you want.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** People export to share with hiring managers or to work in a spreadsheet. When each table exports differently, they get files with hidden columns, raw field names or AI guesses mixed with real data.
8
+
9
+ **Solution:** Export (⇧E) opens one menu: options first (visible columns only, include AI columns, selected rows or all matching rows), then formats, then copy to clipboard. The file matches what is on screen.
10
+
11
+ **Built from:** Export menu, List table, Data grid, Bulk action bar, Toast, Inbox (triage)
12
+
13
+ ## How it works
14
+
15
+ 1. Export sits in the table toolbar with the other global table actions, and in the bulk bar when rows are selected.
16
+ 2. Options come first: rows (selected, or all N matching the filters), visible columns only, include AI columns.
17
+ 3. Then the format (CSV, Excel), then Copy to clipboard for quick pastes.
18
+ 4. Small exports download at once with a toast. Exports likely to take more than about ten seconds run in the background with progress, and finish with an Inbox item linking to the file.
19
+ 5. If the export fails, say why and offer Try again in a banner, not a toast.
20
+
21
+ ## When to use
22
+
23
+ - On any list table, data grid, breakdown table or chart that has a table view.
24
+
25
+ ## When not to use
26
+
27
+ - To share a view with a colleague inside TwoUX. Share the view's link instead (Saved views).
28
+ - As a fix for missing analysis. If many people export the same table, add the analysis to the product.
29
+
30
+ ## Guidelines
31
+
32
+ - The file matches the screen: same rows, same visible columns, same order, column names as shown, not database field names.
33
+ - AI columns are excluded by default and, when included, are named with a suffix such as "(AI)" so generated values are never mistaken for real data.
34
+ - Say the scope in numbers before export: "Export 412 candidates".
35
+ - Long exports do not block the page; people can keep working.
36
+
37
+ ## Content
38
+
39
+ - Menu label: "Export". Actions name the scope: "Export 2 rows as CSV".
40
+
41
+ ## Accessibility
42
+
43
+ - Announce "Export ready" or "Export failed" as a status message.
44
+ - The export menu is a standard menu button reachable by keyboard; ⇧E opens it.
45
+
46
+ ## Do
47
+
48
+ - Do export what the person sees.
49
+ - Do label AI columns in the file.
50
+
51
+ ## Don't
52
+
53
+ - Don't put Export in a different place on each table.
54
+ - Don't block the screen while a large export runs.
55
+
56
+ ## Related
57
+
58
+ - Selection and bulk actions
59
+ - Generated table columns and enrichment
60
+ - Notifications: inbox, bell, toast or email digest
61
+ - Export menu
@@ -0,0 +1,137 @@
1
+ # Data grid
2
+
3
+ An editable, spreadsheet-like grid for enriching and exporting many columns, with pinned columns, cell-by-cell keyboard navigation, a totals row and AI-generated columns.
4
+
5
+ Kind: component · Group: Data · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - People edit, enrich or export many columns of data, and cells contain interactive or editable content.
10
+ - The table is wider than the screen and people compare values across columns, so the identifying column must stay in view.
11
+ - People need summary content such as totals kept in view while they scroll, for example total salary or average apply rate.
12
+
13
+ ## When not to use
14
+
15
+ - For scanning and triage of records. Use the TwoUX List table; a static table keeps reading simpler.
16
+ - For deep edits of one record. Use the side panel or full record page; in-place editing only works for narrow edits.
17
+ - For a handful of records. Use the TwoUX List table without pagination or totals.
18
+ - For spreadsheet work: selecting ranges, copying to and pasting from Excel or Google Sheets, fill down and undo. Use the Sheet grid, with size="dense" for 28px rows.
19
+
20
+ ## Structure
21
+
22
+ 1. Toolbar (view name, Columns, Group, Export)
23
+ 2. Pinned header row with column type icons
24
+ 3. Pinned checkbox and identifying columns
25
+ 4. Scrollable data cells
26
+ 5. AI column (tinted, with run state per cell)
27
+ 6. Pinned totals row
28
+ 7. Status line for save and validation messages
29
+
30
+ ## Guidelines
31
+
32
+ - The grid has one tab stop. Arrow keys move between cells, Home and End go to the first and last cell in the row.
33
+ - Enter or F2 starts editing, Escape cancels, Enter or Tab commits. Validation is per field.
34
+ - Pin the checkbox and Role columns while scrolling sideways, and the header and totals row while scrolling down.
35
+ - Mark editable columns in the header and show the edit affordance on hover.
36
+ - The totals row is chosen per column (sum, average, empty percentage) and counts only rows on screen; say so in the label.
37
+ - The AI column runs on demand with progress, Stop and per-cell state, and never overwrites real data.
38
+ - The Data grid keeps 36px rows. For spreadsheet-like data where people need many rows in view, use the Sheet grid with size="dense" instead of changing the Data grid's height.
39
+
40
+ ## Content
41
+
42
+ - Each column shows its type icon so people recognise number, money, person, link, date and AI columns at a glance.
43
+ - Right-align numbers and money with consistent decimals.
44
+ - Keep three empty states apart: a dash when there is nothing, "Not found" when the AI looked and found nothing, and a red message with Retry when it failed.
45
+ - Validation messages name the field and the rule, for example "Enter at least £23,400, the UK minimum for a full-time role."
46
+
47
+ ## Accessibility
48
+
49
+ - APG grid pattern: role="grid", roving focus on cells, aria-selected on selected rows, aria-sort on the sorted header.
50
+ - 2.1.1 Keyboard: every cell action, including sort, hide, totals and AI run, works from the keyboard.
51
+ - 3.3.1 Error Identification: invalid values stay in edit with a red outline and a text message, not the outline alone.
52
+ - 4.1.3 Status Messages: save confirmations and AI progress are announced from the status line without moving focus.
53
+ - 2.4.11 Focus Not Obscured: pinned columns and rows must not hide the focused cell; scroll it into view.
54
+
55
+ ## Keyboard
56
+
57
+ | Keys | Action |
58
+ |---|---|
59
+ | ← → ↑ ↓ | Move one cell |
60
+ | Home / End | First or last cell in the row |
61
+ | Enter / F2 | Edit the focused cell |
62
+ | Enter (editing) | Save and stay |
63
+ | Tab (editing) | Save and move to the next cell |
64
+ | Esc (editing) | Cancel the edit |
65
+ | ⇧Space | Select or deselect the active row; the bulk action bar shows above the header |
66
+
67
+ ## Examples
68
+
69
+ ### enrich
70
+
71
+ ```tsx
72
+ import { DataGrid, type GridColumn, type GridRow, toast } from "@twoux/ui";
73
+ import { Copy, Trash2 } from "lucide-react";
74
+ import { useState } from "react";
75
+
76
+ export const meta = { title: "Enrich listings in place", description: "Focus the grid, move with the arrow keys and press Enter, F2 or just type to edit. Esc cancels; Enter saves and stays; Tab saves and moves on. Salary must be a whole number above the minimum.", order: 1 };
77
+
78
+ const COLUMNS: GridColumn[] = [
79
+ { id: "role", label: "Role", width: 220, pin: true },
80
+ { id: "team", label: "Team", width: 130, editable: true },
81
+ { id: "location", label: "Location", width: 140, editable: true },
82
+ { id: "salary", label: "Salary from (£)", width: 150, editable: true, type: "number", total: "average", validate: (v) => (!/^[0-9]+$/.test(v.replace(/,/g, "")) ? "Enter a whole number, such as 32000." : Number(v.replace(/,/g, "")) < 23400 ? "Enter at least £23,400, the full-time minimum." : undefined) },
83
+ { id: "applicants", label: "Applicants", width: 120, type: "number", total: "sum" },
84
+ { id: "manager", label: "Hiring manager", width: 160, editable: true },
85
+ ];
86
+
87
+ export default function Example() {
88
+ const [rows, setRows] = useState<GridRow[]>([
89
+ { id: "1", role: "Junior Product Designer", team: "Design", location: "Leeds", salary: 30000, applicants: 38, manager: "Priya Shah" },
90
+ { id: "2", role: "Frontend Engineer", team: "Engineering", location: "Manchester", salary: 52000, applicants: 112, manager: "Tom Becker" },
91
+ { id: "3", role: "Data Analyst", team: "Data", location: "Remote (UK)", salary: 41000, applicants: 64, manager: "" },
92
+ { id: "4", role: "Recruitment Coordinator", team: "People", location: "London", salary: 29000, applicants: 0, manager: "Amara Okafor" },
93
+ { id: "5", role: "Customer Success Lead", team: "Customer", location: "Bristol", salary: 45000, applicants: 21, manager: "" },
94
+ ]);
95
+ const [selected, setSelected] = useState<string[]>([]);
96
+ const n = selected.length;
97
+ const noun = n === 1 ? "listing" : "listings";
98
+ const duplicate = () => {
99
+ setRows((rs) => [...rs, ...rs.filter((r) => selected.includes(r.id)).map((r, i) => ({ ...r, id: `${r.id}-copy-${rs.length + i}`, role: `${r.role} (copy)` }))]);
100
+ toast.success(`Duplicated ${n} ${noun}`);
101
+ setSelected([]);
102
+ };
103
+ const remove = () => {
104
+ const before = rows;
105
+ setRows((rs) => rs.filter((r) => !selected.includes(r.id)));
106
+ toast(`Deleted ${n} ${noun}`, { action: { label: "Undo", onClick: () => setRows(before) } });
107
+ setSelected([]);
108
+ };
109
+ return (
110
+ <DataGrid
111
+ className="w-full"
112
+ aria-label="Listings"
113
+ columns={COLUMNS}
114
+ rows={rows}
115
+ onCellChange={(id, col, v) => setRows((r) => r.map((x) => (x.id === id ? { ...x, [col]: v } : x)))}
116
+ selectedRowIds={selected}
117
+ onSelectedRowIdsChange={setSelected}
118
+ noun={noun}
119
+ bulkActions={[
120
+ { label: "Duplicate", icon: <Copy />, onSelect: duplicate },
121
+ { label: "Delete", icon: <Trash2 />, destructive: true, onSelect: remove },
122
+ ]}
123
+ />
124
+ );
125
+ }
126
+ ```
127
+
128
+ ## Related
129
+
130
+ - List table
131
+ - Columns menu
132
+ - Export menu
133
+ - Saved views
134
+ - Filter pills and filter bar
135
+ - Bulk action bar
136
+ - Sheet grid
137
+ - Dense table