basalt-ui 1.25.0 → 1.27.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 (476) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +26 -0
  3. package/MIGRATING.md +815 -23
  4. package/README.md +45 -32
  5. package/agent/rules/basalt-batteries.md +205 -0
  6. package/agent/rules/basalt-charts.md +125 -555
  7. package/agent/rules/basalt-controls.md +160 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +118 -96
  10. package/agent/rules/basalt-tokens.md +126 -288
  11. package/agent/skills/basalt-app/SKILL.md +70 -195
  12. package/agent/skills/basalt-charts/SKILL.md +54 -234
  13. package/agent/skills/basalt-design/SKILL.md +56 -248
  14. package/agent/templates/CLAUDE-block.md.tpl +31 -40
  15. package/agent/templates/DESIGN.md.tpl +22 -51
  16. package/configs/oxlint-plugin.js +1465 -36
  17. package/configs/oxlint-plugin.test.ts +1190 -11
  18. package/configs/oxlint.json +11 -3
  19. package/dist/charts/cursor/resolve.d.ts +18 -0
  20. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  21. package/dist/charts/cursor/resolve.js +15 -6
  22. package/dist/charts/cursor/scope.d.ts +6 -3
  23. package/dist/charts/cursor/scope.d.ts.map +1 -1
  24. package/dist/charts/cursor/store.d.ts +5 -1
  25. package/dist/charts/cursor/store.d.ts.map +1 -1
  26. package/dist/charts/cursor/store.js +4 -4
  27. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  28. package/dist/charts/hooks/useChartCursor.js +9 -8
  29. package/dist/charts/index.d.ts +2 -2
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/primitives/ChartCard.d.ts +34 -9
  32. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.js +29 -114
  34. package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
  35. package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
  36. package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
  37. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  38. package/dist/charts/sparklines/BarSparkline.js +18 -4
  39. package/dist/cli/check-coverage.test.d.ts +2 -0
  40. package/dist/cli/check-coverage.test.d.ts.map +1 -0
  41. package/dist/cli/index.d.ts +67 -4
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +225 -7
  44. package/dist/connectivity/connectivity-indicator.d.ts +4 -1
  45. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  46. package/dist/connectivity/connectivity-indicator.js +1 -1
  47. package/dist/content/index.d.ts +5 -3
  48. package/dist/content/index.d.ts.map +1 -1
  49. package/dist/content/index.js +4 -2
  50. package/dist/content/prose.module.css +7 -28
  51. package/dist/controls/actions.d.ts +169 -0
  52. package/dist/controls/actions.d.ts.map +1 -0
  53. package/dist/controls/actions.js +287 -0
  54. package/dist/controls/actions.module.css +62 -0
  55. package/dist/controls/actions.module.css.d.ts +13 -0
  56. package/dist/controls/actions.test.d.ts +2 -0
  57. package/dist/controls/actions.test.d.ts.map +1 -0
  58. package/dist/controls/actions.type-guard.test.d.ts +2 -0
  59. package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
  60. package/dist/controls/compare-filter.d.ts +41 -0
  61. package/dist/controls/compare-filter.d.ts.map +1 -0
  62. package/dist/controls/compare-filter.js +23 -0
  63. package/dist/controls/control-group.d.ts +19 -0
  64. package/dist/controls/control-group.d.ts.map +1 -0
  65. package/dist/controls/control-group.js +20 -0
  66. package/dist/controls/control-group.test.d.ts +2 -0
  67. package/dist/controls/control-group.test.d.ts.map +1 -0
  68. package/dist/controls/controls.module.css +542 -0
  69. package/dist/controls/controls.module.css.d.ts +35 -0
  70. package/dist/controls/controls.router.test.d.ts +2 -0
  71. package/dist/controls/controls.router.test.d.ts.map +1 -0
  72. package/dist/controls/enum-filter.d.ts +39 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +93 -0
  75. package/dist/controls/filter-context.d.ts +60 -0
  76. package/dist/controls/filter-context.d.ts.map +1 -0
  77. package/dist/controls/filter-context.js +69 -0
  78. package/dist/controls/filter-pill.d.ts +28 -0
  79. package/dist/controls/filter-pill.d.ts.map +1 -0
  80. package/dist/controls/filter-pill.js +70 -0
  81. package/dist/controls/filter-set.d.ts +13 -0
  82. package/dist/controls/filter-set.d.ts.map +1 -0
  83. package/dist/controls/filter-set.js +125 -0
  84. package/dist/controls/filter-set.test.d.ts +2 -0
  85. package/dist/controls/filter-set.test.d.ts.map +1 -0
  86. package/dist/controls/filter-sheet.d.ts +117 -0
  87. package/dist/controls/filter-sheet.d.ts.map +1 -0
  88. package/dist/controls/filter-sheet.js +146 -0
  89. package/dist/controls/glyphs.d.ts +27 -0
  90. package/dist/controls/glyphs.d.ts.map +1 -0
  91. package/dist/controls/glyphs.js +49 -0
  92. package/dist/controls/index.d.ts +20 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +46 -0
  95. package/dist/controls/multi-select-filter.d.ts +23 -0
  96. package/dist/controls/multi-select-filter.d.ts.map +1 -0
  97. package/dist/controls/multi-select-filter.js +89 -0
  98. package/dist/controls/number-filter.d.ts +26 -0
  99. package/dist/controls/number-filter.d.ts.map +1 -0
  100. package/dist/controls/number-filter.js +143 -0
  101. package/dist/controls/peer-boundary.test.d.ts +2 -0
  102. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  103. package/dist/controls/props-contract.test.d.ts +2 -0
  104. package/dist/controls/props-contract.test.d.ts.map +1 -0
  105. package/dist/controls/range-filter.d.ts +43 -0
  106. package/dist/controls/range-filter.d.ts.map +1 -0
  107. package/dist/controls/range-filter.js +135 -0
  108. package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
  109. package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
  110. package/dist/controls/search-filter.d.ts +10 -0
  111. package/dist/controls/search-filter.d.ts.map +1 -0
  112. package/dist/controls/search-filter.js +81 -0
  113. package/dist/controls/select-filter.d.ts +74 -0
  114. package/dist/controls/select-filter.d.ts.map +1 -0
  115. package/dist/controls/select-filter.js +10 -0
  116. package/dist/controls/sync-button.d.ts +38 -0
  117. package/dist/controls/sync-button.d.ts.map +1 -0
  118. package/dist/controls/sync-button.js +119 -0
  119. package/dist/controls/sync-button.module.css +63 -0
  120. package/dist/controls/sync-button.module.css.d.ts +11 -0
  121. package/dist/controls/sync-button.test.d.ts +2 -0
  122. package/dist/controls/sync-button.test.d.ts.map +1 -0
  123. package/dist/controls/toggle-filter.d.ts +9 -0
  124. package/dist/controls/toggle-filter.d.ts.map +1 -0
  125. package/dist/controls/toggle-filter.js +42 -0
  126. package/dist/controls/view-tabs.d.ts +23 -0
  127. package/dist/controls/view-tabs.d.ts.map +1 -0
  128. package/dist/controls/view-tabs.js +59 -0
  129. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  130. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  131. package/dist/controls-dates/date-range-picker.js +18 -0
  132. package/dist/controls-dates/index.d.ts +9 -0
  133. package/dist/controls-dates/index.d.ts.map +1 -0
  134. package/dist/controls-dates/index.js +4 -0
  135. package/dist/dashboard/delta-badge.d.ts +6 -12
  136. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  137. package/dist/dashboard/delta-badge.js +1 -50
  138. package/dist/dashboard/index.d.ts +1 -1
  139. package/dist/dashboard/index.d.ts.map +1 -1
  140. package/dist/dashboard/index.js +3 -1
  141. package/dist/dashboard/settings-section.d.ts +6 -4
  142. package/dist/dashboard/settings-section.d.ts.map +1 -1
  143. package/dist/dashboard/settings-section.js +23 -16
  144. package/dist/dashboard/settings-section.module.css +10 -0
  145. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  146. package/dist/dashboard/settings-section.test.d.ts +2 -0
  147. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  148. package/dist/dashboard/stat-card.d.ts +95 -9
  149. package/dist/dashboard/stat-card.d.ts.map +1 -1
  150. package/dist/dashboard/stat-card.js +108 -49
  151. package/dist/dashboard/stat-card.module.css +153 -0
  152. package/dist/dashboard/stat-card.module.css.d.ts +15 -0
  153. package/dist/data/data-table.d.ts +37 -15
  154. package/dist/data/data-table.d.ts.map +1 -1
  155. package/dist/data/data-table.js +96 -58
  156. package/dist/guard/grace.test.d.ts +20 -0
  157. package/dist/guard/grace.test.d.ts.map +1 -0
  158. package/dist/guard/index.d.ts +66 -3
  159. package/dist/guard/index.d.ts.map +1 -1
  160. package/dist/guard/index.js +78 -7
  161. package/dist/guard/types.d.ts +3 -3
  162. package/dist/guard/types.d.ts.map +1 -1
  163. package/dist/index.d.ts +13 -3
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js +13 -10
  166. package/dist/nav/types.d.ts +78 -2
  167. package/dist/nav/types.d.ts.map +1 -1
  168. package/dist/notifications/bell.d.ts +4 -1
  169. package/dist/notifications/bell.d.ts.map +1 -1
  170. package/dist/notifications/index.d.ts +1 -1
  171. package/dist/router-tanstack/field.d.ts +14 -0
  172. package/dist/router-tanstack/field.d.ts.map +1 -0
  173. package/dist/router-tanstack/field.js +4 -0
  174. package/dist/router-tanstack/index.d.ts +6 -2
  175. package/dist/router-tanstack/index.d.ts.map +1 -1
  176. package/dist/router-tanstack/index.js +4 -0
  177. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  178. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  179. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  180. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  181. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  182. package/dist/router-tanstack/search-param-store.js +16 -45
  183. package/dist/router-tanstack/search-store.d.ts +84 -0
  184. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  185. package/dist/router-tanstack/search-store.js +271 -0
  186. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  187. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  188. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  189. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  190. package/dist/router-tanstack/use-nav.d.ts +0 -2
  191. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  192. package/dist/router-tanstack/use-nav.js +34 -12
  193. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  194. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  195. package/dist/section/index.d.ts +2 -0
  196. package/dist/section/index.d.ts.map +1 -0
  197. package/dist/section/index.js +4 -0
  198. package/dist/section/section.d.ts +24 -0
  199. package/dist/section/section.d.ts.map +1 -0
  200. package/dist/section/section.js +126 -0
  201. package/dist/section/section.module.css +104 -0
  202. package/dist/section/section.module.css.d.ts +11 -0
  203. package/dist/section/section.test.d.ts +2 -0
  204. package/dist/section/section.test.d.ts.map +1 -0
  205. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  206. package/dist/shell/app-breadcrumbs.js +18 -15
  207. package/dist/shell/app-header.module.css +77 -37
  208. package/dist/shell/app-header.module.css.d.ts +2 -1
  209. package/dist/shell/app-mobile-nav.d.ts +9 -2
  210. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  211. package/dist/shell/app-mobile-nav.js +98 -11
  212. package/dist/shell/app-mobile-nav.module.css +8 -0
  213. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  214. package/dist/shell/app-sidebar.d.ts +55 -28
  215. package/dist/shell/app-sidebar.d.ts.map +1 -1
  216. package/dist/shell/app-sidebar.js +116 -67
  217. package/dist/shell/app-sidebar.module.css +303 -20
  218. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  219. package/dist/shell/index.d.ts +55 -21
  220. package/dist/shell/index.d.ts.map +1 -1
  221. package/dist/shell/index.js +40 -18
  222. package/dist/shell/mobile-nav-model.d.ts +13 -2
  223. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  224. package/dist/shell/mobile-nav-model.js +15 -1
  225. package/dist/shell/nav-count-badge.d.ts +1 -1
  226. package/dist/shell/page-bar.d.ts +51 -0
  227. package/dist/shell/page-bar.d.ts.map +1 -0
  228. package/dist/shell/page-bar.js +154 -0
  229. package/dist/shell/page-bar.module.css +178 -0
  230. package/dist/shell/page-bar.module.css.d.ts +17 -0
  231. package/dist/shell/page-bar.test.d.ts +2 -0
  232. package/dist/shell/page-bar.test.d.ts.map +1 -0
  233. package/dist/shell/sidebar-block-model.d.ts +48 -0
  234. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  235. package/dist/shell/sidebar-block-model.js +39 -0
  236. package/dist/shell/sidebar-blocks.d.ts +32 -0
  237. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  238. package/dist/shell/sidebar-blocks.js +169 -0
  239. package/dist/shell/sidebar-search.d.ts +13 -1
  240. package/dist/shell/sidebar-search.d.ts.map +1 -1
  241. package/dist/shell/sidebar-search.js +34 -1
  242. package/dist/shell/sidebar-search.module.css +26 -0
  243. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  244. package/dist/state/fields.d.ts +565 -0
  245. package/dist/state/fields.d.ts.map +1 -0
  246. package/dist/state/fields.js +405 -0
  247. package/dist/state/fields.test.d.ts +2 -0
  248. package/dist/state/fields.test.d.ts.map +1 -0
  249. package/dist/state/persisted.d.ts +60 -0
  250. package/dist/state/persisted.d.ts.map +1 -0
  251. package/dist/state/persisted.js +123 -0
  252. package/dist/state/persisted.test.d.ts +2 -0
  253. package/dist/state/persisted.test.d.ts.map +1 -0
  254. package/dist/state.d.ts +11 -56
  255. package/dist/state.d.ts.map +1 -1
  256. package/dist/state.js +14 -120
  257. package/dist/styles.css +13 -0
  258. package/dist/surfaces.d.ts +106 -24
  259. package/dist/surfaces.d.ts.map +1 -1
  260. package/dist/surfaces.js +156 -19
  261. package/dist/theme/controls.module.css +16 -1
  262. package/dist/theme/ctl-theme.d.ts +32 -0
  263. package/dist/theme/ctl-theme.d.ts.map +1 -0
  264. package/dist/theme/ctl-theme.js +54 -0
  265. package/dist/theme/ctl-theme.test.d.ts +2 -0
  266. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  267. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  268. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  269. package/dist/theme/divider-law.test.d.ts +2 -0
  270. package/dist/theme/divider-law.test.d.ts.map +1 -0
  271. package/dist/theme/icon-slot.d.ts +44 -0
  272. package/dist/theme/icon-slot.d.ts.map +1 -0
  273. package/dist/theme/icon-slot.js +21 -0
  274. package/dist/theme/icon-slot.module.css +47 -0
  275. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  276. package/dist/theme/icon-slot.test.d.ts +2 -0
  277. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  278. package/dist/theme/index.d.ts +1 -0
  279. package/dist/theme/index.d.ts.map +1 -1
  280. package/dist/theme/index.js +82 -5
  281. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  282. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  283. package/dist/theme/segmented-control.module.css +20 -0
  284. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  285. package/dist/tokens/index.d.ts +12 -0
  286. package/dist/tokens/index.d.ts.map +1 -1
  287. package/dist/tokens/index.js +38 -8
  288. package/dist/tokens/palette.d.ts +97 -61
  289. package/dist/tokens/palette.d.ts.map +1 -1
  290. package/dist/tokens/palette.js +76 -38
  291. package/dist/tokens.css +10 -2
  292. package/dist/widget-header/delta-badge.d.ts +13 -0
  293. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  294. package/dist/widget-header/delta-badge.js +29 -0
  295. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  296. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  297. package/dist/widget-header/index.d.ts +3 -0
  298. package/dist/widget-header/index.d.ts.map +1 -0
  299. package/dist/widget-header/index.js +6 -0
  300. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  301. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  302. package/dist/widget-header/widget-header.d.ts +67 -0
  303. package/dist/widget-header/widget-header.d.ts.map +1 -0
  304. package/dist/widget-header/widget-header.js +89 -0
  305. package/dist/widget-header/widget-header.module.css +350 -0
  306. package/dist/widget-header/widget-header.module.css.d.ts +25 -0
  307. package/dist/widget-header/widget-header.test.d.ts +2 -0
  308. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  309. package/llms.txt +30 -17
  310. package/package.json +14 -1
  311. package/src/charts/cursor/resolve.test.ts +29 -1
  312. package/src/charts/cursor/resolve.ts +38 -6
  313. package/src/charts/cursor/scope.tsx +6 -3
  314. package/src/charts/cursor/store.test.ts +19 -1
  315. package/src/charts/cursor/store.ts +10 -5
  316. package/src/charts/hooks/useChartCursor.ts +16 -8
  317. package/src/charts/index.ts +2 -2
  318. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  319. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  320. package/src/charts/primitives/ChartCard.tsx +86 -146
  321. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  322. package/src/cli/check-coverage.test.ts +297 -0
  323. package/src/cli/check-theme.test.ts +38 -14
  324. package/src/cli/index.ts +392 -16
  325. package/src/cli/init-sync.test.ts +97 -0
  326. package/src/connectivity/connectivity-indicator.tsx +16 -9
  327. package/src/content/index.ts +16 -3
  328. package/src/content/prose.module.css +7 -28
  329. package/src/content/prose.module.css.test.ts +8 -24
  330. package/src/controls/actions.module.css +62 -0
  331. package/src/controls/actions.module.css.d.ts +13 -0
  332. package/src/controls/actions.test.tsx +252 -0
  333. package/src/controls/actions.tsx +655 -0
  334. package/src/controls/actions.type-guard.test.ts +63 -0
  335. package/src/controls/compare-filter.tsx +64 -0
  336. package/src/controls/control-group.test.tsx +229 -0
  337. package/src/controls/control-group.tsx +63 -0
  338. package/src/controls/controls.module.css +542 -0
  339. package/src/controls/controls.module.css.d.ts +35 -0
  340. package/src/controls/controls.router.test.tsx +1053 -0
  341. package/src/controls/enum-filter.tsx +170 -0
  342. package/src/controls/filter-context.tsx +158 -0
  343. package/src/controls/filter-pill.tsx +120 -0
  344. package/src/controls/filter-set.test.tsx +472 -0
  345. package/src/controls/filter-set.tsx +225 -0
  346. package/src/controls/filter-sheet.tsx +285 -0
  347. package/src/controls/glyphs.tsx +81 -0
  348. package/src/controls/index.ts +49 -0
  349. package/src/controls/multi-select-filter.tsx +140 -0
  350. package/src/controls/number-filter.tsx +291 -0
  351. package/src/controls/peer-boundary.test.ts +85 -0
  352. package/src/controls/props-contract.test.tsx +137 -0
  353. package/src/controls/range-filter.tsx +237 -0
  354. package/src/controls/range-filter.type-guard.test.tsx +122 -0
  355. package/src/controls/search-filter.tsx +123 -0
  356. package/src/controls/select-filter.tsx +89 -0
  357. package/src/controls/sync-button.module.css +63 -0
  358. package/src/controls/sync-button.module.css.d.ts +11 -0
  359. package/src/controls/sync-button.test.tsx +183 -0
  360. package/src/controls/sync-button.tsx +197 -0
  361. package/src/controls/toggle-filter.tsx +60 -0
  362. package/src/controls/view-tabs.tsx +110 -0
  363. package/src/controls-dates/date-range-picker.tsx +42 -0
  364. package/src/controls-dates/index.ts +8 -0
  365. package/src/dashboard/delta-badge.tsx +4 -74
  366. package/src/dashboard/index.ts +6 -1
  367. package/src/dashboard/settings-section.module.css +10 -0
  368. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  369. package/src/dashboard/settings-section.test.tsx +56 -0
  370. package/src/dashboard/settings-section.tsx +33 -30
  371. package/src/dashboard/stat-card.module.css +153 -0
  372. package/src/dashboard/stat-card.module.css.d.ts +15 -0
  373. package/src/dashboard/stat-card.test.tsx +298 -1
  374. package/src/dashboard/stat-card.tsx +289 -67
  375. package/src/data/data-table.test.tsx +96 -11
  376. package/src/data/data-table.tsx +228 -84
  377. package/src/guard/check-source.test.ts +159 -13
  378. package/src/guard/grace.test.ts +158 -0
  379. package/src/guard/index.ts +185 -29
  380. package/src/guard/types.ts +4 -2
  381. package/src/index.ts +51 -5
  382. package/src/nav/types.ts +84 -2
  383. package/src/notifications/bell.tsx +6 -3
  384. package/src/notifications/index.ts +1 -1
  385. package/src/router-tanstack/field.ts +35 -0
  386. package/src/router-tanstack/index.ts +28 -2
  387. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  388. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  389. package/src/router-tanstack/search-param-store.ts +42 -159
  390. package/src/router-tanstack/search-store.router.test.tsx +882 -0
  391. package/src/router-tanstack/search-store.test.ts +578 -0
  392. package/src/router-tanstack/search-store.ts +546 -0
  393. package/src/router-tanstack/use-nav.test.tsx +187 -0
  394. package/src/router-tanstack/use-nav.ts +72 -14
  395. package/src/section/index.ts +1 -0
  396. package/src/section/section.module.css +104 -0
  397. package/src/section/section.module.css.d.ts +11 -0
  398. package/src/section/section.test.tsx +204 -0
  399. package/src/section/section.tsx +220 -0
  400. package/src/shell/app-breadcrumbs.tsx +44 -31
  401. package/src/shell/app-header.module.css +77 -37
  402. package/src/shell/app-header.module.css.d.ts +2 -1
  403. package/src/shell/app-mobile-nav.module.css +8 -0
  404. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  405. package/src/shell/app-mobile-nav.tsx +147 -8
  406. package/src/shell/app-sidebar-account.tsx +2 -2
  407. package/src/shell/app-sidebar.module.css +303 -20
  408. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  409. package/src/shell/app-sidebar.test.tsx +456 -41
  410. package/src/shell/app-sidebar.tsx +276 -140
  411. package/src/shell/index.test.tsx +291 -5
  412. package/src/shell/index.tsx +137 -38
  413. package/src/shell/mobile-nav-model.test.ts +228 -7
  414. package/src/shell/mobile-nav-model.ts +53 -3
  415. package/src/shell/nav-count-badge.tsx +1 -1
  416. package/src/shell/page-bar.module.css +178 -0
  417. package/src/shell/page-bar.module.css.d.ts +17 -0
  418. package/src/shell/page-bar.test.tsx +316 -0
  419. package/src/shell/page-bar.tsx +315 -0
  420. package/src/shell/sidebar-block-model.ts +83 -0
  421. package/src/shell/sidebar-blocks.tsx +301 -0
  422. package/src/shell/sidebar-search.module.css +26 -0
  423. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  424. package/src/shell/sidebar-search.tsx +71 -2
  425. package/src/state/fields.test.tsx +512 -0
  426. package/src/state/fields.ts +1150 -0
  427. package/src/state/persisted.test.ts +91 -0
  428. package/src/state/persisted.ts +261 -0
  429. package/src/state.ts +52 -240
  430. package/src/styles.css +13 -0
  431. package/src/surfaces.ts +204 -36
  432. package/src/theme/controls.module.css +16 -1
  433. package/src/theme/ctl-theme.test.tsx +275 -0
  434. package/src/theme/ctl-theme.tsx +138 -0
  435. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  436. package/src/theme/density-relations.test.ts +106 -51
  437. package/src/theme/divider-law.test.ts +259 -0
  438. package/src/theme/icon-slot.module.css +47 -0
  439. package/src/theme/icon-slot.module.css.d.ts +8 -0
  440. package/src/theme/icon-slot.test.tsx +275 -0
  441. package/src/theme/icon-slot.tsx +59 -0
  442. package/src/theme/index.ts +165 -3
  443. package/src/theme/layout-rhythm-css.test.ts +72 -0
  444. package/src/theme/radius.test.ts +11 -1
  445. package/src/theme/segmented-control.module.css +20 -0
  446. package/src/theme/shadow-surfaces.test.ts +20 -7
  447. package/src/theme/spacing.test.ts +75 -27
  448. package/src/theme/use-basalt-spacing.ts +2 -2
  449. package/src/tokens/density.test.ts +7 -25
  450. package/src/tokens/index.ts +48 -8
  451. package/src/tokens/palette.ts +107 -73
  452. package/src/widget-header/delta-badge.test.tsx +60 -0
  453. package/src/widget-header/delta-badge.tsx +57 -0
  454. package/src/widget-header/index.ts +2 -0
  455. package/src/widget-header/no-mantine.test.ts +33 -0
  456. package/src/widget-header/widget-header.module.css +350 -0
  457. package/src/widget-header/widget-header.module.css.d.ts +25 -0
  458. package/src/widget-header/widget-header.test.tsx +187 -0
  459. package/src/widget-header/widget-header.tsx +213 -0
  460. package/agent/rules/basalt-agent.md +0 -452
  461. package/agent/rules/basalt-app.md +0 -246
  462. package/agent/rules/basalt-commands.md +0 -320
  463. package/agent/rules/basalt-content.md +0 -409
  464. package/agent/rules/basalt-data.md +0 -329
  465. package/agent/rules/basalt-forms.md +0 -186
  466. package/agent/rules/basalt-notifications.md +0 -264
  467. package/agent/rules/basalt-query.md +0 -187
  468. package/agent/rules/basalt-router.md +0 -336
  469. package/dist/content/article-filter-bar.d.ts +0 -40
  470. package/dist/content/article-filter-bar.d.ts.map +0 -1
  471. package/dist/content/article-filter-bar.js +0 -56
  472. package/dist/shell/page-header.d.ts +0 -13
  473. package/dist/shell/page-header.d.ts.map +0 -1
  474. package/dist/shell/page-header.js +0 -23
  475. package/src/content/article-filter-bar.tsx +0 -88
  476. package/src/shell/page-header.tsx +0 -35
