basalt-ui 1.24.0 → 1.26.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 (467) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +25 -0
  3. package/MIGRATING.md +547 -21
  4. package/README.md +46 -33
  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 +155 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +113 -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 +1275 -31
  17. package/configs/oxlint-plugin.test.ts +947 -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 +30 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +91 -0
  75. package/dist/controls/filter-context.d.ts +55 -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 +19 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +44 -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/peer-boundary.test.d.ts +2 -0
  99. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  100. package/dist/controls/props-contract.test.d.ts +2 -0
  101. package/dist/controls/props-contract.test.d.ts.map +1 -0
  102. package/dist/controls/range-filter.d.ts +29 -0
  103. package/dist/controls/range-filter.d.ts.map +1 -0
  104. package/dist/controls/range-filter.js +120 -0
  105. package/dist/controls/search-filter.d.ts +10 -0
  106. package/dist/controls/search-filter.d.ts.map +1 -0
  107. package/dist/controls/search-filter.js +81 -0
  108. package/dist/controls/select-filter.d.ts +74 -0
  109. package/dist/controls/select-filter.d.ts.map +1 -0
  110. package/dist/controls/select-filter.js +10 -0
  111. package/dist/controls/sync-button.d.ts +38 -0
  112. package/dist/controls/sync-button.d.ts.map +1 -0
  113. package/dist/controls/sync-button.js +119 -0
  114. package/dist/controls/sync-button.module.css +63 -0
  115. package/dist/controls/sync-button.module.css.d.ts +11 -0
  116. package/dist/controls/sync-button.test.d.ts +2 -0
  117. package/dist/controls/sync-button.test.d.ts.map +1 -0
  118. package/dist/controls/toggle-filter.d.ts +9 -0
  119. package/dist/controls/toggle-filter.d.ts.map +1 -0
  120. package/dist/controls/toggle-filter.js +42 -0
  121. package/dist/controls/view-tabs.d.ts +23 -0
  122. package/dist/controls/view-tabs.d.ts.map +1 -0
  123. package/dist/controls/view-tabs.js +59 -0
  124. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  125. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  126. package/dist/controls-dates/date-range-picker.js +18 -0
  127. package/dist/controls-dates/index.d.ts +9 -0
  128. package/dist/controls-dates/index.d.ts.map +1 -0
  129. package/dist/controls-dates/index.js +4 -0
  130. package/dist/dashboard/delta-badge.d.ts +6 -12
  131. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  132. package/dist/dashboard/delta-badge.js +1 -50
  133. package/dist/dashboard/query-state.d.ts +1 -1
  134. package/dist/dashboard/settings-section.d.ts +6 -4
  135. package/dist/dashboard/settings-section.d.ts.map +1 -1
  136. package/dist/dashboard/settings-section.js +23 -16
  137. package/dist/dashboard/settings-section.module.css +10 -0
  138. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  139. package/dist/dashboard/settings-section.test.d.ts +2 -0
  140. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  141. package/dist/dashboard/stat-card.d.ts +44 -9
  142. package/dist/dashboard/stat-card.d.ts.map +1 -1
  143. package/dist/dashboard/stat-card.js +80 -48
  144. package/dist/dashboard/stat-card.module.css +82 -0
  145. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  146. package/dist/data/data-table.d.ts +102 -20
  147. package/dist/data/data-table.d.ts.map +1 -1
  148. package/dist/data/data-table.js +166 -65
  149. package/dist/data/table.d.ts +1 -1
  150. package/dist/data/table.d.ts.map +1 -1
  151. package/dist/guard/grace.test.d.ts +20 -0
  152. package/dist/guard/grace.test.d.ts.map +1 -0
  153. package/dist/guard/index.d.ts +66 -3
  154. package/dist/guard/index.d.ts.map +1 -1
  155. package/dist/guard/index.js +82 -7
  156. package/dist/guard/types.d.ts +3 -3
  157. package/dist/guard/types.d.ts.map +1 -1
  158. package/dist/index.d.ts +12 -2
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +13 -10
  161. package/dist/nav/types.d.ts +78 -2
  162. package/dist/nav/types.d.ts.map +1 -1
  163. package/dist/notifications/bell.d.ts +4 -1
  164. package/dist/notifications/bell.d.ts.map +1 -1
  165. package/dist/notifications/index.d.ts +1 -1
  166. package/dist/router-tanstack/field.d.ts +14 -0
  167. package/dist/router-tanstack/field.d.ts.map +1 -0
  168. package/dist/router-tanstack/field.js +4 -0
  169. package/dist/router-tanstack/index.d.ts +6 -2
  170. package/dist/router-tanstack/index.d.ts.map +1 -1
  171. package/dist/router-tanstack/index.js +4 -0
  172. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  173. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  175. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  176. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  177. package/dist/router-tanstack/search-param-store.js +16 -45
  178. package/dist/router-tanstack/search-store.d.ts +81 -0
  179. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.js +187 -0
  181. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  182. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  183. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  184. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  185. package/dist/router-tanstack/use-nav.d.ts +0 -2
  186. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  187. package/dist/router-tanstack/use-nav.js +34 -12
  188. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  189. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  190. package/dist/section/index.d.ts +2 -0
  191. package/dist/section/index.d.ts.map +1 -0
  192. package/dist/section/index.js +4 -0
  193. package/dist/section/section.d.ts +24 -0
  194. package/dist/section/section.d.ts.map +1 -0
  195. package/dist/section/section.js +126 -0
  196. package/dist/section/section.module.css +104 -0
  197. package/dist/section/section.module.css.d.ts +11 -0
  198. package/dist/section/section.test.d.ts +2 -0
  199. package/dist/section/section.test.d.ts.map +1 -0
  200. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  201. package/dist/shell/app-breadcrumbs.js +18 -15
  202. package/dist/shell/app-header.module.css +77 -37
  203. package/dist/shell/app-header.module.css.d.ts +2 -1
  204. package/dist/shell/app-mobile-nav.d.ts +9 -2
  205. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  206. package/dist/shell/app-mobile-nav.js +98 -11
  207. package/dist/shell/app-mobile-nav.module.css +8 -0
  208. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  209. package/dist/shell/app-sidebar.d.ts +55 -28
  210. package/dist/shell/app-sidebar.d.ts.map +1 -1
  211. package/dist/shell/app-sidebar.js +116 -67
  212. package/dist/shell/app-sidebar.module.css +303 -20
  213. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  214. package/dist/shell/index.d.ts +55 -21
  215. package/dist/shell/index.d.ts.map +1 -1
  216. package/dist/shell/index.js +40 -18
  217. package/dist/shell/mobile-nav-model.d.ts +13 -2
  218. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  219. package/dist/shell/mobile-nav-model.js +15 -1
  220. package/dist/shell/nav-count-badge.d.ts +1 -1
  221. package/dist/shell/page-bar.d.ts +51 -0
  222. package/dist/shell/page-bar.d.ts.map +1 -0
  223. package/dist/shell/page-bar.js +154 -0
  224. package/dist/shell/page-bar.module.css +178 -0
  225. package/dist/shell/page-bar.module.css.d.ts +17 -0
  226. package/dist/shell/page-bar.test.d.ts +2 -0
  227. package/dist/shell/page-bar.test.d.ts.map +1 -0
  228. package/dist/shell/sidebar-block-model.d.ts +48 -0
  229. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  230. package/dist/shell/sidebar-block-model.js +39 -0
  231. package/dist/shell/sidebar-blocks.d.ts +32 -0
  232. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  233. package/dist/shell/sidebar-blocks.js +169 -0
  234. package/dist/shell/sidebar-search.d.ts +13 -1
  235. package/dist/shell/sidebar-search.d.ts.map +1 -1
  236. package/dist/shell/sidebar-search.js +34 -1
  237. package/dist/shell/sidebar-search.module.css +26 -0
  238. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  239. package/dist/state/fields.d.ts +392 -0
  240. package/dist/state/fields.d.ts.map +1 -0
  241. package/dist/state/fields.js +369 -0
  242. package/dist/state/fields.test.d.ts +2 -0
  243. package/dist/state/fields.test.d.ts.map +1 -0
  244. package/dist/state/persisted.d.ts +48 -0
  245. package/dist/state/persisted.d.ts.map +1 -0
  246. package/dist/state/persisted.js +105 -0
  247. package/dist/state.d.ts +11 -56
  248. package/dist/state.d.ts.map +1 -1
  249. package/dist/state.js +14 -120
  250. package/dist/styles.css +13 -0
  251. package/dist/surfaces.d.ts +106 -24
  252. package/dist/surfaces.d.ts.map +1 -1
  253. package/dist/surfaces.js +155 -19
  254. package/dist/theme/controls.module.css +16 -1
  255. package/dist/theme/ctl-theme.d.ts +32 -0
  256. package/dist/theme/ctl-theme.d.ts.map +1 -0
  257. package/dist/theme/ctl-theme.js +54 -0
  258. package/dist/theme/ctl-theme.test.d.ts +2 -0
  259. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  260. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  261. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  262. package/dist/theme/divider-law.test.d.ts +2 -0
  263. package/dist/theme/divider-law.test.d.ts.map +1 -0
  264. package/dist/theme/icon-slot.d.ts +44 -0
  265. package/dist/theme/icon-slot.d.ts.map +1 -0
  266. package/dist/theme/icon-slot.js +21 -0
  267. package/dist/theme/icon-slot.module.css +47 -0
  268. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  269. package/dist/theme/icon-slot.test.d.ts +2 -0
  270. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  271. package/dist/theme/index.d.ts +1 -0
  272. package/dist/theme/index.d.ts.map +1 -1
  273. package/dist/theme/index.js +82 -5
  274. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  275. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  276. package/dist/theme/segmented-control.module.css +20 -0
  277. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  278. package/dist/tokens/index.d.ts +12 -0
  279. package/dist/tokens/index.d.ts.map +1 -1
  280. package/dist/tokens/index.js +38 -8
  281. package/dist/tokens/palette.d.ts +97 -61
  282. package/dist/tokens/palette.d.ts.map +1 -1
  283. package/dist/tokens/palette.js +76 -38
  284. package/dist/tokens.css +10 -2
  285. package/dist/widget-header/delta-badge.d.ts +13 -0
  286. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  287. package/dist/widget-header/delta-badge.js +29 -0
  288. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  289. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  290. package/dist/widget-header/index.d.ts +3 -0
  291. package/dist/widget-header/index.d.ts.map +1 -0
  292. package/dist/widget-header/index.js +6 -0
  293. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  294. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  295. package/dist/widget-header/widget-header.d.ts +37 -0
  296. package/dist/widget-header/widget-header.d.ts.map +1 -0
  297. package/dist/widget-header/widget-header.js +77 -0
  298. package/dist/widget-header/widget-header.module.css +335 -0
  299. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  300. package/dist/widget-header/widget-header.test.d.ts +2 -0
  301. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  302. package/llms.txt +30 -17
  303. package/package.json +14 -1
  304. package/src/charts/cursor/resolve.test.ts +29 -1
  305. package/src/charts/cursor/resolve.ts +38 -6
  306. package/src/charts/cursor/scope.tsx +6 -3
  307. package/src/charts/cursor/store.test.ts +19 -1
  308. package/src/charts/cursor/store.ts +10 -5
  309. package/src/charts/hooks/useChartCursor.ts +16 -8
  310. package/src/charts/index.ts +2 -2
  311. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  312. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  313. package/src/charts/primitives/ChartCard.tsx +86 -146
  314. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  315. package/src/cli/check-coverage.test.ts +297 -0
  316. package/src/cli/check-theme.test.ts +38 -14
  317. package/src/cli/index.ts +392 -16
  318. package/src/cli/init-sync.test.ts +97 -0
  319. package/src/connectivity/connectivity-indicator.tsx +16 -9
  320. package/src/content/index.ts +16 -3
  321. package/src/content/prose.module.css +7 -28
  322. package/src/content/prose.module.css.test.ts +8 -24
  323. package/src/controls/actions.module.css +62 -0
  324. package/src/controls/actions.module.css.d.ts +13 -0
  325. package/src/controls/actions.test.tsx +252 -0
  326. package/src/controls/actions.tsx +655 -0
  327. package/src/controls/actions.type-guard.test.ts +63 -0
  328. package/src/controls/compare-filter.tsx +64 -0
  329. package/src/controls/control-group.test.tsx +229 -0
  330. package/src/controls/control-group.tsx +63 -0
  331. package/src/controls/controls.module.css +542 -0
  332. package/src/controls/controls.module.css.d.ts +35 -0
  333. package/src/controls/controls.router.test.tsx +637 -0
  334. package/src/controls/enum-filter.tsx +159 -0
  335. package/src/controls/filter-context.tsx +153 -0
  336. package/src/controls/filter-pill.tsx +120 -0
  337. package/src/controls/filter-set.test.tsx +472 -0
  338. package/src/controls/filter-set.tsx +225 -0
  339. package/src/controls/filter-sheet.tsx +285 -0
  340. package/src/controls/glyphs.tsx +81 -0
  341. package/src/controls/index.ts +48 -0
  342. package/src/controls/multi-select-filter.tsx +140 -0
  343. package/src/controls/peer-boundary.test.ts +85 -0
  344. package/src/controls/props-contract.test.tsx +106 -0
  345. package/src/controls/range-filter.tsx +184 -0
  346. package/src/controls/search-filter.tsx +123 -0
  347. package/src/controls/select-filter.tsx +89 -0
  348. package/src/controls/sync-button.module.css +63 -0
  349. package/src/controls/sync-button.module.css.d.ts +11 -0
  350. package/src/controls/sync-button.test.tsx +183 -0
  351. package/src/controls/sync-button.tsx +197 -0
  352. package/src/controls/toggle-filter.tsx +60 -0
  353. package/src/controls/view-tabs.tsx +110 -0
  354. package/src/controls-dates/date-range-picker.tsx +42 -0
  355. package/src/controls-dates/index.ts +8 -0
  356. package/src/dashboard/delta-badge.tsx +4 -74
  357. package/src/dashboard/query-state.tsx +1 -1
  358. package/src/dashboard/settings-section.module.css +10 -0
  359. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  360. package/src/dashboard/settings-section.test.tsx +56 -0
  361. package/src/dashboard/settings-section.tsx +33 -30
  362. package/src/dashboard/stat-card.module.css +82 -0
  363. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  364. package/src/dashboard/stat-card.test.tsx +67 -1
  365. package/src/dashboard/stat-card.tsx +209 -68
  366. package/src/data/data-table.test.tsx +282 -6
  367. package/src/data/data-table.tsx +429 -94
  368. package/src/data/table.ts +2 -0
  369. package/src/guard/check-source.test.ts +126 -13
  370. package/src/guard/grace.test.ts +149 -0
  371. package/src/guard/index.ts +172 -29
  372. package/src/guard/types.ts +4 -2
  373. package/src/index.ts +46 -5
  374. package/src/nav/types.ts +84 -2
  375. package/src/notifications/bell.tsx +6 -3
  376. package/src/notifications/index.ts +1 -1
  377. package/src/router-tanstack/field.ts +31 -0
  378. package/src/router-tanstack/index.ts +24 -2
  379. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  380. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  381. package/src/router-tanstack/search-param-store.ts +42 -159
  382. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  383. package/src/router-tanstack/search-store.test.ts +418 -0
  384. package/src/router-tanstack/search-store.ts +380 -0
  385. package/src/router-tanstack/use-nav.test.tsx +187 -0
  386. package/src/router-tanstack/use-nav.ts +72 -14
  387. package/src/section/index.ts +1 -0
  388. package/src/section/section.module.css +104 -0
  389. package/src/section/section.module.css.d.ts +11 -0
  390. package/src/section/section.test.tsx +204 -0
  391. package/src/section/section.tsx +220 -0
  392. package/src/shell/app-breadcrumbs.tsx +44 -31
  393. package/src/shell/app-header.module.css +77 -37
  394. package/src/shell/app-header.module.css.d.ts +2 -1
  395. package/src/shell/app-mobile-nav.module.css +8 -0
  396. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  397. package/src/shell/app-mobile-nav.tsx +147 -8
  398. package/src/shell/app-sidebar-account.tsx +2 -2
  399. package/src/shell/app-sidebar.module.css +303 -20
  400. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  401. package/src/shell/app-sidebar.test.tsx +456 -41
  402. package/src/shell/app-sidebar.tsx +276 -140
  403. package/src/shell/index.test.tsx +291 -5
  404. package/src/shell/index.tsx +137 -38
  405. package/src/shell/mobile-nav-model.test.ts +228 -7
  406. package/src/shell/mobile-nav-model.ts +53 -3
  407. package/src/shell/nav-count-badge.tsx +1 -1
  408. package/src/shell/page-bar.module.css +178 -0
  409. package/src/shell/page-bar.module.css.d.ts +17 -0
  410. package/src/shell/page-bar.test.tsx +316 -0
  411. package/src/shell/page-bar.tsx +315 -0
  412. package/src/shell/sidebar-block-model.ts +83 -0
  413. package/src/shell/sidebar-blocks.tsx +301 -0
  414. package/src/shell/sidebar-search.module.css +26 -0
  415. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  416. package/src/shell/sidebar-search.tsx +71 -2
  417. package/src/state/fields.test.tsx +258 -0
  418. package/src/state/fields.ts +892 -0
  419. package/src/state/persisted.ts +211 -0
  420. package/src/state.ts +46 -240
  421. package/src/styles.css +13 -0
  422. package/src/surfaces.ts +203 -36
  423. package/src/theme/controls.module.css +16 -1
  424. package/src/theme/ctl-theme.test.tsx +275 -0
  425. package/src/theme/ctl-theme.tsx +138 -0
  426. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  427. package/src/theme/density-relations.test.ts +106 -51
  428. package/src/theme/divider-law.test.ts +259 -0
  429. package/src/theme/icon-slot.module.css +47 -0
  430. package/src/theme/icon-slot.module.css.d.ts +8 -0
  431. package/src/theme/icon-slot.test.tsx +275 -0
  432. package/src/theme/icon-slot.tsx +59 -0
  433. package/src/theme/index.ts +165 -3
  434. package/src/theme/layout-rhythm-css.test.ts +72 -0
  435. package/src/theme/radius.test.ts +11 -1
  436. package/src/theme/segmented-control.module.css +20 -0
  437. package/src/theme/shadow-surfaces.test.ts +20 -7
  438. package/src/theme/spacing.test.ts +75 -27
  439. package/src/theme/use-basalt-spacing.ts +2 -2
  440. package/src/tokens/density.test.ts +7 -25
  441. package/src/tokens/index.ts +48 -8
  442. package/src/tokens/palette.ts +107 -73
  443. package/src/widget-header/delta-badge.test.tsx +60 -0
  444. package/src/widget-header/delta-badge.tsx +57 -0
  445. package/src/widget-header/index.ts +2 -0
  446. package/src/widget-header/no-mantine.test.ts +33 -0
  447. package/src/widget-header/widget-header.module.css +335 -0
  448. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  449. package/src/widget-header/widget-header.test.tsx +187 -0
  450. package/src/widget-header/widget-header.tsx +168 -0
  451. package/agent/rules/basalt-agent.md +0 -452
  452. package/agent/rules/basalt-app.md +0 -246
  453. package/agent/rules/basalt-commands.md +0 -320
  454. package/agent/rules/basalt-content.md +0 -409
  455. package/agent/rules/basalt-data.md +0 -269
  456. package/agent/rules/basalt-forms.md +0 -186
  457. package/agent/rules/basalt-notifications.md +0 -264
  458. package/agent/rules/basalt-query.md +0 -187
  459. package/agent/rules/basalt-router.md +0 -336
  460. package/dist/content/article-filter-bar.d.ts +0 -40
  461. package/dist/content/article-filter-bar.d.ts.map +0 -1
  462. package/dist/content/article-filter-bar.js +0 -56
  463. package/dist/shell/page-header.d.ts +0 -13
  464. package/dist/shell/page-header.d.ts.map +0 -1
  465. package/dist/shell/page-header.js +0 -23
  466. package/src/content/article-filter-bar.tsx +0 -88
  467. package/src/shell/page-header.tsx +0 -35
