basalt-ui 1.25.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +26 -0
  3. package/MIGRATING.md +815 -23
  4. package/README.md +45 -32
  5. package/agent/rules/basalt-batteries.md +205 -0
  6. package/agent/rules/basalt-charts.md +125 -555
  7. package/agent/rules/basalt-controls.md +160 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +118 -96
  10. package/agent/rules/basalt-tokens.md +126 -288
  11. package/agent/skills/basalt-app/SKILL.md +70 -195
  12. package/agent/skills/basalt-charts/SKILL.md +54 -234
  13. package/agent/skills/basalt-design/SKILL.md +56 -248
  14. package/agent/templates/CLAUDE-block.md.tpl +31 -40
  15. package/agent/templates/DESIGN.md.tpl +22 -51
  16. package/configs/oxlint-plugin.js +1465 -36
  17. package/configs/oxlint-plugin.test.ts +1190 -11
  18. package/configs/oxlint.json +11 -3
  19. package/dist/charts/cursor/resolve.d.ts +18 -0
  20. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  21. package/dist/charts/cursor/resolve.js +15 -6
  22. package/dist/charts/cursor/scope.d.ts +6 -3
  23. package/dist/charts/cursor/scope.d.ts.map +1 -1
  24. package/dist/charts/cursor/store.d.ts +5 -1
  25. package/dist/charts/cursor/store.d.ts.map +1 -1
  26. package/dist/charts/cursor/store.js +4 -4
  27. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  28. package/dist/charts/hooks/useChartCursor.js +9 -8
  29. package/dist/charts/index.d.ts +2 -2
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/primitives/ChartCard.d.ts +34 -9
  32. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.js +29 -114
  34. package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
  35. package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
  36. package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
  37. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  38. package/dist/charts/sparklines/BarSparkline.js +18 -4
  39. package/dist/cli/check-coverage.test.d.ts +2 -0
  40. package/dist/cli/check-coverage.test.d.ts.map +1 -0
  41. package/dist/cli/index.d.ts +67 -4
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +225 -7
  44. package/dist/connectivity/connectivity-indicator.d.ts +4 -1
  45. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  46. package/dist/connectivity/connectivity-indicator.js +1 -1
  47. package/dist/content/index.d.ts +5 -3
  48. package/dist/content/index.d.ts.map +1 -1
  49. package/dist/content/index.js +4 -2
  50. package/dist/content/prose.module.css +7 -28
  51. package/dist/controls/actions.d.ts +169 -0
  52. package/dist/controls/actions.d.ts.map +1 -0
  53. package/dist/controls/actions.js +287 -0
  54. package/dist/controls/actions.module.css +62 -0
  55. package/dist/controls/actions.module.css.d.ts +13 -0
  56. package/dist/controls/actions.test.d.ts +2 -0
  57. package/dist/controls/actions.test.d.ts.map +1 -0
  58. package/dist/controls/actions.type-guard.test.d.ts +2 -0
  59. package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
  60. package/dist/controls/compare-filter.d.ts +41 -0
  61. package/dist/controls/compare-filter.d.ts.map +1 -0
  62. package/dist/controls/compare-filter.js +23 -0
  63. package/dist/controls/control-group.d.ts +19 -0
  64. package/dist/controls/control-group.d.ts.map +1 -0
  65. package/dist/controls/control-group.js +20 -0
  66. package/dist/controls/control-group.test.d.ts +2 -0
  67. package/dist/controls/control-group.test.d.ts.map +1 -0
  68. package/dist/controls/controls.module.css +542 -0
  69. package/dist/controls/controls.module.css.d.ts +35 -0
  70. package/dist/controls/controls.router.test.d.ts +2 -0
  71. package/dist/controls/controls.router.test.d.ts.map +1 -0
  72. package/dist/controls/enum-filter.d.ts +39 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +93 -0
  75. package/dist/controls/filter-context.d.ts +60 -0
  76. package/dist/controls/filter-context.d.ts.map +1 -0
  77. package/dist/controls/filter-context.js +69 -0
  78. package/dist/controls/filter-pill.d.ts +28 -0
  79. package/dist/controls/filter-pill.d.ts.map +1 -0
  80. package/dist/controls/filter-pill.js +70 -0
  81. package/dist/controls/filter-set.d.ts +13 -0
  82. package/dist/controls/filter-set.d.ts.map +1 -0
  83. package/dist/controls/filter-set.js +125 -0
  84. package/dist/controls/filter-set.test.d.ts +2 -0
  85. package/dist/controls/filter-set.test.d.ts.map +1 -0
  86. package/dist/controls/filter-sheet.d.ts +117 -0
  87. package/dist/controls/filter-sheet.d.ts.map +1 -0
  88. package/dist/controls/filter-sheet.js +146 -0
  89. package/dist/controls/glyphs.d.ts +27 -0
  90. package/dist/controls/glyphs.d.ts.map +1 -0
  91. package/dist/controls/glyphs.js +49 -0
  92. package/dist/controls/index.d.ts +20 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +46 -0
  95. package/dist/controls/multi-select-filter.d.ts +23 -0
  96. package/dist/controls/multi-select-filter.d.ts.map +1 -0
  97. package/dist/controls/multi-select-filter.js +89 -0
  98. package/dist/controls/number-filter.d.ts +26 -0
  99. package/dist/controls/number-filter.d.ts.map +1 -0
  100. package/dist/controls/number-filter.js +143 -0
  101. package/dist/controls/peer-boundary.test.d.ts +2 -0
  102. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  103. package/dist/controls/props-contract.test.d.ts +2 -0
  104. package/dist/controls/props-contract.test.d.ts.map +1 -0
  105. package/dist/controls/range-filter.d.ts +43 -0
  106. package/dist/controls/range-filter.d.ts.map +1 -0
  107. package/dist/controls/range-filter.js +135 -0
  108. package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
  109. package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
  110. package/dist/controls/search-filter.d.ts +10 -0
  111. package/dist/controls/search-filter.d.ts.map +1 -0
  112. package/dist/controls/search-filter.js +81 -0
  113. package/dist/controls/select-filter.d.ts +74 -0
  114. package/dist/controls/select-filter.d.ts.map +1 -0
  115. package/dist/controls/select-filter.js +10 -0
  116. package/dist/controls/sync-button.d.ts +38 -0
  117. package/dist/controls/sync-button.d.ts.map +1 -0
  118. package/dist/controls/sync-button.js +119 -0
  119. package/dist/controls/sync-button.module.css +63 -0
  120. package/dist/controls/sync-button.module.css.d.ts +11 -0
  121. package/dist/controls/sync-button.test.d.ts +2 -0
  122. package/dist/controls/sync-button.test.d.ts.map +1 -0
  123. package/dist/controls/toggle-filter.d.ts +9 -0
  124. package/dist/controls/toggle-filter.d.ts.map +1 -0
  125. package/dist/controls/toggle-filter.js +42 -0
  126. package/dist/controls/view-tabs.d.ts +23 -0
  127. package/dist/controls/view-tabs.d.ts.map +1 -0
  128. package/dist/controls/view-tabs.js +59 -0
  129. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  130. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  131. package/dist/controls-dates/date-range-picker.js +18 -0
  132. package/dist/controls-dates/index.d.ts +9 -0
  133. package/dist/controls-dates/index.d.ts.map +1 -0
  134. package/dist/controls-dates/index.js +4 -0
  135. package/dist/dashboard/delta-badge.d.ts +6 -12
  136. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  137. package/dist/dashboard/delta-badge.js +1 -50
  138. package/dist/dashboard/index.d.ts +1 -1
  139. package/dist/dashboard/index.d.ts.map +1 -1
  140. package/dist/dashboard/index.js +3 -1
  141. package/dist/dashboard/settings-section.d.ts +6 -4
  142. package/dist/dashboard/settings-section.d.ts.map +1 -1
  143. package/dist/dashboard/settings-section.js +23 -16
  144. package/dist/dashboard/settings-section.module.css +10 -0
  145. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  146. package/dist/dashboard/settings-section.test.d.ts +2 -0
  147. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  148. package/dist/dashboard/stat-card.d.ts +95 -9
  149. package/dist/dashboard/stat-card.d.ts.map +1 -1
  150. package/dist/dashboard/stat-card.js +108 -49
  151. package/dist/dashboard/stat-card.module.css +153 -0
  152. package/dist/dashboard/stat-card.module.css.d.ts +15 -0
  153. package/dist/data/data-table.d.ts +37 -15
  154. package/dist/data/data-table.d.ts.map +1 -1
  155. package/dist/data/data-table.js +96 -58
  156. package/dist/guard/grace.test.d.ts +20 -0
  157. package/dist/guard/grace.test.d.ts.map +1 -0
  158. package/dist/guard/index.d.ts +66 -3
  159. package/dist/guard/index.d.ts.map +1 -1
  160. package/dist/guard/index.js +78 -7
  161. package/dist/guard/types.d.ts +3 -3
  162. package/dist/guard/types.d.ts.map +1 -1
  163. package/dist/index.d.ts +13 -3
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js +13 -10
  166. package/dist/nav/types.d.ts +78 -2
  167. package/dist/nav/types.d.ts.map +1 -1
  168. package/dist/notifications/bell.d.ts +4 -1
  169. package/dist/notifications/bell.d.ts.map +1 -1
  170. package/dist/notifications/index.d.ts +1 -1
  171. package/dist/router-tanstack/field.d.ts +14 -0
  172. package/dist/router-tanstack/field.d.ts.map +1 -0
  173. package/dist/router-tanstack/field.js +4 -0
  174. package/dist/router-tanstack/index.d.ts +6 -2
  175. package/dist/router-tanstack/index.d.ts.map +1 -1
  176. package/dist/router-tanstack/index.js +4 -0
  177. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  178. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  179. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  180. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  181. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  182. package/dist/router-tanstack/search-param-store.js +16 -45
  183. package/dist/router-tanstack/search-store.d.ts +84 -0
  184. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  185. package/dist/router-tanstack/search-store.js +271 -0
  186. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  187. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  188. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  189. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  190. package/dist/router-tanstack/use-nav.d.ts +0 -2
  191. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  192. package/dist/router-tanstack/use-nav.js +34 -12
  193. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  194. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  195. package/dist/section/index.d.ts +2 -0
  196. package/dist/section/index.d.ts.map +1 -0
  197. package/dist/section/index.js +4 -0
  198. package/dist/section/section.d.ts +24 -0
  199. package/dist/section/section.d.ts.map +1 -0
  200. package/dist/section/section.js +126 -0
  201. package/dist/section/section.module.css +104 -0
  202. package/dist/section/section.module.css.d.ts +11 -0
  203. package/dist/section/section.test.d.ts +2 -0
  204. package/dist/section/section.test.d.ts.map +1 -0
  205. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  206. package/dist/shell/app-breadcrumbs.js +18 -15
  207. package/dist/shell/app-header.module.css +77 -37
  208. package/dist/shell/app-header.module.css.d.ts +2 -1
  209. package/dist/shell/app-mobile-nav.d.ts +9 -2
  210. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  211. package/dist/shell/app-mobile-nav.js +98 -11
  212. package/dist/shell/app-mobile-nav.module.css +8 -0
  213. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  214. package/dist/shell/app-sidebar.d.ts +55 -28
  215. package/dist/shell/app-sidebar.d.ts.map +1 -1
  216. package/dist/shell/app-sidebar.js +116 -67
  217. package/dist/shell/app-sidebar.module.css +303 -20
  218. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  219. package/dist/shell/index.d.ts +55 -21
  220. package/dist/shell/index.d.ts.map +1 -1
  221. package/dist/shell/index.js +40 -18
  222. package/dist/shell/mobile-nav-model.d.ts +13 -2
  223. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  224. package/dist/shell/mobile-nav-model.js +15 -1
  225. package/dist/shell/nav-count-badge.d.ts +1 -1
  226. package/dist/shell/page-bar.d.ts +51 -0
  227. package/dist/shell/page-bar.d.ts.map +1 -0
  228. package/dist/shell/page-bar.js +154 -0
  229. package/dist/shell/page-bar.module.css +178 -0
  230. package/dist/shell/page-bar.module.css.d.ts +17 -0
  231. package/dist/shell/page-bar.test.d.ts +2 -0
  232. package/dist/shell/page-bar.test.d.ts.map +1 -0
  233. package/dist/shell/sidebar-block-model.d.ts +48 -0
  234. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  235. package/dist/shell/sidebar-block-model.js +39 -0
  236. package/dist/shell/sidebar-blocks.d.ts +32 -0
  237. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  238. package/dist/shell/sidebar-blocks.js +169 -0
  239. package/dist/shell/sidebar-search.d.ts +13 -1
  240. package/dist/shell/sidebar-search.d.ts.map +1 -1
  241. package/dist/shell/sidebar-search.js +34 -1
  242. package/dist/shell/sidebar-search.module.css +26 -0
  243. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  244. package/dist/state/fields.d.ts +565 -0
  245. package/dist/state/fields.d.ts.map +1 -0
  246. package/dist/state/fields.js +405 -0
  247. package/dist/state/fields.test.d.ts +2 -0
  248. package/dist/state/fields.test.d.ts.map +1 -0
  249. package/dist/state/persisted.d.ts +60 -0
  250. package/dist/state/persisted.d.ts.map +1 -0
  251. package/dist/state/persisted.js +123 -0
  252. package/dist/state/persisted.test.d.ts +2 -0
  253. package/dist/state/persisted.test.d.ts.map +1 -0
  254. package/dist/state.d.ts +11 -56
  255. package/dist/state.d.ts.map +1 -1
  256. package/dist/state.js +14 -120
  257. package/dist/styles.css +13 -0
  258. package/dist/surfaces.d.ts +106 -24
  259. package/dist/surfaces.d.ts.map +1 -1
  260. package/dist/surfaces.js +156 -19
  261. package/dist/theme/controls.module.css +16 -1
  262. package/dist/theme/ctl-theme.d.ts +32 -0
  263. package/dist/theme/ctl-theme.d.ts.map +1 -0
  264. package/dist/theme/ctl-theme.js +54 -0
  265. package/dist/theme/ctl-theme.test.d.ts +2 -0
  266. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  267. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  268. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  269. package/dist/theme/divider-law.test.d.ts +2 -0
  270. package/dist/theme/divider-law.test.d.ts.map +1 -0
  271. package/dist/theme/icon-slot.d.ts +44 -0
  272. package/dist/theme/icon-slot.d.ts.map +1 -0
  273. package/dist/theme/icon-slot.js +21 -0
  274. package/dist/theme/icon-slot.module.css +47 -0
  275. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  276. package/dist/theme/icon-slot.test.d.ts +2 -0
  277. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  278. package/dist/theme/index.d.ts +1 -0
  279. package/dist/theme/index.d.ts.map +1 -1
  280. package/dist/theme/index.js +82 -5
  281. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  282. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  283. package/dist/theme/segmented-control.module.css +20 -0
  284. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  285. package/dist/tokens/index.d.ts +12 -0
  286. package/dist/tokens/index.d.ts.map +1 -1
  287. package/dist/tokens/index.js +38 -8
  288. package/dist/tokens/palette.d.ts +97 -61
  289. package/dist/tokens/palette.d.ts.map +1 -1
  290. package/dist/tokens/palette.js +76 -38
  291. package/dist/tokens.css +10 -2
  292. package/dist/widget-header/delta-badge.d.ts +13 -0
  293. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  294. package/dist/widget-header/delta-badge.js +29 -0
  295. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  296. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  297. package/dist/widget-header/index.d.ts +3 -0
  298. package/dist/widget-header/index.d.ts.map +1 -0
  299. package/dist/widget-header/index.js +6 -0
  300. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  301. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  302. package/dist/widget-header/widget-header.d.ts +67 -0
  303. package/dist/widget-header/widget-header.d.ts.map +1 -0
  304. package/dist/widget-header/widget-header.js +89 -0
  305. package/dist/widget-header/widget-header.module.css +350 -0
  306. package/dist/widget-header/widget-header.module.css.d.ts +25 -0
  307. package/dist/widget-header/widget-header.test.d.ts +2 -0
  308. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  309. package/llms.txt +30 -17
  310. package/package.json +14 -1
  311. package/src/charts/cursor/resolve.test.ts +29 -1
  312. package/src/charts/cursor/resolve.ts +38 -6
  313. package/src/charts/cursor/scope.tsx +6 -3
  314. package/src/charts/cursor/store.test.ts +19 -1
  315. package/src/charts/cursor/store.ts +10 -5
  316. package/src/charts/hooks/useChartCursor.ts +16 -8
  317. package/src/charts/index.ts +2 -2
  318. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  319. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  320. package/src/charts/primitives/ChartCard.tsx +86 -146
  321. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  322. package/src/cli/check-coverage.test.ts +297 -0
  323. package/src/cli/check-theme.test.ts +38 -14
  324. package/src/cli/index.ts +392 -16
  325. package/src/cli/init-sync.test.ts +97 -0
  326. package/src/connectivity/connectivity-indicator.tsx +16 -9
  327. package/src/content/index.ts +16 -3
  328. package/src/content/prose.module.css +7 -28
  329. package/src/content/prose.module.css.test.ts +8 -24
  330. package/src/controls/actions.module.css +62 -0
  331. package/src/controls/actions.module.css.d.ts +13 -0
  332. package/src/controls/actions.test.tsx +252 -0
  333. package/src/controls/actions.tsx +655 -0
  334. package/src/controls/actions.type-guard.test.ts +63 -0
  335. package/src/controls/compare-filter.tsx +64 -0
  336. package/src/controls/control-group.test.tsx +229 -0
  337. package/src/controls/control-group.tsx +63 -0
  338. package/src/controls/controls.module.css +542 -0
  339. package/src/controls/controls.module.css.d.ts +35 -0
  340. package/src/controls/controls.router.test.tsx +1053 -0
  341. package/src/controls/enum-filter.tsx +170 -0
  342. package/src/controls/filter-context.tsx +158 -0
  343. package/src/controls/filter-pill.tsx +120 -0
  344. package/src/controls/filter-set.test.tsx +472 -0
  345. package/src/controls/filter-set.tsx +225 -0
  346. package/src/controls/filter-sheet.tsx +285 -0
  347. package/src/controls/glyphs.tsx +81 -0
  348. package/src/controls/index.ts +49 -0
  349. package/src/controls/multi-select-filter.tsx +140 -0
  350. package/src/controls/number-filter.tsx +291 -0
  351. package/src/controls/peer-boundary.test.ts +85 -0
  352. package/src/controls/props-contract.test.tsx +137 -0
  353. package/src/controls/range-filter.tsx +237 -0
  354. package/src/controls/range-filter.type-guard.test.tsx +122 -0
  355. package/src/controls/search-filter.tsx +123 -0
  356. package/src/controls/select-filter.tsx +89 -0
  357. package/src/controls/sync-button.module.css +63 -0
  358. package/src/controls/sync-button.module.css.d.ts +11 -0
  359. package/src/controls/sync-button.test.tsx +183 -0
  360. package/src/controls/sync-button.tsx +197 -0
  361. package/src/controls/toggle-filter.tsx +60 -0
  362. package/src/controls/view-tabs.tsx +110 -0
  363. package/src/controls-dates/date-range-picker.tsx +42 -0
  364. package/src/controls-dates/index.ts +8 -0
  365. package/src/dashboard/delta-badge.tsx +4 -74
  366. package/src/dashboard/index.ts +6 -1
  367. package/src/dashboard/settings-section.module.css +10 -0
  368. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  369. package/src/dashboard/settings-section.test.tsx +56 -0
  370. package/src/dashboard/settings-section.tsx +33 -30
  371. package/src/dashboard/stat-card.module.css +153 -0
  372. package/src/dashboard/stat-card.module.css.d.ts +15 -0
  373. package/src/dashboard/stat-card.test.tsx +298 -1
  374. package/src/dashboard/stat-card.tsx +289 -67
  375. package/src/data/data-table.test.tsx +96 -11
  376. package/src/data/data-table.tsx +228 -84
  377. package/src/guard/check-source.test.ts +159 -13
  378. package/src/guard/grace.test.ts +158 -0
  379. package/src/guard/index.ts +185 -29
  380. package/src/guard/types.ts +4 -2
  381. package/src/index.ts +51 -5
  382. package/src/nav/types.ts +84 -2
  383. package/src/notifications/bell.tsx +6 -3
  384. package/src/notifications/index.ts +1 -1
  385. package/src/router-tanstack/field.ts +35 -0
  386. package/src/router-tanstack/index.ts +28 -2
  387. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  388. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  389. package/src/router-tanstack/search-param-store.ts +42 -159
  390. package/src/router-tanstack/search-store.router.test.tsx +882 -0
  391. package/src/router-tanstack/search-store.test.ts +578 -0
  392. package/src/router-tanstack/search-store.ts +546 -0
  393. package/src/router-tanstack/use-nav.test.tsx +187 -0
  394. package/src/router-tanstack/use-nav.ts +72 -14
  395. package/src/section/index.ts +1 -0
  396. package/src/section/section.module.css +104 -0
  397. package/src/section/section.module.css.d.ts +11 -0
  398. package/src/section/section.test.tsx +204 -0
  399. package/src/section/section.tsx +220 -0
  400. package/src/shell/app-breadcrumbs.tsx +44 -31
  401. package/src/shell/app-header.module.css +77 -37
  402. package/src/shell/app-header.module.css.d.ts +2 -1
  403. package/src/shell/app-mobile-nav.module.css +8 -0
  404. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  405. package/src/shell/app-mobile-nav.tsx +147 -8
  406. package/src/shell/app-sidebar-account.tsx +2 -2
  407. package/src/shell/app-sidebar.module.css +303 -20
  408. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  409. package/src/shell/app-sidebar.test.tsx +456 -41
  410. package/src/shell/app-sidebar.tsx +276 -140
  411. package/src/shell/index.test.tsx +291 -5
  412. package/src/shell/index.tsx +137 -38
  413. package/src/shell/mobile-nav-model.test.ts +228 -7
  414. package/src/shell/mobile-nav-model.ts +53 -3
  415. package/src/shell/nav-count-badge.tsx +1 -1
  416. package/src/shell/page-bar.module.css +178 -0
  417. package/src/shell/page-bar.module.css.d.ts +17 -0
  418. package/src/shell/page-bar.test.tsx +316 -0
  419. package/src/shell/page-bar.tsx +315 -0
  420. package/src/shell/sidebar-block-model.ts +83 -0
  421. package/src/shell/sidebar-blocks.tsx +301 -0
  422. package/src/shell/sidebar-search.module.css +26 -0
  423. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  424. package/src/shell/sidebar-search.tsx +71 -2
  425. package/src/state/fields.test.tsx +512 -0
  426. package/src/state/fields.ts +1150 -0
  427. package/src/state/persisted.test.ts +91 -0
  428. package/src/state/persisted.ts +261 -0
  429. package/src/state.ts +52 -240
  430. package/src/styles.css +13 -0
  431. package/src/surfaces.ts +204 -36
  432. package/src/theme/controls.module.css +16 -1
  433. package/src/theme/ctl-theme.test.tsx +275 -0
  434. package/src/theme/ctl-theme.tsx +138 -0
  435. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  436. package/src/theme/density-relations.test.ts +106 -51
  437. package/src/theme/divider-law.test.ts +259 -0
  438. package/src/theme/icon-slot.module.css +47 -0
  439. package/src/theme/icon-slot.module.css.d.ts +8 -0
  440. package/src/theme/icon-slot.test.tsx +275 -0
  441. package/src/theme/icon-slot.tsx +59 -0
  442. package/src/theme/index.ts +165 -3
  443. package/src/theme/layout-rhythm-css.test.ts +72 -0
  444. package/src/theme/radius.test.ts +11 -1
  445. package/src/theme/segmented-control.module.css +20 -0
  446. package/src/theme/shadow-surfaces.test.ts +20 -7
  447. package/src/theme/spacing.test.ts +75 -27
  448. package/src/theme/use-basalt-spacing.ts +2 -2
  449. package/src/tokens/density.test.ts +7 -25
  450. package/src/tokens/index.ts +48 -8
  451. package/src/tokens/palette.ts +107 -73
  452. package/src/widget-header/delta-badge.test.tsx +60 -0
  453. package/src/widget-header/delta-badge.tsx +57 -0
  454. package/src/widget-header/index.ts +2 -0
  455. package/src/widget-header/no-mantine.test.ts +33 -0
  456. package/src/widget-header/widget-header.module.css +350 -0
  457. package/src/widget-header/widget-header.module.css.d.ts +25 -0
  458. package/src/widget-header/widget-header.test.tsx +187 -0
  459. package/src/widget-header/widget-header.tsx +213 -0
  460. package/agent/rules/basalt-agent.md +0 -452
  461. package/agent/rules/basalt-app.md +0 -246
  462. package/agent/rules/basalt-commands.md +0 -320
  463. package/agent/rules/basalt-content.md +0 -409
  464. package/agent/rules/basalt-data.md +0 -329
  465. package/agent/rules/basalt-forms.md +0 -186
  466. package/agent/rules/basalt-notifications.md +0 -264
  467. package/agent/rules/basalt-query.md +0 -187
  468. package/agent/rules/basalt-router.md +0 -336
  469. package/dist/content/article-filter-bar.d.ts +0 -40
  470. package/dist/content/article-filter-bar.d.ts.map +0 -1
  471. package/dist/content/article-filter-bar.js +0 -56
  472. package/dist/shell/page-header.d.ts +0 -13
  473. package/dist/shell/page-header.d.ts.map +0 -1
  474. package/dist/shell/page-header.js +0 -23
  475. package/src/content/article-filter-bar.tsx +0 -88
  476. package/src/shell/page-header.tsx +0 -35
