basalt-ui 1.25.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 (462) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +18 -0
  3. package/MIGRATING.md +546 -20
  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 +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/settings-section.d.ts +6 -4
  134. package/dist/dashboard/settings-section.d.ts.map +1 -1
  135. package/dist/dashboard/settings-section.js +23 -16
  136. package/dist/dashboard/settings-section.module.css +10 -0
  137. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  138. package/dist/dashboard/settings-section.test.d.ts +2 -0
  139. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  140. package/dist/dashboard/stat-card.d.ts +44 -9
  141. package/dist/dashboard/stat-card.d.ts.map +1 -1
  142. package/dist/dashboard/stat-card.js +80 -48
  143. package/dist/dashboard/stat-card.module.css +82 -0
  144. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  145. package/dist/data/data-table.d.ts +37 -15
  146. package/dist/data/data-table.d.ts.map +1 -1
  147. package/dist/data/data-table.js +88 -58
  148. package/dist/guard/grace.test.d.ts +20 -0
  149. package/dist/guard/grace.test.d.ts.map +1 -0
  150. package/dist/guard/index.d.ts +66 -3
  151. package/dist/guard/index.d.ts.map +1 -1
  152. package/dist/guard/index.js +82 -7
  153. package/dist/guard/types.d.ts +3 -3
  154. package/dist/guard/types.d.ts.map +1 -1
  155. package/dist/index.d.ts +12 -2
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +13 -10
  158. package/dist/nav/types.d.ts +78 -2
  159. package/dist/nav/types.d.ts.map +1 -1
  160. package/dist/notifications/bell.d.ts +4 -1
  161. package/dist/notifications/bell.d.ts.map +1 -1
  162. package/dist/notifications/index.d.ts +1 -1
  163. package/dist/router-tanstack/field.d.ts +14 -0
  164. package/dist/router-tanstack/field.d.ts.map +1 -0
  165. package/dist/router-tanstack/field.js +4 -0
  166. package/dist/router-tanstack/index.d.ts +6 -2
  167. package/dist/router-tanstack/index.d.ts.map +1 -1
  168. package/dist/router-tanstack/index.js +4 -0
  169. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  170. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  171. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  172. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  173. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/search-param-store.js +16 -45
  175. package/dist/router-tanstack/search-store.d.ts +81 -0
  176. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  177. package/dist/router-tanstack/search-store.js +187 -0
  178. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  179. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  181. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  182. package/dist/router-tanstack/use-nav.d.ts +0 -2
  183. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  184. package/dist/router-tanstack/use-nav.js +34 -12
  185. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  186. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  187. package/dist/section/index.d.ts +2 -0
  188. package/dist/section/index.d.ts.map +1 -0
  189. package/dist/section/index.js +4 -0
  190. package/dist/section/section.d.ts +24 -0
  191. package/dist/section/section.d.ts.map +1 -0
  192. package/dist/section/section.js +126 -0
  193. package/dist/section/section.module.css +104 -0
  194. package/dist/section/section.module.css.d.ts +11 -0
  195. package/dist/section/section.test.d.ts +2 -0
  196. package/dist/section/section.test.d.ts.map +1 -0
  197. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  198. package/dist/shell/app-breadcrumbs.js +18 -15
  199. package/dist/shell/app-header.module.css +77 -37
  200. package/dist/shell/app-header.module.css.d.ts +2 -1
  201. package/dist/shell/app-mobile-nav.d.ts +9 -2
  202. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  203. package/dist/shell/app-mobile-nav.js +98 -11
  204. package/dist/shell/app-mobile-nav.module.css +8 -0
  205. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  206. package/dist/shell/app-sidebar.d.ts +55 -28
  207. package/dist/shell/app-sidebar.d.ts.map +1 -1
  208. package/dist/shell/app-sidebar.js +116 -67
  209. package/dist/shell/app-sidebar.module.css +303 -20
  210. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  211. package/dist/shell/index.d.ts +55 -21
  212. package/dist/shell/index.d.ts.map +1 -1
  213. package/dist/shell/index.js +40 -18
  214. package/dist/shell/mobile-nav-model.d.ts +13 -2
  215. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  216. package/dist/shell/mobile-nav-model.js +15 -1
  217. package/dist/shell/nav-count-badge.d.ts +1 -1
  218. package/dist/shell/page-bar.d.ts +51 -0
  219. package/dist/shell/page-bar.d.ts.map +1 -0
  220. package/dist/shell/page-bar.js +154 -0
  221. package/dist/shell/page-bar.module.css +178 -0
  222. package/dist/shell/page-bar.module.css.d.ts +17 -0
  223. package/dist/shell/page-bar.test.d.ts +2 -0
  224. package/dist/shell/page-bar.test.d.ts.map +1 -0
  225. package/dist/shell/sidebar-block-model.d.ts +48 -0
  226. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  227. package/dist/shell/sidebar-block-model.js +39 -0
  228. package/dist/shell/sidebar-blocks.d.ts +32 -0
  229. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  230. package/dist/shell/sidebar-blocks.js +169 -0
  231. package/dist/shell/sidebar-search.d.ts +13 -1
  232. package/dist/shell/sidebar-search.d.ts.map +1 -1
  233. package/dist/shell/sidebar-search.js +34 -1
  234. package/dist/shell/sidebar-search.module.css +26 -0
  235. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  236. package/dist/state/fields.d.ts +392 -0
  237. package/dist/state/fields.d.ts.map +1 -0
  238. package/dist/state/fields.js +369 -0
  239. package/dist/state/fields.test.d.ts +2 -0
  240. package/dist/state/fields.test.d.ts.map +1 -0
  241. package/dist/state/persisted.d.ts +48 -0
  242. package/dist/state/persisted.d.ts.map +1 -0
  243. package/dist/state/persisted.js +105 -0
  244. package/dist/state.d.ts +11 -56
  245. package/dist/state.d.ts.map +1 -1
  246. package/dist/state.js +14 -120
  247. package/dist/styles.css +13 -0
  248. package/dist/surfaces.d.ts +106 -24
  249. package/dist/surfaces.d.ts.map +1 -1
  250. package/dist/surfaces.js +155 -19
  251. package/dist/theme/controls.module.css +16 -1
  252. package/dist/theme/ctl-theme.d.ts +32 -0
  253. package/dist/theme/ctl-theme.d.ts.map +1 -0
  254. package/dist/theme/ctl-theme.js +54 -0
  255. package/dist/theme/ctl-theme.test.d.ts +2 -0
  256. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  257. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  258. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  259. package/dist/theme/divider-law.test.d.ts +2 -0
  260. package/dist/theme/divider-law.test.d.ts.map +1 -0
  261. package/dist/theme/icon-slot.d.ts +44 -0
  262. package/dist/theme/icon-slot.d.ts.map +1 -0
  263. package/dist/theme/icon-slot.js +21 -0
  264. package/dist/theme/icon-slot.module.css +47 -0
  265. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  266. package/dist/theme/icon-slot.test.d.ts +2 -0
  267. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  268. package/dist/theme/index.d.ts +1 -0
  269. package/dist/theme/index.d.ts.map +1 -1
  270. package/dist/theme/index.js +82 -5
  271. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  272. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  273. package/dist/theme/segmented-control.module.css +20 -0
  274. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  275. package/dist/tokens/index.d.ts +12 -0
  276. package/dist/tokens/index.d.ts.map +1 -1
  277. package/dist/tokens/index.js +38 -8
  278. package/dist/tokens/palette.d.ts +97 -61
  279. package/dist/tokens/palette.d.ts.map +1 -1
  280. package/dist/tokens/palette.js +76 -38
  281. package/dist/tokens.css +10 -2
  282. package/dist/widget-header/delta-badge.d.ts +13 -0
  283. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  284. package/dist/widget-header/delta-badge.js +29 -0
  285. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  286. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  287. package/dist/widget-header/index.d.ts +3 -0
  288. package/dist/widget-header/index.d.ts.map +1 -0
  289. package/dist/widget-header/index.js +6 -0
  290. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  291. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  292. package/dist/widget-header/widget-header.d.ts +37 -0
  293. package/dist/widget-header/widget-header.d.ts.map +1 -0
  294. package/dist/widget-header/widget-header.js +77 -0
  295. package/dist/widget-header/widget-header.module.css +335 -0
  296. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  297. package/dist/widget-header/widget-header.test.d.ts +2 -0
  298. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  299. package/llms.txt +30 -17
  300. package/package.json +14 -1
  301. package/src/charts/cursor/resolve.test.ts +29 -1
  302. package/src/charts/cursor/resolve.ts +38 -6
  303. package/src/charts/cursor/scope.tsx +6 -3
  304. package/src/charts/cursor/store.test.ts +19 -1
  305. package/src/charts/cursor/store.ts +10 -5
  306. package/src/charts/hooks/useChartCursor.ts +16 -8
  307. package/src/charts/index.ts +2 -2
  308. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  309. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  310. package/src/charts/primitives/ChartCard.tsx +86 -146
  311. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  312. package/src/cli/check-coverage.test.ts +297 -0
  313. package/src/cli/check-theme.test.ts +38 -14
  314. package/src/cli/index.ts +392 -16
  315. package/src/cli/init-sync.test.ts +97 -0
  316. package/src/connectivity/connectivity-indicator.tsx +16 -9
  317. package/src/content/index.ts +16 -3
  318. package/src/content/prose.module.css +7 -28
  319. package/src/content/prose.module.css.test.ts +8 -24
  320. package/src/controls/actions.module.css +62 -0
  321. package/src/controls/actions.module.css.d.ts +13 -0
  322. package/src/controls/actions.test.tsx +252 -0
  323. package/src/controls/actions.tsx +655 -0
  324. package/src/controls/actions.type-guard.test.ts +63 -0
  325. package/src/controls/compare-filter.tsx +64 -0
  326. package/src/controls/control-group.test.tsx +229 -0
  327. package/src/controls/control-group.tsx +63 -0
  328. package/src/controls/controls.module.css +542 -0
  329. package/src/controls/controls.module.css.d.ts +35 -0
  330. package/src/controls/controls.router.test.tsx +637 -0
  331. package/src/controls/enum-filter.tsx +159 -0
  332. package/src/controls/filter-context.tsx +153 -0
  333. package/src/controls/filter-pill.tsx +120 -0
  334. package/src/controls/filter-set.test.tsx +472 -0
  335. package/src/controls/filter-set.tsx +225 -0
  336. package/src/controls/filter-sheet.tsx +285 -0
  337. package/src/controls/glyphs.tsx +81 -0
  338. package/src/controls/index.ts +48 -0
  339. package/src/controls/multi-select-filter.tsx +140 -0
  340. package/src/controls/peer-boundary.test.ts +85 -0
  341. package/src/controls/props-contract.test.tsx +106 -0
  342. package/src/controls/range-filter.tsx +184 -0
  343. package/src/controls/search-filter.tsx +123 -0
  344. package/src/controls/select-filter.tsx +89 -0
  345. package/src/controls/sync-button.module.css +63 -0
  346. package/src/controls/sync-button.module.css.d.ts +11 -0
  347. package/src/controls/sync-button.test.tsx +183 -0
  348. package/src/controls/sync-button.tsx +197 -0
  349. package/src/controls/toggle-filter.tsx +60 -0
  350. package/src/controls/view-tabs.tsx +110 -0
  351. package/src/controls-dates/date-range-picker.tsx +42 -0
  352. package/src/controls-dates/index.ts +8 -0
  353. package/src/dashboard/delta-badge.tsx +4 -74
  354. package/src/dashboard/settings-section.module.css +10 -0
  355. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  356. package/src/dashboard/settings-section.test.tsx +56 -0
  357. package/src/dashboard/settings-section.tsx +33 -30
  358. package/src/dashboard/stat-card.module.css +82 -0
  359. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  360. package/src/dashboard/stat-card.test.tsx +67 -1
  361. package/src/dashboard/stat-card.tsx +209 -68
  362. package/src/data/data-table.test.tsx +96 -11
  363. package/src/data/data-table.tsx +220 -84
  364. package/src/guard/check-source.test.ts +126 -13
  365. package/src/guard/grace.test.ts +149 -0
  366. package/src/guard/index.ts +172 -29
  367. package/src/guard/types.ts +4 -2
  368. package/src/index.ts +46 -5
  369. package/src/nav/types.ts +84 -2
  370. package/src/notifications/bell.tsx +6 -3
  371. package/src/notifications/index.ts +1 -1
  372. package/src/router-tanstack/field.ts +31 -0
  373. package/src/router-tanstack/index.ts +24 -2
  374. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  375. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  376. package/src/router-tanstack/search-param-store.ts +42 -159
  377. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  378. package/src/router-tanstack/search-store.test.ts +418 -0
  379. package/src/router-tanstack/search-store.ts +380 -0
  380. package/src/router-tanstack/use-nav.test.tsx +187 -0
  381. package/src/router-tanstack/use-nav.ts +72 -14
  382. package/src/section/index.ts +1 -0
  383. package/src/section/section.module.css +104 -0
  384. package/src/section/section.module.css.d.ts +11 -0
  385. package/src/section/section.test.tsx +204 -0
  386. package/src/section/section.tsx +220 -0
  387. package/src/shell/app-breadcrumbs.tsx +44 -31
  388. package/src/shell/app-header.module.css +77 -37
  389. package/src/shell/app-header.module.css.d.ts +2 -1
  390. package/src/shell/app-mobile-nav.module.css +8 -0
  391. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  392. package/src/shell/app-mobile-nav.tsx +147 -8
  393. package/src/shell/app-sidebar-account.tsx +2 -2
  394. package/src/shell/app-sidebar.module.css +303 -20
  395. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  396. package/src/shell/app-sidebar.test.tsx +456 -41
  397. package/src/shell/app-sidebar.tsx +276 -140
  398. package/src/shell/index.test.tsx +291 -5
  399. package/src/shell/index.tsx +137 -38
  400. package/src/shell/mobile-nav-model.test.ts +228 -7
  401. package/src/shell/mobile-nav-model.ts +53 -3
  402. package/src/shell/nav-count-badge.tsx +1 -1
  403. package/src/shell/page-bar.module.css +178 -0
  404. package/src/shell/page-bar.module.css.d.ts +17 -0
  405. package/src/shell/page-bar.test.tsx +316 -0
  406. package/src/shell/page-bar.tsx +315 -0
  407. package/src/shell/sidebar-block-model.ts +83 -0
  408. package/src/shell/sidebar-blocks.tsx +301 -0
  409. package/src/shell/sidebar-search.module.css +26 -0
  410. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  411. package/src/shell/sidebar-search.tsx +71 -2
  412. package/src/state/fields.test.tsx +258 -0
  413. package/src/state/fields.ts +892 -0
  414. package/src/state/persisted.ts +211 -0
  415. package/src/state.ts +46 -240
  416. package/src/styles.css +13 -0
  417. package/src/surfaces.ts +203 -36
  418. package/src/theme/controls.module.css +16 -1
  419. package/src/theme/ctl-theme.test.tsx +275 -0
  420. package/src/theme/ctl-theme.tsx +138 -0
  421. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  422. package/src/theme/density-relations.test.ts +106 -51
  423. package/src/theme/divider-law.test.ts +259 -0
  424. package/src/theme/icon-slot.module.css +47 -0
  425. package/src/theme/icon-slot.module.css.d.ts +8 -0
  426. package/src/theme/icon-slot.test.tsx +275 -0
  427. package/src/theme/icon-slot.tsx +59 -0
  428. package/src/theme/index.ts +165 -3
  429. package/src/theme/layout-rhythm-css.test.ts +72 -0
  430. package/src/theme/radius.test.ts +11 -1
  431. package/src/theme/segmented-control.module.css +20 -0
  432. package/src/theme/shadow-surfaces.test.ts +20 -7
  433. package/src/theme/spacing.test.ts +75 -27
  434. package/src/theme/use-basalt-spacing.ts +2 -2
  435. package/src/tokens/density.test.ts +7 -25
  436. package/src/tokens/index.ts +48 -8
  437. package/src/tokens/palette.ts +107 -73
  438. package/src/widget-header/delta-badge.test.tsx +60 -0
  439. package/src/widget-header/delta-badge.tsx +57 -0
  440. package/src/widget-header/index.ts +2 -0
  441. package/src/widget-header/no-mantine.test.ts +33 -0
  442. package/src/widget-header/widget-header.module.css +335 -0
  443. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  444. package/src/widget-header/widget-header.test.tsx +187 -0
  445. package/src/widget-header/widget-header.tsx +168 -0
  446. package/agent/rules/basalt-agent.md +0 -452
  447. package/agent/rules/basalt-app.md +0 -246
  448. package/agent/rules/basalt-commands.md +0 -320
  449. package/agent/rules/basalt-content.md +0 -409
  450. package/agent/rules/basalt-data.md +0 -329
  451. package/agent/rules/basalt-forms.md +0 -186
  452. package/agent/rules/basalt-notifications.md +0 -264
  453. package/agent/rules/basalt-query.md +0 -187
  454. package/agent/rules/basalt-router.md +0 -336
  455. package/dist/content/article-filter-bar.d.ts +0 -40
  456. package/dist/content/article-filter-bar.d.ts.map +0 -1
  457. package/dist/content/article-filter-bar.js +0 -56
  458. package/dist/shell/page-header.d.ts +0 -13
  459. package/dist/shell/page-header.d.ts.map +0 -1
  460. package/dist/shell/page-header.js +0 -23
  461. package/src/content/article-filter-bar.tsx +0 -88
  462. package/src/shell/page-header.tsx +0 -35