@@ -1,5 +1,5 @@
1
1
  import type { MantineSpacing } from '@mantine/core';
2
- import type { ColumnDef, ColumnPinningState, PaginationState, RowData, SortingState } from '@tanstack/react-table';
2
+ import type { ColumnDef, ColumnFiltersState, ColumnPinningState, PaginationState, RowData, SortingState } from '@tanstack/react-table';
3
3
  import type { ReactNode } from 'react';
4
4
  /** Horizontal alignment of a column's header AND its cells. */
5
5
  export type DataTableAlign = 'left' | 'center' | 'right';
@@ -24,16 +24,16 @@ declare module '@tanstack/react-table' {
24
24
  numeral?: boolean;
25
25
  }
26
26
  }
27
- /** A single selectable option inside a {@link DataTableFacet}'s Select/MultiSelect. */
27
+ /** A single selectable option inside a {@link DataTableFacet}'s `EnumFilter`/`MultiSelectFilter` pill. */
28
28
  export type DataTableFacetOption = {
29
29
  /** The raw filter value, compared against the column's stringified cell value. */
30
30
  value: string;
31
- /** Label shown in the Select/MultiSelect dropdown. */
31
+ /** Label shown in the pill's popover. */
32
32
  label: string;
33
33
  };
34
34
  /**
35
- * Declares one faceted column filter, rendered as a Mantine Select (or MultiSelect when
36
- * `multiple` is set) inside the toolbar.
35
+ * Declares one faceted column filter, rendered as an `EnumFilter` pill (or `MultiSelectFilter`
36
+ * when `multiple` is set) inside a `FilterSet` in the toolbar (`docs/CONTROLS-SPEC.md` §3).
37
37
  *
38
38
  * @example
39
39
  * const facets: DataTableFacet[] = [
@@ -45,11 +45,11 @@ export type DataTableFacetOption = {
45
45
  export type DataTableFacet = {
46
46
  /** The TanStack column id — the column's manual `id`, or its `accessorKey` string. */