@@ -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"}
@@ -1,53 +1,4 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Badge } from "@mantine/core";
3
- import { alpha, VX } from "../tokens/index.js";
4
- const defaultFormat = (value) => `${Math.abs(value).toFixed(1)}%`;
5
- function DeltaBadge({
6
- value,
7
- format = defaultFormat,
8
- withGlyph = true,
9
- period
10
- }) {
11
- const tone = value > 0 ? VX.status.good : value < 0 ? VX.status.bad : VX.faint;
12
- const glyph = value > 0 ? "\u25B2" : value < 0 ? "\u25BC" : void 0;
13
- const showGlyph = withGlyph && glyph !== void 0;
14
- return /* @__PURE__ */ jsxs(
15
- Badge,
16
- {
17
- styles: {
18
- root: {
19
- backgroundColor: alpha(tone, 0.13),
20
- padding: "var(--vx-space-badge-inset-y) var(--vx-space-badge-inset-x)",
21
- height: "auto"
22
- },
23
- label: {
24
- display: "inline-flex",
25
- // theme-allow inline-display — a Badge `styles.label` part, not a layout node Flex could replace
26
- alignItems: "center",
27
- gap: 3,
28
- // theme-allow inline-spacing — sub-scale glyph-to-label gap, below the smallest scale stop
29
- fontFamily: "var(--basalt-font-mono)",
30
- fontSize: VX.text.xs,
31
- fontWeight: 600,
32
- textTransform: "none",
33
- letterSpacing: "normal",
34
- color: tone
35
- }
36
- },
37
- children: [
38
- showGlyph && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8em" }, children: glyph }),
39
- format(value),
40
- period && /* @__PURE__ */ jsx(
41
- "span",
42
- {
43
- style: { marginLeft: 4, fontWeight: 500, opacity: 0.6 },
44
- children: period
45
- }
46
- )
47
- ]
48
- }
49
- );
50
- }
1
+ import { DeltaBadge } from "../widget-header/delta-badge.js";
51
2
  export {
52
3
  DeltaBadge
53
4
  };
