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