@@ -21,27 +21,18 @@
21
21
  * <BasaltDataTable data={rows} columns={columns} />
22
22
  *
23
23
  * @example
24
- * // With the toolbar + pagination bar:
24
+ * // With the title, the toolbar + pagination bar:
25
25
  * <BasaltDataTable
26
+ * title="Top pages"
26
27
  * data={rows}
27
28
  * columns={columns}
28
29
  * enableGlobalFilter
29
30
  * facets={[{ columnId: 'department', label: 'Department', options: departmentOptions }]}
30
- * toolbarActions={<Button size="xs">Export</Button>}
31
+ * actions={<Button>Export</Button>}
31
32
  * enablePagination
32
33
  * />
33
34
  */
34
- import {
35
- Box,
36
- Group,
37
- MultiSelect,
38
- Pagination,
39
- Select,
40
- Skeleton,
41
- Table,
42
- Text,
43
- TextInput,
44
- } from '@mantine/core'
35
+ import { Box, Group, Pagination, Select, Skeleton, Table, Text, TextInput } from '@mantine/core'
45
36
  import type { MantineSpacing } from '@mantine/core'
46
37
  import type {
47
38
  Column,
@@ -65,7 +56,17 @@ import {
65
56
  } from '@tanstack/react-table'
66
57
  import type { CSSProperties, ReactNode } from 'react'
67
58
  import { useCallback, useMemo, useState } from 'react'
59
+ import { FilterSet } from '../controls'
60
+ // `EnumFilter` is deliberately NOT on the `./controls` barrel (`docs/CONTROLS-SPEC.md` §3 —
61
+ // reaching for it directly is hand-rolling a filter, `basalt/hand-rolled-filter`); a facet column
62
+ // is the one place inside the framework itself that legitimately builds a `FieldHandle` over
63
+ // something that is not a store field, so it reaches past the barrel on purpose.
64
+ import { EnumFilter } from '../controls/enum-filter'
65
+ import { MultiSelectFilter } from '../controls/multi-select-filter'
66
+ import { CtlSlot } from '../theme'
68
67
  import { alpha, VX } from '../tokens'
68
+ import type { EnumField, FieldHandle, MultiField } from '../state'
69
+ import { WidgetHeader } from '../widget-header'
69
70
 
70
71
  // ── Column alignment ──────────────────────────────────────────────────────────
71
72
 
@@ -113,17 +114,17 @@ function resolveAlign<T>(column: Column<T, unknown>): DataTableAlign | undefined
113
114
 
114
115
  // ── Facets ────────────────────────────────────────────────────────────────────
115
116
 
116
- /** A single selectable option inside a {@link DataTableFacet}'s Select/MultiSelect. */
117
+ /** A single selectable option inside a {@link DataTableFacet}'s `EnumFilter`/`MultiSelectFilter` pill. */
117
118
  export type DataTableFacetOption = {
118
119
  /** The raw filter value, compared against the column's stringified cell value. */
119
120
  value: string
120
- /** Label shown in the Select/MultiSelect dropdown. */
121
+ /** Label shown in the pill's popover. */
121
122
  label: string
122
123
  }
123
124
 
124
125
  /**
125
- * Declares one faceted column filter, rendered as a Mantine Select (or MultiSelect when
126
- * `multiple` is set) inside the toolbar.
126
+ * Declares one faceted column filter, rendered as an `EnumFilter` pill (or `MultiSelectFilter`
127
+ * when `multiple` is set) inside a `FilterSet` in the toolbar (`docs/CONTROLS-SPEC.md` §3).
127
128
  *
128
129
  * @example
129
130
  * const facets: DataTableFacet[] = [
@@ -135,14 +136,97 @@ export type DataTableFacetOption = {
135
136
  export type DataTableFacet = {
136
137
  /** The TanStack column id — the column's manual `id`, or its `accessorKey` string. */
137
138
  columnId: string
138
- /** Label shown as the Select/MultiSelect placeholder. */
139
+ /** The pill's label — read at rest (single-select) and as the `Filters`/count fallback (multi). */
139
140
  label: string
140
141
  /** Selectable options for this facet. */
141
142
  options: DataTableFacetOption[]
142
- /** Render a MultiSelect (any-of match) instead of a single Select (exact match). @default false */
143
+ /** Render a `MultiSelectFilter` (any-of match) instead of an `EnumFilter` (exact match). @default false */
143
144
  multiple?: boolean
144
145
  }
