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
@@ -0,0 +1,146 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, Drawer } from "@mantine/core";
3
+ import { useId } from "react";
4
+ import classes from "./controls.module.css";
5
+ function FilterSheet({
6
+ opened,
7
+ onClose,
8
+ onResetAll,
9
+ title = "Filters",
10
+ children
11
+ }) {
12
+ return /* @__PURE__ */ jsx(
13
+ Drawer,
14
+ {
15
+ opened,
16
+ onClose,
17
+ position: "bottom",
18
+ radius: "sm",
19
+ classNames: { header: classes.sheetHeader, title: classes.sheetTitle },
20
+ title: /* @__PURE__ */ jsxs(Fragment, { children: [
21
+ /* @__PURE__ */ jsx("span", { className: classes.sheetTitleText, children: title }),
22
+ /* @__PURE__ */ jsx(Button, { variant: "subtle", size: "ctl", onClick: onResetAll, children: "Reset all" })
23
+ ] }),
24
+ children: /* @__PURE__ */ jsx("div", { className: classes.sheetBody, children })
25
+ }
26
+ );
27
+ }
28
+ function CheckGlyph() {
29
+ return /* @__PURE__ */ jsx(
30
+ "svg",
31
+ {
32
+ width: 16,
33
+ height: 16,
34
+ viewBox: "0 0 24 24",
35
+ fill: "none",
36
+ stroke: "currentColor",
37
+ strokeWidth: 2.25,
38
+ strokeLinecap: "round",
39
+ strokeLinejoin: "round",
40
+ "aria-hidden": true,
41
+ children: /* @__PURE__ */ jsx("path", { d: "M5 12.5l5 5l9 -11" })
42
+ }
43
+ );
44
+ }
45
+ function SheetOptionList({
46
+ mode,
47
+ selected,
48
+ options,
49
+ onToggle,
50
+ labelId
51
+ }) {
52
+ const name = useId();
53
+ return (
54
+ // A real `<fieldset>`, not `role="group"` on a div — the native element carries the grouping
55
+ // semantics and gets its own box reset in the module (`.sheetList`). `aria-labelledby` points at
56
+ // the `SheetField` heading rather than a `<legend>`: the heading is already visible above the
57
+ // list, and a legend would render it a second time.
58
+ /* @__PURE__ */ jsx("fieldset", { className: classes.sheetList, "aria-labelledby": labelId, children: options.map((option) => {
59
+ const isSelected = selected.includes(option.value);
60
+ return /* @__PURE__ */ jsxs(
61
+ "label",
62
+ {
63
+ className: classes.sheetOption,
64
+ ...isSelected && { "data-selected": true },
65
+ ...option.disabled === true && { "data-disabled": true },
66
+ children: [
67
+ /* @__PURE__ */ jsx(
68
+ "input",
69
+ {
70
+ className: classes.sheetOptionInput,
71
+ type: mode === "single" ? "radio" : "checkbox",
72
+ name,
73
+ value: option.value,
74
+ checked: isSelected,
75
+ disabled: option.disabled === true,
76
+ "aria-label": option.label,
77
+ onChange: () => {
78
+ onToggle(option.value);
79
+ }
80
+ }
81
+ ),
82
+ /* @__PURE__ */ jsx("span", { className: classes.sheetOptionLabel, children: option.label }),
83
+ isSelected && /* @__PURE__ */ jsx("span", { className: classes.sheetOptionCheck, "aria-hidden": true, children: /* @__PURE__ */ jsx(CheckGlyph, {}) })
84
+ ]
85
+ },
86
+ option.value
87
+ );
88
+ }) })
89
+ );
90
+ }
91
+ function SheetDisclosure({
92
+ label,
93
+ expanded,
94
+ onToggle,
95
+ children
96
+ }) {
97
+ return /* @__PURE__ */ jsxs("div", { className: classes.sheetList, children: [
98
+ /* @__PURE__ */ jsxs(
99
+ "button",
100
+ {
101
+ type: "button",
102
+ className: classes.sheetOption,
103
+ "aria-expanded": expanded,
104
+ ...expanded && { "data-selected": true },
105
+ onClick: onToggle,
106
+ children: [
107
+ /* @__PURE__ */ jsx("span", { className: classes.sheetOptionLabel, children: label }),
108
+ expanded && /* @__PURE__ */ jsx("span", { className: classes.sheetOptionCheck, "aria-hidden": true, children: /* @__PURE__ */ jsx(CheckGlyph, {}) })
109
+ ]
110
+ }
111
+ ),
112
+ expanded && /* @__PURE__ */ jsx("div", { className: classes.sheetDisclosureBody, children })
113
+ ] });
114
+ }
115
+ function SheetField({ label, labelId, children }) {
116
+ return /* @__PURE__ */ jsxs("div", { className: classes.sheetField, children: [
117
+ /* @__PURE__ */ jsx("span", { className: classes.sheetLabel, ...labelId !== void 0 && { id: labelId }, children: label }),
118
+ children
119
+ ] });
120
+ }
121
+ function useControlName(label, inSheet) {
122
+ const labelId = useId();
123
+ return {
124
+ labelId,
125
+ nameProps: inSheet ? { "aria-labelledby": labelId } : { "aria-label": label }
126
+ };
127
+ }
128
+ function SheetRow({ children }) {
129
+ return /* @__PURE__ */ jsx("div", { className: classes.sheetRow, children });
130
+ }
131
+ const sheetRowClass = classes.sheetRow;
132
+ const sheetRowClassNames = {
133
+ root: classes.sheetRow,
134
+ body: classes.sheetRowBody,
135
+ label: classes.sheetRowLabel
136
+ };
137
+ export {
138
+ FilterSheet,
139
+ SheetDisclosure,
140
+ SheetField,
141
+ SheetOptionList,
142
+ SheetRow,
143
+ sheetRowClass,
144
+ sheetRowClassNames,
145
+ useControlName
146
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The control tier's own inline glyphs (`docs/CONTROLS-SPEC.md` §3).
3
+ *
4
+ * basalt ships NO icon dependency — every `icon` slot in the public API takes a `ReactNode` the
5
+ * consumer fills. These are the few glyphs the FRAMEWORK draws for itself, where a default icon is
6
+ * part of the control's identity rather than a caller's decision: a range filter reads as a date
7
+ * picker because it carries a calendar, a compare filter because it carries the swap arrows, and
8
+ * the mobile `Filters (n)` pill because it carries a funnel. Same convention the shell already uses
9
+ * for its own chrome (`shell/sidebar-search.tsx`, `shell/app-sidebar.tsx`): a 24×24 `viewBox`,
10
+ * `stroke="currentColor"`, sized down by the `size` prop.
11
+ *
12
+ * INTERNAL — not on the `./controls` barrel. A consumer wanting a different glyph passes `icon`.
13
+ */
14
+ import type { ReactNode } from 'react';
15
+ /** `RangeFilter`'s default leading icon. */
16
+ export declare function CalendarGlyph(): ReactNode;
17
+ /** `CompareFilter`'s default leading icon — the arrows-swap glyph. */
18
+ export declare function SwapGlyph(): ReactNode;
19
+ /** The mobile `Filters (n)` pill's leading icon. */
20
+ export declare function FunnelGlyph(): ReactNode;
21
+ /**
22
+ * Every pill's trailing affordance — 14px, muted, and an SVG rather than the `⇅` character it
23
+ * replaced. A text glyph rendered at the label's own font metrics, so it sat off the optical centre
24
+ * and shifted with the font stack; the path is centred in its own box.
25
+ */
26
+ export declare function UpDownGlyph(): ReactNode;
27
+ //# sourceMappingURL=glyphs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphs.d.ts","sourceRoot":"","sources":["../../src/controls/glyphs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAwBtC,4CAA4C;AAC5C,wBAAgB,aAAa,IAAI,SAAS,CASzC;AAED,sEAAsE;AACtE,wBAAgB,SAAS,IAAI,SAAS,CAOrC;AAED,oDAAoD;AACpD,wBAAgB,WAAW,IAAI,SAAS,CAMvC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,SAAS,CAOvC"}
@@ -0,0 +1,49 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ const ICON_SIZE = 16;
3
+ const AFFORDANCE_SIZE = 14;
4
+ function Stroke({ size = ICON_SIZE, children }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "svg",
7
+ {
8
+ width: size,
9
+ height: size,
10
+ viewBox: "0 0 24 24",
11
+ fill: "none",
12
+ stroke: "currentColor",
13
+ strokeWidth: 1.75,
14
+ strokeLinecap: "round",
15
+ strokeLinejoin: "round",
16
+ "aria-hidden": true,
17
+ children
18
+ }
19
+ );
20
+ }
21
+ function CalendarGlyph() {
22
+ return /* @__PURE__ */ jsxs(Stroke, { children: [
23
+ /* @__PURE__ */ jsx("rect", { x: 3, y: 5, width: 18, height: 16, rx: 2.5 }),
24
+ /* @__PURE__ */ jsx("path", { d: "M3 10h18" }),
25
+ /* @__PURE__ */ jsx("path", { d: "M8 3v4" }),
26
+ /* @__PURE__ */ jsx("path", { d: "M16 3v4" })
27
+ ] });
28
+ }
29
+ function SwapGlyph() {
30
+ return /* @__PURE__ */ jsxs(Stroke, { children: [
31
+ /* @__PURE__ */ jsx("path", { d: "M4 8h13l-3.5 -3.5" }),
32
+ /* @__PURE__ */ jsx("path", { d: "M20 16h-13l3.5 3.5" })
33
+ ] });
34
+ }
35
+ function FunnelGlyph() {
36
+ return /* @__PURE__ */ jsx(Stroke, { children: /* @__PURE__ */ jsx("path", { d: "M4 4h16l-6.5 8v7l-3 -1.5v-5.5z" }) });
37
+ }
38
+ function UpDownGlyph() {
39
+ return /* @__PURE__ */ jsxs(Stroke, { size: AFFORDANCE_SIZE, children: [
40
+ /* @__PURE__ */ jsx("path", { d: "M8 9l4 -4l4 4" }),
41
+ /* @__PURE__ */ jsx("path", { d: "M8 15l4 4l4 -4" })
42
+ ] });
43
+ }
44
+ export {
45
+ CalendarGlyph,
46
+ FunnelGlyph,
47
+ SwapGlyph,
48
+ UpDownGlyph
49
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `basalt-ui/controls` — the Mantine-coupled control tier (`docs/CONTROLS-SPEC.md` §3).
3
+ *
4
+ * Every control here renders at the `ctl` tier from the home that hosts it and owns its own
5
+ * desktop/mobile swap in CSS (law C9). Actions and sync land in wave 3; the filter/tab family
6
+ * follows in wave 4.
7
+ */
8
+ export { ActionGroup, OverflowMenu, DESKTOP_SECONDARY_MAX, MOBILE_GLOBAL_BAR_MAX, barActionMobile, globalActionMobile, type BarAction, type BarActionItem, type BarActionMenu, type BarActionCustom, type ActionGroupProps, type GlobalAction, type OverflowMenuProps, } from './actions.js';
9
+ export { SyncButton, formatAge, AGE_REFRESH_MS, type SyncButtonProps } from './sync-button.js';
10
+ export { ControlGroup, type ControlGroupProps } from './control-group.js';
11
+ export { FilterSet, type FilterSetProps } from './filter-set.js';
12
+ export { RangeFilter, type RangeCustomPickerProps, type RangeFilterProps } from './range-filter.js';
13
+ export { CompareFilter, COMPARE_VALUES, type CompareFilterProps, type CompareValue, } from './compare-filter.js';
14
+ export { SelectFilter, type FilterOption, type SelectFilterEnumProps, type SelectFilterProps, type SelectFilterStringProps, } from './select-filter.js';
15
+ export { MultiSelectFilter, type MultiSelectFilterProps } from './multi-select-filter.js';
16
+ export { SearchFilter, type SearchFilterProps } from './search-filter.js';
17
+ export { ToggleFilter, type ToggleFilterProps } from './toggle-filter.js';
18
+ export { ViewTabs, type ViewTabsOption, type ViewTabsProps } from './view-tabs.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controls/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAMtE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAChG,OAAO,EACL,aAAa,EACb,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,44 @@
1
+ import {
2
+ ActionGroup,
3
+ OverflowMenu,
4
+ DESKTOP_SECONDARY_MAX,
5
+ MOBILE_GLOBAL_BAR_MAX,
6
+ barActionMobile,
7
+ globalActionMobile
8
+ } from "./actions.js";
9
+ import { SyncButton, formatAge, AGE_REFRESH_MS } from "./sync-button.js";
10
+ import { ControlGroup } from "./control-group.js";
11
+ import { FilterSet } from "./filter-set.js";
12
+ import { RangeFilter } from "./range-filter.js";
13
+ import {
14
+ CompareFilter,
15
+ COMPARE_VALUES
16
+ } from "./compare-filter.js";
17
+ import {
18
+ SelectFilter
19
+ } from "./select-filter.js";
20
+ import { MultiSelectFilter } from "./multi-select-filter.js";
21
+ import { SearchFilter } from "./search-filter.js";
22
+ import { ToggleFilter } from "./toggle-filter.js";
23
+ import { ViewTabs } from "./view-tabs.js";
24
+ export {
25
+ AGE_REFRESH_MS,
26
+ ActionGroup,
27
+ COMPARE_VALUES,
28
+ CompareFilter,
29
+ ControlGroup,
30
+ DESKTOP_SECONDARY_MAX,
31
+ FilterSet,
32
+ MOBILE_GLOBAL_BAR_MAX,
33
+ MultiSelectFilter,
34
+ OverflowMenu,
35
+ RangeFilter,
36
+ SearchFilter,
37
+ SelectFilter,
38
+ SyncButton,
39
+ ToggleFilter,
40
+ ViewTabs,
41
+ barActionMobile,
42
+ formatAge,
43
+ globalActionMobile
44
+ };
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { FieldHandle, MultiField } from '../state.js';
3
+ import type { FilterOption } from './select-filter.js';
4
+ export type MultiSelectFilterProps<T extends string> = {
5
+ readonly field: FieldHandle<MultiField<T>>;
6
+ readonly label: string;
7
+ readonly icon?: ReactNode;
8
+ /**
9
+ * The plural noun in the `3 channels` count.
10
+ *
11
+ * @default the label, lowercased
12
+ */
13
+ readonly noun?: string;
14
+ /**
15
+ * Overrides `field.options` at render — a runtime catalogue whose labels carry live data
16
+ * (`web · 1.2k`). The VALUES still belong to the field: a multi field is a closed set, so this
17
+ * relabels and reorders rows, it does not open new ones. Unlike `SelectFilter` there is no
18
+ * string-field shape to make it required.
19
+ */
20
+ readonly options?: readonly FilterOption[];
21
+ };
22
+ export declare function MultiSelectFilter<T extends string>({ field, label, icon, noun, options: optionsProp, }: MultiSelectFilterProps<T>): ReactNode;
23
+ //# sourceMappingURL=multi-select-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-select-filter.d.ts","sourceRoot":"","sources":["../../src/controls/multi-select-filter.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAMnD,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,MAAM,IAAI;IACrD,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;CAC3C,CAAA;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,OAAO,EAAE,WAAW,GACrB,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,SAAS,CAsFvC"}
@@ -0,0 +1,89 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, Checkbox, Stack } from "@mantine/core";
3
+ import classes from "./controls.module.css";
4
+ import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
5
+ import { FilterPill } from "./filter-pill.js";
6
+ import { SheetField, SheetOptionList, useControlName } from "./filter-sheet.js";
7
+ function MultiSelectFilter({
8
+ field,
9
+ label,
10
+ icon,
11
+ noun,
12
+ options: optionsProp
13
+ }) {
14
+ const [value, setValue] = field.use();
15
+ const surface = useFilterSurface();
16
+ const isDefault = field.isDefault(value);
17
+ useFilterRegistration(!isDefault, () => {
18
+ setValue(field.fallback);
19
+ });
20
+ const options = optionsProp ?? field.options;
21
+ const inSheet = surface === "sheet";
22
+ const carriesInformation = value.length > 0 && value.length < options.length;
23
+ const { labelId } = useControlName(label, inSheet);
24
+ if (inSheet) {
25
+ return /* @__PURE__ */ jsx(SheetField, { label, labelId, children: /* @__PURE__ */ jsx(
26
+ SheetOptionList,
27
+ {
28
+ mode: "multi",
29
+ labelId,
30
+ selected: value,
31
+ options,
32
+ onToggle: (next) => {
33
+ const has = value.includes(next);
34
+ setValue(has ? value.filter((v) => v !== next) : [...value, next]);
35
+ }
36
+ }
37
+ ) });
38
+ }
39
+ const body = (
40
+ // Mantine's own `label`, for the same reason `enum-filter.tsx` uses it — `Checkbox.Group`
41
+ // overwrites any `aria-label`/`aria-labelledby` with its own. Named from `label`, never from the
42
+ // pill text, which reads `3 channels` and describes the selection rather than the filter.
43
+ /* @__PURE__ */ jsxs(
44
+ Checkbox.Group,
45
+ {
46
+ label,
47
+ classNames: { label: classes.groupLabel },
48
+ value: [...value],
49
+ onChange: (next) => {
50
+ setValue(next);
51
+ },
52
+ children: [
53
+ /* @__PURE__ */ jsx(Stack, { gap: 2, children: options.map((option) => /* @__PURE__ */ jsx(
54
+ Checkbox,
55
+ {
56
+ value: option.value,
57
+ label: option.label,
58
+ ...option.disabled === true && { disabled: true }
59
+ },
60
+ option.value
61
+ )) }),
62
+ !isDefault && /* @__PURE__ */ jsx(
63
+ Button,
64
+ {
65
+ variant: "subtle",
66
+ size: "ctl",
67
+ onClick: () => {
68
+ setValue(field.fallback);
69
+ },
70
+ children: "Clear"
71
+ }
72
+ )
73
+ ]
74
+ }
75
+ )
76
+ );
77
+ return /* @__PURE__ */ jsx(
78
+ FilterPill,
79
+ {
80
+ label: carriesInformation ? `${value.length} ${noun ?? label.toLowerCase()}` : label,
81
+ active: !isDefault,
82
+ ...icon !== void 0 && { icon },
83
+ children: /* @__PURE__ */ jsx("div", { className: classes.optionList, children: body })
84
+ }
85
+ );
86
+ }
87
+ export {
88
+ MultiSelectFilter
89
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=peer-boundary.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-boundary.test.d.ts","sourceRoot":"","sources":["../../src/controls/peer-boundary.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=props-contract.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"props-contract.test.d.ts","sourceRoot":"","sources":["../../src/controls/props-contract.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type { FieldHandle, RangeField } from '../state.js';
3
+ /**
4
+ * The contract a custom-range picker satisfies — `basalt-ui/controls-dates`' `DateRangePicker`, or a
5
+ * consumer's own. Dates are ISO (`YYYY-MM-DD`), the same shape `field.range` stores and
6
+ * `toWindow()` emits, so the picker never learns the store's encoding.
7
+ */
8
+ export type RangeCustomPickerProps = {
9
+ readonly value: {
10
+ readonly from?: string | undefined;
11
+ readonly to?: string | undefined;
12
+ };
13
+ readonly onChange: (next: {
14
+ from: string;
15
+ to: string;
16
+ }) => void;
17
+ };
18
+ export type RangeFilterProps<P extends string> = {
19
+ readonly field: FieldHandle<RangeField<P>>;
20
+ /** Leading pill icon. Defaults to a calendar glyph — a range filter reads as a date control, so
21
+ * the glyph is part of the control's identity, not a per-call-site decision. */
22
+ readonly icon?: ReactNode;
23
+ /** Rendered when the field declares `custom: true`. Omitted → presets only. */
24
+ readonly customPicker?: ComponentType<RangeCustomPickerProps>;
25
+ /** Sheet-form heading. The pill's own label is the VALUE, which is what a bar reads. @default 'Range' */
26
+ readonly label?: string;
27
+ };
28
+ export declare function RangeFilter<P extends string>({ field, icon, customPicker, label, }: RangeFilterProps<P>): ReactNode;
29
+ //# sourceMappingURL=range-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range-filter.d.ts","sourceRoot":"","sources":["../../src/controls/range-filter.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAYnE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IACxF,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAChE,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,IAAI;IAC/C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C;qFACiF;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAA;IAC7D,yGAAyG;IACzG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,EAC5C,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,KAAe,GAChB,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CA8FjC"}
@@ -0,0 +1,120 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { SegmentedControl, Stack } from "@mantine/core";
3
+ import { useState } from "react";
4
+ import classes from "./controls.module.css";
5
+ import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
6
+ import { FilterPill } from "./filter-pill.js";
7
+ import { CalendarGlyph } from "./glyphs.js";
8
+ import { SheetDisclosure, SheetField, SheetOptionList, useControlName } from "./filter-sheet.js";
9
+ const VERTICAL_FROM = 5;
10
+ const NUMERIC_LABEL = /^\d/;
11
+ function RangeFilter({
12
+ field,
13
+ icon,
14
+ customPicker,
15
+ label = "Range"
16
+ }) {
17
+ const [value, setValue] = field.use();
18
+ const surface = useFilterSurface();
19
+ const isDefault = field.isDefault(value);
20
+ useFilterRegistration(!isDefault, () => {
21
+ setValue(field.fallback);
22
+ });
23
+ const options = field.options;
24
+ const presets = options.filter((option) => option.value !== "custom");
25
+ const allowsCustom = options.length !== presets.length;
26
+ const numeric = presets.length > 0 && presets.every((option) => NUMERIC_LABEL.test(option.label));
27
+ const inSheet = surface === "sheet";
28
+ const Picker = customPicker;
29
+ const { labelId, nameProps } = useControlName(label, inSheet);
30
+ const [customOpen, setCustomOpen] = useState(value.preset === "custom");
31
+ const showPicker = allowsCustom && Picker !== void 0;
32
+ if (inSheet) {
33
+ return /* @__PURE__ */ jsxs(SheetField, { label, labelId, children: [
34
+ /* @__PURE__ */ jsx(
35
+ SheetOptionList,
36
+ {
37
+ mode: "single",
38
+ labelId,
39
+ selected: [value.preset],
40
+ options: presets.map((option) => ({ value: option.value, label: option.label })),
41
+ onToggle: (next) => {
42
+ setCustomOpen(false);
43
+ setValue({ preset: next });
44
+ }
45
+ }
46
+ ),
47
+ showPicker && /* @__PURE__ */ jsx(
48
+ SheetDisclosure,
49
+ {
50
+ label: "Custom range\u2026",
51
+ expanded: customOpen,
52
+ onToggle: () => {
53
+ setCustomOpen((open) => !open);
54
+ },
55
+ children: /* @__PURE__ */ jsx(
56
+ Picker,
57
+ {
58
+ value: { from: value.from, to: value.to },
59
+ onChange: ({ from, to }) => {
60
+ setValue({ preset: "custom", from, to });
61
+ }
62
+ }
63
+ )
64
+ }
65
+ )
66
+ ] });
67
+ }
68
+ const body = /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
69
+ /* @__PURE__ */ jsx(
70
+ SegmentedControl,
71
+ {
72
+ ...nameProps,
73
+ size: "ctl",
74
+ orientation: presets.length >= VERTICAL_FROM ? "vertical" : "horizontal",
75
+ value: value.preset,
76
+ data: presets.map((option) => ({ value: option.value, label: option.label })),
77
+ ...numeric && { "data-numeric": true },
78
+ onChange: (next) => {
79
+ setValue({ preset: next });
80
+ }
81
+ }
82
+ ),
83
+ showPicker && /* @__PURE__ */ jsx(
84
+ Picker,
85
+ {
86
+ value: { from: value.from, to: value.to },
87
+ onChange: ({ from, to }) => {
88
+ setValue({ preset: "custom", from, to });
89
+ }
90
+ }
91
+ )
92
+ ] });
93
+ return /* @__PURE__ */ jsx(
94
+ FilterPill,
95
+ {
96
+ label: rangeLabel(value, options),
97
+ active: !isDefault,
98
+ numeric: numeric && value.preset !== "custom",
99
+ icon: icon ?? /* @__PURE__ */ jsx(CalendarGlyph, {}),
100
+ children: /* @__PURE__ */ jsx("div", { className: classes.optionList, children: body })
101
+ }
102
+ );
103
+ }
104
+ const DAY_FORMAT = new Intl.DateTimeFormat("en-US", {
105
+ month: "short",
106
+ day: "numeric",
107
+ // ISO date strings parse as UTC midnight; formatting them in the runtime's zone would render the
108
+ // previous day west of Greenwich — and differently on server than on client (a hydration
109
+ // mismatch), the same hazard `formatArticleDate` avoids.
110
+ timeZone: "UTC"
111
+ });
112
+ function rangeLabel(value, options) {
113
+ if (value.preset === "custom" && value.from !== void 0 && value.to !== void 0) {
114
+ return `${DAY_FORMAT.format(new Date(value.from))} \u2013 ${DAY_FORMAT.format(new Date(value.to))}`;
115
+ }
116
+ return options.find((option) => option.value === value.preset)?.label ?? value.preset;
117
+ }
118
+ export {
119
+ RangeFilter
120
+ };
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { FieldHandle, StringField } from '../state.js';
3
+ export type SearchFilterProps = {
4
+ readonly field: FieldHandle<StringField>;
5
+ readonly placeholder?: string;
6
+ /** Sheet-form heading. @default 'Search' */
7
+ readonly label?: string;
8
+ };
9
+ export declare function SearchFilter({ field, placeholder, label, }: SearchFilterProps): ReactNode;
10
+ //# sourceMappingURL=search-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-filter.d.ts","sourceRoot":"","sources":["../../src/controls/search-filter.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAOxD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,WAAW,EACX,KAAgB,GACjB,EAAE,iBAAiB,GAAG,SAAS,CAiC/B"}