@exxatdesignux/ui 0.2.19 → 0.4.0

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 (716) hide show
  1. package/CHANGELOG.md +662 -7
  2. package/bin/sync-extras.mjs +116 -29
  3. package/consumer-extras/README.md +42 -7
  4. package/consumer-extras/cursor-rules/exxat-accessibility.mdc +39 -0
  5. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +26 -0
  6. package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +21 -0
  7. package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +21 -0
  8. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +44 -0
  9. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +32 -0
  10. package/consumer-extras/cursor-rules/exxat-command-menu.mdc +22 -0
  11. package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +53 -0
  12. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +43 -0
  13. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +25 -0
  14. package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +22 -0
  15. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +56 -0
  16. package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +31 -0
  17. package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +100 -0
  18. package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +28 -0
  19. package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +21 -0
  20. package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +31 -0
  21. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  22. package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +24 -0
  23. package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +31 -0
  24. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +30 -0
  25. package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +78 -0
  26. package/consumer-extras/cursor-rules/exxat-no-toast.mdc +25 -0
  27. package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +23 -0
  28. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +47 -0
  29. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +52 -0
  30. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +34 -0
  31. package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +77 -0
  32. package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +103 -0
  33. package/consumer-extras/cursor-skills/exxat-accessibility/SKILL.md +1 -1
  34. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +3 -3
  35. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +5 -16
  36. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  37. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  38. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +19 -34
  39. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
  40. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  41. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  42. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  43. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +10 -12
  44. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  45. package/consumer-extras/handbook/HANDBOOK.md +187 -0
  46. package/consumer-extras/handbook/glossary.md +58 -0
  47. package/consumer-extras/handbook/reference-implementations.md +153 -0
  48. package/consumer-extras/handbook/voice-and-tone.md +262 -0
  49. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  50. package/consumer-extras/patterns/command-menu-pattern.md +1 -1
  51. package/consumer-extras/patterns/consumer-upgrade-checklist.md +0 -20
  52. package/consumer-extras/patterns/data-views-pattern.md +31 -66
  53. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  54. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +3 -5
  55. package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
  56. package/dist/components/data-table/filter-date-calendar.js +280 -0
  57. package/dist/components/data-table/filter-date-calendar.js.map +1 -0
  58. package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
  59. package/dist/components/data-table/filter-text-value-input.js +561 -0
  60. package/dist/components/data-table/filter-text-value-input.js.map +1 -0
  61. package/dist/components/data-table/index.d.ts +45 -0
  62. package/dist/components/data-table/index.js +3085 -0
  63. package/dist/components/data-table/index.js.map +1 -0
  64. package/dist/components/data-table/pagination.d.ts +28 -0
  65. package/dist/components/data-table/pagination.js +3264 -0
  66. package/dist/components/data-table/pagination.js.map +1 -0
  67. package/dist/components/data-table/types.d.ts +84 -0
  68. package/dist/components/data-table/types.js +3 -0
  69. package/dist/components/data-table/types.js.map +1 -0
  70. package/dist/components/data-table/use-table-state.d.ts +116 -0
  71. package/dist/components/data-table/use-table-state.js +670 -0
  72. package/dist/components/data-table/use-table-state.js.map +1 -0
  73. package/dist/components/data-views/board-card-primitives.d.ts +22 -0
  74. package/dist/components/data-views/board-card-primitives.js +84 -0
  75. package/dist/components/data-views/board-card-primitives.js.map +1 -0
  76. package/dist/components/data-views/data-row-list.d.ts +33 -0
  77. package/dist/components/data-views/data-row-list.js +106 -0
  78. package/dist/components/data-views/data-row-list.js.map +1 -0
  79. package/dist/components/data-views/finder-panel-view.d.ts +54 -0
  80. package/dist/components/data-views/finder-panel-view.js +388 -0
  81. package/dist/components/data-views/finder-panel-view.js.map +1 -0
  82. package/dist/components/data-views/folder-grid-view.d.ts +22 -0
  83. package/dist/components/data-views/folder-grid-view.js +58 -0
  84. package/dist/components/data-views/folder-grid-view.js.map +1 -0
  85. package/dist/components/data-views/hub-table.d.ts +173 -0
  86. package/dist/components/data-views/hub-table.js +5783 -0
  87. package/dist/components/data-views/hub-table.js.map +1 -0
  88. package/dist/components/data-views/index.d.ts +27 -0
  89. package/dist/components/data-views/index.js +6797 -0
  90. package/dist/components/data-views/index.js.map +1 -0
  91. package/dist/components/data-views/list-page-board-card.d.ts +72 -0
  92. package/dist/components/data-views/list-page-board-card.js +264 -0
  93. package/dist/components/data-views/list-page-board-card.js.map +1 -0
  94. package/dist/components/data-views/list-page-board-template.d.ts +24 -0
  95. package/dist/components/data-views/list-page-board-template.js +137 -0
  96. package/dist/components/data-views/list-page-board-template.js.map +1 -0
  97. package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
  98. package/dist/components/data-views/list-page-connected-view-body.js +116 -0
  99. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
  100. package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
  101. package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
  102. package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
  103. package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
  104. package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
  105. package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
  106. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
  107. package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
  108. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
  109. package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
  110. package/dist/components/data-views/list-page-tree-column-header.js +22 -0
  111. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
  112. package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
  113. package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
  114. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
  115. package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
  116. package/dist/components/data-views/os-folder-glyph.js +104 -0
  117. package/dist/components/data-views/os-folder-glyph.js.map +1 -0
  118. package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
  119. package/dist/components/data-views/outline-tree-menu.js +131 -0
  120. package/dist/components/data-views/outline-tree-menu.js.map +1 -0
  121. package/dist/components/table-properties/column-row.d.ts +22 -0
  122. package/dist/components/table-properties/column-row.js +153 -0
  123. package/dist/components/table-properties/column-row.js.map +1 -0
  124. package/dist/components/table-properties/draggable-list.d.ts +24 -0
  125. package/dist/components/table-properties/draggable-list.js +53 -0
  126. package/dist/components/table-properties/draggable-list.js.map +1 -0
  127. package/dist/components/table-properties/drawer-button.d.ts +110 -0
  128. package/dist/components/table-properties/drawer-button.js +2748 -0
  129. package/dist/components/table-properties/drawer-button.js.map +1 -0
  130. package/dist/components/table-properties/drawer.d.ts +100 -0
  131. package/dist/components/table-properties/drawer.js +2595 -0
  132. package/dist/components/table-properties/drawer.js.map +1 -0
  133. package/dist/components/table-properties/filter-card.d.ts +24 -0
  134. package/dist/components/table-properties/filter-card.js +854 -0
  135. package/dist/components/table-properties/filter-card.js.map +1 -0
  136. package/dist/components/table-properties/index.d.ts +14 -0
  137. package/dist/components/table-properties/index.js +2768 -0
  138. package/dist/components/table-properties/index.js.map +1 -0
  139. package/dist/components/table-properties/sort-card.d.ts +20 -0
  140. package/dist/components/table-properties/sort-card.js +102 -0
  141. package/dist/components/table-properties/sort-card.js.map +1 -0
  142. package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
  143. package/dist/components/templates/dedicated-search-landing-template.js +254 -0
  144. package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
  145. package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
  146. package/dist/components/templates/dedicated-search-results-template.js +16 -0
  147. package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
  148. package/dist/components/templates/index.d.ts +9 -0
  149. package/dist/components/templates/index.js +2720 -0
  150. package/dist/components/templates/index.js.map +1 -0
  151. package/dist/components/templates/list-page.d.ts +83 -0
  152. package/dist/components/templates/list-page.js +2433 -0
  153. package/dist/components/templates/list-page.js.map +1 -0
  154. package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
  155. package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
  156. package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
  157. package/dist/components/ui/accordion.d.ts +10 -0
  158. package/dist/components/ui/accordion.js +74 -0
  159. package/dist/components/ui/accordion.js.map +1 -0
  160. package/dist/components/ui/alert-dialog.d.ts +37 -0
  161. package/dist/components/ui/alert-dialog.js +201 -0
  162. package/dist/components/ui/alert-dialog.js.map +1 -0
  163. package/dist/components/ui/avatar.d.ts +84 -0
  164. package/dist/components/ui/avatar.js +328 -0
  165. package/dist/components/ui/avatar.js.map +1 -0
  166. package/dist/components/ui/badge.d.ts +13 -0
  167. package/dist/components/ui/badge.js +49 -0
  168. package/dist/components/ui/badge.js.map +1 -0
  169. package/dist/components/ui/banner.d.ts +62 -0
  170. package/dist/components/ui/banner.js +364 -0
  171. package/dist/components/ui/banner.js.map +1 -0
  172. package/dist/components/ui/breadcrumb.d.ts +14 -0
  173. package/dist/components/ui/breadcrumb.js +114 -0
  174. package/dist/components/ui/breadcrumb.js.map +1 -0
  175. package/dist/components/ui/button.d.ts +16 -0
  176. package/dist/components/ui/button.js +59 -0
  177. package/dist/components/ui/button.js.map +1 -0
  178. package/dist/components/ui/calendar.d.ts +13 -0
  179. package/dist/components/ui/calendar.js +238 -0
  180. package/dist/components/ui/calendar.js.map +1 -0
  181. package/dist/components/ui/card.d.ts +14 -0
  182. package/dist/components/ui/card.js +102 -0
  183. package/dist/components/ui/card.js.map +1 -0
  184. package/dist/components/ui/chart.d.ts +58 -0
  185. package/dist/components/ui/chart.js +292 -0
  186. package/dist/components/ui/chart.js.map +1 -0
  187. package/dist/components/ui/checkbox.d.ts +23 -0
  188. package/dist/components/ui/checkbox.js +155 -0
  189. package/dist/components/ui/checkbox.js.map +1 -0
  190. package/dist/components/ui/coach-mark.d.ts +27 -0
  191. package/dist/components/ui/coach-mark.js +306 -0
  192. package/dist/components/ui/coach-mark.js.map +1 -0
  193. package/dist/components/ui/collapsible.d.ts +8 -0
  194. package/dist/components/ui/collapsible.js +35 -0
  195. package/dist/components/ui/collapsible.js.map +1 -0
  196. package/dist/components/ui/command.d.ts +36 -0
  197. package/dist/components/ui/command.js +274 -0
  198. package/dist/components/ui/command.js.map +1 -0
  199. package/dist/components/ui/context-menu.d.ts +32 -0
  200. package/dist/components/ui/context-menu.js +245 -0
  201. package/dist/components/ui/context-menu.js.map +1 -0
  202. package/dist/components/ui/date-picker-field.d.ts +38 -0
  203. package/dist/components/ui/date-picker-field.js +550 -0
  204. package/dist/components/ui/date-picker-field.js.map +1 -0
  205. package/dist/components/ui/dialog.d.ts +22 -0
  206. package/dist/components/ui/dialog.js +200 -0
  207. package/dist/components/ui/dialog.js.map +1 -0
  208. package/dist/components/ui/dot-pattern.d.ts +21 -0
  209. package/dist/components/ui/dot-pattern.js +139 -0
  210. package/dist/components/ui/dot-pattern.js.map +1 -0
  211. package/dist/components/ui/drag-handle-grip.d.ts +10 -0
  212. package/dist/components/ui/drag-handle-grip.js +15 -0
  213. package/dist/components/ui/drag-handle-grip.js.map +1 -0
  214. package/dist/components/ui/drawer.d.ts +16 -0
  215. package/dist/components/ui/drawer.js +125 -0
  216. package/dist/components/ui/drawer.js.map +1 -0
  217. package/dist/components/ui/dropdown-menu.d.ts +45 -0
  218. package/dist/components/ui/dropdown-menu.js +353 -0
  219. package/dist/components/ui/dropdown-menu.js.map +1 -0
  220. package/dist/components/ui/export-drawer.d.ts +11 -0
  221. package/dist/components/ui/export-drawer.js +1658 -0
  222. package/dist/components/ui/export-drawer.js.map +1 -0
  223. package/dist/components/ui/field.d.ts +30 -0
  224. package/dist/components/ui/field.js +249 -0
  225. package/dist/components/ui/field.js.map +1 -0
  226. package/dist/components/ui/form.d.ts +28 -0
  227. package/dist/components/ui/form.js +110 -0
  228. package/dist/components/ui/form.js.map +1 -0
  229. package/dist/components/ui/hover-card.d.ts +9 -0
  230. package/dist/components/ui/hover-card.js +43 -0
  231. package/dist/components/ui/hover-card.js.map +1 -0
  232. package/dist/components/ui/input-group.d.ts +20 -0
  233. package/dist/components/ui/input-group.js +219 -0
  234. package/dist/components/ui/input-group.js.map +1 -0
  235. package/dist/components/ui/input-mask.d.ts +39 -0
  236. package/dist/components/ui/input-mask.js +118 -0
  237. package/dist/components/ui/input-mask.js.map +1 -0
  238. package/dist/components/ui/input.d.ts +5 -0
  239. package/dist/components/ui/input.js +30 -0
  240. package/dist/components/ui/input.js.map +1 -0
  241. package/dist/components/ui/kbd.d.ts +20 -0
  242. package/dist/components/ui/kbd.js +45 -0
  243. package/dist/components/ui/kbd.js.map +1 -0
  244. package/dist/components/ui/key-metrics-context.d.ts +19 -0
  245. package/dist/components/ui/key-metrics-context.js +26 -0
  246. package/dist/components/ui/key-metrics-context.js.map +1 -0
  247. package/dist/components/ui/key-metrics.d.ts +131 -0
  248. package/dist/components/ui/key-metrics.js +1015 -0
  249. package/dist/components/ui/key-metrics.js.map +1 -0
  250. package/dist/components/ui/label.d.ts +6 -0
  251. package/dist/components/ui/label.js +28 -0
  252. package/dist/components/ui/label.js.map +1 -0
  253. package/dist/components/ui/list-page-view-frame.d.ts +22 -0
  254. package/dist/components/ui/list-page-view-frame.js +24 -0
  255. package/dist/components/ui/list-page-view-frame.js.map +1 -0
  256. package/dist/components/ui/page-header.d.ts +51 -0
  257. package/dist/components/ui/page-header.js +372 -0
  258. package/dist/components/ui/page-header.js.map +1 -0
  259. package/dist/components/ui/payment-card-fields.d.ts +10 -0
  260. package/dist/components/ui/payment-card-fields.js +80 -0
  261. package/dist/components/ui/payment-card-fields.js.map +1 -0
  262. package/dist/components/ui/popover.d.ts +10 -0
  263. package/dist/components/ui/popover.js +47 -0
  264. package/dist/components/ui/popover.js.map +1 -0
  265. package/dist/components/ui/radio-group.d.ts +29 -0
  266. package/dist/components/ui/radio-group.js +190 -0
  267. package/dist/components/ui/radio-group.js.map +1 -0
  268. package/dist/components/ui/resizable.d.ts +16 -0
  269. package/dist/components/ui/resizable.js +51 -0
  270. package/dist/components/ui/resizable.js.map +1 -0
  271. package/dist/components/ui/scroll-area.d.ts +8 -0
  272. package/dist/components/ui/scroll-area.js +66 -0
  273. package/dist/components/ui/scroll-area.js.map +1 -0
  274. package/dist/components/ui/select.d.ts +18 -0
  275. package/dist/components/ui/select.js +186 -0
  276. package/dist/components/ui/select.js.map +1 -0
  277. package/dist/components/ui/selection-tile-grid.d.ts +52 -0
  278. package/dist/components/ui/selection-tile-grid.js +347 -0
  279. package/dist/components/ui/selection-tile-grid.js.map +1 -0
  280. package/dist/components/ui/separator.d.ts +7 -0
  281. package/dist/components/ui/separator.js +33 -0
  282. package/dist/components/ui/separator.js.map +1 -0
  283. package/dist/components/ui/sheet.d.ts +18 -0
  284. package/dist/components/ui/sheet.js +181 -0
  285. package/dist/components/ui/sheet.js.map +1 -0
  286. package/dist/components/ui/sidebar.d.ts +94 -0
  287. package/dist/components/ui/sidebar.js +805 -0
  288. package/dist/components/ui/sidebar.js.map +1 -0
  289. package/dist/components/ui/skeleton.d.ts +5 -0
  290. package/dist/components/ui/skeleton.js +22 -0
  291. package/dist/components/ui/skeleton.js.map +1 -0
  292. package/dist/components/ui/slider.d.ts +7 -0
  293. package/dist/components/ui/slider.js +66 -0
  294. package/dist/components/ui/slider.js.map +1 -0
  295. package/dist/components/ui/sonner.d.ts +6 -0
  296. package/dist/components/ui/sonner.js +38 -0
  297. package/dist/components/ui/sonner.js.map +1 -0
  298. package/dist/components/ui/status-badge.d.ts +38 -0
  299. package/dist/components/ui/status-badge.js +77 -0
  300. package/dist/components/ui/status-badge.js.map +1 -0
  301. package/dist/components/ui/table.d.ts +13 -0
  302. package/dist/components/ui/table.js +115 -0
  303. package/dist/components/ui/table.js.map +1 -0
  304. package/dist/components/ui/tabs.d.ts +15 -0
  305. package/dist/components/ui/tabs.js +93 -0
  306. package/dist/components/ui/tabs.js.map +1 -0
  307. package/dist/components/ui/textarea.d.ts +6 -0
  308. package/dist/components/ui/textarea.js +25 -0
  309. package/dist/components/ui/textarea.js.map +1 -0
  310. package/dist/components/ui/tip.d.ts +12 -0
  311. package/dist/components/ui/tip.js +61 -0
  312. package/dist/components/ui/tip.js.map +1 -0
  313. package/dist/components/ui/toggle-group.d.ts +14 -0
  314. package/dist/components/ui/toggle-group.js +104 -0
  315. package/dist/components/ui/toggle-group.js.map +1 -0
  316. package/dist/components/ui/toggle-switch.d.ts +10 -0
  317. package/dist/components/ui/toggle-switch.js +33 -0
  318. package/dist/components/ui/toggle-switch.js.map +1 -0
  319. package/dist/components/ui/toggle.d.ts +13 -0
  320. package/dist/components/ui/toggle.js +51 -0
  321. package/dist/components/ui/toggle.js.map +1 -0
  322. package/dist/components/ui/tooltip.d.ts +10 -0
  323. package/dist/components/ui/tooltip.js +68 -0
  324. package/dist/components/ui/tooltip.js.map +1 -0
  325. package/dist/components/ui/view-segmented-control.d.ts +31 -0
  326. package/dist/components/ui/view-segmented-control.js +167 -0
  327. package/dist/components/ui/view-segmented-control.js.map +1 -0
  328. package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
  329. package/dist/hooks/use-app-theme.d.ts +24 -0
  330. package/dist/hooks/use-app-theme.js +286 -0
  331. package/dist/hooks/use-app-theme.js.map +1 -0
  332. package/dist/hooks/use-coach-mark.d.ts +86 -0
  333. package/dist/hooks/use-coach-mark.js +218 -0
  334. package/dist/hooks/use-coach-mark.js.map +1 -0
  335. package/dist/hooks/use-mobile.d.ts +3 -0
  336. package/dist/hooks/use-mobile.js +29 -0
  337. package/dist/hooks/use-mobile.js.map +1 -0
  338. package/dist/hooks/use-mod-key-label.d.ts +6 -0
  339. package/dist/hooks/use-mod-key-label.js +25 -0
  340. package/dist/hooks/use-mod-key-label.js.map +1 -0
  341. package/dist/index.d.ts +120 -0
  342. package/dist/index.js +13421 -0
  343. package/dist/index.js.map +1 -0
  344. package/dist/lib/compose-refs.d.ts +6 -0
  345. package/dist/lib/compose-refs.js +17 -0
  346. package/dist/lib/compose-refs.js.map +1 -0
  347. package/dist/lib/conditional-rule-match.d.ts +30 -0
  348. package/dist/lib/conditional-rule-match.js +66 -0
  349. package/dist/lib/conditional-rule-match.js.map +1 -0
  350. package/dist/lib/data-list-display-options.d.ts +26 -0
  351. package/dist/lib/data-list-display-options.js +14 -0
  352. package/dist/lib/data-list-display-options.js.map +1 -0
  353. package/dist/lib/data-list-view-registry.d.ts +2 -0
  354. package/dist/lib/data-list-view-registry.js +102 -0
  355. package/dist/lib/data-list-view-registry.js.map +1 -0
  356. package/dist/lib/data-list-view-surface.d.ts +2 -0
  357. package/dist/lib/data-list-view-surface.js +80 -0
  358. package/dist/lib/data-list-view-surface.js.map +1 -0
  359. package/dist/lib/data-list-view.d.ts +21 -0
  360. package/dist/lib/data-list-view.js +25 -0
  361. package/dist/lib/data-list-view.js.map +1 -0
  362. package/dist/lib/date-filter.d.ts +22 -0
  363. package/dist/lib/date-filter.js +61 -0
  364. package/dist/lib/date-filter.js.map +1 -0
  365. package/dist/lib/dev-log.d.ts +8 -0
  366. package/dist/lib/dev-log.js +10 -0
  367. package/dist/lib/dev-log.js.map +1 -0
  368. package/dist/lib/dropdown-menu-surface.d.ts +14 -0
  369. package/dist/lib/dropdown-menu-surface.js +6 -0
  370. package/dist/lib/dropdown-menu-surface.js.map +1 -0
  371. package/dist/lib/editable-target.d.ts +12 -0
  372. package/dist/lib/editable-target.js +12 -0
  373. package/dist/lib/editable-target.js.map +1 -0
  374. package/dist/lib/list-page-table-properties.d.ts +35 -0
  375. package/dist/lib/list-page-table-properties.js +81 -0
  376. package/dist/lib/list-page-table-properties.js.map +1 -0
  377. package/dist/lib/raf-throttle.d.ts +23 -0
  378. package/dist/lib/raf-throttle.js +27 -0
  379. package/dist/lib/raf-throttle.js.map +1 -0
  380. package/dist/lib/row-height.d.ts +16 -0
  381. package/dist/lib/row-height.js +10 -0
  382. package/dist/lib/row-height.js.map +1 -0
  383. package/dist/lib/table-properties-types.d.ts +83 -0
  384. package/dist/lib/table-properties-types.js +19 -0
  385. package/dist/lib/table-properties-types.js.map +1 -0
  386. package/dist/lib/utils.d.ts +5 -0
  387. package/dist/lib/utils.js +11 -0
  388. package/dist/lib/utils.js.map +1 -0
  389. package/package.json +83 -19
  390. package/src/components/data-table/filter-date-calendar.tsx +38 -0
  391. package/src/components/data-table/filter-text-value-input.tsx +77 -0
  392. package/src/components/data-table/index.tsx +1678 -0
  393. package/src/components/data-table/pagination.tsx +259 -0
  394. package/src/components/data-table/types.ts +96 -0
  395. package/src/components/data-table/use-table-state.ts +767 -0
  396. package/src/components/data-views/board-card-primitives.tsx +93 -0
  397. package/src/components/data-views/data-row-list.tsx +183 -0
  398. package/src/components/data-views/finder-panel-view.tsx +405 -0
  399. package/src/components/data-views/folder-grid-view.tsx +86 -0
  400. package/src/components/data-views/hub-table.tsx +606 -0
  401. package/src/components/data-views/index.ts +28 -0
  402. package/src/components/data-views/list-page-board-card.tsx +192 -0
  403. package/src/components/data-views/list-page-board-template.tsx +122 -0
  404. package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
  405. package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  406. package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  407. package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
  408. package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
  409. package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  410. package/src/components/data-views/os-folder-glyph.tsx +141 -0
  411. package/src/components/data-views/outline-tree-menu.tsx +157 -0
  412. package/src/components/table-properties/column-row.tsx +90 -0
  413. package/src/components/table-properties/draggable-list.ts +54 -0
  414. package/src/components/table-properties/drawer-button.tsx +300 -0
  415. package/src/components/table-properties/drawer.tsx +1148 -0
  416. package/src/components/table-properties/filter-card.tsx +251 -0
  417. package/src/components/table-properties/index.ts +36 -0
  418. package/src/components/table-properties/sort-card.tsx +63 -0
  419. package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
  420. package/src/components/templates/dedicated-search-results-template.tsx +19 -0
  421. package/src/components/templates/index.ts +33 -0
  422. package/src/components/templates/list-page.tsx +602 -0
  423. package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
  424. package/src/components/ui/accordion.tsx +92 -0
  425. package/src/components/ui/alert-dialog.tsx +221 -0
  426. package/src/components/ui/avatar.tsx +13 -2
  427. package/src/components/ui/banner.tsx +2 -2
  428. package/src/components/ui/button.tsx +4 -4
  429. package/src/components/ui/calendar.tsx +1 -1
  430. package/src/components/ui/coach-mark.tsx +1 -1
  431. package/src/components/ui/context-menu.tsx +291 -0
  432. package/src/components/ui/date-picker-field.tsx +2 -2
  433. package/src/components/ui/dot-pattern.tsx +183 -0
  434. package/src/components/ui/export-drawer.tsx +375 -0
  435. package/src/components/ui/hover-card.tsx +66 -0
  436. package/src/components/ui/key-metrics-context.tsx +78 -0
  437. package/src/components/ui/key-metrics.tsx +1133 -0
  438. package/src/components/ui/list-page-view-frame.tsx +64 -0
  439. package/src/components/ui/page-header.tsx +244 -0
  440. package/src/components/ui/payment-card-fields.tsx +2 -2
  441. package/src/components/ui/resizable.tsx +68 -0
  442. package/src/components/ui/scroll-area.tsx +72 -0
  443. package/src/components/ui/selection-tile-grid.tsx +9 -2
  444. package/src/components/ui/sidebar.tsx +84 -12
  445. package/src/components/ui/slider.tsx +83 -0
  446. package/src/globals.css +2201 -7
  447. package/src/globals.d.ts +20 -0
  448. package/src/index.ts +68 -1
  449. package/src/lib/conditional-rule-match.ts +119 -0
  450. package/src/lib/data-list-display-options.ts +35 -0
  451. package/src/lib/data-list-view-registry.ts +104 -0
  452. package/src/lib/data-list-view-surface.ts +83 -0
  453. package/src/lib/data-list-view.ts +47 -0
  454. package/src/lib/dev-log.ts +10 -0
  455. package/src/lib/editable-target.ts +20 -0
  456. package/src/lib/list-page-table-properties.ts +48 -0
  457. package/src/lib/raf-throttle.ts +45 -0
  458. package/src/lib/row-height.ts +19 -0
  459. package/src/lib/table-properties-types.ts +98 -0
  460. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  461. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  462. package/template/.cursor/rules/exxat-command-menu.mdc +2 -2
  463. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +7 -7
  464. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  465. package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
  466. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +7 -7
  467. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  468. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  469. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  470. package/template/AGENTS.md +135 -103
  471. package/template/app/(app)/columns/page.tsx +11 -0
  472. package/template/app/(app)/dashboard/loading.tsx +15 -3
  473. package/template/app/(app)/dashboard/page.tsx +14 -2
  474. package/template/app/(app)/layout.tsx +17 -4
  475. package/template/app/(app)/library/all/page.tsx +11 -0
  476. package/template/app/(app)/library/find/page.tsx +12 -0
  477. package/template/app/(app)/{question-bank → library}/layout.tsx +17 -17
  478. package/template/app/(app)/library/list/page.tsx +12 -0
  479. package/template/app/(app)/library/new/page.tsx +45 -0
  480. package/template/app/(app)/library/page.tsx +11 -0
  481. package/template/app/(app)/loading.tsx +18 -1
  482. package/template/app/(app)/settings/page.tsx +5 -4
  483. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  484. package/template/app/globals.css +14 -16
  485. package/template/components/ask-leo-composer.tsx +2 -2
  486. package/template/components/ask-leo-sidebar.tsx +5 -1
  487. package/template/components/brand-color-picker.tsx +2 -2
  488. package/template/components/charts-overview.tsx +1 -1
  489. package/template/components/columns-client.tsx +158 -0
  490. package/template/components/columns-showcase.tsx +541 -0
  491. package/template/components/dashboard-report-charts.tsx +1 -1
  492. package/template/components/dashboard-tabs.tsx +1 -1
  493. package/template/components/data-table/filter-date-calendar.tsx +1 -38
  494. package/template/components/data-table/filter-text-value-input.tsx +1 -77
  495. package/template/components/data-table/index.tsx +1 -1634
  496. package/template/components/data-table/pagination.tsx +1 -255
  497. package/template/components/data-table/types.ts +1 -94
  498. package/template/components/data-table/use-table-state.test.ts +420 -0
  499. package/template/components/data-table/use-table-state.ts +1 -758
  500. package/template/components/data-views/board-card-primitives.tsx +1 -93
  501. package/template/components/data-views/data-row-list.tsx +1 -183
  502. package/template/components/data-views/finder-panel-view.tsx +1 -405
  503. package/template/components/data-views/folder-grid-view.tsx +1 -86
  504. package/template/components/data-views/hub-table.tsx +1 -0
  505. package/template/components/data-views/index.ts +77 -38
  506. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  507. package/template/components/data-views/list-page-board-card.tsx +1 -192
  508. package/template/components/data-views/list-page-board-template.tsx +1 -122
  509. package/template/components/data-views/list-page-connected-view-body.tsx +1 -66
  510. package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
  511. package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -68
  512. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
  513. package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
  514. package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
  515. package/template/components/data-views/list-page-view-frame.tsx +5 -53
  516. package/template/components/data-views/os-folder-glyph.tsx +1 -129
  517. package/template/components/data-views/outline-tree-menu.tsx +1 -157
  518. package/template/components/data-views/table-cells.tsx +673 -0
  519. package/template/components/export-drawer.test.tsx +71 -0
  520. package/template/components/export-drawer.tsx +1 -375
  521. package/template/components/exxat-product-logo.tsx +5 -5
  522. package/template/components/folder-details-shell.tsx +11 -11
  523. package/template/components/hub-tree-panel-view.tsx +26 -26
  524. package/template/components/invite-collaborators-drawer.tsx +3 -3
  525. package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
  526. package/template/components/key-metrics.tsx +1 -1063
  527. package/template/components/leo-insight-indicator.tsx +2 -2
  528. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  529. package/template/components/{question-bank-client.tsx → library-client.tsx} +83 -83
  530. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  531. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  532. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +44 -44
  533. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +16 -16
  534. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  535. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  536. package/template/components/library-panel-activator.tsx +8 -0
  537. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +63 -63
  538. package/template/components/library-table.tsx +839 -0
  539. package/template/components/list-hub-status-badge.tsx +2 -2
  540. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +489 -441
  541. package/template/components/onboarding/index.ts +9 -0
  542. package/template/components/onboarding/onboarding-01.tsx +1 -1
  543. package/template/components/onboarding/onboarding-02.tsx +1 -1
  544. package/template/components/onboarding/onboarding-03.tsx +1 -1
  545. package/template/components/onboarding/onboarding-04.tsx +1 -1
  546. package/template/components/page-header.tsx +8 -226
  547. package/template/components/product-switcher.tsx +3 -4
  548. package/template/components/product-wordmark.tsx +2 -1
  549. package/template/components/settings-appearance-card.tsx +3 -4
  550. package/template/components/settings-client.tsx +15 -59
  551. package/template/components/settings-form-row.tsx +4 -9
  552. package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
  553. package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +114 -73
  554. package/template/components/sidebar/index.ts +16 -0
  555. package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
  556. package/template/components/sidebar/secondary-panel.tsx +316 -0
  557. package/template/components/sidebar/sidebar-auto-collapse.tsx +27 -0
  558. package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +2 -1
  559. package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
  560. package/template/components/site-header.tsx +1 -1
  561. package/template/components/table-properties/column-row.tsx +1 -90
  562. package/template/components/table-properties/draggable-list.ts +1 -49
  563. package/template/components/table-properties/drawer-button.tsx +1 -262
  564. package/template/components/table-properties/drawer.tsx +1 -1166
  565. package/template/components/table-properties/filter-card.tsx +1 -251
  566. package/template/components/table-properties/sort-card.tsx +1 -59
  567. package/template/components/table-properties/types.ts +28 -71
  568. package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
  569. package/template/components/templates/dedicated-search-results-template.tsx +1 -19
  570. package/template/components/templates/discovery-hub-template.tsx +1 -1
  571. package/template/components/templates/list-page.tsx +1 -608
  572. package/template/components/templates/nested-secondary-panel-shell.tsx +1 -63
  573. package/template/components/templates/new-focus-template.tsx +659 -0
  574. package/template/components/templates/secondary-panel-hub-template.tsx +2 -2
  575. package/template/components/tokens-secondary-nav.tsx +192 -0
  576. package/template/components/tokens-themes-client.tsx +476 -0
  577. package/template/components/tokens-themes-section.tsx +386 -0
  578. package/template/components/ui/accordion.tsx +1 -0
  579. package/template/components/ui/alert-dialog.tsx +1 -0
  580. package/template/components/ui/context-menu.tsx +1 -0
  581. package/template/components/ui/dot-pattern.tsx +1 -183
  582. package/template/components/ui/hover-card.tsx +1 -0
  583. package/template/components/ui/resizable.tsx +1 -68
  584. package/template/components/ui/scroll-area.tsx +1 -0
  585. package/template/components/ui/slider.tsx +1 -0
  586. package/template/docs/HANDBOOK.md +187 -0
  587. package/template/docs/blueprints/README.md +86 -0
  588. package/template/docs/blueprints/_template.md +91 -0
  589. package/template/docs/blueprints/board-card.md +123 -0
  590. package/template/docs/blueprints/data-table.md +139 -0
  591. package/template/docs/blueprints/key-metrics.md +128 -0
  592. package/template/docs/blueprints/list-page-template.md +123 -0
  593. package/template/docs/blueprints/page-header.md +130 -0
  594. package/template/docs/collaboration-access-pattern.md +7 -7
  595. package/template/docs/command-menu-pattern.md +1 -1
  596. package/template/docs/component-selection-guide.md +224 -0
  597. package/template/docs/components-audit-2026-05.md +158 -0
  598. package/template/docs/data-views-pattern.md +31 -66
  599. package/template/docs/drawer-vs-dialog-pattern.md +1 -3
  600. package/template/docs/glossary.md +58 -0
  601. package/template/docs/kpi-flat-band-pattern.md +3 -3
  602. package/template/docs/kpi-trend-pattern.md +18 -3
  603. package/template/docs/large-dataset-strategy.md +155 -0
  604. package/template/docs/library-hub-header-pattern.md +25 -0
  605. package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
  606. package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
  607. package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
  608. package/template/docs/migrations/README.md +100 -0
  609. package/template/docs/migrations/_template.md +64 -0
  610. package/template/docs/reference-implementations.md +151 -0
  611. package/template/docs/shell-surface-elevation-pattern.md +3 -5
  612. package/template/docs/token-taxonomy.md +416 -0
  613. package/template/docs/voice-and-tone.md +262 -0
  614. package/template/eslint.config.mjs +27 -0
  615. package/template/hooks/use-secondary-panel-hub-nav.ts +11 -11
  616. package/template/lib/ask-leo-route-context.ts +6 -18
  617. package/template/lib/coach-mark-registry.ts +0 -16
  618. package/template/lib/command-menu-config.ts +5 -13
  619. package/template/lib/command-menu-search-data.ts +8 -23
  620. package/template/lib/conditional-rule-match.ts +6 -97
  621. package/template/lib/data-list-display-options.ts +1 -49
  622. package/template/lib/data-list-view-registry.ts +1 -104
  623. package/template/lib/data-list-view-surface.ts +1 -83
  624. package/template/lib/data-list-view.ts +1 -47
  625. package/template/lib/data-view-dashboard-storage.ts +35 -38
  626. package/template/lib/dev-log.ts +1 -8
  627. package/template/lib/editable-target.ts +1 -10
  628. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  629. package/template/lib/library-dedicated-search.ts +19 -0
  630. package/template/lib/library-hub-search.ts +90 -0
  631. package/template/lib/library-nav.ts +477 -0
  632. package/template/lib/library-recent-searches.ts +22 -0
  633. package/template/lib/{question-bank-supported-views.ts → library-supported-views.ts} +2 -3
  634. package/template/lib/list-page-table-properties.ts +1 -48
  635. package/template/lib/list-status-badges.ts +16 -11
  636. package/template/lib/mock/dashboard.ts +1 -1
  637. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  638. package/template/lib/mock/library-header-collaborators.ts +54 -0
  639. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  640. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  641. package/template/lib/mock/library.ts +249 -0
  642. package/template/lib/mock/navigation.tsx +32 -35
  643. package/template/lib/raf-throttle.ts +1 -45
  644. package/template/lib/row-height.ts +4 -10
  645. package/template/lib/sidebar-state-cookie.ts +11 -2
  646. package/template/lib/table-state-lifecycle.ts +3 -3
  647. package/template/next.config.mjs +7 -4
  648. package/template/package.json +1 -0
  649. package/template/tests/setup.ts +25 -0
  650. package/consumer-extras/AGENTS.md +0 -76
  651. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +0 -37
  652. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +0 -57
  653. package/consumer-extras/patterns/consumer-app-pattern.md +0 -39
  654. package/consumer-extras/patterns/focused-workflow-page-pattern.md +0 -84
  655. package/src/components/ui/button-group.tsx +0 -81
  656. package/src/theme.css +0 -16
  657. package/src/tokens/README.md +0 -15
  658. package/src/tokens/base.css +0 -337
  659. package/src/tokens/high-contrast.css +0 -1195
  660. package/src/tokens/layers.css +0 -224
  661. package/src/tokens/tailwind-bridge.css +0 -118
  662. package/src/tokens/themes.css +0 -201
  663. package/template/app/(app)/data-list/layout.tsx +0 -43
  664. package/template/app/(app)/data-list/page.tsx +0 -10
  665. package/template/app/(app)/examples/focused-workflow/page.tsx +0 -5
  666. package/template/app/(app)/examples/page.tsx +0 -43
  667. package/template/app/(app)/question-bank/find/page.tsx +0 -13
  668. package/template/app/(app)/question-bank/library/page.tsx +0 -12
  669. package/template/app/(app)/question-bank/list/page.tsx +0 -13
  670. package/template/app/(app)/question-bank/new/page.tsx +0 -50
  671. package/template/app/(app)/question-bank/page.tsx +0 -12
  672. package/template/components/app-route-loading.tsx +0 -14
  673. package/template/components/dashboard-onboarding-gallery.tsx +0 -13
  674. package/template/components/dashboard-onboarding.tsx +0 -21
  675. package/template/components/data-views/list-page-calendar-view.tsx +0 -593
  676. package/template/components/data-views/list-page-folder-columns-panel.tsx +0 -345
  677. package/template/components/examples/focused-workflow-showcase.tsx +0 -183
  678. package/template/components/list-hub-board-view.tsx +0 -68
  679. package/template/components/list-hub-client.tsx +0 -186
  680. package/template/components/list-hub-list-view.tsx +0 -36
  681. package/template/components/list-hub-panel-activator.tsx +0 -8
  682. package/template/components/list-hub-secondary-nav.tsx +0 -121
  683. package/template/components/list-hub-table.tsx +0 -336
  684. package/template/components/question-bank-folder-columns-panel.tsx +0 -104
  685. package/template/components/question-bank-list-view.tsx +0 -53
  686. package/template/components/question-bank-panel-activator.tsx +0 -8
  687. package/template/components/question-bank-table.tsx +0 -729
  688. package/template/components/secondary-panel/nav-link-rows.tsx +0 -83
  689. package/template/components/secondary-panel.tsx +0 -220
  690. package/template/components/secondary-panels/list-hub-panel.tsx +0 -39
  691. package/template/components/secondary-panels/question-bank-panel.tsx +0 -39
  692. package/template/components/secondary-panels/registry.tsx +0 -15
  693. package/template/components/section-cards.tsx +0 -106
  694. package/template/components/sidebar-auto-collapse.tsx +0 -23
  695. package/template/components/templates/focused-workflow-layouts.tsx +0 -448
  696. package/template/components/templates/focused-workflow-page-template.tsx +0 -69
  697. package/template/components/templates/page-loading-shell.tsx +0 -262
  698. package/template/components/ui/button-group.tsx +0 -1
  699. package/template/docs/consumer-app-pattern.md +0 -39
  700. package/template/docs/focused-workflow-page-pattern.md +0 -84
  701. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  702. package/template/lib/list-hub-nav.ts +0 -121
  703. package/template/lib/mock/list-hub-directory.ts +0 -27
  704. package/template/lib/mock/list-hub-kpi.ts +0 -27
  705. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  706. package/template/lib/mock/question-bank.ts +0 -249
  707. package/template/lib/page-loading-variant.ts +0 -40
  708. package/template/lib/question-bank-dedicated-search.ts +0 -19
  709. package/template/lib/question-bank-hub-search.ts +0 -90
  710. package/template/lib/question-bank-nav.ts +0 -477
  711. package/template/lib/question-bank-recent-searches.ts +0 -22
  712. /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
  713. /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
  714. /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
  715. /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
  716. /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