@@ -1,336 +0,0 @@
1
- ---
2
- source: basalt-ui
3
- description: TanStack Router conventions for basalt-ui apps — file-based routes, typed search params, loader/query coupling, and the one typed nav definition that drives both the desktop sidebar and the mobile bar. Advisory (ships an optional ./router-tanstack adapter).
4
- paths:
5
- - 'src/routes/**'
6
- - 'apps/**/src/routes/**'
7
- ---
8
-
9
- # Basalt Router — TanStack Router Conventions
10
-
11
- basalt-ui is **router-agnostic** — the shell renders every pixel of nav chrome and hosts the
12
- consumer's router component through one seam, `SidebarItem.Anchor` (a **`NavAnchor`**). With
13
- TanStack Router you never write that seam by hand: **`defineNav`** declares the whole navigation
14
- once and **`useNav`** resolves it against the live router, returning the two props `BasaltShell`
15
- needs. Breadcrumbs come from **`useRouterBreadcrumbs`**, ad-hoc active checks from
16
- **`useBasaltNav`**. This rule is the recommended opinion layer when you use TanStack Router (the
17
- basalt-ui default). It is **advisory** — adopt it for consistency; nothing in the framework
18
- enforces it.
19
-
20
- ## Adding a page
21
-
22
- 1. Create `src/routes/<page-name>.tsx`.
23
- 2. Export `Route` via `createFileRoute('/<page-name>')`.
24
- 3. Add `validateSearch` with a Zod schema parsed via a function — not the schema passed directly.
25
- 4. Define `loaderDeps` to forward search params to the loader (required for search-param-driven queries).
26
- 5. Add a `loader` that calls `ensureQueryData` for each query the page needs.
27
- 6. Register the page in your `defineNav` definition (see "One typed nav definition" below).
28
-
29
- ## Route structure
30
-
31
- ```ts
32
- import { createFileRoute } from '@tanstack/react-router'
33
- import { useSuspenseQuery } from 'basalt-ui/query'
34
- import { z } from 'zod'
35
- import { resourceQueries } from '../lib/queries/resource'
36
-
37
- const SearchSchema = z.object({
38
- window: z.enum(['7d', '30d', '90d', 'all']).default('30d'),
39
- from: z.string().optional(),
40
- to: z.string().optional(),
41
- })
42
- type SearchParams = z.infer<typeof SearchSchema>
43
-
44
- export const Route = createFileRoute('/resource')({
45
- validateSearch: (raw: Record<string, unknown>) => SearchSchema.parse(raw),
46
- loaderDeps: ({ search }: { search: SearchParams }) => ({
47
- window: search.window,
48
- from: search.from,
49
- to: search.to,
50
- }),
51
- loader: ({ context, deps }) =>
52
- Promise.all([
53
- context.queryClient.ensureQueryData(resourceQueries.summary(deps)),
54
- context.queryClient.ensureQueryData(resourceQueries.series(deps)),
55
- ]),
56
- component: ResourcePage,
57
- })
58
-
59
- function ResourcePage() {
60
- const search = Route.useSearch()
61
- const { data } = useSuspenseQuery(resourceQueries.summary(search))
62
- // render — charts read VX.* tokens; chrome uses Mantine
63
- }
64
- ```
65
-
66
- ## validateSearch
67
-
68
- **Scope of the `createSearchParamStore` mandate — read this before reaching for the store.**
69
- `createSearchParamStore` covers ONE param whose values are a string enum
70
- (`{ key, param, values, fallback }`); `createMultiSearchParamStore` is the multi-select of the same
71
- shape. Where that fits — a range picker, a tab, a single filter — it is **mandatory**: use it, don't
72
- hand-roll persistence.
73
-
74
- Where it doesn't fit, it doesn't apply. A route whose `validateSearch` is a real Zod object (10+
75
- keys, nested shapes, cross-field defaults) cannot be expressed through the store at all — three
76
- consumers hit that wall. **Hand-write `validateSearch` there**, per the rules below. There is no
77
- schema-backed store: `createSearchSchemaStore` is planned and **not shipped** — do not write code
78
- against it.
79
-
80
- - Always parse with the schema-function form: `(raw) => SearchSchema.parse(raw)`.
81
- - Default values live in the schema (`.default()`), not in the component.
82
- - Access via `const search = Route.useSearch()`.
83
- - Update via `navigate({ search: { window: 'all', from: undefined, to: undefined } })` — prefer plain
84
- objects over reducers to avoid `| undefined` assignability errors when the schema has defaults.
85
-
86
- ### Wire the reader, or the store does nothing (1.21.0)
87
-
88
- `validateSearch` is only half the store. It restores the value when you arrive with **no** param —
89
- it cannot do anything about a nav link that declares the param itself, and a link written
90
- `search: { window: '30d' }` at module scope pins the fallback on every click. That is not
91
- hypothetical: the reference consumer adopted the store in three features, hand-rolled the
92
- persistence in all three, and its reader had **zero** call sites. "Remember my window" had never
93
- once worked, with every individual piece looking correct.
94
-
95
- `store.linkSearch` is the fix, and it is on the object the factory already returns:
96
-
97
- ```ts
98
- const dashboardRange = createSearchParamStore({
99
- key: 'dashboard-range',
100
- param: 'range',
101
- values: ['1d', '7d', '30d'] as const,
102
- fallback: '30d',
103
- })
104
- // → { validateSearch, useStore, readStored, linkSearch }
105
-
106
- link: linkOptions({ to: '/dashboard', search: dashboardRange.linkSearch })
107
- ```
108
-
109
- **Pass it by reference, never call it.** It is the click-time thunk
110
- (`() => ({ [param]: readStored() ?? fallback })`); `<Link>` re-evaluates it on every click, so
111
- arriving from outside the sub-tree restores the last selection. A value computed once at module
112
- scope goes stale immediately. Hand-rolling the thunk over `readStored()` still works and is what
113
- you need when the link carries other params too.
114
-
115
- In dev, `validateSearch` warns once per store when the URL pins the fallback, a different value is
116
- persisted, and neither public reader has ever been called — the one combination that has exactly
117
- this cause. Silent in production, silent on a deliberate deep link.
118
- `createMultiSearchParamStore` gets `linkSearch` but no warning: an empty array in the URL is
119
- indistinguishable from an absent param, so a literal link there still restores correctly.
120
-
121
- ## loaderDeps
122
-
123
- - Define `loaderDeps` whenever the loader depends on search params. Without it, search-param changes do
124
- not re-trigger the loader.
125
- - The `deps` object is forwarded to the loader and should be passed directly to the `ensureQueryData`
126
- query factories.
127
-
128
- ## Loaders
129
-
130
- - Use `ensureQueryData` (not `fetchQuery`) so cached data is reused.
131
- - The loader receives `context: { queryClient }` — wire it in your router config.
132
- - Loaders do not return data to the component — components read via `useSuspenseQuery`.
133
- - When a redirect from another route points here, include the required `search` params or TypeScript
134
- raises `MakeRequiredSearchParams`.
135
-
136
- ## Route tree
137
-
138
- `src/routeTree.gen.ts` is auto-generated by the Vite plugin — never edit it. Regenerated by
139
- `tsr generate` (also during `typecheck`). File names starting with `__` are reserved; only `__root.tsx`
140
- is valid.
141
-
142
- The shipped oxlint preset does **not** ban `react-router` or `react-router-dom` — basalt is
143
- router-agnostic and imposes no mechanical guard. TanStack Router is the recommended/idiomatic
144
- choice (this rule documents its conventions, and the `./router-tanstack` adapter targets it), but
145
- using `react-router` is an advisory preference, not an enforced restriction.
146
-
147
- ## ./router-tanstack adapter
148
-
149
- The optional `./router-tanstack` subpath ships a headless (Mantine-free) TanStack Router bridge.
150
- Install `@tanstack/react-router` as a peer, then import from `basalt-ui/router-tanstack`.
151
-
152
- **Prerequisites:**
153
-
154
- - **The only hard prerequisite is the `Register` module augmentation** (see "The prerequisite that
155
- makes all of this real" below). `defineNav` / `navGroup` / `navTarget` / `flattenNav` / `useNav` /
156
- `createSearchParamStore` all work against a hand-written `createRouter({ routeTree })` with
157
- code-defined routes, no Vite plugin and no generated tree — verified end-to-end in a consumer,
158
- including that a bad `to` still prints the real route-path union.
159
- - **`@tanstack/router-plugin/vite` + `tsr generate` are needed only for `staticData`** — that is,
160
- for `useRouterBreadcrumbs` and any nav metadata read off the route tree. The
161
- `StaticDataRouteOption` augmentation takes effect once `src/routeTree.gen.ts` exists. In CI and
162
- after adding routes, run `tsr generate` (or `bun run typecheck`, which triggers it) before relying
163
- on breadcrumb data.
164
- - The loader's `context.queryClient` requires `createRootRouteWithContext<{ queryClient: QueryClient }>()`.
165
- Run `basalt-ui init` to scaffold a `query-client.ts` + `__root.tsx` seed that wires the TanStack
166
- Router + Query context correctly — the generated `__root.tsx` uses
167
- `createRootRouteWithContext<{ queryClient }>()` and passes the client through `RouterProvider`.
168
-
169
- **StaticDataRouteOption augmentation** — declare on any route to drive the adapter:
170
-
171
- ```ts
172
- export const Route = createFileRoute('/reports')({
173
- staticData: { title: 'Reports', navSection: 'Insights' },
174
- component: ReportsPage,
175
- })
176
- ```
177
-
178
- Augmented fields: `title?: string`, `icon?: ReactNode`, `navSection?: string` (all optional — any
179
- route remains valid without them).
180
-
181
- **useBasaltNav** — reactive active-state resolution:
182
-
183
- ```ts
184
- import { useBasaltNav } from 'basalt-ui/router-tanstack'
185
-
186
- const { currentPath, isActive } = useBasaltNav()
187
- isActive('/reports') // prefix match (active on /reports/42 too)
188
- isActive('/reports', { exact: true }) // exact match only
189
- isActive('/') // always exact (root is never a prefix match)
190
- ```
191
-
192
- **useRouterBreadcrumbs** — ancestor→deepest breadcrumb trail from `staticData.title`:
193
-
194
- ```ts
195
- import { useRouterBreadcrumbs } from 'basalt-ui/router-tanstack'
196
-
197
- function AppBreadcrumbs() {
198
- const crumbs = useRouterBreadcrumbs()
199
- return <nav>{crumbs.map((c) => <a key={c.href} href={c.href}>{c.title}</a>)}</nav>
200
- }
201
- ```
202
-
203
- **One typed nav definition** — `defineNav` / `navGroup` / `navTarget` / `useNav`.
204
-
205
- The whole navigation — desktop sidebar and mobile bar — is declared once in a **leaf** module
206
- (`src/lib/nav.tsx`), which imports `@tanstack/react-router` and `basalt-ui/router-tanstack` and
207
- **never** `routeTree.gen` or `__root.tsx`, so command palettes and redirects can import it without
208
- closing a cycle.
209
-
210
- ```tsx
211
- import { linkOptions } from '@tanstack/react-router'
212
- import { defineNav, navGroup } from 'basalt-ui/router-tanstack'
213
- import { garminWindow } from './search-params' // a createSearchParamStore
214
-
215
- const ICON = 18
216
-
217
- export const NAV = defineNav({
218
- groups: [
219
- navGroup({ id: 'health', label: 'Health', icon: <IconHeartbeat size={ICON} /> }, [
220
- {
221
- id: 'garmin',
222
- label: 'Garmin Health',
223
- short: 'Garmin', // bar/menu label — keep it ≤ 10 chars
224
- mobile: 'tab', // give this destination its own slot on the mobile bar
225
- icon: <IconHeartbeat size={ICON} />,
226
- // NOT `search: { window: '30d' }` — a module-scope literal pins the fallback on
227
- // every click and silently overrides the store. Pass the reader by reference.
228
- link: linkOptions({ to: '/garmin-health', search: garminWindow.linkSearch }),
229
- },
230
- {
231
- id: 'reading',
232
- label: 'Reading',
233
- icon: <IconBook size={ICON} />,
234
- link: linkOptions({ to: '/reading' }),
235
- children: [
236
- {
237
- id: 'reading-stats',
238
- label: 'Stats',
239
- icon: <IconChartBar size={ICON} />,
240
- link: linkOptions({ to: '/reading/stats' }),
241
- },
242
- ],
243
- },
244
- ]),
245
- ],
246
- })
247
- ```
248
-
249
- Rules that are not stylistic:
250
-
251
- - **Route options live under `link:`, wrapped in TanStack's own `linkOptions`** — never spread flat
252
- onto the item. `to` / `search` / `params` get the router's full validation there, while `id`,
253
- `label`, `short`, `icon`, `mobile`, `disabled` and `exact` ride outside it. A flat shape
254
- (`{ id, label, to }`) validates `to` by ASSIGNABILITY, which does no excess-property checking, so
255
- a typo'd metadata key would compile silently. A top-level `to:` is a compile error by design.
256
- - **`search` may be a thunk, and for a store-backed param it MUST be** —
257
- `search: () => ({ date: format(new Date(), 'yyyy-MM-dd') })` compiles without `as const` and
258
- re-evaluates at click time, so "today" never goes stale in a long-lived tab. A param backed by
259
- `createSearchParamStore` takes the store's own `linkSearch` (see "validateSearch" above); a
260
- literal there is the one shape that defeats the store.
261
- - **Nesting is `children`**, and nested destinations are indexed by id just like top-level ones.
262
- - **`NavItemMeta`**'s `mobile` field places the destination on the bar: `'tab'` (own slot),
263
- `'more'` (the default — reachable through the More overlay) or `'hidden'`. A group claims a slot
264
- of its own with `mobile: { tab: true }`, and leaves mobile entirely with `mobile: false`.
265
-
266
- ```tsx
267
- // __root.tsx — four lines replace a hand-written sections array, two render callbacks,
268
- // a NAV_TARGETS table and one useMatchRoute call per destination.
269
- import { useNav } from 'basalt-ui/router-tanstack'
270
- import { NAV } from '../lib/nav'
271
-
272
- const nav = useNav(NAV, { badges: { calendar: unread } }) // keys autocomplete; a typo is an error
273
- return (
274
- <BasaltShell brand={brand} {...nav}>
275
- {children}
276
- </BasaltShell>
277
- )
278
- ```
279
-
280
- **`useNav`** returns `{ sections, mobileNav }` — both are `BasaltShell` props, so spread it. It
281
- resolves `active` per destination through `useMatchRoute` (prefix match by default, exact when the
282
- item sets `exact: true`, and always exact for `'/'`), and builds each destination's **`NavAnchor`**
283
- so `preload="intent"`, middle-click and back/forward keep working. `badges` values that are
284
- `number` become `SidebarItem.count` (a desktop **`NavCountBadge`** plus an accent dot on the mobile
285
- slot); anything else becomes `SidebarItem.badge`, which is desktop-only because a count glyph is
286
- unreadable in a 56px tab.
287
-
288
- **`navTarget`** returns one destination's link options, typed per id — spread it into `<Link>`,
289
- `router.navigate()` or `redirect()` instead of restating the route and its default search:
290
-
291
- ```ts
292
- throw redirect(navTarget(NAV, 'garmin')) // exactly { to: '/garmin-health', search: garminWindow.linkSearch }
293
- ```
294
-
295
- **`flattenNav`** walks the definition depth-first (parent, then children), tagging each destination
296
- with `groupId` / `groupLabel` — the leaf every other surface reads, e.g. projecting the whole nav
297
- into Spotlight commands in one `.map()`.
298
-
299
- ### The prerequisite that makes all of this real
300
-
301
- **Without the `Register` module augmentation, `defineNav` validates NOTHING and reports zero
302
- errors.** `RegisteredRouter` falls back to `AnyRouter` when the consumer has not written:
303
-
304
- ```ts
305
- declare module '@tanstack/react-router' {
306
- interface Register {
307
- router: typeof router
308
- }
309
- }
310
- ```
311
-
312
- With that missing, every `to` widens to `string`, a nonexistent route path compiles, a missing
313
- required `search` compiles, and the API looks like it is working while catching nothing. This is
314
- the single highest-value thing to verify when adopting the config: change one `to` to a garbage
315
- path and confirm you get a compile error listing the real route paths. If you don't, the
316
- augmentation is missing (or, on a file-route setup, `tsr generate` has not run).
317
-
318
- Second thing to know before you migrate: whether a missing `search` is caught depends on the
319
- schema's OUTPUT type, not on the `validateSearch` signature. If every key of `T` is required —
320
- which is what a Zod object with defaults produces, since defaults apply on parse and the output has
321
- no optionals — TanStack raises `MakeRequiredSearchParams` and the compiler catches it. Two routes in
322
- one consumer had been shipping nav links with no search params behind a `to={target.to as never}`
323
- cast; removing the callback surfaced both immediately.
324
-
325
- **The caveat holds only when `T` has no required keys** (every key optional, or the validator
326
- returns a partial). There, a forgotten `search` compiles — diff the old target table against the new
327
- definition by hand, key by key. Everywhere else, trust the error.
328
-
329
- Error messages here are the router's own and they are large: one wrong `to` prints the whole
330
- route-path union plus a `RouterCore<…>` blob, because TanStack's `Constrain` falls back to the
331
- whole constraint. That is not misuse — read the union for the intended path and fix the string.
332
- **Do not reach for `as never` to make it quiet**; the checking is the entire point of the config.
333
-
334
- Navigation itself (`.navigate()`, `<Link to={…}>`) stays the consumer's typed concern — the adapter
335
- still exposes no `navigate()` helper, so TanStack's route-tree types remain authoritative. What
336
- `navTarget` hands back is the ARGUMENT to those calls, never the call.
@@ -1,40 +0,0 @@
1
- /**
2
- * ArticleFilterBar — a fully controlled, dumb filter bar over an article list's category and tags
3
- * axes (docs/CONTENT-SPEC.md). It owns no state, no store, no URL knowledge — the store lives in
4
- * `basalt-ui/router-tanstack` (`createSearchParamStore`/`createMultiSearchParamStore`, headless,
5
- * Mantine-free); this is the Mantine half. Wire it to that pair per `createSearchParamStore`'s
6
- * 5-step JSDoc recipe (the filter is its step 3).
7
- *
8
- * Category renders as a `SegmentedControl` (DESIGN-SPEC §5); below the `sm` breakpoint it swaps to
9
- * a `Select` via Mantine's CSS-based `visibleFrom`/`hiddenFrom` props (not `useMediaQuery` — a JS
10
- * media query renders differently on server vs client and would trip a hydration mismatch, the
11
- * same hazard `formatArticleDate` is careful to avoid). Tags render as a `Chip.Group` — Chips wrap
12
- * natively and need no responsive fallback. The tag row is omitted entirely when `tags` is
13
- * absent/empty.
14
- *
15
- * @example
16
- * <ArticleFilterBar
17
- * categories={[{ value: 'all', label: 'All' }, { value: 'guide', label: 'Guide' }]}
18
- * category={category}
19
- * onCategoryChange={setCategory}
20
- * tags={ARTICLE_TAGS}
21
- * selectedTags={tags}
22
- * onTagsChange={setTags}
23
- * />
24
- */
25
- import type { CSSProperties } from 'react';
26
- export type ArticleFilterBarProps = {
27
- readonly categories: readonly {
28
- readonly value: string;
29
- readonly label: string;
30
- }[];
31
- readonly category: string;
32
- readonly onCategoryChange: (next: string) => void;
33
- readonly tags?: readonly string[];
34
- readonly selectedTags?: readonly string[];
35
- readonly onTagsChange?: (next: string[]) => void;
36
- readonly className?: string;
37
- readonly style?: CSSProperties;
38
- };
39
- export declare function ArticleFilterBar({ categories, category, onCategoryChange, tags, selectedTags, onTagsChange, className, style, }: ArticleFilterBarProps): import("react").JSX.Element;
40
- //# sourceMappingURL=article-filter-bar.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"article-filter-bar.d.ts","sourceRoot":"","sources":["../../src/content/article-filter-bar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAG1C,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACjD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,KAAK,GACN,EAAE,qBAAqB,+BAwCvB"}
@@ -1,56 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Chip, Group, SegmentedControl, Select, Stack } from "@mantine/core";
3
- function ArticleFilterBar({
4
- categories,
5
- category,
6
- onCategoryChange,
7
- tags,
8
- selectedTags,
9
- onTagsChange,
10
- className,
11
- style
12
- }) {
13
- const categoryData = categories.map(({ value, label }) => ({ value, label }));
14
- const hasTags = tags !== void 0 && tags.length > 0;
15
- return /* @__PURE__ */ jsxs(
16
- Stack,
17
- {
18
- gap: "sm",
19
- ...className !== void 0 && { className },
20
- ...style !== void 0 && { style },
21
- children: [
22
- /* @__PURE__ */ jsx(
23
- SegmentedControl,
24
- {
25
- visibleFrom: "sm",
26
- data: categoryData,
27
- value: category,
28
- onChange: onCategoryChange
29
- }
30
- ),
31
- /* @__PURE__ */ jsx(
32
- Select,
33
- {
34
- hiddenFrom: "sm",
35
- data: categoryData,
36
- value: category,
37
- allowDeselect: false,
38
- onChange: (next) => next !== null && onCategoryChange(next)
39
- }
40
- ),
41
- hasTags && /* @__PURE__ */ jsx(
42
- Chip.Group,
43
- {
44
- multiple: true,
45
- value: selectedTags !== void 0 ? [...selectedTags] : [],
46
- onChange: (next) => onTagsChange?.(next),
47
- children: /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "wrap", children: tags?.map((tag) => /* @__PURE__ */ jsx(Chip, { value: tag, children: tag }, tag)) })
48
- }
49
- )
50
- ]
51
- }
52
- );
53
- }
54
- export {
55
- ArticleFilterBar
56
- };
@@ -1,13 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- export declare function PageHeaderProvider({ children }: {
3
- children: ReactNode;
4
- }): import("react").JSX.Element;
5
- /** Renders in the app-shell header — the DOM node the active page portals its actions into. */
6
- export declare function PageActionsOutlet({ className }: {
7
- className?: string;
8
- }): import("react").JSX.Element;
9
- /** Used by a page to render its control row into the top-bar page slot. */
10
- export declare function PageActions({ children }: {
11
- children: ReactNode;
12
- }): import("react").ReactPortal | null;
13
- //# sourceMappingURL=page-header.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-header.d.ts","sourceRoot":"","sources":["../../src/shell/page-header.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAOvE;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAGtE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,sCAGhE"}
@@ -1,23 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useState } from "react";
3
- import { createPortal } from "react-dom";
4
- const TargetContext = createContext(null);
5
- const SetTargetContext = createContext(() => {
6
- });
7
- function PageHeaderProvider({ children }) {
8
- const [target, setTarget] = useState(null);
9
- return /* @__PURE__ */ jsx(SetTargetContext.Provider, { value: setTarget, children: /* @__PURE__ */ jsx(TargetContext.Provider, { value: target, children }) });
10
- }
11
- function PageActionsOutlet({ className }) {
12
- const setTarget = useContext(SetTargetContext);
13
- return /* @__PURE__ */ jsx("div", { ref: setTarget, className });
14
- }
15
- function PageActions({ children }) {
16
- const target = useContext(TargetContext);
17
- return target ? createPortal(children, target) : null;
18
- }
19
- export {
20
- PageActions,
21
- PageActionsOutlet,
22
- PageHeaderProvider
23
- };
@@ -1,88 +0,0 @@
1
- /**
2
- * ArticleFilterBar — a fully controlled, dumb filter bar over an article list's category and tags
3
- * axes (docs/CONTENT-SPEC.md). It owns no state, no store, no URL knowledge — the store lives in
4
- * `basalt-ui/router-tanstack` (`createSearchParamStore`/`createMultiSearchParamStore`, headless,
5
- * Mantine-free); this is the Mantine half. Wire it to that pair per `createSearchParamStore`'s
6
- * 5-step JSDoc recipe (the filter is its step 3).
7
- *
8
- * Category renders as a `SegmentedControl` (DESIGN-SPEC §5); below the `sm` breakpoint it swaps to
9
- * a `Select` via Mantine's CSS-based `visibleFrom`/`hiddenFrom` props (not `useMediaQuery` — a JS
10
- * media query renders differently on server vs client and would trip a hydration mismatch, the
11
- * same hazard `formatArticleDate` is careful to avoid). Tags render as a `Chip.Group` — Chips wrap
12
- * natively and need no responsive fallback. The tag row is omitted entirely when `tags` is
13
- * absent/empty.
14
- *
15
- * @example
16
- * <ArticleFilterBar
17
- * categories={[{ value: 'all', label: 'All' }, { value: 'guide', label: 'Guide' }]}
18
- * category={category}
19
- * onCategoryChange={setCategory}
20
- * tags={ARTICLE_TAGS}
21
- * selectedTags={tags}
22
- * onTagsChange={setTags}
23
- * />
24
- */
25
- import type { CSSProperties } from 'react'
26
- import { Chip, Group, SegmentedControl, Select, Stack } from '@mantine/core'
27
-
28
- export type ArticleFilterBarProps = {
29
- readonly categories: readonly { readonly value: string; readonly label: string }[]
30
- readonly category: string
31
- readonly onCategoryChange: (next: string) => void
32
- readonly tags?: readonly string[]
33
- readonly selectedTags?: readonly string[]
34
- readonly onTagsChange?: (next: string[]) => void
35
- readonly className?: string
36
- readonly style?: CSSProperties
37
- }
38
-
39
- export function ArticleFilterBar({
40
- categories,
41
- category,
42
- onCategoryChange,
43
- tags,
44
- selectedTags,
45
- onTagsChange,
46
- className,
47
- style,
48
- }: ArticleFilterBarProps) {
49
- const categoryData = categories.map(({ value, label }) => ({ value, label }))
50
- const hasTags = tags !== undefined && tags.length > 0
51
-
52
- return (
53
- <Stack
54
- gap="sm"
55
- {...(className !== undefined && { className })}
56
- {...(style !== undefined && { style })}
57
- >
58
- <SegmentedControl
59
- visibleFrom="sm"
60
- data={categoryData}
61
- value={category}
62
- onChange={onCategoryChange}
63
- />
64
- <Select
65
- hiddenFrom="sm"
66
- data={categoryData}
67
- value={category}
68
- allowDeselect={false}
69
- onChange={(next) => next !== null && onCategoryChange(next)}
70
- />
71
- {hasTags && (
72
- <Chip.Group
73
- multiple
74
- value={selectedTags !== undefined ? [...selectedTags] : []}
75
- onChange={(next) => onTagsChange?.(next)}
76
- >
77
- <Group gap="xs" wrap="wrap">
78
- {tags?.map((tag) => (
79
- <Chip key={tag} value={tag}>
80
- {tag}
81
- </Chip>
82
- ))}
83
- </Group>
84
- </Chip.Group>
85
- )}
86
- </Stack>
87
- )
88
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Page-header slot system. The slim app-shell top bar carries a GLOBAL slot (shell-owned) and a
3
- * PAGE slot: the active route portals its full control row into the bar via `PageActions`, so each
4
- * page owns its actions without a separate in-body header.
5
- *
6
- * Grounded verbatim in argo `apps/dashboard/src/components/app-shell/page-header.tsx`. This is a
7
- * real, working implementation (the slot/portal mechanism has no app coupling) — not a stub.
8
- */
9
- import { createContext, useContext, useState } from 'react'
10
- import type { ReactNode } from 'react'
11
- import { createPortal } from 'react-dom'
12
-
13
- const TargetContext = createContext<HTMLElement | null>(null)
14
- const SetTargetContext = createContext<(el: HTMLElement | null) => void>(() => {})
15
-
16
- export function PageHeaderProvider({ children }: { children: ReactNode }) {
17
- const [target, setTarget] = useState<HTMLElement | null>(null)
18
- return (
19
- <SetTargetContext.Provider value={setTarget}>
20
- <TargetContext.Provider value={target}>{children}</TargetContext.Provider>
21
- </SetTargetContext.Provider>
22
- )
23
- }
24
-
25
- /** Renders in the app-shell header — the DOM node the active page portals its actions into. */
26
- export function PageActionsOutlet({ className }: { className?: string }) {
27
- const setTarget = useContext(SetTargetContext)
28
- return <div ref={setTarget} className={className} />
29
- }
30
-
31
- /** Used by a page to render its control row into the top-bar page slot. */
32
- export function PageActions({ children }: { children: ReactNode }) {
33
- const target = useContext(TargetContext)
34
- return target ? createPortal(children, target) : null
35
- }