145
146
 
147
+ /**
148
+ * The "no filter" member of a single-select facet's `FieldHandle`. A closed enum field always has
149
+ * a real member as its fallback (C4) — there is no "unset" — so a facet, whose underlying
150
+ * `column.getFilterValue()` genuinely can be `undefined`, needs one synthetic option to stand for
151
+ * that state. Rendered as the enum's first radio; picking it clears the column filter, same as every
152
+ * other value change.
153
+ *
154
+ * Its label is `Any <facet>`, and it used to be a bare `All`. The pill now reads the SELECTED
155
+ * option's label at every value including the fallback (`controls/enum-filter.tsx` — a bar reading
156
+ * `Compare` over a field holding `'previous'` is the bug that law fixes), and a toolbar with three
157
+ * facets would then have read `All` three times. `Any project` is the one label that works in both
158
+ * places at once: it is a legible popover row for "no constraint", and a legible pill readout of the
159
+ * state the filter is actually in. Derived from `facet.label`, so no facet has to declare it.
160
+ */
161
+ const FACET_ALL_VALUE = '__basalt_facet_all__'
162
+
163
+ /** The synthetic row's label — see {@link FACET_ALL_VALUE}. */
164
+ function facetAllLabel(facet: DataTableFacet): string {
165
+ return `Any ${facet.label.toLowerCase()}`
166
+ }
167
+
168
+ /**
169
+ * Presents a single-select facet column as a `FieldHandle<EnumField<string>>` so it can render
170
+ * through the shared `EnumFilter` pill instead of a hand-rolled `Select` (`basalt/hand-rolled-filter`).
171
+ * `use()` reads/writes `column.getFilterValue()`/`setFilterValue()` directly — no internal
172
+ * subscription needed, since the enclosing `BasaltDataTable` already re-renders on every
173
+ * `columnFilters` change and this handle is rebuilt fresh each render alongside its column.
174
+ */
175
+ function facetEnumHandle<T>(
176
+ column: Column<T, unknown>,
177
+ facet: DataTableFacet,
178
+ ): FieldHandle<EnumField<string>> {
179
+ return {
180
+ kind: 'enum',
181
+ fallback: FACET_ALL_VALUE,
182
+ options: [{ value: FACET_ALL_VALUE, label: facetAllLabel(facet) }, ...facet.options],
183
+ use: () => {
184
+ const current = (column.getFilterValue() as string | undefined) ?? FACET_ALL_VALUE
185
+ return [
186
+ current,
187
+ (next: string) => {
188
+ column.setFilterValue(next === FACET_ALL_VALUE ? undefined : next)
189
+ },
190
+ ] as const
191
+ },
192
+ // A facet column has no lane to unset: clearing the column filter IS its fallback state.
193
+ clear: () => {
194
+ column.setFilterValue(undefined)
195
+ },
196
+ isDefault: (value) => value === FACET_ALL_VALUE,
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Presents a multi-select facet column as a `FieldHandle<MultiField<string>>`. Unlike the
202
+ * single-select case, `MultiField`'s own fallback — an empty selection — already means "no
203
+ * constraint", so no synthetic option is needed here.
204
+ */
205
+ function facetMultiHandle<T>(
206
+ column: Column<T, unknown>,
207
+ facet: DataTableFacet,
208
+ ): FieldHandle<MultiField<string>> {
209
+ return {
210
+ kind: 'multi',
211
+ fallback: [],
212
+ options: facet.options,
213
+ use: () => {
214
+ const current = (column.getFilterValue() as readonly string[] | undefined) ?? []
215
+ return [
216
+ current,
217
+ (next: readonly string[]) => {
218
+ column.setFilterValue(next.length > 0 ? [...next] : undefined)
219
+ },
220
+ ] as const
221
+ },
222
+ // Same as the single-select facet: an empty column filter is this handle's fallback.
223
+ clear: () => {
224
+ column.setFilterValue(undefined)
225
+ },
226
+ isDefault: (value) => value.length === 0,
227
+ }
228
+ }
229
+
146
230
  // ── Props ─────────────────────────────────────────────────────────────────────
147
231
 
148
232
  /**
@@ -159,6 +243,19 @@ export type DataTableFacet = {
159
243
  * }
160
244
  */
161
245
  export type BasaltDataTableProps<T> = {
246
+ // ── Header (WidgetHeader, docs/CONTROLS-SPEC.md §2.2) ──────────────────────────
247
+
248
+ /**
249
+ * Optional heading rendered above the toolbar via `WidgetHeader tier="widget"`. `count` always
250
+ * reads `table.getRowCount()` (C11) — never a raw `data.length` — so it tracks the row model
251
+ * (post-filter/-pagination) rather than the unfiltered input.
252
+ */
253
+ title?: string
254
+ /** Optional leading icon, forwarded to `WidgetHeader`. */
255
+ icon?: ReactNode
256
+ /** Optional muted line rendered below the title row. */
257
+ subtitle?: string
258
+
162
259
  /** Row data array. */
163
260
  data: T[]
164
261
  /**
@@ -262,9 +359,9 @@ export type BasaltDataTableProps<T> = {
262
359
  /** Called whenever the internal global filter value changes. */
263
360
  onGlobalFilterChange?: (value: string) => void
264
361
  /**
265
- * Faceted column filters, rendered in the toolbar as Mantine Select/MultiSelect controls wired
266
- * to TanStack's per-column `columnFilters` state. The toolbar renders whenever this array is
267
- * non-empty, `enableGlobalFilter` is set, or `toolbarActions` is passed.
362
+ * Faceted column filters, rendered in the toolbar as `EnumFilter`/`MultiSelectFilter` pills
363
+ * inside a `FilterSet`, wired to TanStack's per-column `columnFilters` state. The toolbar
364
+ * renders whenever this array is non-empty, `enableGlobalFilter` is set, or `actions` is passed.
268
365
  * @example
269
366
  * facets={[{ columnId: 'department', label: 'Department', options: departmentOptions }]}
270
367
  */
@@ -283,9 +380,10 @@ export type BasaltDataTableProps<T> = {
283
380
  manualFiltering?: boolean
284
381
  /**
285
382
  * Right-aligned toolbar slot (e.g. an "Export" button). Renders the toolbar row even when no
286
- * search input or facets are configured.
383
+ * search input or facets are configured. Renamed from `toolbarActions` — the toolbar (search +
384
+ * facets + this slot) is wrapped in `CtlSlot` (C1/C5), so its controls resolve to the `ctl` tier.
287
385
  */
288
- toolbarActions?: ReactNode
386
+ actions?: ReactNode
289
387
 
290
388
  // ── Pagination ────────────────────────────────────────────────────────────────
291
389
 
@@ -389,9 +487,20 @@ export type BasaltDataTableProps<T> = {
389
487
  /** Row separators. Forwarded to Mantine `Table`. @default true (Mantine's own default) */
390
488
  withRowBorders?: boolean
391
489
  /**
392
- * Outer table border. Turn it OFF for a table that already sits inside a `Card`/`ChartCard`,
393
- * where the card owns the frame and a second one reads as a nested box.
394
- * @default true
490
+ * Outer table border — a full box around the table.
491
+ *
492
+ * **`false` by default since this minor** (it was `true`). Three separate faults, all visible on
493
+ * the playground's `Controls (mobile)` page at 390px: its TOP edge sat directly under the table's
494
+ * own `WidgetHeader`, separating a header from its own content; its BOTTOM edge landed on the last
495
+ * row's own hairline, two rules at zero distance; and its left/right edges carried no information
496
+ * at all on a table that fills the page (`docs/DESIGN-SPEC.md` §8 — a border is a layout divider,
497
+ * and there is nothing here to divide). The head-row rule and the between-row rules stay, so the
498
+ * table still reads as a table.
499
+ *
500
+ * Pass `true` for a table that genuinely needs a frame — a small table floating in whitespace
501
+ * beside other content, where the box IS the grouping.
502
+ *
503
+ * @default false
395
504
  */
396
505
  withTableBorder?: boolean
397
506
  }
@@ -428,6 +537,23 @@ const RANGE_LABEL_STYLE: CSSProperties = {
428
537
 
429
538
  const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100]
430
539
 
540
+ /**
541
+ * The header row's two halves. The lead SHRINKS and the toolbar does not — a title can ellipsize, a
542
+ * 220px search field with a facet pill beside it cannot, and when both were shrinkable flex split
543
+ * the overflow between them and clipped the search's placeholder mid-word.
544
+ */
545
+ const TABLE_HEADER_LEAD_STYLE: CSSProperties = { flex: '1 1 auto', minWidth: 0 }
546
+ const TABLE_TOOLBAR_STYLE: CSSProperties = { flex: '0 0 auto' }
547
+ /**
548
+ * The toolbar search's width. Mantine's Input grows to its container, which in a `justify:
549
+ * space-between` row means "whatever is left" — so the field was 600px on a wide table and 180px on a
550
+ * narrow one, for the same one-word query. A stated width also makes the toolbar's own minimum width
551
+ * knowable, which is what lets it wrap under the title below `sm` with no media query.
552
+ */
553
+ const SEARCH_WIDTH = 220
554
+ /** The rows-per-page Select's width — see `SEARCH_WIDTH` for why a toolbar field states one. */
555
+ const PAGE_SIZE_WIDTH = 116
556
+
431
557
  // ── Column pinning helpers ────────────────────────────────────────────────────
432
558
 
433
559
  // Merges the left/center/right header-group triad TanStack exposes for pinning into the single
@@ -597,6 +723,9 @@ function enforceManualPaginationContract(breaches: ManualPaginationBreach[]): vo
597
723
  * />
598
724
  */
599
725
  export function BasaltDataTable<T>({
726
+ title,
727
+ icon,
728
+ subtitle,
600
729
  data,
601
730
  columns,
602
731
  enableSorting = true,
@@ -616,7 +745,7 @@ export function BasaltDataTable<T>({
616
745
  facets,
617
746
  onColumnFiltersChange,
618
747
  manualFiltering = false,
619
- toolbarActions,
748
+ actions,
620
749
  enablePagination = false,
621
750
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
622
751
  initialPagination,
@@ -633,7 +762,7 @@ export function BasaltDataTable<T>({
633
762
  verticalSpacing,
634
763
  horizontalSpacing,
635
764
  withRowBorders,
636
- withTableBorder = true,
765
+ withTableBorder = false,
637
766
  }: BasaltDataTableProps<T>) {
638
767
  const [sorting, setSorting] = useState<SortingState>(initialSorting ?? [])
639
768
  const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter ?? '')
@@ -773,7 +902,7 @@ export function BasaltDataTable<T>({
773
902
 
774
903
  const showSearch = enableGlobalFilter && filteringEnabled
775
904
  const showFacets = hasFacets && filteringEnabled
776
- const showToolbar = showSearch || showFacets || Boolean(toolbarActions)
905
+ const showToolbar = showSearch || showFacets || Boolean(actions)
777
906
  const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups()
778
907
  // The rows actually rendered, not `data` — a filter or a page index that matches nothing leaves
779
908
  // `data` non-empty while the body has nothing in it, and a blank body reads as a broken table.
@@ -906,63 +1035,81 @@ export function BasaltDataTable<T>({
906
1035
  const scrolls = maxHeight !== undefined || minWidth !== undefined
907
1036
 
908
1037
  const paginationState = table.getState().pagination
1038
+ // The CURRENT page size is always an option, even when it is not in `pageSizeOptions`. Mantine's
1039
+ // Select renders EMPTY when its `value` matches no row, so a table opened at
1040
+ // `initialPagination.pageSize: 5` against the default `[10, 25, 50, 100]` showed a blank
1041
+ // rows-per-page box — a control stating nothing, next to a range label stating `1–5`.
1042
+ const pageSizeRows = [...new Set([...pageSizeOptions, paginationState.pageSize])]
1043
+ .sort((a, b) => a - b)
1044
+ .map((size) => ({ value: String(size), label: `${size} / page` }))
909
1045
  const total = table.getRowCount()
910
1046
  const rangeStart = total === 0 ? 0 : paginationState.pageIndex * paginationState.pageSize + 1
911
1047
  const rangeEnd = Math.min((paginationState.pageIndex + 1) * paginationState.pageSize, total)
912
1048
 
913
1049
  return (
914
1050
  <>
915
- {showToolbar && (
916
- <Group justify="space-between" align="flex-end" wrap="wrap" gap="xs" mb="xs">
917
- <Group gap="xs" wrap="wrap" align="flex-end">
918
- {showSearch && (
919
- <TextInput
920
- size="xs"
921
- radius="md"
922
- placeholder={globalFilterPlaceholder}
923
- leftSection={searchIcon}
924
- value={globalFilter}
925
- onChange={(event) => handleGlobalFilterChange(event.currentTarget.value)}
926
- w={220}
1051
+ {/*
1052
+ * ONE header row: the `WidgetHeader` (title · count) on the left, the toolbar right-aligned in
1053
+ * the SAME row. It was two stacked rows with `mb="xs"` on each, so a titled table with search
1054
+ * spent title → 11px → search → 11px → thead before a single datum appeared, and the toolbar
1055
+ * read as page furniture rather than as this table's controls.
1056
+ *
1057
+ * `wrap="wrap"` is the below-`sm` behaviour and needs no media query: the toolbar's minimum
1058
+ * width (a 220px search plus its pills) exceeds what is left beside a title on a phone, so flex
1059
+ * wraps it under the title on its own. `align="center"` puts the 30px toolbar controls on the
1060
+ * title's optical line rather than on its baseline.
1061
+ */}
1062
+ {(title !== undefined || showToolbar) && (
1063
+ <Group justify="space-between" align="center" wrap="wrap" gap="xs" mb="xs">
1064
+ {title !== undefined ? (
1065
+ <Box style={TABLE_HEADER_LEAD_STYLE}>
1066
+ <WidgetHeader
1067
+ tier="widget"
1068
+ title={title}
1069
+ {...(icon !== undefined && { icon })}
1070
+ {...(subtitle !== undefined && { subtitle })}
1071
+ count={table.getRowCount()}
927
1072
  />
928
- )}
929
- {showFacets &&
930
- facets?.map((facet) => {
931
- const column = table.getColumn(facet.columnId)
932
- if (!column) return null
933
- if (facet.multiple) {
934
- const value = (column.getFilterValue() as string[] | undefined) ?? []
935
- return (
936
- <MultiSelect
937
- key={facet.columnId}
938
- size="xs"
939
- radius="md"
940
- placeholder={facet.label}
941
- data={facet.options}
942
- value={value}
943
- onChange={(next) => column.setFilterValue(next.length > 0 ? next : undefined)}
944
- clearable
945
- w={200}
946
- />
947
- )
948
- }
949
- const value = (column.getFilterValue() as string | undefined) ?? null
950
- return (
951
- <Select
952
- key={facet.columnId}
953
- size="xs"
1073
+ </Box>
1074
+ ) : null}
1075
+ {showToolbar && (
1076
+ <CtlSlot>
1077
+ <Group gap="xs" wrap="wrap" align="center" style={TABLE_TOOLBAR_STYLE}>
1078
+ {showSearch && (
1079
+ <TextInput
954
1080
  radius="md"
955
- placeholder={facet.label}
956
- data={facet.options}
957
- value={value}
958
- onChange={(next) => column.setFilterValue(next ?? undefined)}
959
- clearable
960
- w={180}
1081
+ w={SEARCH_WIDTH}
1082
+ placeholder={globalFilterPlaceholder}
1083
+ leftSection={searchIcon}
1084
+ value={globalFilter}
1085
+ onChange={(event) => handleGlobalFilterChange(event.currentTarget.value)}
961
1086
  />
962
- )
963
- })}
964
- </Group>
965
- {toolbarActions}
1087
+ )}
1088
+ {showFacets && (
1089
+ <FilterSet>
1090
+ {facets?.map((facet) => {
1091
+ const column = table.getColumn(facet.columnId)
1092
+ if (!column) return null
1093
+ return facet.multiple ? (
1094
+ <MultiSelectFilter
1095
+ key={facet.columnId}
1096
+ field={facetMultiHandle(column, facet)}
1097
+ label={facet.label}
1098
+ />
1099
+ ) : (
1100
+ <EnumFilter
1101
+ key={facet.columnId}
1102
+ field={facetEnumHandle(column, facet)}
1103
+ label={facet.label}
1104
+ />
1105
+ )
1106
+ })}
1107
+ </FilterSet>
1108
+ )}
1109
+ {actions}
1110
+ </Group>
1111
+ </CtlSlot>
1112
+ )}
966
1113
  </Group>
967
1114
  )}
968
1115
  {scrolls ? (
@@ -986,16 +1133,13 @@ export function BasaltDataTable<T>({
986
1133
  </Text>
987
1134
  <Group gap="xs" align="center">
988
1135
  <Select
989
- size="xs"
1136
+ size="ctl"
990
1137
  radius="md"
991
- data={pageSizeOptions.map((size) => ({
992
- value: String(size),
993
- label: `${size} / page`,
994
- }))}
1138
+ w={PAGE_SIZE_WIDTH}
1139
+ data={pageSizeRows}
995
1140
  value={String(paginationState.pageSize)}
996
1141
  onChange={(value) => value && table.setPageSize(Number(value))}
997
1142
  allowDeselect={false}
998
- w={110}
999
1143
  />
1000
1144
  <Pagination
1001
1145
  size="sm"