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
@@ -0,0 +1,81 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { TextInput } from "@mantine/core";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
5
+ import { SheetField, useControlName } from "./filter-sheet.js";
6
+ const DEBOUNCE_MS = 200;
7
+ function SearchFilter({
8
+ field,
9
+ placeholder,
10
+ label = "Search"
11
+ }) {
12
+ const [value, setValue] = field.use();
13
+ const surface = useFilterSurface();
14
+ const draft = useDebouncedField(value, setValue);
15
+ useFilterRegistration(!field.isDefault(value), () => {
16
+ setValue(field.fallback);
17
+ });
18
+ const { labelId, nameProps } = useControlName(label, surface === "sheet");
19
+ const input = /* @__PURE__ */ jsx(
20
+ TextInput,
21
+ {
22
+ ...nameProps,
23
+ size: "ctl",
24
+ value: draft.value,
25
+ leftSection: /* @__PURE__ */ jsx(SearchIcon, {}),
26
+ ...placeholder !== void 0 && { placeholder },
27
+ onChange: (event) => {
28
+ draft.set(event.currentTarget.value);
29
+ }
30
+ }
31
+ );
32
+ if (surface === "sheet") {
33
+ return /* @__PURE__ */ jsx(SheetField, { label, labelId, children: input });
34
+ }
35
+ return input;
36
+ }
37
+ function useDebouncedField(value, setValue) {
38
+ const [draft, setDraft] = useState(value);
39
+ const seen = useRef(value);
40
+ const commit = useRef(setValue);
41
+ commit.current = setValue;
42
+ useEffect(() => {
43
+ if (seen.current === value) return;
44
+ seen.current = value;
45
+ setDraft(value);
46
+ }, [value]);
47
+ useEffect(() => {
48
+ if (draft === value) return;
49
+ const timer = setTimeout(() => {
50
+ seen.current = draft;
51
+ commit.current(draft);
52
+ }, DEBOUNCE_MS);
53
+ return () => {
54
+ clearTimeout(timer);
55
+ };
56
+ }, [draft, value]);
57
+ return { value: draft, set: setDraft };
58
+ }
59
+ function SearchIcon() {
60
+ return /* @__PURE__ */ jsxs(
61
+ "svg",
62
+ {
63
+ width: 16,
64
+ height: 16,
65
+ viewBox: "0 0 24 24",
66
+ fill: "none",
67
+ stroke: "currentColor",
68
+ strokeWidth: 2,
69
+ strokeLinecap: "round",
70
+ strokeLinejoin: "round",
71
+ "aria-hidden": true,
72
+ children: [
73
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
74
+ /* @__PURE__ */ jsx("path", { d: "M21 21l-4.35 -4.35" })
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ export {
80
+ SearchFilter
81
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `SelectFilter` — one choice, one pill (`docs/CONTROLS-SPEC.md` §3). Takes `field`, never
3
+ * `value`/`onChange`/`size` (C2/C5): the handle owns the URL write, the localStorage mirror and the
4
+ * option labels (`store.labels()`), so the call site is the field plus its human name.
5
+ *
6
+ * `options` is the escape hatch for a catalogue that only exists at runtime — the label carries live
7
+ * data (`EUR · 1.08`), or the id set is fetched rather than declared. It OVERRIDES `field.options`
8
+ * at render, and it is what makes the second `field` shape legal: a `StringField` handle plus
9
+ * `options` covers an id set no enum could close over (a project picker, a device list), while a
10
+ * `StringField` handle WITHOUT `options` stays a type error — nothing would supply the rows, so the
11
+ * filter would render an empty popover.
12
+ *
13
+ * @example
14
+ * <SelectFilter field={analytics.field.currency} label="Currency" clearable />
15
+ *
16
+ * @example
17
+ * // A runtime catalogue over a closed enum — same field, live labels.
18
+ * <SelectFilter
19
+ * field={analytics.field.currency}
20
+ * label="Currency"
21
+ * options={rates.map((r) => ({ value: r.code, label: `${r.code} · ${r.rate}` }))}
22
+ * />
23
+ *
24
+ * @example
25
+ * // An id set the enum cannot close over — a string field, options mandatory.
26
+ * <SelectFilter
27
+ * field={dashboard.field.projectId}
28
+ * label="Project"
29
+ * options={projects.map((p) => ({ value: p.id, label: p.name, disabled: p.archived }))}
30
+ * />
31
+ */
32
+ import type { ReactNode } from 'react';
33
+ import type { EnumField, FieldHandle, StringField } from '../state.js';
34
+ /**
35
+ * One row of a runtime catalogue. Wider than the store's own `FieldOption` by exactly one member:
36
+ * `disabled` renders the row and refuses the selection, which a declared enum expresses by leaving
37
+ * the value out and a live catalogue cannot (an archived project still labels the rows already
38
+ * pointing at it).
39
+ */
40
+ export type FilterOption = {
41
+ readonly value: string;
42
+ readonly label: string;
43
+ readonly disabled?: boolean;
44
+ };
45
+ type SelectFilterCommon = {
46
+ readonly label: string;
47
+ readonly icon?: ReactNode;
48
+ /** Adds a `Clear` action that writes the field back to its fallback. */
49
+ readonly clearable?: boolean;
50
+ };
51
+ /** A closed enum field: `options` relabels rows the field already declares, so it is optional. */
52
+ export type SelectFilterEnumProps<T extends string> = SelectFilterCommon & {
53
+ readonly field: FieldHandle<EnumField<T>>;
54
+ /** Overrides `field.options` at render — a runtime catalogue, labels included. */
55
+ readonly options?: readonly FilterOption[];
56
+ };
57
+ /** A string field: it declares no values, so `options` is the only row source and is required. */
58
+ export type SelectFilterStringProps = SelectFilterCommon & {
59
+ readonly field: FieldHandle<StringField>;
60
+ /** Required here — see this type's doc. */
61
+ readonly options: readonly FilterOption[];
62
+ };
63
+ export type SelectFilterProps<T extends string> = SelectFilterEnumProps<T> | SelectFilterStringProps;
64
+ /**
65
+ * TWO OVERLOADS, not one conditional prop type, and the reason is the error text. A union prop type
66
+ * reports only the constituent it likes best — for a `StringField` handle passed without `options`
67
+ * that is the enum branch, so the message reads `Type '"string"' is not assignable to type '"enum"'`
68
+ * and never mentions the prop that would fix it. The overload form reports both halves, ending in
69
+ * `Property 'options' is missing`, which is the actual instruction.
70
+ */
71
+ export declare function SelectFilter<T extends string>(props: SelectFilterEnumProps<T>): ReactNode;
72
+ export declare function SelectFilter(props: SelectFilterStringProps): ReactNode;
73
+ export {};
74
+ //# sourceMappingURL=select-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-filter.d.ts","sourceRoot":"","sources":["../../src/controls/select-filter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAGnE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,kGAAkG;AAClG,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,MAAM,IAAI,kBAAkB,GAAG;IACzE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;CAC3C,CAAA;AAED,kGAAkG;AAClG,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG;IACzD,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACxC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,qBAAqB,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAA;AAEpG;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;AAC1F,wBAAgB,YAAY,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { EnumFilter } from "./enum-filter.js";
3
+ function SelectFilter(props) {
4
+ const { field, ...rest } = props;
5
+ if (field.kind === "string") return /* @__PURE__ */ jsx(EnumFilter, { ...rest, field });
6
+ return /* @__PURE__ */ jsx(EnumFilter, { ...rest, field });
7
+ }
8
+ export {
9
+ SelectFilter
10
+ };
@@ -0,0 +1,38 @@
1
+ import type { ReactNode } from 'react';
2
+ export type SyncButtonProps = {
3
+ syncing: boolean;
4
+ /** Last successful completion. `undefined`/`null` renders no age at all. */
5
+ lastCompletedAt?: number | Date | null;
6
+ onSync: () => void;
7
+ /** The home, and with it the shape: `'global'` is icon-only at every width, `'page'` is labelled
8
+ * on desktop and icon-only below `sm` — see this module's doc. */
9
+ scope: 'page' | 'global';
10
+ /** Visible label at `scope: 'page'` on desktop, and the accessible name in every other form.
11
+ * @default 'Sync' */
12
+ label?: string;
13
+ /**
14
+ * Last failure. Puts the control in the danger tone, carries the message in the tooltip (which
15
+ * opens on hover, focus AND touch) and folds it into the button's accessible name, so the red is
16
+ * never the only signal.
17
+ */
18
+ error?: string;
19
+ };
20
+ /** How often the relative age re-renders. One interval, only while mounted. */
21
+ export declare const AGE_REFRESH_MS = 30000;
22
+ /**
23
+ * Relative age, coarse on purpose: a sync indicator answers "is this stale?", not "how stale to
24
+ * the second". Anything under a minute is `just now`, so the label stops flickering while a sync
25
+ * has just landed.
26
+ */
27
+ export declare function formatAge(ms: number): string;
28
+ /**
29
+ * @example
30
+ * // page scope — the page's own data
31
+ * <PageBar sync={{ syncing, lastCompletedAt, onSync: refetch }} />
32
+ *
33
+ * // global scope — the shell header, icon-only at every width
34
+ * <BasaltShell globalActions={[{ key: 'sync', mobile: 'bar',
35
+ * node: <SyncButton scope="global" syncing={s} lastCompletedAt={t} onSync={run} /> }]} />
36
+ */
37
+ export declare function SyncButton({ syncing, lastCompletedAt, onSync, scope, label, error, }: SyncButtonProps): ReactNode;
38
+ //# sourceMappingURL=sync-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-button.d.ts","sourceRoot":"","sources":["../../src/controls/sync-button.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB;sEACkE;IAClE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAA;IACxB;yBACqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,cAAc,QAAS,CAAA;AAEpC;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAO5C;AA0DD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,eAAe,EACf,MAAM,EACN,KAAK,EACL,KAAc,EACd,KAAK,GACN,EAAE,eAAe,GAAG,SAAS,CA4D7B"}
@@ -0,0 +1,119 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { ActionIcon, Button, Tooltip } from "@mantine/core";
3
+ import { useEffect, useState } from "react";
4
+ import { IconSlot } from "../theme/icon-slot.js";
5
+ import classes from "./sync-button.module.css";
6
+ const AGE_REFRESH_MS = 3e4;
7
+ function formatAge(ms) {
8
+ if (ms < 6e4) return "just now";
9
+ const minutes = Math.floor(ms / 6e4);
10
+ if (minutes < 60) return `${minutes}m ago`;
11
+ const hours = Math.floor(minutes / 60);
12
+ if (hours < 24) return `${hours}h ago`;
13
+ return `${Math.floor(hours / 24)}d ago`;
14
+ }
15
+ function toMillis(at) {
16
+ if (at === null || at === void 0) return void 0;
17
+ return at instanceof Date ? at.getTime() : at;
18
+ }
19
+ function useRelativeAge(at) {
20
+ const [age, setAge] = useState(void 0);
21
+ const timestamp = toMillis(at);
22
+ useEffect(() => {
23
+ if (timestamp === void 0) {
24
+ setAge(void 0);
25
+ return;
26
+ }
27
+ const publish = () => {
28
+ setAge(formatAge(Date.now() - timestamp));
29
+ };
30
+ publish();
31
+ const id = setInterval(publish, AGE_REFRESH_MS);
32
+ return () => clearInterval(id);
33
+ }, [timestamp]);
34
+ return age;
35
+ }
36
+ function RefreshGlyph({ spinning }) {
37
+ return /* @__PURE__ */ jsx(
38
+ "svg",
39
+ {
40
+ width: 16,
41
+ height: 16,
42
+ viewBox: "0 0 24 24",
43
+ fill: "none",
44
+ "aria-hidden": true,
45
+ className: spinning ? classes.spin : void 0,
46
+ children: /* @__PURE__ */ jsx(
47
+ "path",
48
+ {
49
+ d: "M20 11a8 8 0 1 0-2.34 5.66M20 5v6h-6",
50
+ stroke: "currentColor",
51
+ strokeWidth: 2,
52
+ strokeLinecap: "round",
53
+ strokeLinejoin: "round"
54
+ }
55
+ )
56
+ }
57
+ );
58
+ }
59
+ function SyncButton({
60
+ syncing,
61
+ lastCompletedAt,
62
+ onSync,
63
+ scope,
64
+ label = "Sync",
65
+ error
66
+ }) {
67
+ const age = useRelativeAge(lastCompletedAt);
68
+ const tooltip = error ?? age;
69
+ const name = error !== void 0 ? `${label} \u2014 ${error}` : label;
70
+ const control = {
71
+ "data-basalt-sync-scope": scope,
72
+ "aria-busy": syncing,
73
+ // NOT the native `disabled`. That attribute drops focus to <body> the moment a keyboard user
74
+ // presses the button, so when the sync lands focus is gone and the next Tab restarts from
75
+ // the top of the document — and an `aria-busy` element you cannot focus announces nothing.
76
+ // `aria-disabled` + Mantine's `data-disabled` styling say the same thing and keep the button
77
+ // where the user left it; the handler does the actual refusing.
78
+ "aria-disabled": syncing,
79
+ "aria-label": name,
80
+ ...syncing && { "data-disabled": true },
81
+ ...error !== void 0 && { color: "red" },
82
+ onClick: () => {
83
+ if (!syncing) onSync();
84
+ }
85
+ };
86
+ return (
87
+ // `events` is not a default: Mantine's Tooltip opens on HOVER only, so on the stock settings a
88
+ // keyboard user and a phone user never reach the error — and the age's "the tooltip carries it
89
+ // below `sm`" claim in the CSS was false on touch. `aria-label` is the belt to that braces: a
90
+ // tooltip announces only while open, and an error has to be readable without one.
91
+ /* @__PURE__ */ jsx(
92
+ Tooltip,
93
+ {
94
+ label: tooltip ?? "",
95
+ disabled: tooltip === void 0,
96
+ events: { hover: true, focus: true, touch: true },
97
+ withArrow: true,
98
+ children: scope === "global" ? /* @__PURE__ */ jsx(ActionIcon, { variant: "default", ...control, children: /* @__PURE__ */ jsx(IconSlot, { children: /* @__PURE__ */ jsx(RefreshGlyph, { spinning: syncing }) }) }) : /* @__PURE__ */ jsxs(
99
+ Button,
100
+ {
101
+ variant: "default",
102
+ className: classes.pageButton,
103
+ leftSection: /* @__PURE__ */ jsx(IconSlot, { children: /* @__PURE__ */ jsx(RefreshGlyph, { spinning: syncing }) }),
104
+ ...control,
105
+ children: [
106
+ /* @__PURE__ */ jsx("span", { className: classes.label, children: label }),
107
+ age !== void 0 && /* @__PURE__ */ jsx("span", { className: classes.age, children: age })
108
+ ]
109
+ }
110
+ )
111
+ }
112
+ )
113
+ );
114
+ }
115
+ export {
116
+ AGE_REFRESH_MS,
117
+ SyncButton,
118
+ formatAge
119
+ };
@@ -0,0 +1,63 @@
1
+ /*
2
+ * `SyncButton` chrome — the rotating glyph and the desktop-only age suffix.
3
+ */
4
+
5
+ .spin {
6
+ animation: basalt-sync-spin 900ms linear infinite;
7
+ }
8
+
9
+ @keyframes basalt-sync-spin {
10
+ to {
11
+ transform: rotate(360deg);
12
+ }
13
+ }
14
+
15
+ /*
16
+ * Reduced motion drops the rotation, never the icon: the `aria-busy`/`aria-disabled` pair is what
17
+ * actually announces "syncing", so removing the spin costs no information.
18
+ */
19
+ @media (prefers-reduced-motion: reduce) {
20
+ .spin {
21
+ animation: none;
22
+ }
23
+ }
24
+
25
+ /*
26
+ * The age rides beside the label on desktop and disappears below `sm`, where the tooltip carries
27
+ * it instead — one mount, a CSS-only swap (law C9).
28
+ */
29
+ .age {
30
+ margin-inline-start: var(--vx-space-control-gap);
31
+ color: var(--vx-muted);
32
+ font-family: var(--basalt-font-mono);
33
+ font-size: var(--vx-text-xs);
34
+ }
35
+
36
+ /*
37
+ * `scope: 'page'` below `sm`: the label and the age go, the glyph stays, and the button collapses
38
+ * to the square icon form (spec §3, "icon-only, age in tooltip"). Hidden, not unmounted — a second
39
+ * mount under `hiddenFrom` is exactly the responsive twin law C9 forbids, which is also why the
40
+ * accessible name lives on `aria-label` rather than in this text.
41
+ *
42
+ * Three declarations, because Mantine's Button spends its width in three places: `--button-padding-x`
43
+ * (the inline padding, which `[data-with-left-section]` divides by 1.5), the left section's own
44
+ * `margin-inline-end` (a fixed `--mantine-spacing-xs`, not a variable, so it takes a rule), and the
45
+ * width itself, pinned to the `ctl` height so the box is square. This module is unlayered, so it
46
+ * outranks `@layer mantine` regardless of specificity.
47
+ */
48
+ @media (max-width: 47.99375em) {
49
+ .label,
50
+ .age {
51
+ display: none;
52
+ }
53
+
54
+ .pageButton {
55
+ --button-padding-x: 0;
56
+
57
+ width: calc(var(--vx-space-control-height-ctl, 1.875rem) * var(--mantine-scale, 1));
58
+ }
59
+
60
+ .pageButton :global(.mantine-Button-section[data-position='left']) {
61
+ margin-inline-end: 0;
62
+ }
63
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Typed CSS-module declaration (the S4 CSS-modules path). Named string props — not an index
3
+ * signature — so dot access stays verbatim under the package's strict flags.
4
+ */
5
+ declare const classes: {
6
+ readonly spin: string
7
+ readonly age: string
8
+ readonly label: string
9
+ readonly pageButton: string
10
+ }
11
+ export default classes
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sync-button.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-button.test.d.ts","sourceRoot":"","sources":["../../src/controls/sync-button.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { BooleanField, FieldHandle } from '../state.js';
3
+ export type ToggleFilterProps = {
4
+ readonly field: FieldHandle<BooleanField>;
5
+ readonly label: string;
6
+ readonly icon?: ReactNode;
7
+ };
8
+ export declare function ToggleFilter({ field, label, icon }: ToggleFilterProps): ReactNode;
9
+ //# sourceMappingURL=toggle-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-filter.d.ts","sourceRoot":"","sources":["../../src/controls/toggle-filter.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAKzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAC1B,CAAA;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAoCjF"}
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Switch } from "@mantine/core";
3
+ import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
4
+ import { FilterPill } from "./filter-pill.js";
5
+ import { sheetRowClassNames } from "./filter-sheet.js";
6
+ function ToggleFilter({ field, label, icon }) {
7
+ const [value, setValue] = field.use();
8
+ const surface = useFilterSurface();
9
+ useFilterRegistration(!field.isDefault(value), () => {
10
+ setValue(field.fallback);
11
+ });
12
+ if (surface === "sheet") {
13
+ return /* @__PURE__ */ jsx(
14
+ Switch,
15
+ {
16
+ classNames: sheetRowClassNames,
17
+ checked: value,
18
+ label,
19
+ labelPosition: "left",
20
+ onChange: (event) => {
21
+ setValue(event.currentTarget.checked);
22
+ }
23
+ }
24
+ );
25
+ }
26
+ return /* @__PURE__ */ jsx(
27
+ FilterPill,
28
+ {
29
+ label,
30
+ active: value,
31
+ pressed: value,
32
+ hideGlyph: true,
33
+ ...icon !== void 0 && { icon },
34
+ onClick: () => {
35
+ setValue(!value);
36
+ }
37
+ }
38
+ );
39
+ }
40
+ export {
41
+ ToggleFilter
42
+ };
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { EnumField, FieldHandle } from '../state.js';
3
+ export type ViewTabsOption<T extends string> = {
4
+ readonly value: T;
5
+ readonly label: string;
6
+ /** Render this option on ONE viewport only. Omitted → both. */
7
+ readonly only?: 'sm-up' | 'sm-down';
8
+ };
9
+ export type ViewTabsProps<T extends string> = {
10
+ readonly field: FieldHandle<EnumField<T>>;
11
+ /** Defaults to `field.options` (every declared value, labelled by `store.labels()`). */
12
+ readonly options?: readonly ViewTabsOption<T>[];
13
+ /**
14
+ * The switch's accessible name. Both forms render without a visible Mantine `label` (a home has
15
+ * no room for one), so without this the desktop track is an unnamed `radiogroup` and the phone
16
+ * form an unnamed `combobox`.
17
+ *
18
+ * @default 'View'
19
+ */
20
+ readonly label?: string;
21
+ };
22
+ export declare function ViewTabs<T extends string>({ field, options, label, }: ViewTabsProps<T>): ReactNode;
23
+ //# sourceMappingURL=view-tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-tabs.d.ts","sourceRoot":"","sources":["../../src/controls/view-tabs.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAMtD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,IAAI;IAC7C,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CACpC,CAAA;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI;IAC5C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,wFAAwF;IACxF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,cAAc,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/C;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,EACzC,KAAK,EACL,OAAO,EACP,KAAc,GACf,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAoD9B"}
@@ -0,0 +1,59 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { SegmentedControl, Select } from "@mantine/core";
3
+ import classes from "./controls.module.css";
4
+ const PHONE_TRACK_MAX = 3;
5
+ function ViewTabs({
6
+ field,
7
+ options,
8
+ label = "View"
9
+ }) {
10
+ const [value, setValue] = field.use();
11
+ const all = options ?? field.options.map((option) => ({ value: option.value, label: option.label }));
12
+ const desktop = all.filter((option) => option.only !== "sm-down");
13
+ const phone = all.filter((option) => option.only !== "sm-up");
14
+ const commit = (next) => {
15
+ setValue(next);
16
+ };
17
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
18
+ /* @__PURE__ */ jsx(
19
+ SegmentedControl,
20
+ {
21
+ "aria-label": label,
22
+ visibleFrom: "sm",
23
+ size: "ctl",
24
+ value,
25
+ data: desktop.map((option) => ({ value: option.value, label: option.label })),
26
+ onChange: commit
27
+ }
28
+ ),
29
+ phone.length <= PHONE_TRACK_MAX ? /* @__PURE__ */ jsx(
30
+ SegmentedControl,
31
+ {
32
+ "aria-label": label,
33
+ hiddenFrom: "sm",
34
+ size: "ctl",
35
+ fullWidth: true,
36
+ value,
37
+ data: phone.map((option) => ({ value: option.value, label: option.label })),
38
+ onChange: commit
39
+ }
40
+ ) : /* @__PURE__ */ jsx(
41
+ Select,
42
+ {
43
+ "aria-label": label,
44
+ hiddenFrom: "sm",
45
+ size: "ctl",
46
+ className: classes.phoneSelect,
47
+ value,
48
+ allowDeselect: false,
49
+ data: phone.map((option) => ({ value: option.value, label: option.label })),
50
+ onChange: (next) => {
51
+ if (next !== null) commit(next);
52
+ }
53
+ }
54
+ )
55
+ ] });
56
+ }
57
+ export {
58
+ ViewTabs
59
+ };
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { RangeCustomPickerProps } from '../controls/range-filter.js';
3
+ export declare function DateRangePicker({ value, onChange }: RangeCustomPickerProps): ReactNode;
4
+ //# sourceMappingURL=date-range-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-range-picker.d.ts","sourceRoot":"","sources":["../../src/controls-dates/date-range-picker.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAEtE,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,sBAAsB,GAAG,SAAS,CAatF"}
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { DatePicker } from "@mantine/dates";
3
+ function DateRangePicker({ value, onChange }) {
4
+ return /* @__PURE__ */ jsx(
5
+ DatePicker,
6
+ {
7
+ type: "range",
8
+ allowSingleDateInRange: true,
9
+ value: [value.from ?? null, value.to ?? null],
10
+ onChange: ([from, to]) => {
11
+ if (from !== null && to !== null) onChange({ from, to });
12
+ }
13
+ }
14
+ );
15
+ }
16
+ export {
17
+ DateRangePicker
18
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `basalt-ui/controls-dates` — the one export that needs `@mantine/dates`
3
+ * (`docs/CONTROLS-SPEC.md` §3). Split off `./controls` on purpose: `./controls` resolves and renders
4
+ * with no `@mantine/dates` installed, and this subpath is the only place that stops being true.
5
+ * Proven from outside the package by `scripts/pack-test.sh`.
6
+ */
7
+ export { DateRangePicker } from './date-range-picker.js';
8
+ export type { RangeCustomPickerProps } from '../controls/range-filter.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controls-dates/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { DateRangePicker } from "./date-range-picker.js";
2
+ export {
3
+ DateRangePicker
4
+ };
@@ -1,13 +1,7 @@
1
- export type DeltaBadgeProps = {
2
- /** Signed delta driving tone (positive/negative/zero) and, by default, the label magnitude. */
3
- value: number;
4
- /** Formats the value into the label text. Defaults to `${Math.abs(value).toFixed(1)}%`. */
5
- format?: (value: number) => string;
6
- /** Render the ▲/▼ direction glyph. Defaults to `true`; a zero value never shows a glyph. */
7
- withGlyph?: boolean;
8
- /** Optional comparison timeframe rendered directly after the value in a dimmer shade of the same
9
- * tone (e.g. `MoM`, `WoW`, `YTD`) — states what the delta is measured against without a hover. */
10
- period?: string | undefined;
11
- };
12
- export declare function DeltaBadge({ value, format, withGlyph, period, }: DeltaBadgeProps): import("react").JSX.Element;
1
+ /**
2
+ * `DeltaBadge` moved to `src/widget-header/` (docs/CONTROLS-SPEC.md §2.2) so `WidgetHeader` can
3
+ * compose it Mantine-free. Re-exported here unchanged so this deep import path — and `StatCard`'s
4
+ * own `./delta-badge` import — keep working without a wave-3 edit.
5
+ */
6
+ export { DeltaBadge, type DeltaBadgeProps } from '../widget-header/delta-badge.js';
13
7
  //# sourceMappingURL=delta-badge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delta-badge.d.ts","sourceRoot":"","sources":["../../src/dashboard/delta-badge.tsx"],"names":[],"mappings":"AAkBA,MAAM,MAAM,eAAe,GAAG;IAC5B,+FAA+F;IAC/F,KAAK,EAAE,MAAM,CAAA;IACb,2FAA2F;IAC3F,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;sGACkG;IAClG,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B,CAAA;AAID,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAsB,EACtB,SAAgB,EAChB,MAAM,GACP,EAAE,eAAe,+BAsCjB"}
1
+ {"version":3,"file":"delta-badge.d.ts","sourceRoot":"","sources":["../../src/dashboard/delta-badge.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAA"}