47
47
  columnId: string;
48
- /** Label shown as the Select/MultiSelect placeholder. */
48
+ /** The pill's label — read at rest (single-select) and as the `Filters`/count fallback (multi). */
49
49
  label: string;
50
50
  /** Selectable options for this facet. */
51
51
  options: DataTableFacetOption[];
52
- /** Render a MultiSelect (any-of match) instead of a single Select (exact match). @default false */
52
+ /** Render a `MultiSelectFilter` (any-of match) instead of an `EnumFilter` (exact match). @default false */
53
53
  multiple?: boolean;
54
54
  };
55
55
  /**
@@ -66,6 +66,16 @@ export type DataTableFacet = {
66
66
  * }
67
67
  */
68
68
  export type BasaltDataTableProps<T> = {
69
+ /**
70
+ * Optional heading rendered above the toolbar via `WidgetHeader tier="widget"`. `count` always
71
+ * reads `table.getRowCount()` (C11) — never a raw `data.length` — so it tracks the row model
72
+ * (post-filter/-pagination) rather than the unfiltered input.
73
+ */
74
+ title?: string;
75
+ /** Optional leading icon, forwarded to `WidgetHeader`. */
76
+ icon?: ReactNode;
77
+ /** Optional muted line rendered below the title row. */
78
+ subtitle?: string;
69
79
  /** Row data array. */
70
80
  data: T[];
71
81
  /**
@@ -74,15 +84,43 @@ export type BasaltDataTableProps<T> = {
74
84
  */
75
85
  columns: ColumnDef<T, any>[];
76
86
  /**
77
- * Enable column sorting via clickable headers.
87
+ * Enable column sorting via clickable headers. Client-side — the rows in `data` are what get
88
+ * reordered.
89
+ *
90
+ * Under `manualPagination` that is one page, so leaving this on without also passing
91
+ * `manualSorting` is a contract violation and throws in dev — see `manualPagination`.
78
92
  * @default true
79
93
  */
80
94
  enableSorting?: boolean;
95
+ /**
96
+ * Hand sorting to the server: the table stops reordering `data` locally and renders it in the
97
+ * order given, while the headers keep toggling `SortingState` and reporting it through
98
+ * `onSortingChange` — which is where you re-request the sorted page.
99
+ *
100
+ * This is the required companion to `manualPagination` whenever sort headers are live; without
101
+ * it a server-paginated table sorts one page and the "of N" bar presents that as a sort of all N.
102
+ * @default false
103
+ * @example
104
+ * <BasaltDataTable
105
+ * data={page.rows}
106
+ * columns={columns}
107
+ * enablePagination
108
+ * manualPagination
109
+ * manualSorting
110
+ * rowCount={page.total}
111
+ * onSortingChange={(s) => refetch({ sort: s[0] })}
112
+ * />
113
+ */
114
+ manualSorting?: boolean;
81
115
  /** Stripe alternate rows. Forwarded to Mantine `Table` — `'odd'`/`'even'` pick the phase. */
82
116
  striped?: boolean | 'odd' | 'even';
83
117
  /** Highlight hovered rows. Forwarded to Mantine `Table`. */
84
118
  highlightOnHover?: boolean;
85
- /** Rendered when `data` is empty. Falls back to a simple message when omitted. */
119
+ /**
120
+ * Rendered when no rows are VISIBLE — `data` is empty, or a filter/page matches none of it.
121
+ * Falls back to a simple message when omitted. (Before 1.25.0 this tracked `data.length` alone,
122
+ * so a search matching nothing rendered a blank `<tbody>` with no message at all.)
123
+ */
86
124
  emptyState?: ReactNode;
87
125
  /**
88
126
  * When true, renders skeleton placeholder rows instead of the empty-state branch.
@@ -134,18 +172,31 @@ export type BasaltDataTableProps<T> = {
134
172
  /** Called whenever the internal global filter value changes. */
135
173
  onGlobalFilterChange?: (value: string) => void;
136
174
  /**
137
- * Faceted column filters, rendered in the toolbar as Mantine Select/MultiSelect controls wired
138
- * to TanStack's per-column `columnFilters` state. The toolbar renders whenever this array is
139
- * non-empty, `enableGlobalFilter` is set, or `toolbarActions` is passed.
175
+ * Faceted column filters, rendered in the toolbar as `EnumFilter`/`MultiSelectFilter` pills
176
+ * inside a `FilterSet`, wired to TanStack's per-column `columnFilters` state. The toolbar
177
+ * renders whenever this array is non-empty, `enableGlobalFilter` is set, or `actions` is passed.
140
178
  * @example
141
179
  * facets={[{ columnId: 'department', label: 'Department', options: departmentOptions }]}
142
180
  */
143
181
  facets?: DataTableFacet[];
182
+ /** Called whenever the faceted column filters change — the seam for server-side faceting. */
183
+ onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
184
+ /**
185
+ * Hand filtering to the server: the table stops narrowing `data` locally and renders every row
186
+ * given, while the search input and facets keep reporting through `onGlobalFilterChange` /
187
+ * `onColumnFiltersChange` — which is where you re-request the filtered page.
188
+ *
189
+ * Required alongside `manualPagination` whenever `enableGlobalFilter` or `facets` is set;
190
+ * without it the controls narrow one page while "of N" keeps counting the whole set.
191
+ * @default false
192
+ */
193
+ manualFiltering?: boolean;
144
194
  /**
145
195
  * Right-aligned toolbar slot (e.g. an "Export" button). Renders the toolbar row even when no
146
- * search input or facets are configured.
196
+ * search input or facets are configured. Renamed from `toolbarActions` — the toolbar (search +
197
+ * facets + this slot) is wrapped in `CtlSlot` (C1/C5), so its controls resolve to the `ctl` tier.
147
198
  */
148
- toolbarActions?: ReactNode;
199
+ actions?: ReactNode;
149
200
  /**
150
201
  * Enables the bottom pagination bar ("Showing X–Y of N", rows-per-page `Select`, Mantine
151
202
  * `Pagination`) wired to TanStack's `pagination` state. Client-side by default — see
@@ -169,6 +220,21 @@ export type BasaltDataTableProps<T> = {
169
220
  * Disables local pagination slicing — pass the already-paginated page of `data` plus `rowCount`
170
221
  * (and optionally `pageCount`) for server-driven pagination. `onPaginationChange` is where you
171
222
  * fetch the next page.
223
+ *
224
+ * **It imposes a contract, enforced at render.** `data` is now one page, but the pagination bar
225
+ * says "Showing 1–25 of 412" — so every OTHER client-side control becomes a claim about 412 rows
226
+ * that it can only make about 25. Each has to be resolved explicitly, because only the call site
227
+ * knows whether the server does the work:
228
+ *
229
+ * - `rowCount` (or `pageCount`) must be given, or "of N" counts the page and the pager collapses
230
+ * to a single page nobody can leave;
231
+ * - sorting must be handed over with `manualSorting`, or switched off with `enableSorting={false}`;
232
+ * - `enableGlobalFilter` / `facets`, if used, must be handed over with `manualFiltering`;
233
+ * - `enablePagination` must be on, or `manualPagination` is inert and the page renders as if it
234
+ * were the whole table.
235
+ *
236
+ * Unresolved, each throws in dev and degrades to the honest render in production (no sort
237
+ * headers, no filter controls, no "of N") — never to the plausible wrong answer.
172
238
  * @default false
173
239
  * @example
174
240
  * <BasaltDataTable
@@ -225,9 +291,20 @@ export type BasaltDataTableProps<T> = {
225
291
  /** Row separators. Forwarded to Mantine `Table`. @default true (Mantine's own default) */
226
292
  withRowBorders?: boolean;
227
293
  /**
228
- * Outer table border. Turn it OFF for a table that already sits inside a `Card`/`ChartCard`,
229
- * where the card owns the frame and a second one reads as a nested box.
230
- * @default true
294
+ * Outer table border — a full box around the table.
295
+ *
296
+ * **`false` by default since this minor** (it was `true`). Three separate faults, all visible on
297
+ * the playground's `Controls (mobile)` page at 390px: its TOP edge sat directly under the table's
298
+ * own `WidgetHeader`, separating a header from its own content; its BOTTOM edge landed on the last
299
+ * row's own hairline, two rules at zero distance; and its left/right edges carried no information
300
+ * at all on a table that fills the page (`docs/DESIGN-SPEC.md` §8 — a border is a layout divider,
301
+ * and there is nothing here to divide). The head-row rule and the between-row rules stay, so the
302
+ * table still reads as a table.
303
+ *
304
+ * Pass `true` for a table that genuinely needs a frame — a small table floating in whitespace
305
+ * beside other content, where the box IS the grouping.
306
+ *
307
+ * @default false
231
308
  */
232
309
  withTableBorder?: boolean;
233
310
  };
