@exxatdesignux/ui 0.2.19 → 0.3.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 (688) hide show
  1. package/CHANGELOG.md +60 -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 +41 -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-list-page-connected-views.mdc +24 -0
  22. package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +31 -0
  23. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +30 -0
  24. package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +78 -0
  25. package/consumer-extras/cursor-rules/exxat-no-toast.mdc +25 -0
  26. package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +23 -0
  27. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +47 -0
  28. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +52 -0
  29. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +28 -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 +2 -2
  35. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +4 -15
  36. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +13 -28
  37. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
  38. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +2 -4
  39. package/consumer-extras/handbook/HANDBOOK.md +185 -0
  40. package/consumer-extras/handbook/glossary.md +57 -0
  41. package/consumer-extras/handbook/reference-implementations.md +126 -0
  42. package/consumer-extras/handbook/voice-and-tone.md +262 -0
  43. package/consumer-extras/patterns/command-menu-pattern.md +1 -1
  44. package/consumer-extras/patterns/consumer-upgrade-checklist.md +0 -20
  45. package/consumer-extras/patterns/data-views-pattern.md +17 -54
  46. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +3 -5
  47. package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
  48. package/dist/components/data-table/filter-date-calendar.js +280 -0
  49. package/dist/components/data-table/filter-date-calendar.js.map +1 -0
  50. package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
  51. package/dist/components/data-table/filter-text-value-input.js +561 -0
  52. package/dist/components/data-table/filter-text-value-input.js.map +1 -0
  53. package/dist/components/data-table/index.d.ts +45 -0
  54. package/dist/components/data-table/index.js +3085 -0
  55. package/dist/components/data-table/index.js.map +1 -0
  56. package/dist/components/data-table/pagination.d.ts +28 -0
  57. package/dist/components/data-table/pagination.js +3264 -0
  58. package/dist/components/data-table/pagination.js.map +1 -0
  59. package/dist/components/data-table/types.d.ts +84 -0
  60. package/dist/components/data-table/types.js +3 -0
  61. package/dist/components/data-table/types.js.map +1 -0
  62. package/dist/components/data-table/use-table-state.d.ts +116 -0
  63. package/dist/components/data-table/use-table-state.js +670 -0
  64. package/dist/components/data-table/use-table-state.js.map +1 -0
  65. package/dist/components/data-views/board-card-primitives.d.ts +22 -0
  66. package/dist/components/data-views/board-card-primitives.js +84 -0
  67. package/dist/components/data-views/board-card-primitives.js.map +1 -0
  68. package/dist/components/data-views/data-row-list.d.ts +33 -0
  69. package/dist/components/data-views/data-row-list.js +106 -0
  70. package/dist/components/data-views/data-row-list.js.map +1 -0
  71. package/dist/components/data-views/finder-panel-view.d.ts +54 -0
  72. package/dist/components/data-views/finder-panel-view.js +388 -0
  73. package/dist/components/data-views/finder-panel-view.js.map +1 -0
  74. package/dist/components/data-views/folder-grid-view.d.ts +22 -0
  75. package/dist/components/data-views/folder-grid-view.js +58 -0
  76. package/dist/components/data-views/folder-grid-view.js.map +1 -0
  77. package/dist/components/data-views/hub-table.d.ts +167 -0
  78. package/dist/components/data-views/hub-table.js +5561 -0
  79. package/dist/components/data-views/hub-table.js.map +1 -0
  80. package/dist/components/data-views/index.d.ts +27 -0
  81. package/dist/components/data-views/index.js +6575 -0
  82. package/dist/components/data-views/index.js.map +1 -0
  83. package/dist/components/data-views/list-page-board-card.d.ts +72 -0
  84. package/dist/components/data-views/list-page-board-card.js +264 -0
  85. package/dist/components/data-views/list-page-board-card.js.map +1 -0
  86. package/dist/components/data-views/list-page-board-template.d.ts +24 -0
  87. package/dist/components/data-views/list-page-board-template.js +137 -0
  88. package/dist/components/data-views/list-page-board-template.js.map +1 -0
  89. package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
  90. package/dist/components/data-views/list-page-connected-view-body.js +116 -0
  91. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
  92. package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
  93. package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
  94. package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
  95. package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
  96. package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
  97. package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
  98. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
  99. package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
  100. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
  101. package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
  102. package/dist/components/data-views/list-page-tree-column-header.js +22 -0
  103. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
  104. package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
  105. package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
  106. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
  107. package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
  108. package/dist/components/data-views/os-folder-glyph.js +104 -0
  109. package/dist/components/data-views/os-folder-glyph.js.map +1 -0
  110. package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
  111. package/dist/components/data-views/outline-tree-menu.js +131 -0
  112. package/dist/components/data-views/outline-tree-menu.js.map +1 -0
  113. package/dist/components/table-properties/column-row.d.ts +22 -0
  114. package/dist/components/table-properties/column-row.js +153 -0
  115. package/dist/components/table-properties/column-row.js.map +1 -0
  116. package/dist/components/table-properties/draggable-list.d.ts +24 -0
  117. package/dist/components/table-properties/draggable-list.js +53 -0
  118. package/dist/components/table-properties/draggable-list.js.map +1 -0
  119. package/dist/components/table-properties/drawer-button.d.ts +110 -0
  120. package/dist/components/table-properties/drawer-button.js +2748 -0
  121. package/dist/components/table-properties/drawer-button.js.map +1 -0
  122. package/dist/components/table-properties/drawer.d.ts +100 -0
  123. package/dist/components/table-properties/drawer.js +2595 -0
  124. package/dist/components/table-properties/drawer.js.map +1 -0
  125. package/dist/components/table-properties/filter-card.d.ts +24 -0
  126. package/dist/components/table-properties/filter-card.js +854 -0
  127. package/dist/components/table-properties/filter-card.js.map +1 -0
  128. package/dist/components/table-properties/index.d.ts +14 -0
  129. package/dist/components/table-properties/index.js +2768 -0
  130. package/dist/components/table-properties/index.js.map +1 -0
  131. package/dist/components/table-properties/sort-card.d.ts +20 -0
  132. package/dist/components/table-properties/sort-card.js +102 -0
  133. package/dist/components/table-properties/sort-card.js.map +1 -0
  134. package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
  135. package/dist/components/templates/dedicated-search-landing-template.js +254 -0
  136. package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
  137. package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
  138. package/dist/components/templates/dedicated-search-results-template.js +16 -0
  139. package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
  140. package/dist/components/templates/index.d.ts +9 -0
  141. package/dist/components/templates/index.js +2720 -0
  142. package/dist/components/templates/index.js.map +1 -0
  143. package/dist/components/templates/list-page.d.ts +83 -0
  144. package/dist/components/templates/list-page.js +2433 -0
  145. package/dist/components/templates/list-page.js.map +1 -0
  146. package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
  147. package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
  148. package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
  149. package/dist/components/ui/accordion.d.ts +10 -0
  150. package/dist/components/ui/accordion.js +74 -0
  151. package/dist/components/ui/accordion.js.map +1 -0
  152. package/dist/components/ui/alert-dialog.d.ts +37 -0
  153. package/dist/components/ui/alert-dialog.js +201 -0
  154. package/dist/components/ui/alert-dialog.js.map +1 -0
  155. package/dist/components/ui/avatar.d.ts +84 -0
  156. package/dist/components/ui/avatar.js +328 -0
  157. package/dist/components/ui/avatar.js.map +1 -0
  158. package/dist/components/ui/badge.d.ts +13 -0
  159. package/dist/components/ui/badge.js +49 -0
  160. package/dist/components/ui/badge.js.map +1 -0
  161. package/dist/components/ui/banner.d.ts +62 -0
  162. package/dist/components/ui/banner.js +364 -0
  163. package/dist/components/ui/banner.js.map +1 -0
  164. package/dist/components/ui/breadcrumb.d.ts +14 -0
  165. package/dist/components/ui/breadcrumb.js +114 -0
  166. package/dist/components/ui/breadcrumb.js.map +1 -0
  167. package/dist/components/ui/button.d.ts +16 -0
  168. package/dist/components/ui/button.js +59 -0
  169. package/dist/components/ui/button.js.map +1 -0
  170. package/dist/components/ui/calendar.d.ts +13 -0
  171. package/dist/components/ui/calendar.js +238 -0
  172. package/dist/components/ui/calendar.js.map +1 -0
  173. package/dist/components/ui/card.d.ts +14 -0
  174. package/dist/components/ui/card.js +102 -0
  175. package/dist/components/ui/card.js.map +1 -0
  176. package/dist/components/ui/chart.d.ts +58 -0
  177. package/dist/components/ui/chart.js +292 -0
  178. package/dist/components/ui/chart.js.map +1 -0
  179. package/dist/components/ui/checkbox.d.ts +23 -0
  180. package/dist/components/ui/checkbox.js +155 -0
  181. package/dist/components/ui/checkbox.js.map +1 -0
  182. package/dist/components/ui/coach-mark.d.ts +27 -0
  183. package/dist/components/ui/coach-mark.js +306 -0
  184. package/dist/components/ui/coach-mark.js.map +1 -0
  185. package/dist/components/ui/collapsible.d.ts +8 -0
  186. package/dist/components/ui/collapsible.js +35 -0
  187. package/dist/components/ui/collapsible.js.map +1 -0
  188. package/dist/components/ui/command.d.ts +36 -0
  189. package/dist/components/ui/command.js +274 -0
  190. package/dist/components/ui/command.js.map +1 -0
  191. package/dist/components/ui/context-menu.d.ts +32 -0
  192. package/dist/components/ui/context-menu.js +245 -0
  193. package/dist/components/ui/context-menu.js.map +1 -0
  194. package/dist/components/ui/date-picker-field.d.ts +38 -0
  195. package/dist/components/ui/date-picker-field.js +550 -0
  196. package/dist/components/ui/date-picker-field.js.map +1 -0
  197. package/dist/components/ui/dialog.d.ts +22 -0
  198. package/dist/components/ui/dialog.js +200 -0
  199. package/dist/components/ui/dialog.js.map +1 -0
  200. package/dist/components/ui/dot-pattern.d.ts +21 -0
  201. package/dist/components/ui/dot-pattern.js +139 -0
  202. package/dist/components/ui/dot-pattern.js.map +1 -0
  203. package/dist/components/ui/drag-handle-grip.d.ts +10 -0
  204. package/dist/components/ui/drag-handle-grip.js +15 -0
  205. package/dist/components/ui/drag-handle-grip.js.map +1 -0
  206. package/dist/components/ui/drawer.d.ts +16 -0
  207. package/dist/components/ui/drawer.js +125 -0
  208. package/dist/components/ui/drawer.js.map +1 -0
  209. package/dist/components/ui/dropdown-menu.d.ts +45 -0
  210. package/dist/components/ui/dropdown-menu.js +353 -0
  211. package/dist/components/ui/dropdown-menu.js.map +1 -0
  212. package/dist/components/ui/export-drawer.d.ts +11 -0
  213. package/dist/components/ui/export-drawer.js +1658 -0
  214. package/dist/components/ui/export-drawer.js.map +1 -0
  215. package/dist/components/ui/field.d.ts +30 -0
  216. package/dist/components/ui/field.js +249 -0
  217. package/dist/components/ui/field.js.map +1 -0
  218. package/dist/components/ui/form.d.ts +28 -0
  219. package/dist/components/ui/form.js +110 -0
  220. package/dist/components/ui/form.js.map +1 -0
  221. package/dist/components/ui/hover-card.d.ts +9 -0
  222. package/dist/components/ui/hover-card.js +43 -0
  223. package/dist/components/ui/hover-card.js.map +1 -0
  224. package/dist/components/ui/input-group.d.ts +20 -0
  225. package/dist/components/ui/input-group.js +219 -0
  226. package/dist/components/ui/input-group.js.map +1 -0
  227. package/dist/components/ui/input-mask.d.ts +39 -0
  228. package/dist/components/ui/input-mask.js +118 -0
  229. package/dist/components/ui/input-mask.js.map +1 -0
  230. package/dist/components/ui/input.d.ts +5 -0
  231. package/dist/components/ui/input.js +30 -0
  232. package/dist/components/ui/input.js.map +1 -0
  233. package/dist/components/ui/kbd.d.ts +20 -0
  234. package/dist/components/ui/kbd.js +45 -0
  235. package/dist/components/ui/kbd.js.map +1 -0
  236. package/dist/components/ui/key-metrics-context.d.ts +19 -0
  237. package/dist/components/ui/key-metrics-context.js +26 -0
  238. package/dist/components/ui/key-metrics-context.js.map +1 -0
  239. package/dist/components/ui/key-metrics.d.ts +131 -0
  240. package/dist/components/ui/key-metrics.js +1015 -0
  241. package/dist/components/ui/key-metrics.js.map +1 -0
  242. package/dist/components/ui/label.d.ts +6 -0
  243. package/dist/components/ui/label.js +28 -0
  244. package/dist/components/ui/label.js.map +1 -0
  245. package/dist/components/ui/list-page-view-frame.d.ts +22 -0
  246. package/dist/components/ui/list-page-view-frame.js +24 -0
  247. package/dist/components/ui/list-page-view-frame.js.map +1 -0
  248. package/dist/components/ui/page-header.d.ts +51 -0
  249. package/dist/components/ui/page-header.js +372 -0
  250. package/dist/components/ui/page-header.js.map +1 -0
  251. package/dist/components/ui/payment-card-fields.d.ts +10 -0
  252. package/dist/components/ui/payment-card-fields.js +80 -0
  253. package/dist/components/ui/payment-card-fields.js.map +1 -0
  254. package/dist/components/ui/popover.d.ts +10 -0
  255. package/dist/components/ui/popover.js +47 -0
  256. package/dist/components/ui/popover.js.map +1 -0
  257. package/dist/components/ui/radio-group.d.ts +29 -0
  258. package/dist/components/ui/radio-group.js +190 -0
  259. package/dist/components/ui/radio-group.js.map +1 -0
  260. package/dist/components/ui/resizable.d.ts +16 -0
  261. package/dist/components/ui/resizable.js +51 -0
  262. package/dist/components/ui/resizable.js.map +1 -0
  263. package/dist/components/ui/scroll-area.d.ts +8 -0
  264. package/dist/components/ui/scroll-area.js +66 -0
  265. package/dist/components/ui/scroll-area.js.map +1 -0
  266. package/dist/components/ui/select.d.ts +18 -0
  267. package/dist/components/ui/select.js +186 -0
  268. package/dist/components/ui/select.js.map +1 -0
  269. package/dist/components/ui/selection-tile-grid.d.ts +52 -0
  270. package/dist/components/ui/selection-tile-grid.js +347 -0
  271. package/dist/components/ui/selection-tile-grid.js.map +1 -0
  272. package/dist/components/ui/separator.d.ts +7 -0
  273. package/dist/components/ui/separator.js +33 -0
  274. package/dist/components/ui/separator.js.map +1 -0
  275. package/dist/components/ui/sheet.d.ts +18 -0
  276. package/dist/components/ui/sheet.js +181 -0
  277. package/dist/components/ui/sheet.js.map +1 -0
  278. package/dist/components/ui/sidebar.d.ts +94 -0
  279. package/dist/components/ui/sidebar.js +805 -0
  280. package/dist/components/ui/sidebar.js.map +1 -0
  281. package/dist/components/ui/skeleton.d.ts +5 -0
  282. package/dist/components/ui/skeleton.js +22 -0
  283. package/dist/components/ui/skeleton.js.map +1 -0
  284. package/dist/components/ui/slider.d.ts +7 -0
  285. package/dist/components/ui/slider.js +66 -0
  286. package/dist/components/ui/slider.js.map +1 -0
  287. package/dist/components/ui/sonner.d.ts +6 -0
  288. package/dist/components/ui/sonner.js +38 -0
  289. package/dist/components/ui/sonner.js.map +1 -0
  290. package/dist/components/ui/status-badge.d.ts +38 -0
  291. package/dist/components/ui/status-badge.js +77 -0
  292. package/dist/components/ui/status-badge.js.map +1 -0
  293. package/dist/components/ui/table.d.ts +13 -0
  294. package/dist/components/ui/table.js +115 -0
  295. package/dist/components/ui/table.js.map +1 -0
  296. package/dist/components/ui/tabs.d.ts +15 -0
  297. package/dist/components/ui/tabs.js +93 -0
  298. package/dist/components/ui/tabs.js.map +1 -0
  299. package/dist/components/ui/textarea.d.ts +6 -0
  300. package/dist/components/ui/textarea.js +25 -0
  301. package/dist/components/ui/textarea.js.map +1 -0
  302. package/dist/components/ui/tip.d.ts +12 -0
  303. package/dist/components/ui/tip.js +61 -0
  304. package/dist/components/ui/tip.js.map +1 -0
  305. package/dist/components/ui/toggle-group.d.ts +14 -0
  306. package/dist/components/ui/toggle-group.js +104 -0
  307. package/dist/components/ui/toggle-group.js.map +1 -0
  308. package/dist/components/ui/toggle-switch.d.ts +10 -0
  309. package/dist/components/ui/toggle-switch.js +33 -0
  310. package/dist/components/ui/toggle-switch.js.map +1 -0
  311. package/dist/components/ui/toggle.d.ts +13 -0
  312. package/dist/components/ui/toggle.js +51 -0
  313. package/dist/components/ui/toggle.js.map +1 -0
  314. package/dist/components/ui/tooltip.d.ts +10 -0
  315. package/dist/components/ui/tooltip.js +68 -0
  316. package/dist/components/ui/tooltip.js.map +1 -0
  317. package/dist/components/ui/view-segmented-control.d.ts +31 -0
  318. package/dist/components/ui/view-segmented-control.js +167 -0
  319. package/dist/components/ui/view-segmented-control.js.map +1 -0
  320. package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
  321. package/dist/hooks/use-app-theme.d.ts +24 -0
  322. package/dist/hooks/use-app-theme.js +286 -0
  323. package/dist/hooks/use-app-theme.js.map +1 -0
  324. package/dist/hooks/use-coach-mark.d.ts +86 -0
  325. package/dist/hooks/use-coach-mark.js +218 -0
  326. package/dist/hooks/use-coach-mark.js.map +1 -0
  327. package/dist/hooks/use-mobile.d.ts +3 -0
  328. package/dist/hooks/use-mobile.js +29 -0
  329. package/dist/hooks/use-mobile.js.map +1 -0
  330. package/dist/hooks/use-mod-key-label.d.ts +6 -0
  331. package/dist/hooks/use-mod-key-label.js +25 -0
  332. package/dist/hooks/use-mod-key-label.js.map +1 -0
  333. package/dist/index.d.ts +120 -0
  334. package/dist/index.js +13324 -0
  335. package/dist/index.js.map +1 -0
  336. package/dist/lib/compose-refs.d.ts +6 -0
  337. package/dist/lib/compose-refs.js +17 -0
  338. package/dist/lib/compose-refs.js.map +1 -0
  339. package/dist/lib/conditional-rule-match.d.ts +30 -0
  340. package/dist/lib/conditional-rule-match.js +66 -0
  341. package/dist/lib/conditional-rule-match.js.map +1 -0
  342. package/dist/lib/data-list-display-options.d.ts +26 -0
  343. package/dist/lib/data-list-display-options.js +14 -0
  344. package/dist/lib/data-list-display-options.js.map +1 -0
  345. package/dist/lib/data-list-view-registry.d.ts +2 -0
  346. package/dist/lib/data-list-view-registry.js +102 -0
  347. package/dist/lib/data-list-view-registry.js.map +1 -0
  348. package/dist/lib/data-list-view-surface.d.ts +2 -0
  349. package/dist/lib/data-list-view-surface.js +80 -0
  350. package/dist/lib/data-list-view-surface.js.map +1 -0
  351. package/dist/lib/data-list-view.d.ts +21 -0
  352. package/dist/lib/data-list-view.js +25 -0
  353. package/dist/lib/data-list-view.js.map +1 -0
  354. package/dist/lib/date-filter.d.ts +22 -0
  355. package/dist/lib/date-filter.js +61 -0
  356. package/dist/lib/date-filter.js.map +1 -0
  357. package/dist/lib/dev-log.d.ts +8 -0
  358. package/dist/lib/dev-log.js +10 -0
  359. package/dist/lib/dev-log.js.map +1 -0
  360. package/dist/lib/dropdown-menu-surface.d.ts +14 -0
  361. package/dist/lib/dropdown-menu-surface.js +6 -0
  362. package/dist/lib/dropdown-menu-surface.js.map +1 -0
  363. package/dist/lib/editable-target.d.ts +12 -0
  364. package/dist/lib/editable-target.js +12 -0
  365. package/dist/lib/editable-target.js.map +1 -0
  366. package/dist/lib/list-page-table-properties.d.ts +35 -0
  367. package/dist/lib/list-page-table-properties.js +81 -0
  368. package/dist/lib/list-page-table-properties.js.map +1 -0
  369. package/dist/lib/raf-throttle.d.ts +23 -0
  370. package/dist/lib/raf-throttle.js +27 -0
  371. package/dist/lib/raf-throttle.js.map +1 -0
  372. package/dist/lib/row-height.d.ts +16 -0
  373. package/dist/lib/row-height.js +10 -0
  374. package/dist/lib/row-height.js.map +1 -0
  375. package/dist/lib/table-properties-types.d.ts +83 -0
  376. package/dist/lib/table-properties-types.js +19 -0
  377. package/dist/lib/table-properties-types.js.map +1 -0
  378. package/dist/lib/utils.d.ts +5 -0
  379. package/dist/lib/utils.js +11 -0
  380. package/dist/lib/utils.js.map +1 -0
  381. package/package.json +83 -19
  382. package/src/components/data-table/filter-date-calendar.tsx +38 -0
  383. package/src/components/data-table/filter-text-value-input.tsx +77 -0
  384. package/src/components/data-table/index.tsx +1678 -0
  385. package/src/components/data-table/pagination.tsx +255 -0
  386. package/src/components/data-table/types.ts +96 -0
  387. package/src/components/data-table/use-table-state.ts +767 -0
  388. package/src/components/data-views/board-card-primitives.tsx +93 -0
  389. package/src/components/data-views/data-row-list.tsx +183 -0
  390. package/src/components/data-views/finder-panel-view.tsx +405 -0
  391. package/src/components/data-views/folder-grid-view.tsx +86 -0
  392. package/src/components/data-views/hub-table.tsx +498 -0
  393. package/src/components/data-views/index.ts +28 -0
  394. package/src/components/data-views/list-page-board-card.tsx +192 -0
  395. package/src/components/data-views/list-page-board-template.tsx +122 -0
  396. package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
  397. package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  398. package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  399. package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
  400. package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
  401. package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  402. package/src/components/data-views/os-folder-glyph.tsx +141 -0
  403. package/src/components/data-views/outline-tree-menu.tsx +157 -0
  404. package/src/components/table-properties/column-row.tsx +90 -0
  405. package/src/components/table-properties/draggable-list.ts +54 -0
  406. package/src/components/table-properties/drawer-button.tsx +300 -0
  407. package/src/components/table-properties/drawer.tsx +1148 -0
  408. package/src/components/table-properties/filter-card.tsx +251 -0
  409. package/src/components/table-properties/index.ts +36 -0
  410. package/src/components/table-properties/sort-card.tsx +63 -0
  411. package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
  412. package/src/components/templates/dedicated-search-results-template.tsx +19 -0
  413. package/src/components/templates/index.ts +33 -0
  414. package/src/components/templates/list-page.tsx +602 -0
  415. package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
  416. package/src/components/ui/accordion.tsx +92 -0
  417. package/src/components/ui/alert-dialog.tsx +221 -0
  418. package/src/components/ui/avatar.tsx +13 -2
  419. package/src/components/ui/banner.tsx +2 -2
  420. package/src/components/ui/button.tsx +4 -4
  421. package/src/components/ui/calendar.tsx +1 -1
  422. package/src/components/ui/coach-mark.tsx +1 -1
  423. package/src/components/ui/context-menu.tsx +291 -0
  424. package/src/components/ui/date-picker-field.tsx +2 -2
  425. package/src/components/ui/dot-pattern.tsx +183 -0
  426. package/src/components/ui/export-drawer.tsx +375 -0
  427. package/src/components/ui/hover-card.tsx +66 -0
  428. package/src/components/ui/key-metrics-context.tsx +78 -0
  429. package/src/components/ui/key-metrics.tsx +1133 -0
  430. package/src/components/ui/list-page-view-frame.tsx +64 -0
  431. package/src/components/ui/page-header.tsx +244 -0
  432. package/src/components/ui/payment-card-fields.tsx +2 -2
  433. package/src/components/ui/resizable.tsx +68 -0
  434. package/src/components/ui/scroll-area.tsx +72 -0
  435. package/src/components/ui/selection-tile-grid.tsx +9 -2
  436. package/src/components/ui/sidebar.tsx +84 -12
  437. package/src/components/ui/slider.tsx +83 -0
  438. package/src/globals.css +2201 -7
  439. package/src/globals.d.ts +20 -0
  440. package/src/index.ts +68 -1
  441. package/src/lib/conditional-rule-match.ts +119 -0
  442. package/src/lib/data-list-display-options.ts +35 -0
  443. package/src/lib/data-list-view-registry.ts +104 -0
  444. package/src/lib/data-list-view-surface.ts +83 -0
  445. package/src/lib/data-list-view.ts +47 -0
  446. package/src/lib/dev-log.ts +10 -0
  447. package/src/lib/editable-target.ts +20 -0
  448. package/src/lib/list-page-table-properties.ts +48 -0
  449. package/src/lib/raf-throttle.ts +45 -0
  450. package/src/lib/row-height.ts +19 -0
  451. package/src/lib/table-properties-types.ts +98 -0
  452. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  453. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +3 -3
  454. package/template/.cursor/rules/exxat-data-tables.mdc +1 -1
  455. package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
  456. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +2 -2
  457. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  458. package/template/AGENTS.md +104 -78
  459. package/template/app/(app)/dashboard/loading.tsx +15 -3
  460. package/template/app/(app)/dashboard/page.tsx +14 -2
  461. package/template/app/(app)/examples/page.tsx +0 -2
  462. package/template/app/(app)/layout.tsx +17 -4
  463. package/template/app/(app)/loading.tsx +18 -1
  464. package/template/app/(app)/question-bank/find/page.tsx +1 -2
  465. package/template/app/(app)/question-bank/layout.tsx +1 -1
  466. package/template/app/(app)/question-bank/library/page.tsx +1 -2
  467. package/template/app/(app)/question-bank/list/page.tsx +1 -2
  468. package/template/app/(app)/question-bank/new/page.tsx +15 -20
  469. package/template/app/(app)/question-bank/page.tsx +1 -2
  470. package/template/app/(app)/settings/page.tsx +5 -4
  471. package/template/app/globals.css +14 -16
  472. package/template/components/ask-leo-sidebar.tsx +5 -1
  473. package/template/components/brand-color-picker.tsx +2 -2
  474. package/template/components/charts-overview.tsx +1 -1
  475. package/template/components/compliance-board-view.tsx +142 -0
  476. package/template/components/compliance-client.tsx +92 -0
  477. package/template/components/compliance-page-header.tsx +89 -0
  478. package/template/components/compliance-table.tsx +468 -0
  479. package/template/components/dashboard-report-charts.tsx +1 -1
  480. package/template/components/dashboard-tabs.tsx +1 -1
  481. package/template/components/data-table/filter-date-calendar.tsx +1 -38
  482. package/template/components/data-table/filter-text-value-input.tsx +1 -77
  483. package/template/components/data-table/index.tsx +1 -1634
  484. package/template/components/data-table/pagination.tsx +1 -255
  485. package/template/components/data-table/types.ts +1 -94
  486. package/template/components/data-table/use-table-state.test.ts +420 -0
  487. package/template/components/data-table/use-table-state.ts +1 -758
  488. package/template/components/data-view-dashboard-charts-compliance.tsx +963 -0
  489. package/template/components/data-view-dashboard-charts-team.tsx +971 -0
  490. package/template/components/data-view-dashboard-charts.tsx +1503 -0
  491. package/template/components/data-views/board-card-primitives.tsx +1 -93
  492. package/template/components/data-views/data-row-list.tsx +1 -183
  493. package/template/components/data-views/finder-panel-view.tsx +1 -405
  494. package/template/components/data-views/folder-grid-view.tsx +1 -86
  495. package/template/components/data-views/hub-table.tsx +1 -0
  496. package/template/components/data-views/index.ts +50 -37
  497. package/template/components/data-views/list-page-board-card.tsx +1 -192
  498. package/template/components/data-views/list-page-board-template.tsx +1 -122
  499. package/template/components/data-views/list-page-connected-view-body.tsx +1 -66
  500. package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
  501. package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -68
  502. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
  503. package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
  504. package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
  505. package/template/components/data-views/list-page-view-frame.tsx +5 -53
  506. package/template/components/data-views/os-folder-glyph.tsx +1 -129
  507. package/template/components/data-views/outline-tree-menu.tsx +1 -157
  508. package/template/components/export-drawer.test.tsx +71 -0
  509. package/template/components/export-drawer.tsx +1 -375
  510. package/template/components/exxat-product-logo.tsx +5 -5
  511. package/template/components/hub-tree-panel-view.tsx +2 -2
  512. package/template/components/invite-collaborators-drawer.tsx +3 -3
  513. package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
  514. package/template/components/key-metrics.tsx +1 -1063
  515. package/template/components/leo-insight-indicator.tsx +2 -2
  516. package/template/components/new-placement-back-btn.tsx +28 -0
  517. package/template/components/new-placement-form.tsx +942 -0
  518. package/template/components/new-question-composer.tsx +456 -408
  519. package/template/components/onboarding/index.ts +9 -0
  520. package/template/components/onboarding/onboarding-01.tsx +1 -1
  521. package/template/components/onboarding/onboarding-02.tsx +1 -1
  522. package/template/components/onboarding/onboarding-03.tsx +1 -1
  523. package/template/components/onboarding/onboarding-04.tsx +1 -1
  524. package/template/components/page-header.tsx +8 -226
  525. package/template/components/placement-board-card.tsx +250 -0
  526. package/template/components/placement-detail.tsx +438 -0
  527. package/template/components/placements-board-view.tsx +397 -0
  528. package/template/components/placements-client.tsx +220 -0
  529. package/template/components/placements-list-view.tsx +124 -0
  530. package/template/components/placements-page-header.tsx +166 -0
  531. package/template/components/placements-table-cells.test.tsx +22 -0
  532. package/template/components/placements-table-cells.tsx +173 -0
  533. package/template/components/placements-table-columns.tsx +210 -0
  534. package/template/components/placements-table.tsx +934 -0
  535. package/template/components/product-switcher.tsx +3 -4
  536. package/template/components/product-wordmark.tsx +2 -1
  537. package/template/components/question-bank-client.tsx +5 -5
  538. package/template/components/question-bank-hub-client.tsx +1 -1
  539. package/template/components/question-bank-new-folder-sheet.tsx +2 -2
  540. package/template/components/question-bank-secondary-nav.tsx +3 -3
  541. package/template/components/question-bank-table.tsx +541 -431
  542. package/template/components/rotations-empty-state.tsx +50 -0
  543. package/template/components/rotations-panel-activator.tsx +8 -0
  544. package/template/components/settings-appearance-card.tsx +3 -4
  545. package/template/components/settings-client.tsx +15 -59
  546. package/template/components/settings-form-row.tsx +4 -9
  547. package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
  548. package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +59 -74
  549. package/template/components/sidebar/index.ts +16 -0
  550. package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
  551. package/template/components/{secondary-panel.tsx → sidebar/secondary-panel.tsx} +50 -7
  552. package/template/components/{sidebar-auto-collapse.tsx → sidebar/sidebar-auto-collapse.tsx} +6 -2
  553. package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
  554. package/template/components/site-header.tsx +1 -1
  555. package/template/components/sites-board-view.tsx +67 -0
  556. package/template/components/sites-client.tsx +154 -0
  557. package/template/components/sites-table.tsx +249 -0
  558. package/template/components/table-properties/column-row.tsx +1 -90
  559. package/template/components/table-properties/draggable-list.ts +1 -49
  560. package/template/components/table-properties/drawer-button.tsx +1 -262
  561. package/template/components/table-properties/drawer.tsx +1 -1166
  562. package/template/components/table-properties/filter-card.tsx +1 -251
  563. package/template/components/table-properties/sort-card.tsx +1 -59
  564. package/template/components/table-properties/types.ts +28 -71
  565. package/template/components/team-board-view.tsx +122 -0
  566. package/template/components/team-client.tsx +100 -0
  567. package/template/components/team-page-header.tsx +92 -0
  568. package/template/components/team-table.tsx +553 -0
  569. package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
  570. package/template/components/templates/dedicated-search-results-template.tsx +1 -19
  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 +1 -1
  575. package/template/components/ui/accordion.tsx +1 -0
  576. package/template/components/ui/alert-dialog.tsx +1 -0
  577. package/template/components/ui/context-menu.tsx +1 -0
  578. package/template/components/ui/dot-pattern.tsx +1 -183
  579. package/template/components/ui/hover-card.tsx +1 -0
  580. package/template/components/ui/resizable.tsx +1 -68
  581. package/template/components/ui/scroll-area.tsx +1 -0
  582. package/template/components/ui/slider.tsx +1 -0
  583. package/template/docs/blueprints/README.md +86 -0
  584. package/template/docs/blueprints/_template.md +91 -0
  585. package/template/docs/blueprints/board-card.md +123 -0
  586. package/template/docs/blueprints/data-table.md +139 -0
  587. package/template/docs/blueprints/key-metrics.md +128 -0
  588. package/template/docs/blueprints/list-page-template.md +123 -0
  589. package/template/docs/blueprints/page-header.md +130 -0
  590. package/template/docs/command-menu-pattern.md +1 -1
  591. package/template/docs/component-selection-guide.md +224 -0
  592. package/template/docs/components-audit-2026-05.md +158 -0
  593. package/template/docs/data-views-pattern.md +17 -54
  594. package/template/docs/drawer-vs-dialog-pattern.md +1 -3
  595. package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
  596. package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
  597. package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
  598. package/template/docs/migrations/README.md +100 -0
  599. package/template/docs/migrations/_template.md +64 -0
  600. package/template/docs/shell-surface-elevation-pattern.md +3 -5
  601. package/template/docs/token-taxonomy.md +416 -0
  602. package/template/eslint.config.mjs +27 -0
  603. package/template/hooks/use-secondary-panel-hub-nav.ts +1 -1
  604. package/template/lib/command-menu-config.ts +0 -1
  605. package/template/lib/command-menu-search-data.ts +27 -11
  606. package/template/lib/compliance-supported-views.ts +10 -0
  607. package/template/lib/conditional-rule-match.ts +6 -97
  608. package/template/lib/data-list-display-options.ts +1 -49
  609. package/template/lib/data-list-view-registry.ts +1 -104
  610. package/template/lib/data-list-view-surface.ts +1 -83
  611. package/template/lib/data-list-view.ts +1 -47
  612. package/template/lib/data-view-dashboard-placements-layout.ts +215 -0
  613. package/template/lib/data-view-dashboard-storage.ts +35 -38
  614. package/template/lib/dev-log.ts +1 -8
  615. package/template/lib/editable-target.ts +1 -10
  616. package/template/lib/list-page-table-properties.ts +1 -48
  617. package/template/lib/list-status-badges.ts +97 -4
  618. package/template/lib/mock/compliance-kpi.ts +61 -0
  619. package/template/lib/mock/compliance.ts +146 -0
  620. package/template/lib/mock/navigation.tsx +0 -9
  621. package/template/lib/mock/placements-kpi.ts +134 -0
  622. package/template/lib/mock/placements.ts +176 -0
  623. package/template/lib/mock/sites-directory.ts +16 -0
  624. package/template/lib/mock/sites-kpi.ts +25 -0
  625. package/template/lib/mock/team-kpi.ts +60 -0
  626. package/template/lib/mock/team.ts +118 -0
  627. package/template/lib/placement-board-card-layout.ts +79 -0
  628. package/template/lib/placements-supported-views.ts +12 -0
  629. package/template/lib/question-bank-supported-views.ts +0 -1
  630. package/template/lib/raf-throttle.ts +1 -45
  631. package/template/lib/row-height.ts +4 -10
  632. package/template/lib/sidebar-state-cookie.ts +11 -2
  633. package/template/lib/sites-supported-views.ts +10 -0
  634. package/template/lib/table-state-lifecycle.ts +2 -2
  635. package/template/lib/team-supported-views.ts +10 -0
  636. package/template/package.json +1 -0
  637. package/template/tests/setup.ts +25 -0
  638. package/consumer-extras/AGENTS.md +0 -76
  639. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +0 -37
  640. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +0 -57
  641. package/consumer-extras/patterns/consumer-app-pattern.md +0 -39
  642. package/consumer-extras/patterns/focused-workflow-page-pattern.md +0 -84
  643. package/src/components/ui/button-group.tsx +0 -81
  644. package/src/theme.css +0 -16
  645. package/src/tokens/README.md +0 -15
  646. package/src/tokens/base.css +0 -337
  647. package/src/tokens/high-contrast.css +0 -1195
  648. package/src/tokens/layers.css +0 -224
  649. package/src/tokens/tailwind-bridge.css +0 -118
  650. package/src/tokens/themes.css +0 -201
  651. package/template/app/(app)/data-list/layout.tsx +0 -43
  652. package/template/app/(app)/data-list/page.tsx +0 -10
  653. package/template/app/(app)/examples/focused-workflow/page.tsx +0 -5
  654. package/template/components/app-route-loading.tsx +0 -14
  655. package/template/components/dashboard-onboarding-gallery.tsx +0 -13
  656. package/template/components/dashboard-onboarding.tsx +0 -21
  657. package/template/components/data-views/list-page-calendar-view.tsx +0 -593
  658. package/template/components/data-views/list-page-folder-columns-panel.tsx +0 -345
  659. package/template/components/examples/focused-workflow-showcase.tsx +0 -183
  660. package/template/components/list-hub-board-view.tsx +0 -68
  661. package/template/components/list-hub-client.tsx +0 -186
  662. package/template/components/list-hub-list-view.tsx +0 -36
  663. package/template/components/list-hub-panel-activator.tsx +0 -8
  664. package/template/components/list-hub-secondary-nav.tsx +0 -121
  665. package/template/components/list-hub-table.tsx +0 -336
  666. package/template/components/question-bank-folder-columns-panel.tsx +0 -104
  667. package/template/components/question-bank-list-view.tsx +0 -53
  668. package/template/components/secondary-panel/nav-link-rows.tsx +0 -83
  669. package/template/components/secondary-panels/list-hub-panel.tsx +0 -39
  670. package/template/components/secondary-panels/question-bank-panel.tsx +0 -39
  671. package/template/components/secondary-panels/registry.tsx +0 -15
  672. package/template/components/section-cards.tsx +0 -106
  673. package/template/components/templates/focused-workflow-layouts.tsx +0 -448
  674. package/template/components/templates/focused-workflow-page-template.tsx +0 -69
  675. package/template/components/templates/page-loading-shell.tsx +0 -262
  676. package/template/components/ui/button-group.tsx +0 -1
  677. package/template/docs/consumer-app-pattern.md +0 -39
  678. package/template/docs/focused-workflow-page-pattern.md +0 -84
  679. package/template/lib/list-hub-nav.ts +0 -121
  680. package/template/lib/mock/list-hub-directory.ts +0 -27
  681. package/template/lib/mock/list-hub-kpi.ts +0 -27
  682. package/template/lib/page-loading-variant.ts +0 -40
  683. /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
  684. /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
  685. /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
  686. /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
  687. /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
  688. /package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +0 -0