@@ -1,5 +1,5 @@
1
1
  export { DeltaBadge, type DeltaBadgeProps } from './delta-badge.js';
2
- export { StatCard, type StatCardProps, type StatCardTone } from './stat-card.js';
2
+ export { StatCard, type StatCardBreakdownRow, type StatCardProps, type StatCardTone, } from './stat-card.js';
3
3
  export { EmptyState, type EmptyStateProps } from './empty-state.js';
4
4
  export { SettingsSection, type SettingsSectionProps, SettingsRow, type SettingsRowProps, DangerZone, type DangerZoneProps, } from './settings-section.js';
5
5
  export { QueryState, type QueryStateProps, type QueryStateLike, type QueryStateVariant, type QueryEmptyCopy, LoadingState, type LoadingStateProps, ErrorState, type ErrorStateProps, } from './query-state.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,WAAW,EACX,KAAK,gBAAgB,EACrB,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,YAAY,EACZ,KAAK,iBAAiB,EACtB,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EACL,QAAQ,EACR,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,WAAW,EACX,KAAK,gBAAgB,EACrB,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,YAAY,EACZ,KAAK,iBAAiB,EACtB,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,eAAe,CAAA"}
@@ -1,5 +1,7 @@
1
1
  import { DeltaBadge } from "./delta-badge.js";