@@ -235,8 +312,13 @@ export type BasaltDataTableProps<T> = {
235
312
  * A sortable data table backed by TanStack Table and rendered with Mantine primitives.
236
313
  * Generic over `T` — column types are inferred from `ColumnDef<T>`.
237
314
  *
238
- * Sorting is local/client-side (no `any`). When `enableSorting` is `false` the table
239
- * is read-only. An `emptyState` node is rendered when `data` is empty.
315
+ * Sorting is local/client-side by default (no `any`); `manualSorting` hands it to the server.
316
+ * When `enableSorting` is `false` the table is read-only. An `emptyState` node is rendered
317
+ * whenever no rows are visible — empty `data`, or a filter that matches none of it.
318
+ *
319
+ * `manualPagination` imposes a contract on the rest of the props (see its docblock): every
320
+ * client-side control it would turn into a false claim about the unseen rows must be handed to the
321
+ * server or switched off, and an unresolved one throws in dev rather than misreporting.
240
322
  *
241
323
  * Toolbar (search + facets), pagination, and column pinning are opt-in chrome layered on top —
242
324
  * see the corresponding props below. None of them change rendering unless explicitly enabled.
@@ -251,5 +333,5 @@ export type BasaltDataTableProps<T> = {
251
333
  * emptyState={<Text c="dimmed">No users found.</Text>}
252
334
  * />
253
335
  */
254
- export declare function BasaltDataTable<T>({ data, columns, enableSorting, striped, highlightOnHover, emptyState, isLoading, skeletonRows, initialSorting, onSortingChange, enableGlobalFilter, globalFilterPlaceholder, searchIcon, initialGlobalFilter, onGlobalFilterChange, facets, toolbarActions, enablePagination, pageSizeOptions, initialPagination, onPaginationChange, manualPagination, rowCount, pageCount, enablePinning, initialColumnPinning, maxHeight, minWidth, stickyHeader, stickyHeaderOffset, verticalSpacing, horizontalSpacing, withRowBorders, withTableBorder, }: BasaltDataTableProps<T>): import("react").JSX.Element;
336
+ export declare function BasaltDataTable<T>({ title, icon, subtitle, data, columns, enableSorting, manualSorting, striped, highlightOnHover, emptyState, isLoading, skeletonRows, initialSorting, onSortingChange, enableGlobalFilter, globalFilterPlaceholder, searchIcon, initialGlobalFilter, onGlobalFilterChange, facets, onColumnFiltersChange, manualFiltering, actions, enablePagination, pageSizeOptions, initialPagination, onPaginationChange, manualPagination, rowCount, pageCount, enablePinning, initialColumnPinning, maxHeight, minWidth, stickyHeader, stickyHeaderOffset, verticalSpacing, horizontalSpacing, withRowBorders, withTableBorder, }: BasaltDataTableProps<T>): import("react").JSX.Element;
255
337
  //# sourceMappingURL=data-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/data/data-table.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/data/data-table.tsx"],"names":[],"mappings":"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"}
@@ -1,15 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import {
3
- Box,
4
- Group,
5
- MultiSelect,
6
- Pagination,
7
- Select,
8
- Skeleton,
9
- Table,
10
- Text,
11
- TextInput
12
- } from "@mantine/core";
2
+ import { Box, Group, Pagination, Select, Skeleton, Table, Text, TextInput } from "@mantine/core";
13
3
  import {
14
4
  flexRender,
15
5
  getCoreRowModel,
@@ -19,7 +9,12 @@ import {
19
9
  useReactTable
20
10
  } from "@tanstack/react-table";
21
11
  import { useCallback, useMemo, useState } from "react";
12
+ import { FilterSet } from "../controls/index.js";
13
+ import { EnumFilter } from "../controls/enum-filter.js";
14
+ import { MultiSelectFilter } from "../controls/multi-select-filter.js";
15
+ import { CtlSlot } from "../theme/index.js";
22
16
  import { alpha, VX } from "../tokens/index.js";
17
+ import { WidgetHeader } from "../widget-header/index.js";
23
18
  const ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right"]);
24
19
  function resolveAlign(column) {
25
20
  const align = column.columnDef.meta?.align;
@@ -31,6 +26,44 @@ function resolveAlign(column) {
31
26
  }
32
27
  return align;
33
28
  }
29
+ const FACET_ALL_VALUE = "__basalt_facet_all__";
30
+ function facetAllLabel(facet) {
31
+ return `Any ${facet.label.toLowerCase()}`;
32
+ }
33
+ function facetEnumHandle(column, facet) {
34
+ return {
35
+ kind: "enum",
36
+ fallback: FACET_ALL_VALUE,
37
+ options: [{ value: FACET_ALL_VALUE, label: facetAllLabel(facet) }, ...facet.options],
38
+ use: () => {
39
+ const current = column.getFilterValue() ?? FACET_ALL_VALUE;
40
+ return [
41
+ current,
42
+ (next) => {
43
+ column.setFilterValue(next === FACET_ALL_VALUE ? void 0 : next);
44
+ }
45
+ ];
46
+ },
47
+ isDefault: (value) => value === FACET_ALL_VALUE
48
+ };
49
+ }
50
+ function facetMultiHandle(column, facet) {
51
+ return {
52
+ kind: "multi",
53
+ fallback: [],
54
+ options: facet.options,
55
+ use: () => {
56
+ const current = column.getFilterValue() ?? [];
57
+ return [
58
+ current,
59
+ (next) => {
60
+ column.setFilterValue(next.length > 0 ? [...next] : void 0);
61
+ }
62
+ ];
63
+ },
64
+ isDefault: (value) => value.length === 0
65
+ };
66
+ }
34
67
  function SortIndicator({ direction }) {
35
68
  if (!direction) return null;
36
69
  return /* @__PURE__ */ jsx(Box, { component: "span", ml: 4, "aria-hidden": true, children: direction === "asc" ? "\u2191" : "\u2193" });
@@ -49,6 +82,10 @@ const RANGE_LABEL_STYLE = {
49
82
  color: "var(--vx-faint)"
50
83
  };
51
84
  const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
85
+ const TABLE_HEADER_LEAD_STYLE = { flex: "1 1 auto", minWidth: 0 };
86
+ const TABLE_TOOLBAR_STYLE = { flex: "0 0 auto" };
87
+ const SEARCH_WIDTH = 220;
88
+ const PAGE_SIZE_WIDTH = 116;
52
89
  function getOrderedHeaderGroups(table) {
53
90
  const left = table.getLeftHeaderGroups();
54
91
  const center = table.getCenterHeaderGroups();
@@ -85,10 +122,46 @@ function getPinnedCellStyle(table, column) {
85
122
  boxShadow: isEdge ? `-4px 0 6px -4px ${alpha(VX.ink, 0.18)}` : void 0
86
123
  };
87
124
  }
125
+ function isDev() {
126
+ return process.env["NODE_ENV"] !== "production";
127
+ }
128
+ const BREACH_REMEDY = {
129
+ inert: "`enablePagination` is not set, so `manualPagination` never reaches the table and the server page renders as if it were the whole set, with no bar to say otherwise. Pass `enablePagination`, or drop `manualPagination`.",
130
+ total: 'neither `rowCount` nor `pageCount` was given, so "of N" counts only the rows on this page and the pager collapses to one page nobody can leave. Pass `rowCount={total}`.',
131
+ sorting: 'client-side sorting is still armed. It reorders only the rows in `data` \u2014 one page \u2014 while the header chevron and "of N" together present it as a sort of all of them. Pass `manualSorting` and sort in the request you make from `onSortingChange`, or `enableSorting={false}`.',
132
+ filtering: 'the search input / facets still filter client-side. They narrow only the rows in `data` \u2014 one page \u2014 while "of N" keeps counting the whole set. Pass `manualFiltering` and filter in the request you make from `onGlobalFilterChange` / `onColumnFiltersChange`, or drop `enableGlobalFilter` / `facets`.'
133
+ };
134
+ function manualPaginationBreaches(config) {
135
+ if (!config.manualPagination) return [];
136
+ if (!config.enablePagination) return ["inert"];
137
+ const breaches = [];
138
+ if (config.rowCount === void 0 && config.pageCount === void 0) breaches.push("total");
139
+ const anyColumnMaySort = config.columns.some((column) => column.enableSorting !== false);
140
+ if (config.enableSorting && anyColumnMaySort && !config.manualSorting) breaches.push("sorting");
141
+ if ((config.enableGlobalFilter || config.hasFacets) && !config.manualFiltering)
142
+ breaches.push("filtering");
143
+ return breaches;
144
+ }
145
+ function manualPaginationMessage(breaches) {
146
+ return "BasaltDataTable: `manualPagination` is set, so `data` is one server page \u2014 but:\n \xB7 " + breaches.map((breach) => BREACH_REMEDY[breach]).join("\n \xB7 ") + "\nEach has to be resolved explicitly: only the call site knows whether the server does the work, so there is no default that is not a guess about the data.";
147
+ }
148
+ const reportedBreaches = /* @__PURE__ */ new Set();
149
+ function enforceManualPaginationContract(breaches) {
150
+ if (breaches.length === 0) return;
151
+ const message = manualPaginationMessage(breaches);
152
+ if (isDev()) throw new Error(message);
153
+ if (reportedBreaches.has(message)) return;
154
+ reportedBreaches.add(message);
155
+ console.error(message);
156
+ }
88
157
  function BasaltDataTable({
158
+ title,
159
+ icon,
160
+ subtitle,
89
161
  data,
90
162
  columns,
91
163
  enableSorting = true,
164
+ manualSorting = false,
92
165
  striped,
93
166
  highlightOnHover,
94
167
  emptyState,
@@ -102,7 +175,9 @@ function BasaltDataTable({
102
175
  initialGlobalFilter,
103
176
  onGlobalFilterChange,
104
177
  facets,
105
- toolbarActions,
178
+ onColumnFiltersChange,
179
+ manualFiltering = false,
180
+ actions,
106
181
  enablePagination = false,
107
182
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
108
183
  initialPagination,
@@ -119,7 +194,7 @@ function BasaltDataTable({
119
194
  verticalSpacing,
120
195
  horizontalSpacing,
121
196
  withRowBorders,
122
- withTableBorder = true
197
+ withTableBorder = false
123
198
  }) {
124
199
  const [sorting, setSorting] = useState(initialSorting ?? []);
125
200
  const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter ?? "");
@@ -148,6 +223,16 @@ function BasaltDataTable({
148
223
  },
149
224
  [onGlobalFilterChange]
150
225
  );
226
+ const handleColumnFiltersChange = useCallback(
227
+ (updater) => {
228
+ setColumnFilters((prev) => {
229
+ const next = typeof updater === "function" ? updater(prev) : updater;
230
+ onColumnFiltersChange?.(next);
231
+ return next;
232
+ });
233
+ },
234
+ [onColumnFiltersChange]
235
+ );
151
236
  const handlePaginationChange = useCallback(
152
237
  (updater) => {
153
238
  setPagination((prev) => {
@@ -170,6 +255,23 @@ function BasaltDataTable({
170
255
  return { ...column, filterFn };
171
256
  });
172
257
  }, [columns, facets]);
258
+ const hasFacets = Boolean(facets && facets.length > 0);
259
+ const breaches = manualPaginationBreaches({
260
+ columns,
261
+ enablePagination,
262
+ manualPagination,
263
+ rowCount,
264
+ pageCount,
265
+ enableSorting,
266
+ manualSorting,
267
+ enableGlobalFilter,
268
+ hasFacets,
269
+ manualFiltering
270
+ });
271
+ enforceManualPaginationContract(breaches);
272
+ const sortingEnabled = enableSorting && !breaches.includes("sorting");
273
+ const filteringEnabled = !breaches.includes("filtering");
274
+ const totalIsAuthoritative = !breaches.includes("total");
173
275
  const table = useReactTable({
174
276
  data,
175
277
  columns: tableColumns,
@@ -182,23 +284,33 @@ function BasaltDataTable({
182
284
  },
183
285
  onSortingChange: handleSortingChange,
184
286
  onGlobalFilterChange: handleGlobalFilterChange,
185
- onColumnFiltersChange: setColumnFilters,
287
+ onColumnFiltersChange: handleColumnFiltersChange,
186
288
  onColumnPinningChange: setColumnPinning,
187
289
  ...enablePagination && { onPaginationChange: handlePaginationChange },
188
290
  getCoreRowModel: getCoreRowModel(),
189
291
  getSortedRowModel: getSortedRowModel(),
190
292
  getFilteredRowModel: getFilteredRowModel(),
293
+ // Both row models stay installed and are BYPASSED by these flags, which is TanStack's own
294
+ // seam — so a degraded table renders `data` in the order and completeness the server sent it.
295
+ // Forced only by a BREACH, never by `enableSorting={false}` on its own: that pair still applies
296
+ // an `initialSorting` today (it disables the header control, not the row model) and a table
297
+ // that is not misreporting has no business losing it.
298
+ manualSorting: manualSorting || breaches.includes("sorting"),
299
+ manualFiltering: manualFiltering || breaches.includes("filtering"),
191
300
  ...enablePagination && {
192
301
  getPaginationRowModel: getPaginationRowModel(),
193
302
  manualPagination,
194
303
  ...manualPagination && rowCount !== void 0 && { rowCount },
195
304
  ...manualPagination && pageCount !== void 0 && { pageCount }
196
305
  },
197
- enableSorting,
306
+ enableSorting: sortingEnabled,
198
307
  enableColumnPinning: enablePinning
199
308
  });
200
- const showToolbar = enableGlobalFilter || Boolean(facets && facets.length > 0) || Boolean(toolbarActions);
309
+ const showSearch = enableGlobalFilter && filteringEnabled;
310
+ const showFacets = hasFacets && filteringEnabled;
311
+ const showToolbar = showSearch || showFacets || Boolean(actions);
201
312
  const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups();
313
+ const rows = table.getRowModel().rows;
202
314
  const tableNode = /* @__PURE__ */ jsxs(
203
315
  Table,
204
316
  {
@@ -212,7 +324,7 @@ function BasaltDataTable({
212
324
  withTableBorder,
213
325
  children: [
214
326
  /* @__PURE__ */ jsx(Table.Thead, { children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(Table.Tr, { children: headerGroup.headers.map((header) => {
215
- const canSort = enableSorting && header.column.getCanSort();
327
+ const canSort = sortingEnabled && header.column.getCanSort();
216
328
  const sorted = header.column.getIsSorted();
217
329
  const toggleSorting = header.column.getToggleSortingHandler();
218
330
  const pinnedStyle = enablePinning ? getPinnedCellStyle(table, header.column) : void 0;
@@ -241,7 +353,7 @@ function BasaltDataTable({
241
353
  header.id
242
354
  );
243
355
  }) }, headerGroup.id)) }),
244
- /* @__PURE__ */ jsx(Table.Tbody, { children: isLoading ? Array.from({ length: skeletonRows }, (_, rowIndex) => /* @__PURE__ */ jsx(Table.Tr, { children: columns.map((_2, colIndex) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Skeleton, { height: 16, radius: "sm" }) }, `skeleton-${rowIndex}-${colIndex}`)) }, `skeleton-${rowIndex}`)) : data.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columns.length, children: emptyState ?? /* @__PURE__ */ jsx(Text, { c: "dimmed", ta: "center", size: "sm", py: "sm", children: "No data to display." }) }) }) : table.getRowModel().rows.map((row) => {
356
+ /* @__PURE__ */ jsx(Table.Tbody, { children: isLoading ? Array.from({ length: skeletonRows }, (_, rowIndex) => /* @__PURE__ */ jsx(Table.Tr, { children: columns.map((_2, colIndex) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Skeleton, { height: 16, radius: "sm" }) }, `skeleton-${rowIndex}-${colIndex}`)) }, `skeleton-${rowIndex}`)) : rows.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columns.length, children: emptyState ?? /* @__PURE__ */ jsx(Text, { c: "dimmed", ta: "center", size: "sm", py: "sm", children: "No data to display." }) }) }) : rows.map((row) => {
245
357
  const cells = enablePinning ? [
246
358
  ...row.getLeftVisibleCells(),
247
359
  ...row.getCenterVisibleCells(),
@@ -269,62 +381,55 @@ function BasaltDataTable({
269
381
  );
270
382
  const scrolls = maxHeight !== void 0 || minWidth !== void 0;
271
383
  const paginationState = table.getState().pagination;
384
+ const pageSizeRows = [.../* @__PURE__ */ new Set([...pageSizeOptions, paginationState.pageSize])].sort((a, b) => a - b).map((size) => ({ value: String(size), label: `${size} / page` }));
272
385
  const total = table.getRowCount();
273
386
  const rangeStart = total === 0 ? 0 : paginationState.pageIndex * paginationState.pageSize + 1;
274
387
  const rangeEnd = Math.min((paginationState.pageIndex + 1) * paginationState.pageSize, total);
275
388
  return /* @__PURE__ */ jsxs(Fragment, { children: [
276
- showToolbar && /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-end", wrap: "wrap", gap: "xs", mb: "xs", children: [
277
- /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", align: "flex-end", children: [
278
- enableGlobalFilter && /* @__PURE__ */ jsx(
389
+ (title !== void 0 || showToolbar) && /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", wrap: "wrap", gap: "xs", mb: "xs", children: [
390
+ title !== void 0 ? /* @__PURE__ */ jsx(Box, { style: TABLE_HEADER_LEAD_STYLE, children: /* @__PURE__ */ jsx(
391
+ WidgetHeader,
392
+ {
393
+ tier: "widget",
394
+ title,
395
+ ...icon !== void 0 && { icon },
396
+ ...subtitle !== void 0 && { subtitle },
397
+ count: table.getRowCount()
398
+ }
399
+ ) }) : null,
400
+ showToolbar && /* @__PURE__ */ jsx(CtlSlot, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", align: "center", style: TABLE_TOOLBAR_STYLE, children: [
401
+ showSearch && /* @__PURE__ */ jsx(
279
402
  TextInput,
280
403
  {
281
- size: "xs",
282
404
  radius: "md",
405
+ w: SEARCH_WIDTH,
283
406
  placeholder: globalFilterPlaceholder,
284
407
  leftSection: searchIcon,
285
408
  value: globalFilter,
286
- onChange: (event) => handleGlobalFilterChange(event.currentTarget.value),
287
- w: 220
409
+ onChange: (event) => handleGlobalFilterChange(event.currentTarget.value)
288
410
  }
289
411
  ),
290
- facets?.map((facet) => {
412
+ showFacets && /* @__PURE__ */ jsx(FilterSet, { children: facets?.map((facet) => {
291
413
  const column = table.getColumn(facet.columnId);
292
414
  if (!column) return null;
293
- if (facet.multiple) {
294
- const value2 = column.getFilterValue() ?? [];
295
- return /* @__PURE__ */ jsx(
296
- MultiSelect,
297
- {
298
- size: "xs",
299
- radius: "md",
300
- placeholder: facet.label,
301
- data: facet.options,
302
- value: value2,
303
- onChange: (next) => column.setFilterValue(next.length > 0 ? next : void 0),
304
- clearable: true,
305
- w: 200
306
- },
307
- facet.columnId
308
- );
309
- }
310
- const value = column.getFilterValue() ?? null;
311
- return /* @__PURE__ */ jsx(
312
- Select,
415
+ return facet.multiple ? /* @__PURE__ */ jsx(
416
+ MultiSelectFilter,
313
417
  {
314
- size: "xs",
315
- radius: "md",
316
- placeholder: facet.label,
317
- data: facet.options,
318
- value,
319
- onChange: (next) => column.setFilterValue(next ?? void 0),
320
- clearable: true,
321
- w: 180
418
+ field: facetMultiHandle(column, facet),
419
+ label: facet.label
420
+ },
421
+ facet.columnId
422
+ ) : /* @__PURE__ */ jsx(
423
+ EnumFilter,
424
+ {
425
+ field: facetEnumHandle(column, facet),
426
+ label: facet.label
322
427
  },
323
428
  facet.columnId
324
429
  );
325
- })
326
- ] }),
327
- toolbarActions
430
+ }) }),
431
+ actions
432
+ ] }) })
328
433
  ] }),
329
434
  scrolls ? /* @__PURE__ */ jsx(
330
435
  Table.ScrollContainer,
@@ -341,23 +446,19 @@ function BasaltDataTable({
341
446
  rangeStart,
342
447
  "\u2013",
343
448
  rangeEnd,
344
- " of ",
345
- total
449
+ totalIsAuthoritative ? ` of ${total}` : ""
346
450
  ] }),
347
451
  /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "center", children: [
348
452
  /* @__PURE__ */ jsx(
349
453
  Select,
350
454
  {
351
- size: "xs",
455
+ size: "ctl",
352
456
  radius: "md",
353
- data: pageSizeOptions.map((size) => ({
354
- value: String(size),
355
- label: `${size} / page`
356
- })),
457
+ w: PAGE_SIZE_WIDTH,
458
+ data: pageSizeRows,
357
459
  value: String(paginationState.pageSize),
358
460
  onChange: (value) => value && table.setPageSize(Number(value)),
359
- allowDeselect: false,
360
- w: 110
461
+ allowDeselect: false
361
462
  }
362
463
  ),
363
464
  /* @__PURE__ */ jsx(
@@ -20,5 +20,5 @@
20
20
  */
21
21
  export { BasaltDataTable } from './data-table.js';
22
22
  export type { BasaltDataTableProps, DataTableAlign, DataTableFacet, DataTableFacetOption, } from './data-table.js';
23
- export { createColumnHelper, useReactTable, flexRender, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, type ColumnDef, type SortingState, type ColumnHelper, type PaginationState, type ColumnPinningState, } from '@tanstack/react-table';
23
+ export { createColumnHelper, useReactTable, flexRender, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, type ColumnDef, type SortingState, type ColumnHelper, type PaginationState, type ColumnPinningState, type ColumnFiltersState, } from '@tanstack/react-table';
24
24
  //# sourceMappingURL=table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/data/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,cAAc,CAAA;AASrB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/data/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,cAAc,CAAA;AASrB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAEvB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,20 @@
1
+ type GraceEntry = {
2
+ since: string;
3
+ promote: string;
4
+ why: string;
5
+ };
6
+ /**
7
+ * Tiny local semver compare — no dependency. Assumes plain `x.y.z`, no pre-release identifiers
8
+ * (basalt-ui bans majors and ships no pre-releases). Returns negative when `a` sorts before `b`,
9
+ * positive when after, zero when equal. Compares each component numerically, not lexically, so
10
+ * `'1.9.0' < '1.10.0'` and `'1.3.0' < '1.26.0'` — a string compare would get both backwards.
11
+ */
12
+ export declare function compareSemver(a: string, b: string): number;
13
+ /**
14
+ * The C16 gate itself, extracted so it can be exercised against a synthetic ledger as well as the
15
+ * real one. Throws naming the first offending entry when either invariant breaks: `since` must
16
+ * precede `promote` for every entry, and no entry's `promote` may be `<=` the given `version`.
17
+ */
18
+ export declare function assertGraceLedger(ledger: Record<string, GraceEntry>, version: string): void;
19
+ export {};
20
+ //# sourceMappingURL=grace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grace.test.d.ts","sourceRoot":"","sources":["../../src/guard/grace.test.ts"],"names":[],"mappings":"AA8BA,KAAK,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjE;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAc3F"}