@@ -1,1166 +1 @@
1
- "use client"
2
- import * as React from "react"
3
- import { cn } from "@/lib/utils"
4
- import type { DataListViewType } from "@/lib/data-list-view"
5
- import { DATA_LIST_VIEW_TILES, dataListViewLabel } from "@/lib/data-list-view"
6
- import type { RowHeight } from "@/lib/row-height"
7
- import { ROW_HEIGHT_TILES } from "@/lib/row-height"
8
- import { SelectionTileGrid } from "@/components/ui/selection-tile-grid"
9
- import {
10
- DropdownMenu,
11
- DropdownMenuContent,
12
- DropdownMenuItem,
13
- DropdownMenuLabel,
14
- DropdownMenuSeparator,
15
- DropdownMenuTrigger,
16
- } from "@/components/ui/dropdown-menu"
17
- import {
18
- Sheet,
19
- SheetContent,
20
- SheetTitle,
21
- } from "@/components/ui/sheet"
22
- import {
23
- Select,
24
- SelectContent,
25
- SelectItem,
26
- SelectTrigger,
27
- SelectValue,
28
- } from "@/components/ui/select"
29
- import {
30
- CALENDAR_MAIN_VIEW_TILES,
31
- type DataListDisplayOptions,
32
- } from "@/lib/data-list-display-options"
33
- import { Tip } from "@/components/ui/tip"
34
- import { ToggleSwitch } from "@/components/ui/toggle-switch"
35
- import { Button } from "@/components/ui/button"
36
- import { DrawerFilterCard } from "./filter-card"
37
- import { DrawerSortCard } from "./sort-card"
38
- import { ColumnRow } from "./column-row"
39
- import { useDraggableList } from "./draggable-list"
40
- import {
41
- type ActiveFilter,
42
- type SortRule,
43
- type ConditionalRule,
44
- type FilterFieldDef,
45
- COLUMNS,
46
- FILTER_FIELDS,
47
- RULE_COLORS,
48
- } from "./types"
49
-
50
- export interface TablePropertiesDrawerProps {
51
- open: boolean
52
- onOpenChange: (open: boolean) => void
53
- // Display
54
- showGridlines: boolean
55
- onShowGridlinesChange: (v: boolean) => void
56
- rowHeight: RowHeight
57
- onRowHeightChange: (v: RowHeight) => void
58
- pagination: boolean
59
- onPaginationChange: (v: boolean) => void
60
- // Filters
61
- activeFilters: ActiveFilter[]
62
- onAddFilter: (fieldKey: string) => void
63
- onUpdateFilter: (id: string, patch: Partial<ActiveFilter>) => void
64
- onRemoveFilter: (id: string) => void
65
- /** How the filter after `leftFilterId` combines with the one above (default "and"). */
66
- getFilterConnector: (leftFilterId: string) => "and" | "or"
67
- onToggleFilterConnector: (leftFilterId: string) => void
68
- filterBarVisible: boolean
69
- onFilterBarVisibleChange: (v: boolean) => void
70
- drawerExpandedFilters: Set<string>
71
- onDrawerExpandedFiltersChange: React.Dispatch<React.SetStateAction<Set<string>>>
72
- totalRows: number
73
- filteredRows: number
74
- // Sort
75
- sortRules: SortRule[]
76
- onSortRulesChange: (rules: SortRule[]) => void
77
- onAddSortRule: (fieldKey: string) => void
78
- onRemoveSortRule: (id: string) => void
79
- onToggleSortDir: (id: string) => void
80
- // Columns
81
- colOrder: string[]
82
- onColOrderChange: (order: string[]) => void
83
- hiddenCols: Set<string>
84
- onToggleColVisibility: (key: string) => void
85
- onMoveCol: (key: string, dir: "up" | "down") => void
86
- // Group
87
- groupBy: string | null
88
- onGroupByChange: (key: string | null) => void
89
- // Sort key for display in main panel
90
- primarySortKey?: string
91
- // Conditional formatting
92
- conditionalRules: ConditionalRule[]
93
- onAddConditionalRule: (rule: Omit<ConditionalRule, "id">) => void
94
- onRemoveConditionalRule: (id: string) => void
95
- onUpdateConditionalRule: (id: string, patch: Partial<ConditionalRule>) => void
96
- /** Filter field defs for drawer + conditional rules — defaults to FILTER_FIELDS; pass column-derived defs to match the table */
97
- filterFields?: FilterFieldDef[]
98
- // View type
99
- currentView?: DataListViewType
100
- onViewChange?: (view: DataListViewType) => void
101
- /**
102
- * View-type tiles in Properties — defaults to all `DATA_LIST_VIEW_TILES`.
103
- * Pass hub-filtered options (same set as `ListPageTemplate` `supportedViewTypes`).
104
- */
105
- viewTypeOptions?: readonly { value: DataListViewType; label: string; icon: string }[]
106
- /** Lifecycle context (e.g. tab filter) — shown in the drawer header */
107
- lifecycleTabLabel?: string
108
- /**
109
- * Column labels for the active table definition (placements use dynamic columns per tab).
110
- * When set, overrides static `COLUMNS` from types for Columns / Sort / Group labels.
111
- */
112
- fieldDefinitions?: { key: string; label: string; sortable?: boolean }[]
113
- resolveColumnLabel?: (key: string) => string
114
- /** Shared display options (table + board); persisted at page level. */
115
- displayOptions: DataListDisplayOptions
116
- onDisplayOptionsChange: (patch: Partial<DataListDisplayOptions>) => void
117
- /**
118
- * When the active view is Board and more than one entry is provided, shows a control to pick
119
- * which field defines swimlane columns (`displayOptions.boardGroupByColumnKey`).
120
- */
121
- boardGroupByColumnOptions?: { key: string; label: string }[]
122
- /** Optional custom option renderer for filter values (e.g. status chips). */
123
- renderFilterOptionValue?: (fieldKey: string, value: string) => React.ReactNode
124
- }
125
-
126
- type SheetPanel = "main" | "table-display" | "filter" | "sort" | "group" | "columns" | "conditional-rules"
127
-
128
- /** Properties sheet uses `z-[80]`; default portaled menus are `z-50` and sit underneath. */
129
- const PROPERTIES_SHEET_PORTAL_Z = "z-[90]"
130
-
131
- export function TablePropertiesDrawer({
132
- open,
133
- onOpenChange,
134
- showGridlines,
135
- onShowGridlinesChange,
136
- rowHeight,
137
- onRowHeightChange,
138
- pagination,
139
- onPaginationChange,
140
- activeFilters,
141
- onAddFilter,
142
- onUpdateFilter,
143
- onRemoveFilter,
144
- getFilterConnector,
145
- onToggleFilterConnector,
146
- filterBarVisible,
147
- onFilterBarVisibleChange,
148
- drawerExpandedFilters,
149
- onDrawerExpandedFiltersChange,
150
- totalRows,
151
- filteredRows,
152
- sortRules,
153
- onSortRulesChange,
154
- onAddSortRule,
155
- onRemoveSortRule,
156
- onToggleSortDir,
157
- colOrder,
158
- onColOrderChange,
159
- hiddenCols,
160
- onToggleColVisibility,
161
- onMoveCol,
162
- groupBy,
163
- onGroupByChange,
164
- primarySortKey,
165
- conditionalRules,
166
- onAddConditionalRule,
167
- onRemoveConditionalRule,
168
- onUpdateConditionalRule,
169
- filterFields = FILTER_FIELDS,
170
- currentView,
171
- onViewChange,
172
- viewTypeOptions = DATA_LIST_VIEW_TILES,
173
- lifecycleTabLabel,
174
- fieldDefinitions,
175
- resolveColumnLabel: resolveColumnLabelProp,
176
- displayOptions,
177
- onDisplayOptionsChange,
178
- boardGroupByColumnOptions,
179
- renderFilterOptionValue,
180
- }: TablePropertiesDrawerProps) {
181
- const [sheetPanel, setSheetPanel] = React.useState<SheetPanel>("main")
182
-
183
- // Reset to main panel when drawer is closed
184
- React.useEffect(() => {
185
- if (!open) setSheetPanel("main")
186
- }, [open])
187
-
188
- const resolveColumnLabel = React.useCallback(
189
- (key: string) =>
190
- resolveColumnLabelProp?.(key)
191
- ?? COLUMNS.find(c => c.key === key)?.label
192
- ?? key,
193
- [resolveColumnLabelProp],
194
- )
195
-
196
- const sortFieldList = React.useMemo(() => {
197
- if (fieldDefinitions?.length) {
198
- return fieldDefinitions.filter(f => f.sortable !== false && f.key !== "select" && f.key !== "actions")
199
- }
200
- return COLUMNS.filter(c => c.sortable && c.sortKey).map(c => ({ key: c.key, label: c.label, sortable: true }))
201
- }, [fieldDefinitions])
202
-
203
- const groupFieldList = React.useMemo(() => {
204
- if (fieldDefinitions?.length) {
205
- return fieldDefinitions.filter(f => f.key !== "select" && f.key !== "actions")
206
- }
207
- return COLUMNS.filter(c => c.key !== "select" && c.key !== "actions")
208
- }, [fieldDefinitions])
209
-
210
- const viewSurface = currentView ?? "table"
211
- const isBoardView = viewSurface === "board"
212
- const isCalendarView = viewSurface === "calendar"
213
- const boardGroupByLabel =
214
- boardGroupByColumnOptions?.find(o => o.key === displayOptions.boardGroupByColumnKey)?.label
215
- const viewDisplayLabel = dataListViewLabel(viewSurface)
216
- const viewDisplayDesc = (() => {
217
- if (viewSurface === "board") {
218
- return [
219
- boardGroupByLabel ? `By ${boardGroupByLabel}` : null,
220
- `${displayOptions.boardLineCount}-line`,
221
- displayOptions.showColumnLabels ? "Column labels" : "No labels",
222
- ]
223
- .filter(Boolean)
224
- .join(" · ")
225
- }
226
- if (viewSurface === "list") {
227
- return [
228
- displayOptions.showColumnLabels ? "Column labels" : "No labels",
229
- displayOptions.showToolbarSearch ? "Toolbar search" : "No search",
230
- ].join(" · ")
231
- }
232
- if (viewSurface === "dashboard") {
233
- return "Charts · KPI metrics"
234
- }
235
- if (viewSurface === "calendar") {
236
- return [
237
- displayOptions.showCalendarSummaryPanel ? "Summary panel" : "No summary",
238
- displayOptions.calendarMainView === "week" ? "Week layout" : "Month layout",
239
- ].join(" · ")
240
- }
241
- return [showGridlines ? "Gridlines" : null, pagination ? "Paginated" : null].filter(Boolean).join(" · ") || "Default"
242
- })()
243
- const viewDisplayIcon =
244
- viewTypeOptions.find(t => t.value === viewSurface)?.icon ?? "fa-table"
245
-
246
- // ── Sort drag-and-drop ────────────────────────────────────────────────────
247
- const sortDrag = useDraggableList(sortRules, r => r.id, onSortRulesChange)
248
-
249
- // ── Columns drag-and-drop ─────────────────────────────────────────────────
250
- const orderable = colOrder.filter(k => k !== "select" && k !== "actions")
251
- const colDrag = useDraggableList(
252
- orderable,
253
- k => k,
254
- newOrder => onColOrderChange(["select", ...newOrder, "actions"]),
255
- )
256
-
257
- // Current primary sort label for display in main panel
258
- const primarySortLabel = primarySortKey
259
- ? resolveColumnLabel(primarySortKey)
260
- : sortRules[0]?.fieldKey
261
- ? resolveColumnLabel(sortRules[0].fieldKey)
262
- : "—"
263
-
264
- return (
265
- <Sheet open={open} onOpenChange={onOpenChange} modal={false}>
266
- <SheetContent
267
- side="right"
268
- showCloseButton={false}
269
- showOverlay={false}
270
- // w-[min(20rem,calc(100vw-1rem))]: cap to viewport width - 1rem at narrow/zoomed viewports
271
- // so the drawer never overflows horizontally. Use 100svh so height is correct on mobile.
272
- className="z-[80] w-[min(20rem,calc(100vw-1rem))] p-0 gap-0 flex flex-col border border-border shadow-xl rounded-xl overflow-hidden"
273
- style={{ top: "0.5rem", bottom: "0.5rem", right: "0.5rem", height: "calc(100svh - 1rem)" }}
274
- >
275
-
276
- {sheetPanel === "main" ? (
277
- <>
278
- {/* Header */}
279
- <div className="flex items-center justify-between gap-3 px-4 pt-5 pb-3">
280
- <div className="min-w-0">
281
- <SheetTitle className="text-base font-semibold leading-tight">Properties</SheetTitle>
282
- {lifecycleTabLabel ? (
283
- <p className="text-xs text-muted-foreground mt-0.5 truncate" title={lifecycleTabLabel}>
284
- {lifecycleTabLabel}
285
- </p>
286
- ) : null}
287
- </div>
288
- <Tip label="Close" side="bottom">
289
- <Button
290
- type="button"
291
- variant="ghost"
292
- size="icon-sm"
293
- aria-label="Close"
294
- onClick={() => onOpenChange(false)}
295
- >
296
- <i className="fa-light fa-xmark text-[13px]" aria-hidden="true" />
297
- </Button>
298
- </Tip>
299
- </div>
300
-
301
- {/* View type switcher — card tiles like export file format */}
302
- {onViewChange && currentView && (
303
- <div className="px-4 pb-3">
304
- <SelectionTileGrid<DataListViewType>
305
- sectionLabel="View type"
306
- options={viewTypeOptions}
307
- columns={4}
308
- value={currentView}
309
- onValueChange={onViewChange}
310
- interaction="button"
311
- idPrefix="props-view"
312
- />
313
- </div>
314
- )}
315
-
316
- {/* Option list — inset rows + rounded hover (not edge-to-edge) */}
317
- <div className="flex-1 overflow-y-auto py-2 px-3 space-y-1">
318
- {([
319
- {
320
- id: "table-display" as SheetPanel,
321
- icon: viewDisplayIcon,
322
- label: viewDisplayLabel,
323
- desc: viewDisplayDesc,
324
- },
325
- {
326
- id: "filter" as SheetPanel,
327
- icon: "fa-filter",
328
- label: "Filter",
329
- desc: activeFilters.length === 0
330
- ? `Showing all ${filteredRows} rows.`
331
- : `${activeFilters.length} filter${activeFilters.length !== 1 ? "s" : ""} active · ${filteredRows} rows.`,
332
- },
333
- {
334
- id: "sort" as SheetPanel,
335
- icon: "fa-arrow-up-arrow-down",
336
- label: "Sort",
337
- desc: `Sorted by ${primarySortLabel}.`,
338
- },
339
- {
340
- id: "group" as SheetPanel,
341
- icon: "fa-layer-group",
342
- label: "Group",
343
- desc: groupBy
344
- ? `Grouped by ${resolveColumnLabel(groupBy)}.`
345
- : "No grouping.",
346
- },
347
- {
348
- id: "columns" as SheetPanel,
349
- icon: "fa-table-columns",
350
- label: "Columns",
351
- desc: hiddenCols.size === 0
352
- ? "All columns visible."
353
- : `${hiddenCols.size} column${hiddenCols.size !== 1 ? "s" : ""} hidden.`,
354
- },
355
- {
356
- id: "conditional-rules" as SheetPanel,
357
- icon: "fa-palette",
358
- label: "Conditional rules",
359
- desc: conditionalRules.length === 0
360
- ? "No rules applied."
361
- : `${conditionalRules.length} rule${conditionalRules.length !== 1 ? "s" : ""} active.`,
362
- },
363
- ] as { id: SheetPanel; icon: string; label: string; desc: string }[]).map(item => (
364
- <Button
365
- key={item.id}
366
- type="button"
367
- variant="ghost"
368
- onClick={() => setSheetPanel(item.id)}
369
- className={cn(
370
- "w-full h-auto justify-start gap-3 px-3 py-3 rounded-2xl font-normal border border-transparent",
371
- "hover:bg-muted/60 hover:text-foreground",
372
- "focus-visible:bg-muted/60 focus-visible:text-foreground",
373
- )}
374
- >
375
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
376
- <i className={`fa-light ${item.icon} text-[15px] text-secondary-foreground`} aria-hidden="true" />
377
- </span>
378
- <span className="flex-1 min-w-0 text-left">
379
- <span className="block text-sm font-medium text-foreground">{item.label}</span>
380
- <span className="block text-xs text-muted-foreground mt-0.5">{item.desc}</span>
381
- </span>
382
- <i className="fa-light fa-chevron-right text-xs text-muted-foreground shrink-0" aria-hidden="true" />
383
- </Button>
384
- ))}
385
- </div>
386
- </>
387
- ) : (
388
- <>
389
- {/* Sub-panel header — back + title stack as one cluster; close aligns to row center */}
390
- <div className="flex items-center justify-between gap-3 px-4 pt-4 pb-3">
391
- <div className="flex items-center gap-2 min-w-0 flex-1">
392
- <Tip label="Back to Properties" side="bottom">
393
- <Button
394
- type="button"
395
- variant="ghost"
396
- size="icon-sm"
397
- className="shrink-0"
398
- aria-label="Back to Properties"
399
- onClick={() => setSheetPanel("main")}
400
- >
401
- <i className="fa-light fa-chevron-left text-[13px]" aria-hidden="true" />
402
- </Button>
403
- </Tip>
404
- <div className="min-w-0">
405
- <SheetTitle className="text-base font-semibold text-foreground leading-tight flex items-center gap-1.5">
406
- {{
407
- "table-display": viewDisplayLabel,
408
- filter: "Filter",
409
- sort: "Sort",
410
- group: "Group",
411
- columns: "Columns",
412
- "conditional-rules": "Conditional rules",
413
- main: "",
414
- }[sheetPanel]}
415
- {sheetPanel === "filter" && (
416
- <i className="fa-light fa-circle-question text-xs text-muted-foreground" aria-hidden="true" />
417
- )}
418
- </SheetTitle>
419
- {sheetPanel === "filter" && (
420
- <p
421
- className="text-xs text-muted-foreground mt-0.5"
422
- aria-live="polite"
423
- aria-atomic="true"
424
- >
425
- {activeFilters.length === 0
426
- ? `Showing all ${filteredRows} rows`
427
- : `${filteredRows} of ${totalRows} rows match · ${activeFilters.length} filter${activeFilters.length !== 1 ? "s" : ""} active`}
428
- </p>
429
- )}
430
- </div>
431
- </div>
432
- <Tip label="Close" side="bottom">
433
- <Button
434
- type="button"
435
- variant="ghost"
436
- size="icon-sm"
437
- className="shrink-0"
438
- aria-label="Close panel"
439
- onClick={() => onOpenChange(false)}
440
- >
441
- <i className="fa-light fa-xmark text-[13px]" aria-hidden="true" />
442
- </Button>
443
- </Tip>
444
- </div>
445
-
446
- <div className="flex-1 overflow-y-auto">
447
-
448
- {/* ── Table / Board display ── */}
449
- {sheetPanel === "table-display" && (
450
- <div className="p-4 space-y-5">
451
- {isBoardView ? (
452
- <p className="text-xs text-muted-foreground leading-relaxed">
453
- {dataListViewLabel("board")} groups rows into columns. Sort, filter, and column settings apply to the same dataset as other views (e.g. Table view).
454
- </p>
455
- ) : null}
456
-
457
- {isCalendarView ? (
458
- <p className="text-xs text-muted-foreground leading-relaxed">
459
- {dataListViewLabel("calendar")} uses the same filtered rows as table and board. Scroll the main calendar vertically to move between months; use the summary panel for a mini month picker and event list.
460
- </p>
461
- ) : null}
462
-
463
- {isBoardView && boardGroupByColumnOptions && boardGroupByColumnOptions.length > 1 ? (
464
- <div className="flex items-center justify-between gap-3 py-2">
465
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
466
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
467
- <i className="fa-light fa-table-columns text-[15px] text-secondary-foreground" aria-hidden="true" />
468
- </span>
469
- <div className="min-w-0">
470
- <p className="text-sm font-medium text-foreground leading-tight">Board columns</p>
471
- <p className="text-xs text-muted-foreground mt-0.5">Choose which field splits the board into swimlanes.</p>
472
- </div>
473
- </div>
474
- <Select
475
- value={
476
- boardGroupByColumnOptions.some(o => o.key === displayOptions.boardGroupByColumnKey)
477
- ? displayOptions.boardGroupByColumnKey
478
- : boardGroupByColumnOptions[0]!.key
479
- }
480
- onValueChange={v => onDisplayOptionsChange({ boardGroupByColumnKey: v })}
481
- >
482
- <SelectTrigger
483
- size="sm"
484
- className="w-[9.5rem] shrink-0"
485
- id="board-group-by-field"
486
- aria-label="Field for board columns"
487
- >
488
- <SelectValue />
489
- </SelectTrigger>
490
- <SelectContent align="end" className={PROPERTIES_SHEET_PORTAL_Z}>
491
- {boardGroupByColumnOptions.map(o => (
492
- <SelectItem key={o.key} value={o.key}>
493
- {o.label}
494
- </SelectItem>
495
- ))}
496
- </SelectContent>
497
- </Select>
498
- </div>
499
- ) : null}
500
-
501
- {viewSurface === "table" ? (
502
- <>
503
- <div>
504
- <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">Appearance</p>
505
- <div className="space-y-1">
506
- {([
507
- { id: "gridlines", icon: "fa-border-all", label: "Gridlines", checked: showGridlines, onChange: onShowGridlinesChange },
508
- { id: "pagination", icon: "fa-table-list", label: "Pagination", checked: pagination, onChange: onPaginationChange },
509
- ] as { id: string; icon: string; label: string; checked: boolean; onChange: (v: boolean) => void }[]).map(row => (
510
- <div key={row.id} className="flex items-center justify-between py-2">
511
- <div className="flex items-center gap-2.5 text-sm">
512
- <i className={`fa-light ${row.icon} text-muted-foreground w-4 text-center`} aria-hidden="true" />
513
- <label htmlFor={`toggle-${row.id}`} className="cursor-pointer select-none">{row.label}</label>
514
- </div>
515
- <ToggleSwitch id={`toggle-${row.id}`} checked={row.checked} onChange={row.onChange} />
516
- </div>
517
- ))}
518
- </div>
519
- </div>
520
-
521
- <div className="border-t border-border pt-4">
522
- <SelectionTileGrid<RowHeight>
523
- sectionLabel="Row height"
524
- options={ROW_HEIGHT_TILES}
525
- columns={3}
526
- value={rowHeight}
527
- onValueChange={onRowHeightChange}
528
- interaction="button"
529
- idPrefix="row-height"
530
- />
531
- </div>
532
- </>
533
- ) : null}
534
-
535
- <div
536
- className={cn(
537
- "space-y-3",
538
- (viewSurface === "board" || viewSurface === "table" || isCalendarView) &&
539
- "border-t border-border pt-4",
540
- )}
541
- >
542
- <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">Display options</p>
543
- <div className="space-y-1">
544
- {isBoardView && (
545
- <div className="flex items-center justify-between gap-2 py-2">
546
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
547
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
548
- <i className="fa-light fa-file-lines text-[15px] text-secondary-foreground" aria-hidden="true" />
549
- </span>
550
- <div className="min-w-0">
551
- <p className="text-sm font-medium text-foreground leading-tight">Line count</p>
552
- </div>
553
- </div>
554
- <Select
555
- value={String(displayOptions.boardLineCount)}
556
- onValueChange={v =>
557
- onDisplayOptionsChange({ boardLineCount: Number(v) as 1 | 2 | 3 })}
558
- >
559
- <SelectTrigger size="sm" className="w-[6.5rem] shrink-0" id="board-line-count" aria-label="Line count">
560
- <SelectValue />
561
- </SelectTrigger>
562
- <SelectContent align="end" className={PROPERTIES_SHEET_PORTAL_Z}>
563
- <SelectItem value="1">1 line</SelectItem>
564
- <SelectItem value="2">2 lines</SelectItem>
565
- <SelectItem value="3">3 lines</SelectItem>
566
- </SelectContent>
567
- </Select>
568
- </div>
569
- )}
570
-
571
- {viewSurface === "table" && (
572
- <div className="flex items-center justify-between gap-2 py-2">
573
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
574
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
575
- <i className="fa-light fa-font text-[15px] text-secondary-foreground" aria-hidden="true" />
576
- </span>
577
- <div className="min-w-0">
578
- <p className="text-sm font-medium text-foreground leading-tight">Table title</p>
579
- <p className="text-xs text-muted-foreground mt-0.5">Show the page heading and subtitle.</p>
580
- </div>
581
- </div>
582
- <ToggleSwitch
583
- id="toggle-view-title"
584
- checked={displayOptions.showViewTitle}
585
- onChange={v => onDisplayOptionsChange({ showViewTitle: v })}
586
- />
587
- </div>
588
- )}
589
-
590
- <div className="flex items-center justify-between gap-2 py-2">
591
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
592
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
593
- <i className="fa-light fa-table-columns text-[15px] text-secondary-foreground" aria-hidden="true" />
594
- </span>
595
- <div className="min-w-0">
596
- <p className="text-sm font-medium text-foreground leading-tight">Column labels</p>
597
- {viewSurface === "table" ? (
598
- <p className="text-xs text-muted-foreground mt-0.5">Column headers in the table.</p>
599
- ) : viewSurface === "list" ? (
600
- <p className="text-xs text-muted-foreground mt-0.5">Column headers in the list.</p>
601
- ) : null}
602
- </div>
603
- </div>
604
- <ToggleSwitch
605
- id="toggle-column-labels"
606
- checked={displayOptions.showColumnLabels}
607
- onChange={v => onDisplayOptionsChange({ showColumnLabels: v })}
608
- />
609
- </div>
610
-
611
- {isBoardView && (
612
- <>
613
- <div className="flex items-center justify-between gap-2 py-2">
614
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
615
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
616
- <i className="fa-light fa-hashtag text-[15px] text-secondary-foreground" aria-hidden="true" />
617
- </span>
618
- <div className="min-w-0">
619
- <p className="text-sm font-medium text-foreground leading-tight">Column counts</p>
620
- </div>
621
- </div>
622
- <ToggleSwitch
623
- id="toggle-board-counts"
624
- checked={displayOptions.showBoardColumnCounts}
625
- onChange={v => onDisplayOptionsChange({ showBoardColumnCounts: v })}
626
- />
627
- </div>
628
-
629
- <div className="flex items-center justify-between gap-2 py-2">
630
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
631
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
632
- <i className="fa-light fa-square-plus text-[15px] text-secondary-foreground" aria-hidden="true" />
633
- </span>
634
- <div className="min-w-0">
635
- <p className="text-sm font-medium text-foreground leading-tight">Above new card button</p>
636
- </div>
637
- </div>
638
- <ToggleSwitch
639
- id="toggle-new-card-above"
640
- checked={displayOptions.boardNewCardAbove}
641
- onChange={v => onDisplayOptionsChange({ boardNewCardAbove: v })}
642
- />
643
- </div>
644
- </>
645
- )}
646
-
647
- {isCalendarView && (
648
- <>
649
- <div className="flex items-center justify-between gap-2 py-2">
650
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
651
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
652
- <i className="fa-light fa-sidebar text-[15px] text-secondary-foreground" aria-hidden="true" />
653
- </span>
654
- <div className="min-w-0">
655
- <p className="text-sm font-medium text-foreground leading-tight">Summary panel</p>
656
- <p className="text-xs text-muted-foreground mt-0.5">
657
- Mini month, event list, and layout on the left.
658
- </p>
659
- </div>
660
- </div>
661
- <ToggleSwitch
662
- id="toggle-calendar-summary"
663
- checked={displayOptions.showCalendarSummaryPanel}
664
- onChange={v => onDisplayOptionsChange({ showCalendarSummaryPanel: v })}
665
- />
666
- </div>
667
- <div className="pt-2">
668
- <SelectionTileGrid
669
- sectionLabel="Main calendar layout"
670
- options={CALENDAR_MAIN_VIEW_TILES.map(t => ({
671
- value: t.value,
672
- label: t.label,
673
- icon: t.icon,
674
- }))}
675
- columns={2}
676
- value={displayOptions.calendarMainView}
677
- onValueChange={v => onDisplayOptionsChange({ calendarMainView: v })}
678
- interaction="button"
679
- idPrefix="props-calendar-main-view"
680
- />
681
- </div>
682
- </>
683
- )}
684
-
685
- {(viewSurface === "table" || viewSurface === "list") && (
686
- <div className="flex items-center justify-between gap-2 py-2">
687
- <div className="flex items-center gap-2.5 min-w-0 flex-1">
688
- <span className="inline-flex items-center justify-center size-9 rounded-lg bg-secondary border border-border shrink-0">
689
- <i className="fa-light fa-magnifying-glass text-[15px] text-secondary-foreground" aria-hidden="true" />
690
- </span>
691
- <div className="min-w-0">
692
- <p className="text-sm font-medium text-foreground leading-tight">Search</p>
693
- <p className="text-xs text-muted-foreground mt-0.5">Toolbar search for this view.</p>
694
- </div>
695
- </div>
696
- <ToggleSwitch
697
- id="toggle-toolbar-search"
698
- checked={displayOptions.showToolbarSearch}
699
- onChange={v => onDisplayOptionsChange({ showToolbarSearch: v })}
700
- />
701
- </div>
702
- )}
703
- </div>
704
- </div>
705
- </div>
706
- )}
707
-
708
- {/* ── Filter ── */}
709
- {sheetPanel === "filter" && (
710
- <div className="px-4 py-4 space-y-2">
711
- {activeFilters.length === 0 ? (
712
- <div className="rounded-xl border border-border bg-muted/40 p-4 space-y-3">
713
- <div className="flex items-center gap-2">
714
- <span className="inline-flex items-center justify-center size-7 rounded-lg bg-background border border-border shrink-0">
715
- <i className="fa-light fa-filter text-muted-foreground text-xs" aria-hidden="true" />
716
- </span>
717
- <p className="text-sm font-medium text-foreground">No filters yet</p>
718
- </div>
719
- <p className="text-xs text-muted-foreground leading-relaxed">
720
- Use filters to show only the rows you need. With multiple filters, use <span className="font-medium text-foreground/80">and</span> or <span className="font-medium text-foreground/80">or</span> between them to control how they combine.
721
- </p>
722
- <div className="space-y-1.5">
723
- {[
724
- { icon: "fa-circle-1", text: "Click \"Add filter\" below" },
725
- { icon: "fa-circle-2", text: "Choose a field to filter by" },
726
- { icon: "fa-circle-3", text: "Pick at least one value — the grid updates immediately" },
727
- ].map(step => (
728
- <div key={step.icon} className="flex items-center gap-2 text-xs text-muted-foreground">
729
- <i className={`fa-light ${step.icon} text-muted-foreground text-xs shrink-0`} aria-hidden="true" />
730
- {step.text}
731
- </div>
732
- ))}
733
- </div>
734
- </div>
735
- ) : (
736
- <>
737
- {activeFilters.map((f, idx) => {
738
- const fieldDef = filterFields.find(fd => fd.key === f.fieldKey)
739
- if (!fieldDef) return null
740
- const leftId = idx > 0 ? activeFilters[idx - 1]!.id : null
741
- const connector = leftId ? getFilterConnector(leftId) : "and"
742
- return (
743
- <React.Fragment key={f.id}>
744
- {idx > 0 && leftId && (
745
- <div className="flex items-center gap-2 py-1">
746
- <div className="flex-1 h-px bg-border" aria-hidden="true" />
747
- <Tip label="Click to switch: AND — every filter must match; OR — any matching filter is enough." side="top">
748
- <button
749
- type="button"
750
- onClick={() => onToggleFilterConnector(leftId)}
751
- className={cn(
752
- "shrink-0 rounded-md border px-2.5 py-0.5 text-xs font-semibold uppercase tracking-wide transition-colors",
753
- "border-border bg-muted/40 text-muted-foreground hover:bg-interactive-hover hover:text-interactive-hover-foreground",
754
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
755
- )}
756
- aria-label={
757
- connector === "and"
758
- ? "Filters are combined with AND. Click to use OR instead."
759
- : "Filters are combined with OR. Click to use AND instead."
760
- }
761
- >
762
- {connector}
763
- </button>
764
- </Tip>
765
- <div className="flex-1 h-px bg-border" aria-hidden="true" />
766
- </div>
767
- )}
768
- <DrawerFilterCard
769
- filter={f}
770
- fieldDef={fieldDef}
771
- expanded={drawerExpandedFilters.has(f.id)}
772
- onToggleExpand={() => onDrawerExpandedFiltersChange(prev => {
773
- const next = new Set(prev)
774
- if (next.has(f.id)) next.delete(f.id)
775
- else next.add(f.id)
776
- return next
777
- })}
778
- onUpdate={onUpdateFilter}
779
- onRemove={id => {
780
- onRemoveFilter(id)
781
- onDrawerExpandedFiltersChange(prev => { const next = new Set(prev); next.delete(id); return next })
782
- }}
783
- renderOptionLabel={value => renderFilterOptionValue?.(f.fieldKey, value)}
784
- />
785
- </React.Fragment>
786
- )
787
- })}
788
- </>
789
- )}
790
-
791
- {/* Add filter + Remove all */}
792
- <div className="flex items-center gap-2 pt-2">
793
- <DropdownMenu modal={false}>
794
- <DropdownMenuTrigger asChild>
795
- <Button
796
- type="button"
797
- variant="outline"
798
- className="flex-1 gap-1.5 h-8 border-dashed text-muted-foreground"
799
- >
800
- <i className="fa-light fa-plus text-xs" aria-hidden="true" />
801
- Add filter
802
- </Button>
803
- </DropdownMenuTrigger>
804
- <DropdownMenuContent align="start" className={PROPERTIES_SHEET_PORTAL_Z}>
805
- <DropdownMenuLabel className="text-xs">Filter by field</DropdownMenuLabel>
806
- <DropdownMenuSeparator />
807
- {filterFields.map(f => (
808
- <DropdownMenuItem key={f.key} onSelect={() => onAddFilter(f.key)}>
809
- <i className={`fa-light ${f.icon}`} aria-hidden="true" />
810
- {f.label}
811
- </DropdownMenuItem>
812
- ))}
813
- </DropdownMenuContent>
814
- </DropdownMenu>
815
- {activeFilters.length > 0 && (
816
- <Button
817
- type="button"
818
- variant="ghost"
819
- size="sm"
820
- className="shrink-0 text-destructive hover:text-destructive hover:bg-destructive/10"
821
- onClick={() => { activeFilters.forEach(f => onRemoveFilter(f.id)); onDrawerExpandedFiltersChange(new Set()) }}
822
- >
823
- Remove all
824
- </Button>
825
- )}
826
- </div>
827
-
828
- {/* Enable filter bar toggle */}
829
- <div className="flex items-start justify-between gap-3 pt-3 mt-1 border-t border-border">
830
- <div>
831
- <label htmlFor="toggle-filter-bar" className="text-sm font-medium text-foreground cursor-pointer">Enable filter bar</label>
832
- <p className="text-xs text-muted-foreground mt-0.5">Show filters above the table.</p>
833
- </div>
834
- <ToggleSwitch id="toggle-filter-bar" checked={filterBarVisible} onChange={onFilterBarVisibleChange} />
835
- </div>
836
- </div>
837
- )}
838
-
839
- {/* ── Sort ── */}
840
- {sheetPanel === "sort" && (
841
- <div className="px-4 py-4 space-y-2">
842
- {sortRules.length === 0 ? (
843
- /* Empty state */
844
- <div className="rounded-xl border border-dashed border-border bg-muted/30 px-4 py-6 text-center space-y-2">
845
- <div className="inline-flex items-center justify-center size-9 rounded-lg bg-muted mb-1">
846
- <i className="fa-light fa-arrow-up-arrow-down text-muted-foreground text-[16px]" aria-hidden="true" />
847
- </div>
848
- <p className="text-sm font-medium text-foreground">No sorts applied</p>
849
- <p className="text-xs text-muted-foreground leading-relaxed">
850
- Add a sort rule to order rows by any field. Multiple rules are applied in priority order.
851
- </p>
852
- <div className="space-y-1.5 text-left pt-1">
853
- {[
854
- { icon: "fa-circle-1", text: "Click \"Add sort\" below" },
855
- { icon: "fa-circle-2", text: "Choose a field to sort by" },
856
- { icon: "fa-circle-3", text: "Toggle ascending or descending" },
857
- ].map(step => (
858
- <div key={step.icon} className="flex items-center gap-2 text-xs text-muted-foreground">
859
- <i className={`fa-light ${step.icon} text-muted-foreground text-xs shrink-0`} aria-hidden="true" />
860
- {step.text}
861
- </div>
862
- ))}
863
- </div>
864
- </div>
865
- ) : (
866
- sortRules.map((rule, idx) => {
867
- const dragProps = sortDrag.getItemProps(rule.id)
868
- return (
869
- <React.Fragment key={rule.id}>
870
- {idx > 0 && (
871
- <div className="flex items-center gap-2 py-0.5">
872
- <div className="flex-1 h-px bg-border" />
873
- <span className="text-xs font-medium text-muted-foreground px-1">then by</span>
874
- <div className="flex-1 h-px bg-border" />
875
- </div>
876
- )}
877
- <div
878
- {...dragProps}
879
- className={cn(
880
- "transition-all",
881
- dragProps["data-dragging"] && "opacity-40",
882
- dragProps["data-over"] && "ring-2 ring-ring bg-accent/30 rounded-lg",
883
- )}
884
- >
885
- <DrawerSortCard
886
- rule={rule}
887
- fieldLabel={resolveColumnLabel(rule.fieldKey)}
888
- isPrimary={idx === 0}
889
- onRemove={() => onRemoveSortRule(rule.id)}
890
- onToggleDir={() => onToggleSortDir(rule.id)}
891
- />
892
- </div>
893
- </React.Fragment>
894
- )
895
- })
896
- )}
897
-
898
- {/* Add sort + Remove all */}
899
- <div className="flex items-center gap-2 pt-2">
900
- <DropdownMenu modal={false}>
901
- <DropdownMenuTrigger asChild>
902
- <Button
903
- type="button"
904
- variant="outline"
905
- className="flex-1 gap-1.5 h-8 border-dashed text-muted-foreground"
906
- >
907
- <i className="fa-light fa-plus text-xs" aria-hidden="true" />
908
- Add sort
909
- </Button>
910
- </DropdownMenuTrigger>
911
- <DropdownMenuContent align="start" className={PROPERTIES_SHEET_PORTAL_Z}>
912
- <DropdownMenuLabel className="text-xs">Sort by field</DropdownMenuLabel>
913
- <DropdownMenuSeparator />
914
- {sortFieldList.filter(f => !sortRules.some(r => r.fieldKey === f.key)).map(col => (
915
- <DropdownMenuItem key={col.key} onSelect={() => onAddSortRule(col.key)}>
916
- <i className="fa-light fa-arrow-up-arrow-down text-xs" aria-hidden="true" />
917
- {col.label}
918
- </DropdownMenuItem>
919
- ))}
920
- {sortFieldList.filter(f => !sortRules.some(r => r.fieldKey === f.key)).length === 0 && (
921
- <p className="px-2 py-1.5 text-xs text-muted-foreground">All fields added</p>
922
- )}
923
- </DropdownMenuContent>
924
- </DropdownMenu>
925
- {sortRules.length > 0 && (
926
- <Button
927
- type="button"
928
- variant="ghost"
929
- size="sm"
930
- className="shrink-0 text-destructive hover:text-destructive hover:bg-destructive/10"
931
- onClick={() => onSortRulesChange([])}
932
- >
933
- Remove all
934
- </Button>
935
- )}
936
- </div>
937
- </div>
938
- )}
939
-
940
- {/* ── Group ── */}
941
- {sheetPanel === "group" && (
942
- <div className="p-4 space-y-2">
943
- <p className="text-xs text-muted-foreground mb-3">
944
- {groupBy ? `Grouped by ${resolveColumnLabel(groupBy)}.` : "No grouping applied."}
945
- </p>
946
- <Button
947
- type="button"
948
- variant="ghost"
949
- onClick={() => onGroupByChange(null)}
950
- className={cn("w-full justify-start gap-2 px-3 py-2 h-auto text-sm font-normal",
951
- !groupBy ? "bg-accent text-accent-foreground font-medium" : "text-muted-foreground",
952
- )}
953
- >
954
- <i className="fa-light fa-ban text-xs" aria-hidden="true" />
955
- None
956
- </Button>
957
- {groupFieldList.map(col => (
958
- <Button
959
- key={col.key}
960
- type="button"
961
- variant="ghost"
962
- onClick={() => onGroupByChange(groupBy === col.key ? null : col.key)}
963
- className={cn("w-full justify-start gap-2 px-3 py-2 h-auto text-sm font-normal",
964
- groupBy === col.key ? "bg-accent text-accent-foreground font-medium" : "",
965
- )}
966
- >
967
- <i className="fa-light fa-layer-group text-xs text-muted-foreground" aria-hidden="true" />
968
- {col.label}
969
- {groupBy === col.key && <i className="fa-solid fa-check text-accent-foreground text-xs ml-auto" aria-hidden="true" />}
970
- </Button>
971
- ))}
972
- </div>
973
- )}
974
-
975
- {/* ── Columns ── */}
976
- {sheetPanel === "columns" && (
977
- <div className="px-4 py-4">
978
- {isBoardView ? (
979
- <p className="text-xs text-muted-foreground mb-3">
980
- Column visibility and order apply when you use Table view. They are saved with this tab.
981
- </p>
982
- ) : null}
983
- <p className="text-xs text-muted-foreground mb-3">
984
- {hiddenCols.size === 0
985
- ? "All columns visible. Drag to reorder."
986
- : `${hiddenCols.size} column${hiddenCols.size !== 1 ? "s" : ""} hidden. Drag handle to reorder.`}
987
- </p>
988
- <div className="space-y-0.5" role="list" aria-label="Column order and visibility">
989
- {orderable.map((key, idx, arr) => {
990
- const dragProps = colDrag.getItemProps(key)
991
- return (
992
- <ColumnRow
993
- key={key}
994
- label={resolveColumnLabel(key)}
995
- isFirst={idx === 0}
996
- isLast={idx === arr.length - 1}
997
- visible={!hiddenCols.has(key)}
998
- onToggleVisible={() => onToggleColVisibility(key)}
999
- onMoveUp={() => onMoveCol(key, "up")}
1000
- onMoveDown={() => onMoveCol(key, "down")}
1001
- draggable={dragProps.draggable}
1002
- onDragStart={dragProps.onDragStart}
1003
- onDragOver={dragProps.onDragOver}
1004
- onDrop={dragProps.onDrop}
1005
- onDragEnd={dragProps.onDragEnd}
1006
- isDragging={dragProps["data-dragging"]}
1007
- isOver={dragProps["data-over"]}
1008
- />
1009
- )
1010
- })}
1011
- </div>
1012
- </div>
1013
- )}
1014
-
1015
- {/* ── Conditional rules ── */}
1016
- {sheetPanel === "conditional-rules" && (
1017
- <ConditionalRulesPanel
1018
- filterFields={filterFields}
1019
- rules={conditionalRules}
1020
- onAdd={onAddConditionalRule}
1021
- onRemove={onRemoveConditionalRule}
1022
- onUpdate={onUpdateConditionalRule}
1023
- renderFilterOptionValue={renderFilterOptionValue}
1024
- />
1025
- )}
1026
-
1027
- </div>
1028
- </>
1029
- )}
1030
-
1031
- </SheetContent>
1032
- </Sheet>
1033
- )
1034
- }
1035
-
1036
- // ─────────────────────────────────────────────────────────────────────────────
1037
- // ConditionalRulesPanel — same DrawerFilterCard as filters (incl. operator cycle);
1038
- // highlight color lives inside the card. Adding a rule expands only that card (like
1039
- // add filter from drawer). No And/Or connectors.
1040
- // ─────────────────────────────────────────────────────────────────────────────
1041
-
1042
- function ConditionalRulesPanel({
1043
- filterFields,
1044
- rules,
1045
- onAdd,
1046
- onRemove,
1047
- onUpdate,
1048
- renderFilterOptionValue,
1049
- }: {
1050
- filterFields: FilterFieldDef[]
1051
- rules: ConditionalRule[]
1052
- onAdd: (rule: Omit<ConditionalRule, "id">) => void
1053
- onRemove: (id: string) => void
1054
- onUpdate: (id: string, patch: Partial<ConditionalRule>) => void
1055
- renderFilterOptionValue?: (fieldKey: string, value: string) => React.ReactNode
1056
- }) {
1057
- const [expandedIds, setExpandedIds] = React.useState<Set<string>>(() => new Set())
1058
-
1059
- const prevLenRef = React.useRef(rules.length)
1060
- React.useEffect(() => {
1061
- if (rules.length > prevLenRef.current && rules.length > 0) {
1062
- const last = rules[rules.length - 1]
1063
- setExpandedIds(new Set([last.id]))
1064
- }
1065
- prevLenRef.current = rules.length
1066
- }, [rules])
1067
-
1068
- function toggleExpanded(id: string) {
1069
- setExpandedIds(prev => {
1070
- const next = new Set(prev)
1071
- if (next.has(id)) next.delete(id)
1072
- else next.add(id)
1073
- return next
1074
- })
1075
- }
1076
-
1077
- return (
1078
- <div className="px-4 py-4 space-y-2">
1079
- {rules.length === 0 ? (
1080
- <div className="rounded-xl border border-dashed border-border bg-muted/30 px-4 py-6 text-center space-y-2">
1081
- <div className="inline-flex items-center justify-center size-9 rounded-lg bg-muted mb-1">
1082
- <i className="fa-light fa-palette text-muted-foreground text-[16px]" aria-hidden="true" />
1083
- </div>
1084
- <p className="text-sm font-medium text-foreground">No rules yet</p>
1085
- <p className="text-xs text-muted-foreground leading-relaxed">
1086
- Highlight cells with a background color based on their value.
1087
- </p>
1088
- </div>
1089
- ) : (
1090
- <div className="space-y-2">
1091
- {rules.map(rule => {
1092
- const fd = filterFields.find(f => f.key === rule.fieldKey)
1093
- if (!fd) return null
1094
- return (
1095
- <DrawerFilterCard
1096
- key={rule.id}
1097
- variant="conditional"
1098
- filter={rule}
1099
- fieldDef={fd}
1100
- expanded={expandedIds.has(rule.id)}
1101
- onToggleExpand={() => toggleExpanded(rule.id)}
1102
- onUpdate={onUpdate}
1103
- onRemove={id => {
1104
- onRemove(id)
1105
- setExpandedIds(prev => {
1106
- const next = new Set(prev)
1107
- next.delete(id)
1108
- return next
1109
- })
1110
- }}
1111
- renderOptionLabel={value => renderFilterOptionValue?.(rule.fieldKey, value)}
1112
- />
1113
- )
1114
- })}
1115
- </div>
1116
- )}
1117
-
1118
- <div className="flex items-center gap-2 pt-2">
1119
- <DropdownMenu modal={false}>
1120
- <DropdownMenuTrigger asChild>
1121
- <Button
1122
- type="button"
1123
- variant="outline"
1124
- className="flex-1 gap-1.5 h-8 border-dashed text-muted-foreground"
1125
- >
1126
- <i className="fa-light fa-plus text-xs" aria-hidden="true" />
1127
- Add rule
1128
- </Button>
1129
- </DropdownMenuTrigger>
1130
- <DropdownMenuContent align="start" className={PROPERTIES_SHEET_PORTAL_Z}>
1131
- <DropdownMenuLabel className="text-xs">Rule for column</DropdownMenuLabel>
1132
- <DropdownMenuSeparator />
1133
- {filterFields.map(f => (
1134
- <DropdownMenuItem
1135
- key={f.key}
1136
- onSelect={() => onAdd({
1137
- fieldKey: f.key,
1138
- operator: f.operators[0],
1139
- values: [],
1140
- bgColor: RULE_COLORS[0].bg,
1141
- })}
1142
- >
1143
- <i className={`fa-light ${f.icon}`} aria-hidden="true" />
1144
- {f.label}
1145
- </DropdownMenuItem>
1146
- ))}
1147
- </DropdownMenuContent>
1148
- </DropdownMenu>
1149
- {rules.length > 0 && (
1150
- <Button
1151
- type="button"
1152
- variant="ghost"
1153
- size="sm"
1154
- className="shrink-0 text-destructive hover:text-destructive hover:bg-destructive/10"
1155
- onClick={() => {
1156
- rules.forEach(r => onRemove(r.id))
1157
- setExpandedIds(new Set())
1158
- }}
1159
- >
1160
- Remove all
1161
- </Button>
1162
- )}
1163
- </div>
1164
- </div>
1165
- )
1166
- }
1
+ export * from "@exxatdesignux/ui/components/table-properties/drawer"