@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,758 +1 @@
1
- "use client"
2
-
3
- // ─────────────────────────────────────────────────────────────────────────────
4
- // useTableState — all non-display state shared by DataTable and DataTablePaginated
5
- // ─────────────────────────────────────────────────────────────────────────────
6
-
7
- import * as React from "react"
8
- import type { RowHeight } from "@/lib/row-height"
9
- import type { ColumnDef, SortDir } from "./types"
10
- import type { ActiveFilter, FilterOperator, SortRule } from "@/components/table-properties/types"
11
- import { parseRowDateToYmd } from "@/lib/date-filter"
12
-
13
- let _filterId = 0
14
- function nextFilterId() { return `f-${++_filterId}` }
15
-
16
- /**
17
- * “Reflow” / high-zoom short viewport. At 200% zoom a 1080p monitor’s CSS
18
- * height is ≈ 540px — `500px` was too low and never disabled pins. 640px
19
- * catches typical 200% cases and small laptop tops without breaking `500px` flows.
20
- * Column stickies + edge shadows harm reflow (WCAG 1.4.10).
21
- */
22
- const REFLOW_VIEWPORT_MQ = "(max-height: 640px)"
23
-
24
- function subscribeReflowViewport(callback: () => void) {
25
- if (typeof window === "undefined") return () => {}
26
- const mql = window.matchMedia(REFLOW_VIEWPORT_MQ)
27
- mql.addEventListener("change", callback)
28
- return () => mql.removeEventListener("change", callback)
29
- }
30
- function getReflowViewportSnapshot() {
31
- if (typeof window === "undefined") return false
32
- return window.matchMedia(REFLOW_VIEWPORT_MQ).matches
33
- }
34
- function getServerReflowViewportSnapshot() {
35
- return false
36
- }
37
-
38
- // ─────────────────────────────────────────────────────────────────────────────
39
- // Helpers
40
- // ─────────────────────────────────────────────────────────────────────────────
41
-
42
- function digitsOnly(s: string): string {
43
- return s.replace(/\D/g, "")
44
- }
45
-
46
- /** Build the default widths map from column defs */
47
- function buildDefaultWidths<TData>(columns: ColumnDef<TData>[]): Record<string, number> {
48
- const map: Record<string, number> = {}
49
- for (const col of columns) {
50
- if (col.width !== undefined) map[col.key] = col.width
51
- }
52
- return map
53
- }
54
-
55
- /** Build the initial pin state from column defs */
56
- function buildDefaultPins<TData>(columns: ColumnDef<TData>[]): Record<string, "left" | "right"> {
57
- const map: Record<string, "left" | "right"> = {}
58
- for (const col of columns) {
59
- if (col.defaultPin) map[col.key] = col.defaultPin
60
- }
61
- return map
62
- }
63
-
64
- function compareUnknownSort(a: unknown, b: unknown): number {
65
- if (a === b) return 0
66
- if (a == null && b == null) return 0
67
- if (a == null) return 1
68
- if (b == null) return -1
69
- if (typeof a === "number" && typeof b === "number") return a < b ? -1 : a > b ? 1 : 0
70
- if (typeof a === "string" && typeof b === "string") return a < b ? -1 : a > b ? 1 : 0
71
- const as = String(a)
72
- const bs = String(b)
73
- return as < bs ? -1 : as > bs ? 1 : 0
74
- }
75
-
76
- /** Build the locked-pin set (columns that can never be unpinned) */
77
- function buildLockedPins<TData>(columns: ColumnDef<TData>[]): Record<string, "left" | "right"> {
78
- const map: Record<string, "left" | "right"> = {}
79
- for (const col of columns) {
80
- if (col.lockPin && col.defaultPin) map[col.key] = col.defaultPin
81
- }
82
- return map
83
- }
84
-
85
- // ─────────────────────────────────────────────────────────────────────────────
86
- // Hook
87
- // ─────────────────────────────────────────────────────────────────────────────
88
-
89
- export function useTableState<TData extends Record<string, unknown>>(
90
- data: TData[],
91
- columns: ColumnDef<TData>[],
92
- defaultSort?: { key: string; dir: SortDir },
93
- paginationOverride?: { page: number; pageSize: number },
94
- /**
95
- * When defined (including `""`), toolbar search is synced from the URL (`?q=`).
96
- * Use `searchParams.get("q") ?? ""` on question bank list routes; omit for other hubs.
97
- */
98
- syncedSearchFromUrl?: string,
99
- ) {
100
- // ── Sort ──────────────────────────────────────────────────────────────────
101
- const [sortRules, setSortRules] = React.useState<SortRule[]>(() => {
102
- if (defaultSort) {
103
- return [{ id: "sort-default", fieldKey: defaultSort.key, direction: defaultSort.dir }]
104
- }
105
- return []
106
- })
107
-
108
- const primarySort = sortRules[0] ?? null
109
- const sortKey: string = primarySort?.fieldKey ?? ""
110
- const sortDir: SortDir = primarySort?.direction ?? "asc"
111
-
112
- const addSortRule = React.useCallback((fieldKey: string) => {
113
- setSortRules(prev => {
114
- if (prev.some(r => r.fieldKey === fieldKey)) return prev
115
- // New drawer sorts are primary (same as column-header sort), not trailing.
116
- return [{ id: `sort-${Date.now()}`, fieldKey, direction: "asc" }, ...prev]
117
- })
118
- }, [setSortRules])
119
-
120
- const removeSortRule = React.useCallback((id: string) => {
121
- setSortRules(prev => prev.filter(r => r.id !== id))
122
- }, [setSortRules])
123
-
124
- const toggleSortDir = React.useCallback((id: string) => {
125
- setSortRules(prev => prev.map(r =>
126
- r.id === id ? { ...r, direction: r.direction === "asc" ? "desc" : "asc" } : r
127
- ))
128
- }, [setSortRules])
129
-
130
- const handleSortByKey = React.useCallback((colKey: string) => {
131
- setSortRules(prev => {
132
- const idx = prev.findIndex(r => r.fieldKey === colKey)
133
- if (idx === 0) {
134
- return prev.map((r, i) => i === 0 ? { ...r, direction: r.direction === "asc" ? "desc" : "asc" } : r)
135
- }
136
- const filtered = prev.filter(r => r.fieldKey !== colKey)
137
- return [{ id: `sort-${Date.now()}`, fieldKey: colKey, direction: "asc" }, ...filtered]
138
- })
139
- }, [setSortRules])
140
-
141
- // ── Filters ───────────────────────────────────────────────────────────────
142
- const [search, setSearch] = React.useState(() =>
143
- syncedSearchFromUrl !== undefined ? syncedSearchFromUrl.trim() : "",
144
- )
145
- const [searchOpen, setSearchOpen] = React.useState(() =>
146
- syncedSearchFromUrl !== undefined && Boolean(syncedSearchFromUrl.trim()),
147
- )
148
- const searchRef = React.useRef<HTMLInputElement>(null)
149
- const [activeFilters, setActiveFilters] = React.useState<ActiveFilter[]>([])
150
- const [filterConnectors, setFilterConnectors] = React.useState<Record<string, "and" | "or">>({})
151
- const [openFilterId, setOpenFilterId] = React.useState<string | null>(null)
152
- const [filterBarVisible, setFilterBarVisible] = React.useState(true)
153
- const [drawerExpandedFilters, setDrawerExpandedFilters] = React.useState<Set<string>>(new Set())
154
-
155
- React.useEffect(() => {
156
- if (syncedSearchFromUrl === undefined) return
157
- const next = syncedSearchFromUrl.trim()
158
- setSearch(next)
159
- setSearchOpen(next.length > 0)
160
- }, [syncedSearchFromUrl])
161
-
162
- const toggleConnector = React.useCallback((leftId: string) => {
163
- setFilterConnectors(prev => ({ ...prev, [leftId]: prev[leftId] === "or" ? "and" : "or" }))
164
- }, [setFilterConnectors])
165
-
166
- function getConnector(leftId: string): "and" | "or" {
167
- return filterConnectors[leftId] ?? "and"
168
- }
169
-
170
- const addFilter = React.useCallback((fieldKey: string, fromDrawer = false) => {
171
- const col = columns.find(c => c.key === fieldKey)
172
- if (!col?.filter) return
173
- const id = nextFilterId()
174
- const f = col.filter
175
- const firstOperator: FilterOperator = (() => {
176
- if (f.type === "select" || f.type === "date") {
177
- const pick = f.operators?.find(o => o === "is" || o === "is_not")
178
- return pick ?? "is"
179
- }
180
- return f.operators?.[0] ?? "contains"
181
- })()
182
- const newFilter: ActiveFilter = { id, fieldKey, operator: firstOperator, values: [] }
183
- setActiveFilters(prev => [...prev, newFilter])
184
- if (fromDrawer) {
185
- setDrawerExpandedFilters(() => new Set([id]))
186
- // Keep toolbar pills hidden until a value is chosen — avoids mounting every
187
- // FilterPill (heavy) on each drawer "Add filter" click.
188
- } else {
189
- setOpenFilterId(id)
190
- setFilterBarVisible(true)
191
- }
192
- }, [columns, setActiveFilters, setDrawerExpandedFilters, setOpenFilterId, setFilterBarVisible])
193
-
194
- const updateFilter = React.useCallback((id: string, patch: Partial<ActiveFilter>) => {
195
- let shouldShowFilterBar = false
196
- setActiveFilters(prev => {
197
- const next = prev.map(f => {
198
- if (f.id !== id) return f
199
- const merged = { ...f, ...patch }
200
- const col = columns.find(c => c.key === merged.fieldKey)
201
- if (merged.values.length > 0) {
202
- shouldShowFilterBar =
203
- col?.filter?.type === "text"
204
- ? (merged.values[0] ?? "").trim().length > 0
205
- : true
206
- }
207
- return merged
208
- })
209
- return next
210
- })
211
- if (shouldShowFilterBar) setFilterBarVisible(true)
212
- }, [columns, setActiveFilters, setFilterBarVisible])
213
-
214
- const removeFilter = React.useCallback((id: string) => {
215
- // Use functional updates only — no stale-closure risk on activeFilters.
216
- setActiveFilters(prev => {
217
- const idx = prev.findIndex(f => f.id === id)
218
- const next = prev.filter(f => f.id !== id)
219
- setFilterConnectors(prevC => {
220
- const c = { ...prevC }
221
- if (idx > 0 && next.length > 0) {
222
- const leftId = prev[idx - 1].id
223
- c[leftId] = prevC[id] ?? prevC[leftId] ?? "and"
224
- }
225
- delete c[id]
226
- return c
227
- })
228
- return next
229
- })
230
- setOpenFilterId(prev => prev === id ? null : prev)
231
- }, [setActiveFilters, setFilterConnectors, setOpenFilterId])
232
-
233
- // ── Group by ──────────────────────────────────────────────────────────────
234
- const [groupBy, setGroupBy] = React.useState<string | null>(null)
235
-
236
- // ── Per-column quick-search ───────────────────────────────────────────────
237
- const [colMenuSearch, setColMenuSearch] = React.useState<Record<string, string>>({})
238
-
239
- // ── Selection ─────────────────────────────────────────────────────────────
240
- const [selected, setSelected] = React.useState<Set<string | number>>(new Set())
241
-
242
- // ── Column widths ─────────────────────────────────────────────────────────
243
- const [colWidths, setColWidths] = React.useState<Record<string, number>>(() => buildDefaultWidths(columns))
244
- const resizeRef = React.useRef<{ key: string; startX: number; startW: number } | null>(null)
245
-
246
- // ── Column order ──────────────────────────────────────────────────────────
247
- const [colOrder, setColOrder] = React.useState<string[]>(() => columns.map(c => c.key))
248
-
249
- // ── Column pins ───────────────────────────────────────────────────────────
250
- const [colPins, setColPins] = React.useState<Record<string, "left" | "right">>(() => buildDefaultPins(columns))
251
- const lockedPins = React.useMemo(() => buildLockedPins(columns), [columns])
252
-
253
- // ── Column wrap ───────────────────────────────────────────────────────────
254
- const [colWrap, setColWrap] = React.useState<Record<string, boolean>>({})
255
-
256
- // ── Drawer / display settings ─────────────────────────────────────────────
257
- const [sheetOpen, setSheetOpen] = React.useState(false)
258
- const [showGridlines, setShowGridlines] = React.useState(true)
259
- const [rowHeight, setRowHeight] = React.useState<RowHeight>("default")
260
- const [hiddenCols, setHiddenCols] = React.useState<Set<string>>(new Set())
261
-
262
- const toggleColVisibility = React.useCallback((key: string) => {
263
- setHiddenCols(prev => {
264
- const next = new Set(prev)
265
- if (next.has(key)) next.delete(key)
266
- else next.add(key)
267
- return next
268
- })
269
- }, [setHiddenCols])
270
-
271
- const moveCol = React.useCallback((key: string, dir: "up" | "down") => {
272
- setColOrder(prev => {
273
- const lockedLeft = columns.filter(c => c.lockPin && c.defaultPin === "left").map(c => c.key)
274
- const lockedRight = columns.filter(c => c.lockPin && c.defaultPin === "right").map(c => c.key)
275
- const orderable = prev.filter(k => !lockedLeft.includes(k) && !lockedRight.includes(k))
276
- const idx = orderable.indexOf(key)
277
- if (dir === "up" && idx <= 0) return prev
278
- if (dir === "down" && idx >= orderable.length - 1) return prev
279
- const next = [...orderable]
280
- const swap = dir === "up" ? idx - 1 : idx + 1
281
- ;[next[idx], next[swap]] = [next[swap], next[idx]]
282
- return [...lockedLeft, ...next, ...lockedRight]
283
- })
284
- }, [columns, setColOrder])
285
-
286
- // ── Drag-to-reorder ───────────────────────────────────────────────────────
287
- const draggedKey = React.useRef<string | null>(null)
288
- const [dragOverKey, setDragOverKey] = React.useState<string | null>(null)
289
-
290
- // ── Scroll / overflow ─────────────────────────────────────────────────────
291
- const scrollRef = React.useRef<HTMLDivElement>(null)
292
- const [scrolled, setScrolled] = React.useState(false)
293
- const [scrollEnd, setScrollEnd] = React.useState(false)
294
- const [isOverflowing, setIsOverflowing] = React.useState(false)
295
-
296
- const isReflowViewport = React.useSyncExternalStore(
297
- subscribeReflowViewport,
298
- getReflowViewportSnapshot,
299
- getServerReflowViewportSnapshot,
300
- )
301
-
302
- // ── Hovered row ───────────────────────────────────────────────────────────
303
- const [hoveredRow, setHoveredRow] = React.useState<string | number | null>(null)
304
-
305
- // ── Column lookup index (stable per `columns` reference) ─────────────────
306
- // The previous implementation called `columns.find(c => c.key === ...)` inside
307
- // every filter/sort comparator and every sticky-offset getter — for large
308
- // datasets that's O(rows × cols) per render. Map lookups make those O(1).
309
- const columnsByKey = React.useMemo(() => {
310
- const map = new Map<string, ColumnDef<TData>>()
311
- for (const col of columns) map.set(col.key, col)
312
- return map
313
- }, [columns])
314
-
315
- // Searchable text cache. Per row, concatenate every value into one
316
- // lower-cased blob ONCE and reuse it across keystrokes. Keyed by row
317
- // identity via WeakMap so it never holds onto rows the consumer dropped.
318
- const searchableTextCache = React.useRef<WeakMap<object, string>>(new WeakMap())
319
- const getSearchableText = React.useCallback((row: TData): string => {
320
- const cache = searchableTextCache.current
321
- const cached = cache.get(row)
322
- if (cached !== undefined) return cached
323
- let blob = ""
324
- for (const v of Object.values(row)) {
325
- if (v == null) continue
326
- blob += String(v).toLowerCase() + "\n"
327
- }
328
- cache.set(row, blob)
329
- return blob
330
- }, [])
331
-
332
- // Per-row per-column lower-cased value cache (column quick-search +
333
- // text-mask filters). One `Map` per row, lazily filled on first lookup.
334
- const lowerValueCache = React.useRef<WeakMap<object, Map<string, string>>>(new WeakMap())
335
- const getLowerValue = React.useCallback((row: TData, key: string): string => {
336
- const wm = lowerValueCache.current
337
- let perRow = wm.get(row)
338
- if (!perRow) {
339
- perRow = new Map()
340
- wm.set(row, perRow)
341
- }
342
- const cached = perRow.get(key)
343
- if (cached !== undefined) return cached
344
- const computed = String(row[key] ?? "").toLowerCase()
345
- perRow.set(key, computed)
346
- return computed
347
- }, [])
348
-
349
- // Reset the row-keyed caches whenever the dataset reference changes so we
350
- // don't pin stale strings for rows the consumer just replaced.
351
- React.useEffect(() => {
352
- searchableTextCache.current = new WeakMap()
353
- lowerValueCache.current = new WeakMap()
354
- }, [data])
355
-
356
- // ── Derived: filtered + sorted rows ──────────────────────────────────────
357
- const rows = React.useMemo(() => {
358
- let result = data.slice()
359
-
360
- const q = search.trim().toLowerCase()
361
- if (q) {
362
- result = result.filter(r => getSearchableText(r).includes(q))
363
- }
364
-
365
- const activeWithValues = activeFilters.filter(f => {
366
- if (f.values.length === 0) return false
367
- const col = columnsByKey.get(f.fieldKey)
368
- if (col?.filter?.type === "text") {
369
- return (f.values[0] ?? "").trim().length > 0
370
- }
371
- return true
372
- })
373
- if (activeWithValues.length > 0) {
374
- // Pre-resolve column, operator, normalised needle, and select-value Set
375
- // for each active filter ONCE (instead of per row).
376
- type CompiledFilter = {
377
- col: ColumnDef<TData>
378
- id: string
379
- type: "select" | "date" | "text"
380
- operator: ActiveFilter["operator"]
381
- selectValues?: Set<string>
382
- dateTarget?: string
383
- textNeedle?: string
384
- digitsNeedle?: string
385
- isDigitsMask?: boolean
386
- }
387
- const compiled: CompiledFilter[] = []
388
- for (const f of activeWithValues) {
389
- const col = columnsByKey.get(f.fieldKey)
390
- if (!col?.filter) continue
391
- if (col.filter.type === "select") {
392
- compiled.push({
393
- col,
394
- id: f.id,
395
- type: "select",
396
- operator: f.operator,
397
- selectValues: new Set(f.values),
398
- })
399
- } else if (col.filter.type === "date") {
400
- compiled.push({
401
- col,
402
- id: f.id,
403
- type: "date",
404
- operator: f.operator,
405
- dateTarget: f.values[0],
406
- })
407
- } else {
408
- const raw = f.values[0] ?? ""
409
- const isDigitsMask = col.filter.textMask === "phone" || col.filter.textMask === "zip"
410
- compiled.push({
411
- col,
412
- id: f.id,
413
- type: "text",
414
- operator: f.operator,
415
- isDigitsMask,
416
- digitsNeedle: isDigitsMask ? digitsOnly(raw) : undefined,
417
- textNeedle: !isDigitsMask ? raw.toLowerCase() : undefined,
418
- })
419
- }
420
- }
421
-
422
- const matchesCompiled = (r: TData, f: CompiledFilter): boolean => {
423
- const rowVal = String(r[f.col.key] ?? "")
424
- if (f.type === "select") {
425
- const hit = f.selectValues!.has(rowVal)
426
- return f.operator === "is" ? hit : !hit
427
- }
428
- if (f.type === "date") {
429
- if (!f.dateTarget) return true
430
- const rowYmd = parseRowDateToYmd(rowVal)
431
- const op = f.operator === "is_not" ? "is_not" : "is"
432
- if (rowYmd === null) return op === "is_not"
433
- return op === "is" ? rowYmd === f.dateTarget : rowYmd !== f.dateTarget
434
- }
435
- if (f.isDigitsMask) {
436
- if (!f.digitsNeedle) return true
437
- const hay = digitsOnly(rowVal)
438
- return f.operator === "contains" ? hay.includes(f.digitsNeedle) : !hay.includes(f.digitsNeedle)
439
- }
440
- if (!f.textNeedle) return true
441
- const hay = getLowerValue(r, f.col.key)
442
- return f.operator === "contains" ? hay.includes(f.textNeedle) : !hay.includes(f.textNeedle)
443
- }
444
-
445
- if (compiled.length > 0) {
446
- result = result.filter(r => {
447
- let res = matchesCompiled(r, compiled[0])
448
- for (let i = 1; i < compiled.length; i++) {
449
- const connector = filterConnectors[compiled[i - 1].id] ?? "and"
450
- const match = matchesCompiled(r, compiled[i])
451
- res = connector === "and" ? res && match : res || match
452
- }
453
- return res
454
- })
455
- }
456
- }
457
-
458
- // Column menu quick-search — pre-normalise needles outside the row loop.
459
- const colMenuEntries: { key: string; lower: string }[] = []
460
- for (const [key, raw] of Object.entries(colMenuSearch)) {
461
- const trimmed = raw.trim()
462
- if (trimmed) colMenuEntries.push({ key, lower: trimmed.toLowerCase() })
463
- }
464
- if (colMenuEntries.length > 0) {
465
- result = result.filter(r => {
466
- for (const { key, lower } of colMenuEntries) {
467
- if (!getLowerValue(r, key).includes(lower)) return false
468
- }
469
- return true
470
- })
471
- }
472
-
473
- // Sort — resolve each rule's sort key ONCE, then run the comparator over
474
- // an indexed list so the inner loop is a tight array walk, not a chain of
475
- // `columns.find` lookups per comparison.
476
- if (sortRules.length > 0) {
477
- const resolved: { sk: string; dir: SortDir }[] = []
478
- for (const rule of sortRules) {
479
- const col = columnsByKey.get(rule.fieldKey)
480
- const sk = col?.sortKey ?? col?.key
481
- if (sk) resolved.push({ sk: sk as string, dir: rule.direction })
482
- }
483
- if (resolved.length > 0) {
484
- result.sort((a, b) => {
485
- for (let i = 0; i < resolved.length; i++) {
486
- const { sk, dir } = resolved[i]
487
- const cmp = compareUnknownSort(a[sk], b[sk])
488
- if (cmp !== 0) return dir === "asc" ? cmp : -cmp
489
- }
490
- return 0
491
- })
492
- }
493
- }
494
-
495
- return result
496
- }, [
497
- data,
498
- search,
499
- activeFilters,
500
- filterConnectors,
501
- colMenuSearch,
502
- sortRules,
503
- columnsByKey,
504
- getSearchableText,
505
- getLowerValue,
506
- ])
507
-
508
- // ── Paged rows (slice of rows when pagination is active) ─────────────────
509
- const pagedRows = React.useMemo(() => {
510
- if (!paginationOverride || paginationOverride.pageSize <= 0) return rows
511
- const { page, pageSize } = paginationOverride
512
- const safePage = Math.max(1, page)
513
- return rows.slice((safePage - 1) * pageSize, safePage * pageSize)
514
- // eslint-disable-next-line react-hooks/exhaustive-deps
515
- }, [rows, paginationOverride?.page, paginationOverride?.pageSize])
516
-
517
- // ── Grouped rows ──────────────────────────────────────────────────────────
518
- const groupedRows = React.useMemo(() => {
519
- if (!groupBy) return [{ groupKey: null as string | null, groupLabel: null as string | null, rows }]
520
- const groups = new Map<string, TData[]>()
521
- rows.forEach(row => {
522
- const val = String(row[groupBy] ?? "—")
523
- if (!groups.has(val)) groups.set(val, [])
524
- groups.get(val)!.push(row)
525
- })
526
- return [...groups.entries()]
527
- .sort(([a], [b]) => a.localeCompare(b))
528
- .map(([key, groupRows]) => ({ groupKey: key, groupLabel: key, rows: groupRows }))
529
- }, [rows, groupBy])
530
-
531
- // ── Effective pins (respect overflow) ─────────────────────────────────────
532
- const LOCKED_KEYS = React.useMemo(() => new Set(Object.keys(lockedPins)), [lockedPins])
533
-
534
- // When the table fits within its container (not overflowing) there is no need
535
- // to sticky-pin any column — even locked ones. Pins only activate once the
536
- // user has to scroll horizontally so the selection / action edges stay visible.
537
- // In reflow viewports (high zoom), disable all column stickies — shadow + sticky
538
- // fight the short viewport and overlap content.
539
- const effectivePins = React.useMemo(() => {
540
- if (isReflowViewport || !isOverflowing) return {}
541
- const result: Record<string, "left" | "right"> = {}
542
- for (const [key, pin] of Object.entries(colPins)) {
543
- result[key] = pin
544
- }
545
- return result
546
- }, [colPins, isOverflowing, isReflowViewport])
547
-
548
- // ── Display columns ───────────────────────────────────────────────────────
549
- const displayCols = React.useMemo(() => {
550
- const leftPinned: string[] = []
551
- const free: string[] = []
552
- const rightPinned: string[] = []
553
- for (const k of colOrder) {
554
- const pin = colPins[k]
555
- if (pin === "left") leftPinned.push(k)
556
- else if (pin === "right") rightPinned.push(k)
557
- else free.push(k)
558
- }
559
- const ordered = [...leftPinned, ...free, ...rightPinned]
560
- const out: ColumnDef<TData>[] = []
561
- for (const k of ordered) {
562
- if (hiddenCols.has(k)) continue
563
- const col = columnsByKey.get(k)
564
- if (col) out.push(col)
565
- }
566
- return out
567
- }, [colOrder, colPins, hiddenCols, columnsByKey])
568
-
569
- // ── Column actions ────────────────────────────────────────────────────────
570
- function startResize(key: string, e: React.MouseEvent) {
571
- e.preventDefault()
572
- e.stopPropagation()
573
- const minW = columns.find(c => c.key === key)?.minWidth ?? 60
574
- const startW = colWidths[key] ?? (columns.find(c => c.key === key)?.width ?? 100)
575
- resizeRef.current = { key, startX: e.clientX, startW }
576
- const onMove = (ev: MouseEvent) => {
577
- if (!resizeRef.current) return
578
- const { key: k, startX, startW: sw } = resizeRef.current
579
- setColWidths(p => ({ ...p, [k]: Math.max(minW, sw + ev.clientX - startX) }))
580
- }
581
- const onUp = () => {
582
- resizeRef.current = null
583
- document.removeEventListener("mousemove", onMove)
584
- document.removeEventListener("mouseup", onUp)
585
- }
586
- document.addEventListener("mousemove", onMove)
587
- document.addEventListener("mouseup", onUp)
588
- }
589
-
590
- function handleDragStart(key: string, e: React.DragEvent<HTMLTableCellElement>) {
591
- draggedKey.current = key
592
- e.dataTransfer.effectAllowed = "move"
593
- }
594
- function handleDragOver(key: string, e: React.DragEvent<HTMLTableCellElement>) {
595
- e.preventDefault()
596
- e.dataTransfer.dropEffect = "move"
597
- if (draggedKey.current && draggedKey.current !== key) setDragOverKey(key)
598
- }
599
- function handleDrop(key: string) {
600
- if (!draggedKey.current || draggedKey.current === key) { setDragOverKey(null); return }
601
- const order = [...colOrder]
602
- const from = order.indexOf(draggedKey.current)
603
- const to = order.indexOf(key)
604
- order.splice(from, 1)
605
- order.splice(to, 0, draggedKey.current!)
606
- setColOrder(order)
607
- draggedKey.current = null
608
- setDragOverKey(null)
609
- }
610
- function handleDragEnd() { draggedKey.current = null; setDragOverKey(null) }
611
-
612
- function pinColumn(key: string, pin: "left" | "right") {
613
- setColPins(p => ({ ...p, [key]: pin }))
614
- }
615
- function unpinColumn(key: string) {
616
- if (lockedPins[key]) return
617
- setColPins(p => { const n = { ...p }; delete n[key]; return n })
618
- }
619
- function toggleWrap(key: string) {
620
- setColWrap(p => ({ ...p, [key]: !p[key] }))
621
- }
622
-
623
- // ── Scroll handlers ───────────────────────────────────────────────────────
624
- function checkOverflow() {
625
- const el = scrollRef.current
626
- if (!el) return
627
- setIsOverflowing(el.scrollWidth > el.clientWidth + 1)
628
- }
629
- function handleScroll() {
630
- const el = scrollRef.current
631
- if (!el) return
632
- setScrolled(el.scrollLeft > 1)
633
- setScrollEnd(el.scrollLeft >= el.scrollWidth - el.clientWidth - 1)
634
- setIsOverflowing(el.scrollWidth > el.clientWidth + 1)
635
- }
636
-
637
- // ── Selection helpers ─────────────────────────────────────────────────────
638
- function getRowId(row: TData, index: number, getIdFn?: (r: TData, i: number) => string | number): string | number {
639
- return getIdFn ? getIdFn(row, index) : (row.id as string | number ?? index)
640
- }
641
-
642
- const toggleRow = React.useCallback((id: string | number) => {
643
- setSelected(prev => {
644
- const next = new Set(prev)
645
- if (next.has(id)) next.delete(id)
646
- else next.add(id)
647
- return next
648
- })
649
- }, [setSelected])
650
-
651
- const toggleAll = React.useCallback((allRowIds: (string | number)[]) => {
652
- setSelected(prev => prev.size === allRowIds.length ? new Set() : new Set(allRowIds))
653
- }, [setSelected])
654
-
655
- // ── Sticky offset calculations ────────────────────────────────────────────
656
- // Precompute every pinned column's offset ONCE per render so the per-cell
657
- // `stickyStyle()` call is an O(1) map lookup instead of an O(cols) walk.
658
- // With `cells = rows × cols`, the previous O(rows × cols²) became the
659
- // dominant cost on wide tables.
660
- const stickyOffsets = React.useMemo(() => {
661
- const left = new Map<string, number>()
662
- const right = new Map<string, number>()
663
- let leftOffset = 0
664
- for (const col of displayCols) {
665
- if (effectivePins[col.key] !== "left") break
666
- left.set(col.key, leftOffset)
667
- leftOffset += colWidths[col.key] ?? col.width ?? 100
668
- }
669
- let rightOffset = 0
670
- for (let i = displayCols.length - 1; i >= 0; i--) {
671
- const col = displayCols[i]
672
- if (effectivePins[col.key] !== "right") break
673
- right.set(col.key, rightOffset)
674
- rightOffset += colWidths[col.key] ?? col.width ?? 100
675
- }
676
- return { left, right }
677
- }, [displayCols, effectivePins, colWidths])
678
-
679
- const getStickyLeft = React.useCallback((key: string): number => {
680
- return stickyOffsets.left.get(key) ?? 0
681
- }, [stickyOffsets])
682
-
683
- const getStickyRight = React.useCallback((key: string): number => {
684
- return stickyOffsets.right.get(key) ?? 0
685
- }, [stickyOffsets])
686
-
687
- const stickyStyle = React.useCallback(
688
- (key: string, isHeader = false): React.CSSProperties => {
689
- if (isReflowViewport) return {}
690
- const pin = effectivePins[key]
691
- if (pin === "left") {
692
- return isHeader
693
- ? { position: "sticky", left: stickyOffsets.left.get(key) ?? 0, top: 0 }
694
- : { position: "sticky", left: stickyOffsets.left.get(key) ?? 0 }
695
- }
696
- if (pin === "right") {
697
- return isHeader
698
- ? { position: "sticky", right: stickyOffsets.right.get(key) ?? 0, top: 0 }
699
- : { position: "sticky", right: stickyOffsets.right.get(key) ?? 0 }
700
- }
701
- return isHeader ? { position: "sticky", top: 0 } : {}
702
- },
703
- [effectivePins, isReflowViewport, stickyOffsets],
704
- )
705
-
706
- const totalWidth = React.useMemo(
707
- () => displayCols.reduce((s, c) => s + (colWidths[c.key] ?? c.width ?? 100), 0),
708
- [displayCols, colWidths],
709
- )
710
-
711
- return {
712
- // Sort
713
- sortRules, setSortRules,
714
- sortKey, sortDir,
715
- addSortRule, removeSortRule, toggleSortDir, handleSortByKey,
716
- // Filters
717
- search, setSearch,
718
- searchOpen, setSearchOpen,
719
- searchRef,
720
- activeFilters, setActiveFilters,
721
- filterConnectors, setFilterConnectors, toggleConnector, getConnector,
722
- openFilterId, setOpenFilterId,
723
- filterBarVisible, setFilterBarVisible,
724
- drawerExpandedFilters, setDrawerExpandedFilters,
725
- addFilter, updateFilter, removeFilter,
726
- // Group
727
- groupBy, setGroupBy,
728
- // Column quick-search
729
- colMenuSearch, setColMenuSearch,
730
- // Selection
731
- selected, setSelected, toggleRow, toggleAll, getRowId,
732
- // Column widths / order / pins / wrap
733
- colWidths, setColWidths, resizeRef, startResize,
734
- colOrder, setColOrder, moveCol,
735
- colPins, setColPins, lockedPins, LOCKED_KEYS,
736
- pinColumn, unpinColumn,
737
- colWrap, setColWrap, toggleWrap,
738
- // Drag-to-reorder
739
- draggedKey, dragOverKey,
740
- handleDragStart, handleDragOver, handleDrop, handleDragEnd,
741
- // Scroll
742
- scrollRef, scrolled, scrollEnd, isOverflowing,
743
- checkOverflow, handleScroll,
744
- // Hover
745
- hoveredRow, setHoveredRow,
746
- // Derived
747
- rows, pagedRows, groupedRows,
748
- effectivePins, displayCols,
749
- isReflowViewport,
750
- getStickyLeft, getStickyRight, stickyStyle,
751
- totalWidth,
752
- // Display settings
753
- sheetOpen, setSheetOpen,
754
- showGridlines, setShowGridlines,
755
- rowHeight, setRowHeight,
756
- hiddenCols, setHiddenCols, toggleColVisibility,
757
- }
758
- }
1
+ export * from "@exxatdesignux/ui/components/data-table/use-table-state"