2
- import { StatCard } from "./stat-card.js";
2
+ import {
3
+ StatCard
4
+ } from "./stat-card.js";
3
5
  import { EmptyState } from "./empty-state.js";
4
6
  import {
5
7
  SettingsSection,
@@ -1,13 +1,15 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export type SettingsSectionProps = {
3
- /** Head-font section title (15px, weight 550, ink). */
3
+ /** Head-font section title, rendered via `WidgetHeader tier="section"`. */
4
4
  title: string;
5
5
  /** Optional 13px muted description below the title. */
6
- description?: string;
6
+ subtitle?: string;
7
+ /** Header-right slot — wrapped in `CtlSlot` (C1/C5). */
8
+ actions?: ReactNode;
7
9
  /** Section body — typically a stack of `SettingsRow`s. */
8
10
  children: ReactNode;
9
11
  };
10
- export declare function SettingsSection({ title, description, children }: SettingsSectionProps): import("react").JSX.Element;
12
+ export declare function SettingsSection({ title, subtitle, actions, children }: SettingsSectionProps): import("react").JSX.Element;
11
13
  export type SettingsRowProps = {
12
14
  /** 13px ink label on the left. */
13
15
  label: string;
@@ -20,5 +22,5 @@ export type SettingsRowProps = {
20
22
  };
21
23
  export declare function SettingsRow({ label, description, control, children }: SettingsRowProps): import("react").JSX.Element;
22
24
  export type DangerZoneProps = SettingsSectionProps;
23
- export declare function DangerZone({ title, description, children }: DangerZoneProps): import("react").JSX.Element;
25
+ export declare function DangerZone({ title, subtitle, actions, children }: DangerZoneProps): import("react").JSX.Element;
24
26
  //# sourceMappingURL=settings-section.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings-section.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAqBtC,MAAM,MAAM,oBAAoB,GAAG;IACjC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,oBAAoB,+BAYrF;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BAYtF;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAElD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,eAAe,+BAkB3E"}
1
+ {"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings-section.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAetC,MAAM,MAAM,oBAAoB,GAAG;IACjC,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,0DAA0D;IAC1D,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,oBAAoB,+BAc3F;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BAYtF;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAElD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,+BAoBjF"}
@@ -1,14 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Card, Stack } from "@mantine/core";
3
+ import { WidgetHeader } from "../widget-header/index.js";
4
+ import { CtlSlot } from "../theme/index.js";
3
5
  import { alpha, VX } from "../tokens/index.js";
4
6
  import classes from "./settings-section.module.css";
5
- const titleStyle = {
6
- fontFamily: "var(--basalt-font-head)",
7
- fontStretch: "88%",
8
- fontSize: VX.text.md,
9
- fontWeight: 550,
10
- color: VX.ink
11
- };
12
7
  const eyebrowStyle = {
13
8
  fontFamily: "var(--basalt-font-mono)",
14
9
  fontSize: VX.text.micro,
@@ -17,12 +12,17 @@ const eyebrowStyle = {
17
12
  textTransform: "uppercase",
18
13
  color: VX.status.bad
19
14
  };
20
- function SettingsSection({ title, description, children }) {
15
+ function SettingsSection({ title, subtitle, actions, children }) {
21
16
  return /* @__PURE__ */ jsxs(Card, { style: { padding: "var(--mantine-spacing-xs) var(--mantine-spacing-sm)" }, children: [
22
- /* @__PURE__ */ jsxs(Stack, { gap: 2, mb: "sm", children: [
23
- /* @__PURE__ */ jsx("span", { style: titleStyle, children: title }),
24
- description && /* @__PURE__ */ jsx("span", { style: { fontSize: VX.text.sm, color: VX.muted }, children: description })
25
- ] }),
17
+ /* @__PURE__ */ jsx("div", { className: classes.header, children: /* @__PURE__ */ jsx(
18
+ WidgetHeader,
19
+ {
20
+ tier: "section",
21
+ title,
22
+ ...subtitle !== void 0 && { subtitle },
23
+ ...actions !== void 0 && { actions: /* @__PURE__ */ jsx(CtlSlot, { children: actions }) }
24
+ }
25
+ ) }),
26
26
  /* @__PURE__ */ jsx("div", { className: classes.rows, children })
27
27
  ] });
28
28
  }
@@ -35,7 +35,7 @@ function SettingsRow({ label, description, control, children }) {
35
35
  /* @__PURE__ */ jsx("div", { className: classes.control, children: control ?? children })
36
36
  ] });
37
37
  }