@@ -0,0 +1,673 @@
1
+ "use client"
2
+
3
+ /**
4
+ * Reusable `DataTable` / `HubTable` cell primitives — extracted from
5
+ * `columns-showcase.tsx` so every hub composes its grid from the same set of
6
+ * named, accessible, copy-paste-free renderers.
7
+ *
8
+ * **Why this module exists.** Without a shared home, each hub would re-derive
9
+ * progress bars, currency formatting, rating stars, attachment chips, relative
10
+ * times, etc. — drifting in spacing, color, and a11y treatment. These cells
11
+ * pair color + glyph (WCAG 1.4.1), keep tabular numbers right-aligned, and
12
+ * expose a focusable `Tip` for any glyph-only signal.
13
+ *
14
+ * **Composition only.** Every renderer is a pure composition of existing
15
+ * primitives (`@/components/ui/*`, `@/components/list-hub-status-badge`,
16
+ * `Intl` formatters, Font Awesome icon classes). No new design tokens, no new
17
+ * package surface — drop these into any `ColumnDef<TRow>['cell']`.
18
+ *
19
+ * **Live catalog:** `apps/web/components/columns-showcase.tsx` (hosted at
20
+ * `/columns`) renders every export below as its own column so designers,
21
+ * engineers, and AI agents can see the cell in situ before picking it.
22
+ *
23
+ * **Skill reference:** `.cursor/skills/exxat-token-economy/SKILL.md` §3 names
24
+ * each export below in its "primitive aliases" table so the AI imports
25
+ * directly instead of re-implementing.
26
+ */
27
+
28
+ import * as React from "react"
29
+ import { AvatarGroup, AvatarGroupCount, AvatarInitials } from "@/components/ui/avatar"
30
+ import { Badge } from "@/components/ui/badge"
31
+ import { Button } from "@/components/ui/button"
32
+ import {
33
+ DropdownMenu,
34
+ DropdownMenuContent,
35
+ DropdownMenuItem,
36
+ DropdownMenuSeparator,
37
+ DropdownMenuTrigger,
38
+ } from "@/components/ui/dropdown-menu"
39
+ import { Tip } from "@/components/ui/tip"
40
+ import { ToggleSwitch } from "@/components/ui/toggle-switch"
41
+ import { cn } from "@/lib/utils"
42
+
43
+ /* ────────────────────────────────────────────────────────────────────────── *
44
+ * Shared helpers
45
+ * ────────────────────────────────────────────────────────────────────────── */
46
+
47
+ const EMPTY_DASH = (
48
+ <span className="text-sm text-muted-foreground" aria-hidden="true">
49
+
50
+ </span>
51
+ )
52
+
53
+ /** Truthy-only dash with an accessible label so screen-reader users get a hint
54
+ * for "no value" cells across every hub. */
55
+ function EmptyCell({ label = "No value" }: { label?: string }) {
56
+ return (
57
+ <span className="text-sm text-muted-foreground" aria-label={label}>
58
+
59
+ </span>
60
+ )
61
+ }
62
+
63
+ /* ────────────────────────────────────────────────────────────────────────── *
64
+ * Numeric / monetary
65
+ * ────────────────────────────────────────────────────────────────────────── */
66
+
67
+ /**
68
+ * Right-aligned plain numeric cell. Use for counts where the grid benefits
69
+ * from column-aligned digits (attempts, downloads, file size N).
70
+ */
71
+ export function NumericCell({
72
+ value,
73
+ fractionDigits = 0,
74
+ className,
75
+ }: {
76
+ value: number | null | undefined
77
+ fractionDigits?: number
78
+ className?: string
79
+ }) {
80
+ if (value == null || Number.isNaN(value)) return <EmptyCell />
81
+ return (
82
+ <span className={cn("block text-right text-sm tabular-nums text-foreground", className)}>
83
+ {Number(value).toLocaleString(undefined, {
84
+ minimumFractionDigits: fractionDigits,
85
+ maximumFractionDigits: fractionDigits,
86
+ })}
87
+ </span>
88
+ )
89
+ }
90
+
91
+ /**
92
+ * Currency cell — right-aligned, `tabular-nums`. `Intl.NumberFormat` honors
93
+ * locale + currency; defaults to USD because the product is US-first.
94
+ */
95
+ export function CurrencyCell({
96
+ value,
97
+ currency = "USD",
98
+ locale = "en-US",
99
+ maximumFractionDigits = 2,
100
+ }: {
101
+ value: number | null | undefined
102
+ currency?: string
103
+ locale?: string
104
+ maximumFractionDigits?: number
105
+ }) {
106
+ if (value == null || Number.isNaN(value)) return <EmptyCell label="No amount" />
107
+ const fmt = new Intl.NumberFormat(locale, {
108
+ style: "currency",
109
+ currency,
110
+ maximumFractionDigits,
111
+ })
112
+ return (
113
+ <span className="block text-right text-sm tabular-nums text-foreground">
114
+ {fmt.format(value)}
115
+ </span>
116
+ )
117
+ }
118
+
119
+ /* ────────────────────────────────────────────────────────────────────────── *
120
+ * Progress + signal
121
+ * ────────────────────────────────────────────────────────────────────────── */
122
+
123
+ export type ProgressTone = "auto" | "success" | "warning" | "danger" | "info"
124
+
125
+ /**
126
+ * Progress bar — track + filled fill + numeric label. Auto-tones in thirds:
127
+ * <34% destructive, <67% warning, ≥67% success. Pass an explicit `tone` to
128
+ * override (e.g. "info" for non-judgmental quantity bars).
129
+ */
130
+ export function ProgressCell({
131
+ value,
132
+ max = 100,
133
+ tone = "auto",
134
+ label,
135
+ className,
136
+ }: {
137
+ value: number | null | undefined
138
+ max?: number
139
+ tone?: ProgressTone
140
+ /** Right-side label. Defaults to `${pct}%`. Pass `false` to hide. */
141
+ label?: React.ReactNode | false
142
+ className?: string
143
+ }) {
144
+ if (value == null || Number.isNaN(value)) return <EmptyCell label="No progress" />
145
+ const pct = Math.max(0, Math.min(100, Math.round((value / max) * 100)))
146
+ const autoTone =
147
+ pct < 34 ? "bg-destructive" :
148
+ pct < 67 ? "bg-amber-500" :
149
+ "bg-emerald-500"
150
+ const toneClass =
151
+ tone === "success" ? "bg-emerald-500" :
152
+ tone === "warning" ? "bg-amber-500" :
153
+ tone === "danger" ? "bg-destructive" :
154
+ tone === "info" ? "bg-primary" :
155
+ autoTone
156
+ const labelNode =
157
+ label === false ? null :
158
+ label ?? <span className="text-[11px] tabular-nums text-muted-foreground">{pct}%</span>
159
+ return (
160
+ <div className={cn("flex min-w-[140px] max-w-[180px] flex-col gap-1.5", className)}>
161
+ <div
162
+ role="progressbar"
163
+ aria-valuemin={0}
164
+ aria-valuemax={100}
165
+ aria-valuenow={pct}
166
+ aria-label={`Progress ${pct} percent`}
167
+ className="h-1.5 overflow-hidden rounded-full bg-muted"
168
+ >
169
+ <div
170
+ className={cn("h-full rounded-full transition-[width]", toneClass)}
171
+ style={{ width: `${pct}%` }}
172
+ />
173
+ </div>
174
+ {labelNode}
175
+ </div>
176
+ )
177
+ }
178
+
179
+ export type SignalTone = "success" | "warning" | "danger" | "info" | "neutral"
180
+
181
+ /**
182
+ * Three-bar signal indicator — same metaphor as Wi-Fi / cellular bars. Use
183
+ * for ordinal scales (low/medium/high; easy/medium/hard). Color is *paired*
184
+ * with bar count so the cell still communicates on monochrome + forced-colors.
185
+ */
186
+ export function SignalBarsCell({
187
+ level,
188
+ max = 3,
189
+ tone = "info",
190
+ label,
191
+ }: {
192
+ /** 1-indexed level. */
193
+ level: number
194
+ /** Total number of bars. Default 3. */
195
+ max?: number
196
+ tone?: SignalTone
197
+ /** Accessible name; also used as the `Tip` content. */
198
+ label: string
199
+ }) {
200
+ const lvl = Math.max(0, Math.min(max, Math.round(level)))
201
+ const toneClass =
202
+ tone === "success" ? "bg-emerald-500" :
203
+ tone === "warning" ? "bg-amber-500" :
204
+ tone === "danger" ? "bg-destructive" :
205
+ tone === "info" ? "bg-primary" :
206
+ "bg-foreground"
207
+ return (
208
+ <Tip side="top" label={label}>
209
+ <span
210
+ className="inline-flex items-end gap-0.5 cursor-default"
211
+ role="img"
212
+ aria-label={label}
213
+ tabIndex={0}
214
+ >
215
+ {Array.from({ length: max }, (_, i) => {
216
+ const bar = i + 1
217
+ const filled = bar <= lvl
218
+ // Stair-step the heights so the metaphor reads visually.
219
+ const heightClass =
220
+ bar === 1 ? "h-2" :
221
+ bar === 2 ? "h-3" :
222
+ bar === 3 ? "h-4" :
223
+ "h-5"
224
+ return (
225
+ <span
226
+ key={bar}
227
+ className={cn("w-1 rounded-sm", filled ? toneClass : "bg-muted", heightClass)}
228
+ aria-hidden="true"
229
+ />
230
+ )
231
+ })}
232
+ </span>
233
+ </Tip>
234
+ )
235
+ }
236
+
237
+ /* ────────────────────────────────────────────────────────────────────────── *
238
+ * People
239
+ * ────────────────────────────────────────────────────────────────────────── */
240
+
241
+ export interface PersonStub {
242
+ name: string
243
+ initials: string
244
+ }
245
+
246
+ /**
247
+ * Face rail — list of people with a `+N more` overflow chip. Each face gets a
248
+ * `Tip` of the person's name; the overflow chip's tip lists the hidden names.
249
+ * Uses non-overlapping avatars (gap, not negative margin) per Exxat DS rule.
250
+ */
251
+ export function PeopleAvatarRailCell({
252
+ people,
253
+ visibleMax = 3,
254
+ size = "sm",
255
+ emptyLabel = "No people",
256
+ }: {
257
+ people: PersonStub[] | undefined
258
+ /** How many faces to show before `+N`. Default 3. */
259
+ visibleMax?: number
260
+ size?: "sm" | "md"
261
+ emptyLabel?: string
262
+ }) {
263
+ if (!people?.length) return <EmptyCell label={emptyLabel} />
264
+ const visible = people.slice(0, visibleMax)
265
+ const overflow = people.length - visible.length
266
+ const sizeClass = size === "md" ? "size-7 text-[11px]" : "size-6 text-[10px]"
267
+ return (
268
+ <AvatarGroup data-size={size} className="gap-1">
269
+ {visible.map((p) => (
270
+ <Tip key={`${p.name}-${p.initials}`} side="top" label={p.name}>
271
+ <AvatarInitials
272
+ initials={p.initials}
273
+ className={sizeClass}
274
+ fallbackClassName={size === "md" ? "text-[11px]" : "text-[10px]"}
275
+ />
276
+ </Tip>
277
+ ))}
278
+ {overflow > 0 && (
279
+ <Tip side="top" label={people.slice(visibleMax).map((p) => p.name).join(", ")}>
280
+ <AvatarGroupCount
281
+ tabIndex={0}
282
+ aria-label={`${overflow} more${overflow === 1 ? "" : "s"}`}
283
+ className={sizeClass}
284
+ >
285
+ +{overflow}
286
+ </AvatarGroupCount>
287
+ </Tip>
288
+ )}
289
+ </AvatarGroup>
290
+ )
291
+ }
292
+
293
+ /* ────────────────────────────────────────────────────────────────────────── *
294
+ * Pills + chips
295
+ * ────────────────────────────────────────────────────────────────────────── */
296
+
297
+ /**
298
+ * Outlined pill with a leading FA icon — the "Type" pattern. Use for
299
+ * single-select categorical fields where color isn't carrying meaning
300
+ * (otherwise reach for `ListHubStatusBadge`).
301
+ */
302
+ export function PillCell({
303
+ label,
304
+ icon,
305
+ iconClassName,
306
+ className,
307
+ }: {
308
+ label: React.ReactNode
309
+ /** FA glyph name without the family prefix, e.g. `"fa-list-check"`. */
310
+ icon?: string
311
+ iconClassName?: string
312
+ className?: string
313
+ }) {
314
+ return (
315
+ <Badge
316
+ variant="outline"
317
+ className={cn(
318
+ "h-6 gap-1.5 border-border bg-background px-2 text-xs font-medium",
319
+ className,
320
+ )}
321
+ >
322
+ {icon ? (
323
+ <i
324
+ className={cn("fa-light text-[11px] text-muted-foreground", icon, iconClassName)}
325
+ aria-hidden="true"
326
+ />
327
+ ) : null}
328
+ <span className="text-foreground">{label}</span>
329
+ </Badge>
330
+ )
331
+ }
332
+
333
+ /**
334
+ * Tag list with `+N` overflow. Use for free-form keyword tags (`#tag`). For
335
+ * categorical pills, see `PillCell`; for status, see `ListHubStatusBadge`.
336
+ */
337
+ export function TagListCell({
338
+ tags,
339
+ visibleMax = 2,
340
+ formatLabel = (t) => `#${t}`,
341
+ }: {
342
+ tags: string[] | undefined
343
+ visibleMax?: number
344
+ formatLabel?: (tag: string) => string
345
+ }) {
346
+ if (!tags?.length) return <EmptyCell label="No tags" />
347
+ const visible = tags.slice(0, visibleMax)
348
+ const overflow = tags.length - visible.length
349
+ return (
350
+ <div className="flex flex-wrap items-center gap-1">
351
+ {visible.map((t) => (
352
+ <Badge
353
+ key={t}
354
+ variant="secondary"
355
+ className="h-5 px-1.5 text-[11px] font-medium leading-none"
356
+ >
357
+ {formatLabel(t)}
358
+ </Badge>
359
+ ))}
360
+ {overflow > 0 && (
361
+ <Tip side="top" label={tags.slice(visibleMax).map(formatLabel).join(", ")}>
362
+ <span
363
+ className="inline-flex h-5 cursor-default items-center justify-center rounded-md bg-muted px-1.5 text-[11px] font-medium leading-none text-muted-foreground"
364
+ tabIndex={0}
365
+ aria-label={`${overflow} more tag${overflow === 1 ? "" : "s"}`}
366
+ >
367
+ +{overflow}
368
+ </span>
369
+ </Tip>
370
+ )}
371
+ </div>
372
+ )
373
+ }
374
+
375
+ /* ────────────────────────────────────────────────────────────────────────── *
376
+ * Rating
377
+ * ────────────────────────────────────────────────────────────────────────── */
378
+
379
+ /**
380
+ * Star rating — N of `max` FA stars + numeric value. Color (amber) + glyph
381
+ * change (solid vs. light) pair so the cell still reads on monochrome /
382
+ * forced-colors modes (WCAG 1.4.1).
383
+ */
384
+ export function RatingCell({
385
+ value,
386
+ max = 5,
387
+ showValue = true,
388
+ }: {
389
+ value: number | null | undefined
390
+ max?: number
391
+ showValue?: boolean
392
+ }) {
393
+ if (value == null || Number.isNaN(value)) return <EmptyCell label="No rating" />
394
+ const n = Math.max(0, Math.min(max, Math.round(value)))
395
+ const label = `Rated ${n} of ${max}`
396
+ return (
397
+ <Tip side="top" label={label}>
398
+ <span
399
+ role="img"
400
+ aria-label={label}
401
+ tabIndex={0}
402
+ className="inline-flex items-center gap-1 rounded-md cursor-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
403
+ >
404
+ <span className="inline-flex items-center gap-0.5" aria-hidden="true">
405
+ {Array.from({ length: max }, (_, i) => {
406
+ const filled = i < n
407
+ return (
408
+ <i
409
+ key={i}
410
+ className={cn(
411
+ filled ? "fa-solid text-amber-500" : "fa-light text-muted-foreground/50",
412
+ "fa-star text-[11px]",
413
+ )}
414
+ />
415
+ )
416
+ })}
417
+ </span>
418
+ {showValue ? (
419
+ <span className="text-xs tabular-nums text-muted-foreground">{n}.0</span>
420
+ ) : null}
421
+ </span>
422
+ </Tip>
423
+ )
424
+ }
425
+
426
+ /* ────────────────────────────────────────────────────────────────────────── *
427
+ * Booleans
428
+ * ────────────────────────────────────────────────────────────────────────── */
429
+
430
+ /**
431
+ * Inline toggle — `ToggleSwitch` for a boolean lifecycle field (Published,
432
+ * Active, Enabled). The callback receives the *next* checked state; the cell
433
+ * stops row click propagation so toggling never opens the row.
434
+ *
435
+ * `ToggleSwitch` does not currently support a `disabled` state — if you need
436
+ * to lock a row's toggle, render a static badge (`PillCell` with the current
437
+ * state) instead.
438
+ */
439
+ export function BooleanToggleCell({
440
+ checked,
441
+ onChange,
442
+ labelOn = "On — click to turn off",
443
+ labelOff = "Off — click to turn on",
444
+ }: {
445
+ checked: boolean
446
+ onChange: (next: boolean) => void
447
+ labelOn?: string
448
+ labelOff?: string
449
+ }) {
450
+ return (
451
+ <Tip side="top" label={checked ? labelOn : labelOff}>
452
+ <span
453
+ className="inline-flex items-center"
454
+ onClick={(e) => e.stopPropagation()}
455
+ >
456
+ <ToggleSwitch
457
+ checked={checked}
458
+ onChange={() => onChange(!checked)}
459
+ />
460
+ </span>
461
+ </Tip>
462
+ )
463
+ }
464
+
465
+ /* ────────────────────────────────────────────────────────────────────────── *
466
+ * Attachments / links / time
467
+ * ────────────────────────────────────────────────────────────────────────── */
468
+
469
+ /**
470
+ * Attachment indicator — paperclip + count chip; muted dash when zero. A
471
+ * focusable `Tip` exposes the count for screen-reader users; the chip is
472
+ * non-interactive — wire `onClick` from the column def if you need a popover.
473
+ */
474
+ export function AttachmentCountCell({
475
+ count,
476
+ }: {
477
+ count: number | null | undefined
478
+ }) {
479
+ if (!count) return <EmptyCell label="No files" />
480
+ const labelText = `${count} attachment${count === 1 ? "" : "s"}`
481
+ return (
482
+ <Tip side="top" label={labelText}>
483
+ <span
484
+ className="inline-flex h-6 cursor-default items-center gap-1 rounded-md border border-border bg-background px-1.5 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
485
+ role="img"
486
+ aria-label={labelText}
487
+ tabIndex={0}
488
+ >
489
+ <i className="fa-light fa-paperclip text-[11px] text-muted-foreground" aria-hidden="true" />
490
+ <span className="tabular-nums">{count}</span>
491
+ </span>
492
+ </Tip>
493
+ )
494
+ }
495
+
496
+ /**
497
+ * External link — truncated host label + `fa-arrow-up-right-from-square` mark.
498
+ * Opens in a new tab with `noopener`; full URL surfaces in the `Tip`. The link
499
+ * stops row click propagation so it never collides with the row's `onClick`.
500
+ */
501
+ export function ExternalLinkCell({
502
+ url,
503
+ label,
504
+ className,
505
+ }: {
506
+ url: string | null | undefined
507
+ /** Override the host-only label (e.g. "View source"). */
508
+ label?: React.ReactNode
509
+ className?: string
510
+ }) {
511
+ if (!url) return <EmptyCell label="No link" />
512
+ let host = url
513
+ try {
514
+ host = new URL(url).hostname.replace(/^www\./, "")
515
+ } catch {
516
+ /* keep the raw url */
517
+ }
518
+ return (
519
+ <Tip side="top" label={url}>
520
+ <a
521
+ href={url}
522
+ target="_blank"
523
+ rel="noopener noreferrer"
524
+ onClick={(e) => e.stopPropagation()}
525
+ className={cn(
526
+ "inline-flex max-w-[180px] items-center gap-1 truncate rounded text-sm text-foreground transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
527
+ className,
528
+ )}
529
+ >
530
+ <span className="truncate">{label ?? host}</span>
531
+ <i className="fa-light fa-arrow-up-right-from-square text-[11px] text-muted-foreground" aria-hidden="true" />
532
+ </a>
533
+ </Tip>
534
+ )
535
+ }
536
+
537
+ /* ────────────────────────────────────────────────────────────────────────── *
538
+ * Time
539
+ * ────────────────────────────────────────────────────────────────────────── */
540
+
541
+ const RELATIVE_FMT = new Intl.RelativeTimeFormat("en-US", { numeric: "auto" })
542
+ const ABS_FMT = new Intl.DateTimeFormat("en-US", {
543
+ dateStyle: "medium",
544
+ timeStyle: "short",
545
+ })
546
+
547
+ function formatRelativeAndAbsolute(
548
+ iso: string,
549
+ now: number = Date.now(),
550
+ ): { relative: string; absolute: string } | null {
551
+ const d = new Date(iso)
552
+ if (Number.isNaN(d.getTime())) return null
553
+ const diffSec = Math.round((d.getTime() - now) / 1000)
554
+ const abs = Math.abs(diffSec)
555
+ let unit: Intl.RelativeTimeFormatUnit
556
+ let value: number
557
+ if (abs < 60) { unit = "second"; value = diffSec }
558
+ else if (abs < 3600) { unit = "minute"; value = Math.round(diffSec / 60) }
559
+ else if (abs < 86400) { unit = "hour"; value = Math.round(diffSec / 3600) }
560
+ else if (abs < 86400 * 7) { unit = "day"; value = Math.round(diffSec / 86400) }
561
+ else if (abs < 86400 * 30) { unit = "week"; value = Math.round(diffSec / (86400 * 7)) }
562
+ else if (abs < 86400 * 365){ unit = "month"; value = Math.round(diffSec / (86400 * 30)) }
563
+ else { unit = "year"; value = Math.round(diffSec / (86400 * 365)) }
564
+ return { relative: RELATIVE_FMT.format(value, unit), absolute: ABS_FMT.format(d) }
565
+ }
566
+
567
+ /**
568
+ * Relative time — "3 hours ago" / "2 days ago" with a `Tip` exposing the
569
+ * absolute timestamp on hover/focus. The visible label is the relative form
570
+ * so scanning readers see recency at a glance.
571
+ */
572
+ export function RelativeTimeCell({
573
+ iso,
574
+ now,
575
+ }: {
576
+ iso: string | null | undefined
577
+ /** Override "now" for deterministic snapshots. */
578
+ now?: number
579
+ }) {
580
+ if (!iso) return <EmptyCell label="No date" />
581
+ const fmt = formatRelativeAndAbsolute(iso, now)
582
+ if (!fmt) return <EmptyCell label="Invalid date" />
583
+ return (
584
+ <Tip side="top" label={fmt.absolute}>
585
+ <span
586
+ className="inline-block text-sm text-foreground/90 whitespace-nowrap rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
587
+ tabIndex={0}
588
+ >
589
+ {fmt.relative}
590
+ </span>
591
+ </Tip>
592
+ )
593
+ }
594
+
595
+ /* ────────────────────────────────────────────────────────────────────────── *
596
+ * Row actions ⋯
597
+ * ────────────────────────────────────────────────────────────────────────── */
598
+
599
+ export interface RowActionDef<TRow> {
600
+ label: string
601
+ /** FA glyph name without the family prefix, e.g. `"fa-pen-to-square"`. */
602
+ icon: string
603
+ onSelect: (row: TRow) => void
604
+ /** Render as the destructive variant — separator + red label. */
605
+ variant?: "destructive"
606
+ /** Optional menu-item keyboard shortcut hint (e.g. `"⌘E"`). */
607
+ shortcut?: string
608
+ /** Disable the item without hiding it. */
609
+ disabled?: boolean
610
+ }
611
+
612
+ /**
613
+ * Row overflow `⋯` menu — generic across hubs. Pass the row and an array of
614
+ * `{ label, icon, onSelect, variant?, shortcut? }`; destructive items
615
+ * automatically gain a separator above. The trigger keeps an `aria-label` so
616
+ * the button is named for screen readers.
617
+ */
618
+ export function RowActionsCell<TRow>({
619
+ row,
620
+ actions,
621
+ triggerLabel = "More options",
622
+ align = "end",
623
+ }: {
624
+ row: TRow
625
+ actions: RowActionDef<TRow>[]
626
+ /** Both the `Tip` content and the `aria-label` fallback. */
627
+ triggerLabel?: string
628
+ align?: "start" | "center" | "end"
629
+ }) {
630
+ return (
631
+ <DropdownMenu>
632
+ <Tip side="top" label={triggerLabel}>
633
+ <DropdownMenuTrigger asChild>
634
+ <Button
635
+ size="icon-sm"
636
+ variant="ghost"
637
+ aria-label={triggerLabel}
638
+ onClick={(e) => e.stopPropagation()}
639
+ >
640
+ <i className="fa-light fa-ellipsis text-sm" aria-hidden="true" />
641
+ </Button>
642
+ </DropdownMenuTrigger>
643
+ </Tip>
644
+ <DropdownMenuContent align={align}>
645
+ {actions.map((a, i) => {
646
+ const prev = actions[i - 1]
647
+ const needsSeparator =
648
+ a.variant === "destructive" && prev && prev.variant !== "destructive"
649
+ return (
650
+ <React.Fragment key={a.label}>
651
+ {needsSeparator ? <DropdownMenuSeparator /> : null}
652
+ <DropdownMenuItem
653
+ onSelect={() => a.onSelect(row)}
654
+ disabled={a.disabled}
655
+ shortcut={a.shortcut}
656
+ className={a.variant === "destructive" ? "text-destructive focus:text-destructive" : ""}
657
+ >
658
+ <i className={`fa-light ${a.icon}`} aria-hidden="true" />
659
+ {a.label}
660
+ </DropdownMenuItem>
661
+ </React.Fragment>
662
+ )
663
+ })}
664
+ </DropdownMenuContent>
665
+ </DropdownMenu>
666
+ )
667
+ }
668
+
669
+ /* ────────────────────────────────────────────────────────────────────────── *
670
+ * Exports — see `columns-showcase.tsx` for the live catalog.
671
+ * ────────────────────────────────────────────────────────────────────────── */
672
+
673
+ export { EMPTY_DASH }