38
- function DangerZone({ title, description, children }) {
38
+ function DangerZone({ title, subtitle, actions, children }) {
39
39
  return /* @__PURE__ */ jsxs(
40
40
  Card,
41
41
  {
@@ -44,10 +44,17 @@ function DangerZone({ title, description, children }) {
44
44
  boxShadow: `${VX.shadowCard}, 0 0 0 1px ${alpha(VX.status.bad, 0.25)}`
45
45
  },
46
46
  children: [
47
- /* @__PURE__ */ jsxs(Stack, { gap: 2, mb: "sm", children: [
47
+ /* @__PURE__ */ jsxs("div", { className: classes.header, children: [
48
48
  /* @__PURE__ */ jsx("span", { style: eyebrowStyle, children: "Danger Zone" }),
49
- /* @__PURE__ */ jsx("span", { style: titleStyle, children: title }),
50
- description && /* @__PURE__ */ jsx("span", { style: { fontSize: VX.text.sm, color: VX.muted }, children: description })
49
+ /* @__PURE__ */ jsx(
50
+ WidgetHeader,
51
+ {
52
+ tier: "section",
53
+ title,
54
+ ...subtitle !== void 0 && { subtitle },
55
+ ...actions !== void 0 && { actions: /* @__PURE__ */ jsx(CtlSlot, { children: actions }) }
56
+ }
57
+ )
51
58
  ] }),
52
59
  /* @__PURE__ */ jsx("div", { className: classes.rows, children })
53
60
  ]
@@ -3,6 +3,16 @@
3
3
  * vertical padding and a 1px `--vx-divider` rule below it, EXCEPT the last row in the section —
4
4
  * `:not(:last-child)` strips the divider there so the section doesn't end on a trailing line.
5
5
  */
6
+
7
+ /* The WidgetHeader block (plus DangerZone's eyebrow, when present) sits above `.rows` with the
8
+ * same gap the old inline `Stack gap={2} mb="sm"` produced. */
9
+ .header {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 2px;
13
+ margin-bottom: var(--mantine-spacing-sm);
14
+ }
15
+
6
16
  .rows > * {
7
17
  padding: var(--vx-space-settings-row-inset-y) 0;
8
18
  }
@@ -3,6 +3,7 @@
3
3
  * signature — so dot access stays verbatim under the package's strict flags.
4
4
  */
5
5
  declare const classes: {
6
+ readonly header: string
6
7
  readonly rows: string
7
8
  readonly row: string
8
9
  readonly control: string
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=settings-section.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-section.test.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings-section.test.tsx"],"names":[],"mappings":""}
@@ -1,25 +1,111 @@
1
1
  import type { ReactNode } from 'react';
2
2
  /** A threshold verdict on the card's value. `undefined` is never tinted — see the module docblock. */
3
3
  export type StatCardTone = 'good' | 'warn' | 'bad';
4
+ /**
5
+ * One row of a {@link StatCardProps.breakdown} — the two or three parts a hero number is made of.
6
+ *
7
+ * `value` is a pre-formatted `string` for the same reason the hero's is: the card cannot know the
8
+ * locale, the currency or the precision the number was measured at. `tone` marks ONE row as past a
9
+ * threshold and is the same vocabulary as the card's own `tone`, so a row saying `bad` and a rail
10
+ * saying `bad` are the same claim at two scales; omitting it is untinted, never `'good'`.
11
+ */
12
+ export type StatCardBreakdownRow = {
13
+ readonly label: string;
14
+ readonly value: string;
15
+ readonly tone?: StatCardTone;
16
+ };
17
+ /**
18
+ * The measured box a `sparkline` render prop receives.
19
+ *
20
+ * A `LineSparkline`/`BarSparkline` takes `width`/`height` as NUMBERS (they are SVG attributes — an
21
+ * svg cannot size itself from CSS and then scale its scales), so "full bleed" was never expressible
22
+ * as a `ReactNode`: the caller had to guess a pixel width, or write its own `useChartSize` wrapper
23
+ * component. Four consumers wrote that wrapper; two of them hardcoded a width and drew a sparkline
24
+ * that stopped short of the card edge on every viewport but the one they built on.
25
+ */
26
+ export type StatCardSparklineSize = {
27
+ width: number;
28
+ height: number;
29
+ };
4
30
  export type StatCardProps = {
5
- /** Mono uppercase micro-label (docs/DESIGN-SPEC.md §3: 12.5px, tracking 0.06em, faint). */
6
- label: string;
7
- /** Pre-formatted KPI value string (mono ~24px, weight 600, ink). */
31
+ /** Head-font title, rendered via `WidgetHeader tier="widget"`. */
32
+ title: string;
33
+ /** Optional leading icon, forwarded to `WidgetHeader`. */
34
+ icon?: ReactNode;
35
+ /** Muted line under the hero row, forwarded to `WidgetHeader` — the unit or basis a
36
+ * pre-formatted `value` cannot carry (`per day`, `of 40 planned`). Not a second metric. */
37
+ subtitle?: string;
38
+ /** Info tooltip beside the title, forwarded to `WidgetHeader` — how the number is computed. Never
39
+ * part of the heading's accessible name; see `WidgetHeaderProps.info`. */
40
+ info?: string;
41
+ /** Pre-formatted KPI value string (mono ~24px, weight 600, ink) — the hero-row value. */
8
42
  value: string;
43
+ /** The value's unit, muted and mono at `--vx-text-sm`, immediately after it on the hero row —
44
+ * forwarded to `WidgetHeader`. `412` + `TSS`, not `"412 TSS"`: a unit painted at the hero's 24px
45
+ * reads as a second numeral. The BASIS (`7-day rolling`) is still `subtitle`; see
46
+ * `WidgetHeaderProps.unit`. */
47
+ unit?: string;
48
+ /**
49
+ * The parts the hero number is made of — compact rows under it, one line each, no hairlines.
50
+ *
51
+ * It exists because three consumers wanted a KPI card that also SPLITS its number (revenue by
52
+ * channel, uptime by probe) and the only shapes basalt offered were a `subtitle` (one muted line,
53
+ * no second column) and a table. Both hand-rolled a card instead — which is the fork
54
+ * `shadow-basalt-export` reports as a `HeroCard`.
55
+ *
56
+ * Deliberately not a table and deliberately unruled: the divider law puts a hairline between
57
+ * OPTION rows and nowhere else (`docs/CONTROLS-SPEC.md` §2.1), and a KPI card that draws three
58
+ * of them stops reading as one card. Keep it to two or three rows — past that the card is a
59
+ * table, and a table is `BasaltDataTable` in a `Section`.
60
+ */
61
+ breakdown?: readonly StatCardBreakdownRow[];
9
62
  /** Signed delta rendered via `DeltaBadge`; omit to hide the trend chip entirely. */
10
63
  delta?: number;
11
64
  /** Optional comparison timeframe shown after the delta (e.g. `MoM`) — forwarded to `DeltaBadge`. */
12
65
  deltaPeriod?: string;
13
- /** Optional trend visual — a slot; pass a `LineSparkline`/`BarSparkline` from `basalt-ui/charts`.
14
- * Rendered full-bleed to the card's left/right/bottom edges, clipped to the corner radius. */
15
- sparkline?: ReactNode;
16
- /** Optional header-right slot (e.g. a ghost "..." menu trigger) — consumer-owned. */
17
- menu?: ReactNode;
66
+ /**
67
+ * Formats `delta` into the chip's label — forwarded to `WidgetHeader` and on to `DeltaBadge`.
68
+ * Defaults to `${Math.abs(delta).toFixed(1)}%`.
69
+ *
70
+ * A delta is not always a percentage, and the default silently claimed it was: a pace card's trend
71
+ * (`0:12 /km`) and a speed card's (`0.3 km/h`) both rendered as `0.3%` — a wrong unit on a KPI is
72
+ * the one failure worse than no chip, and it is why the consumer that needed it kept the card
73
+ * hand-rolled instead (the `HeroCard` fork `shadow-basalt-export` reports).
74
+ *
75
+ * @example
76
+ * // An absolute delta that prints its own sign — no percent, no glyph saying it twice.
77
+ * <StatCard title="Pace" value="5:31" unit="/km" delta={-12}
78
+ * deltaFormat={(s) => `${s < 0 ? '−' : '+'}0:${String(Math.abs(s)).padStart(2, '0')} /km`}
79
+ * deltaGlyph={false} />
80
+ */
81
+ deltaFormat?: (delta: number) => string;
82
+ /** Render the ▲/▼ glyph on the delta chip. Defaults to `true`; a zero delta never shows one. Pass
83
+ * `false` when `deltaFormat` prints the sign itself. */
84
+ deltaGlyph?: boolean;
85
+ /**
86
+ * Optional trend visual. Either a node, or a RENDER PROP receiving the slot's measured box.
87
+ *
88
+ * Prefer the render prop for anything from `basalt-ui/charts`: those take numeric `width`/`height`
89
+ * (SVG attributes), so a plain node has to hardcode a width, and a hardcoded width is not
90
+ * full-bleed on any viewport but the one it was typed on. With `sparklinePlacement="bleed"` the
91
+ * measured width is the card's own inner width including the bled inset; with `'right'` it is the
92
+ * fixed 72×26 slot (see {@link SPARKLINE_RIGHT_WIDTH} for why that one is not measured).
93
+ *
94
+ * @example
95
+ * sparkline={({ width, height }) => <BarSparkline data={history} width={width} height={height} />}
96
+ */
97
+ sparkline?: ReactNode | ((size: StatCardSparklineSize) => ReactNode);
98
+ /** Where `sparkline` sits. `'bleed'` (default) is today's full-width row bled to the card edges;
99
+ * `'right'` sits it beside the hero value row. Collapses to `'bleed'` below `sm`. */
100
+ sparklinePlacement?: 'bleed' | 'right';
101
+ /** Header-right slot (e.g. a ghost "..." menu trigger) — wrapped in `CtlSlot tier="widget"`, so a
102
+ * raw `ActionIcon` with no `size` lands on the 24px step the 28px header row holds (C1/C5). */
103
+ actions?: ReactNode;
18
104
  /** Threshold verdict — draws an accent rail down the card's leading edge and announces itself to
19
105
  * assistive tech. Omitting it is NOT `'good'`: omitted covers a reading that is fine AND one that
20
106
  * is absent, and stays untinted so a card with nothing measured can never render green. Pass
21
107
  * `'good'` only to assert a measured value that has earned the verdict. */
22
108
  tone?: StatCardTone;
23
109
  };
24
- export declare function StatCard({ label, value, delta, deltaPeriod, sparkline, menu, tone, }: StatCardProps): import("react").JSX.Element;
110
+ export declare function StatCard({ title, icon, subtitle, info, value, unit, breakdown, delta, deltaPeriod, deltaFormat, deltaGlyph, sparkline, sparklinePlacement, actions, tone, }: StatCardProps): import("react").JSX.Element;
25
111
  //# sourceMappingURL=stat-card.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"AAsDA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,sGAAsG;AACtG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAYlD,MAAM,MAAM,aAAa,GAAG;IAC1B,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;kGAC8F;IAC9F,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,qFAAqF;IACrF,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;+EAG2E;IAC3E,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB,CAAA;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,WAAW,EACX,SAAS,EACT,IAAI,EACJ,IAAI,GACL,EAAE,aAAa,+BAyFf"}
1
+ {"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"AA6FA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAMrD,sGAAsG;AACtG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAElD;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAA;CAC7B,CAAA;AAcD;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAkDD,MAAM,MAAM,aAAa,GAAG;IAC1B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;+FAC2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;8EAC0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb;;;mCAG+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC3C,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC;4DACwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE,qBAAqB,KAAK,SAAS,CAAC,CAAA;IACpE;yFACqF;IACrF,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACtC;mGAC+F;IAC/F,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;+EAG2E;IAC3E,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB,CAAA;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,SAAS,EACT,KAAK,EACL,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,kBAA4B,EAC5B,OAAO,EACP,IAAI,GACL,EAAE,aAAa,+BA4Hf"}