basalt-ui 1.30.2 → 1.32.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 (357) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +54 -0
  3. package/MIGRATING.md +256 -12
  4. package/agent/rules/basalt-controls.md +19 -19
  5. package/agent/rules/basalt-mantine.md +5 -5
  6. package/configs/oxlint-plugin.js +371 -4
  7. package/configs/oxlint-plugin.test.ts +327 -2
  8. package/configs/oxlint.json +2 -1
  9. package/dist/agent-chat/composer.d.ts.map +1 -1
  10. package/dist/agent-chat/composer.js +41 -43
  11. package/dist/agent-chat/thread-workspace.d.ts +2 -2
  12. package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
  13. package/dist/agent-chat/thread-workspace.js +3 -4
  14. package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
  15. package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
  16. package/dist/agent-chat/use-keyboard-inset.js +34 -0
  17. package/dist/charts/cursor/touch-pin.d.ts +45 -0
  18. package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
  19. package/dist/charts/cursor/touch-pin.js +79 -0
  20. package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
  21. package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
  22. package/dist/charts/hooks/use-discrete-cursor.js +198 -0
  23. package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
  24. package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
  25. package/dist/charts/hooks/use-hysteresis.js +12 -0
  26. package/dist/charts/hooks/useBandPlot.d.ts +7 -0
  27. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
  28. package/dist/charts/hooks/useBandPlot.js +62 -18
  29. package/dist/charts/hooks/useChartCursor.d.ts +40 -11
  30. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  31. package/dist/charts/hooks/useChartCursor.js +102 -11
  32. package/dist/charts/index.d.ts +0 -1
  33. package/dist/charts/index.d.ts.map +1 -1
  34. package/dist/charts/index.js +0 -10
  35. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  36. package/dist/charts/kinds/BandStrip.js +47 -82
  37. package/dist/charts/kinds/Donut.d.ts +9 -3
  38. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  39. package/dist/charts/kinds/Donut.js +110 -48
  40. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  41. package/dist/charts/kinds/DualPanel.js +138 -145
  42. package/dist/charts/kinds/Heatmap.d.ts +7 -0
  43. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  44. package/dist/charts/kinds/Heatmap.js +37 -16
  45. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  46. package/dist/charts/kinds/MirroredBars.js +68 -111
  47. package/dist/charts/kinds/band-chrome.d.ts +31 -0
  48. package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
  49. package/dist/charts/kinds/band-chrome.js +71 -0
  50. package/dist/charts/kinds/donut-layout.d.ts +47 -0
  51. package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
  52. package/dist/charts/kinds/donut-layout.js +52 -0
  53. package/dist/charts/layout/auto-margin.d.ts +7 -2
  54. package/dist/charts/layout/auto-margin.d.ts.map +1 -1
  55. package/dist/charts/layout/auto-margin.js +12 -3
  56. package/dist/charts/layout/end-labels.d.ts +64 -0
  57. package/dist/charts/layout/end-labels.d.ts.map +1 -0
  58. package/dist/charts/layout/end-labels.js +76 -0
  59. package/dist/charts/primitives/Axes.d.ts +32 -4
  60. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  61. package/dist/charts/primitives/Axes.js +62 -16
  62. package/dist/charts/primitives/CartesianChart.d.ts +7 -6
  63. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  64. package/dist/charts/primitives/CartesianChart.js +264 -145
  65. package/dist/charts/primitives/ChartCard.d.ts +0 -14
  66. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  67. package/dist/charts/primitives/ChartCard.js +56 -21
  68. package/dist/charts/primitives/ChartFrame.d.ts +28 -22
  69. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  70. package/dist/charts/primitives/ChartFrame.js +116 -34
  71. package/dist/charts/primitives/ChartLegend.d.ts +19 -15
  72. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  73. package/dist/charts/primitives/ChartLegend.js +86 -73
  74. package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
  75. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  76. package/dist/charts/primitives/ChartTooltip.js +20 -4
  77. package/dist/charts/primitives/Crosshair.js +2 -2
  78. package/dist/charts/primitives/HoverOverlay.d.ts +29 -9
  79. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  80. package/dist/charts/primitives/HoverOverlay.js +54 -27
  81. package/dist/charts/primitives/HoverOverlay.module.css +13 -0
  82. package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  83. package/dist/charts/primitives/chart-card-context.d.ts +33 -0
  84. package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
  85. package/dist/charts/primitives/chart-card-context.js +18 -0
  86. package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
  87. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
  88. package/dist/charts/primitives/chart-frame-layout.js +75 -34
  89. package/dist/charts/primitives/chart-layout.d.ts +112 -0
  90. package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
  91. package/dist/charts/primitives/chart-layout.js +175 -0
  92. package/dist/charts/primitives/chart-tier.d.ts +13 -11
  93. package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
  94. package/dist/charts/primitives/chart-tier.js +42 -13
  95. package/dist/charts/primitives/legend-disclosure.d.ts +27 -0
  96. package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
  97. package/dist/charts/primitives/legend-disclosure.js +153 -0
  98. package/dist/charts/series.d.ts +4 -1
  99. package/dist/charts/series.d.ts.map +1 -1
  100. package/dist/charts/utils/ticks.d.ts +27 -8
  101. package/dist/charts/utils/ticks.d.ts.map +1 -1
  102. package/dist/charts/utils/ticks.js +35 -11
  103. package/dist/common/errors.d.ts +11 -1
  104. package/dist/common/errors.d.ts.map +1 -1
  105. package/dist/common/errors.js +16 -2
  106. package/dist/common/hit-floor.d.ts +10 -0
  107. package/dist/common/hit-floor.d.ts.map +1 -0
  108. package/dist/common/hit-floor.js +6 -0
  109. package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
  110. package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
  111. package/dist/common/use-measured-widths.d.ts +37 -0
  112. package/dist/common/use-measured-widths.d.ts.map +1 -0
  113. package/dist/common/use-measured-widths.js +51 -0
  114. package/dist/common/use-media-query.d.ts +6 -0
  115. package/dist/common/use-media-query.d.ts.map +1 -0
  116. package/dist/common/use-media-query.js +40 -0
  117. package/dist/common/validate.d.ts +9 -0
  118. package/dist/common/validate.d.ts.map +1 -1
  119. package/dist/common/validate.js +9 -4
  120. package/dist/content/article-layout.d.ts +4 -2
  121. package/dist/content/article-layout.d.ts.map +1 -1
  122. package/dist/content/article-layout.js +43 -3
  123. package/dist/content/article-layout.module.css +45 -4
  124. package/dist/content/article-layout.module.css.d.ts +3 -0
  125. package/dist/content/code-block.d.ts.map +1 -1
  126. package/dist/content/code-block.js +40 -2
  127. package/dist/content/code-block.module.css +32 -2
  128. package/dist/content/prose.module.css +13 -8
  129. package/dist/controls/actions.d.ts +29 -6
  130. package/dist/controls/actions.d.ts.map +1 -1
  131. package/dist/controls/actions.js +158 -27
  132. package/dist/controls/actions.module.css +14 -1
  133. package/dist/controls/actions.module.css.d.ts +1 -0
  134. package/dist/controls/controls.module.css +13 -31
  135. package/dist/controls/enum-filter.js +2 -2
  136. package/dist/controls/filter-context.d.ts +10 -0
  137. package/dist/controls/filter-context.d.ts.map +1 -1
  138. package/dist/controls/filter-context.js +2 -0
  139. package/dist/controls/filter-pill.d.ts.map +1 -1
  140. package/dist/controls/filter-pill.js +1 -0
  141. package/dist/controls/index.d.ts +1 -1
  142. package/dist/controls/index.d.ts.map +1 -1
  143. package/dist/controls/index.js +0 -2
  144. package/dist/controls/multi-select-filter.js +2 -2
  145. package/dist/controls/sync-button.module.css +2 -1
  146. package/dist/dashboard/section.d.ts.map +1 -1
  147. package/dist/dashboard/section.js +1 -0
  148. package/dist/dashboard/section.module.css +1 -26
  149. package/dist/dashboard/stat-card.d.ts +1 -1
  150. package/dist/dashboard/stat-card.d.ts.map +1 -1
  151. package/dist/dashboard/stat-card.js +4 -1
  152. package/dist/dashboard/stat-card.module.css +6 -9
  153. package/dist/dashboard/stat-group.module.css +33 -66
  154. package/dist/dashboard/widget-grid.d.ts.map +1 -1
  155. package/dist/dashboard/widget-grid.module.css +11 -38
  156. package/dist/dashboard/widget-header.d.ts +14 -4
  157. package/dist/dashboard/widget-header.d.ts.map +1 -1
  158. package/dist/dashboard/widget-header.js +23 -6
  159. package/dist/dashboard/widget-header.module.css +145 -133
  160. package/dist/dashboard/widget-header.module.css.d.ts +2 -0
  161. package/dist/data/data-table-state.d.ts +83 -0
  162. package/dist/data/data-table-state.d.ts.map +1 -0
  163. package/dist/data/data-table-state.js +139 -0
  164. package/dist/data/data-table.d.ts +56 -2
  165. package/dist/data/data-table.d.ts.map +1 -1
  166. package/dist/data/data-table.js +436 -336
  167. package/dist/data/data-table.module.css +94 -0
  168. package/dist/data/data-table.module.css.d.ts +11 -0
  169. package/dist/data/virtual-list.d.ts.map +1 -1
  170. package/dist/data/virtual-list.js +10 -0
  171. package/dist/forms/form-layout.d.ts.map +1 -1
  172. package/dist/forms/form-layout.js +3 -3
  173. package/dist/forms/form-layout.module.css +20 -5
  174. package/dist/forms/form-layout.module.css.d.ts +1 -0
  175. package/dist/guard/index.d.ts +8 -2
  176. package/dist/guard/index.d.ts.map +1 -1
  177. package/dist/guard/index.js +68 -2
  178. package/dist/guard/types.d.ts +3 -3
  179. package/dist/guard/types.d.ts.map +1 -1
  180. package/dist/index.d.ts +1 -1
  181. package/dist/index.d.ts.map +1 -1
  182. package/dist/index.js +3 -1
  183. package/dist/notifications/center.d.ts.map +1 -1
  184. package/dist/provider/index.d.ts +20 -1
  185. package/dist/provider/index.d.ts.map +1 -1
  186. package/dist/provider/index.js +30 -8
  187. package/dist/shell/app-brand.d.ts.map +1 -1
  188. package/dist/shell/app-brand.js +1 -0
  189. package/dist/shell/app-brand.module.css +2 -2
  190. package/dist/shell/app-header.module.css +18 -8
  191. package/dist/shell/app-main.module.css +54 -3
  192. package/dist/shell/app-main.module.css.d.ts +1 -0
  193. package/dist/shell/app-mobile-nav.module.css +16 -4
  194. package/dist/shell/app-sidebar.module.css +4 -6
  195. package/dist/shell/aside-docking.d.ts +31 -0
  196. package/dist/shell/aside-docking.d.ts.map +1 -0
  197. package/dist/shell/aside-docking.js +19 -0
  198. package/dist/shell/index.d.ts +1 -0
  199. package/dist/shell/index.d.ts.map +1 -1
  200. package/dist/shell/index.js +56 -16
  201. package/dist/shell/mobile-nav-model.d.ts +1 -1
  202. package/dist/shell/page-aside.d.ts +22 -3
  203. package/dist/shell/page-aside.d.ts.map +1 -1
  204. package/dist/shell/page-aside.js +27 -13
  205. package/dist/shell/page-bar.d.ts.map +1 -1
  206. package/dist/shell/page-bar.js +10 -5
  207. package/dist/shell/page-bar.module.css +10 -34
  208. package/dist/shell/use-breakpoint.d.ts +3 -4
  209. package/dist/shell/use-breakpoint.d.ts.map +1 -1
  210. package/dist/shell/use-breakpoint.js +3 -25
  211. package/dist/shell/use-size-class.d.ts +6 -0
  212. package/dist/shell/use-size-class.d.ts.map +1 -0
  213. package/dist/shell/use-size-class.js +19 -0
  214. package/dist/state/persisted-or-local.d.ts +4 -1
  215. package/dist/state/persisted-or-local.d.ts.map +1 -1
  216. package/dist/state/persisted-or-local.js +4 -3
  217. package/dist/state/persisted.d.ts +10 -0
  218. package/dist/state/persisted.d.ts.map +1 -1
  219. package/dist/state/persisted.js +44 -14
  220. package/dist/styles.css +41 -0
  221. package/dist/surfaces.d.ts +4 -4
  222. package/dist/surfaces.d.ts.map +1 -1
  223. package/dist/surfaces.js +11 -3
  224. package/dist/theme/controls.module.css +7 -0
  225. package/dist/theme/ctl-theme.d.ts +1 -0
  226. package/dist/theme/ctl-theme.d.ts.map +1 -1
  227. package/dist/theme/ctl-theme.js +12 -4
  228. package/dist/theme/floating.module.css +37 -0
  229. package/dist/theme/floating.module.css.d.ts +3 -0
  230. package/dist/theme/index.d.ts +2 -2
  231. package/dist/theme/index.d.ts.map +1 -1
  232. package/dist/theme/index.js +34 -5
  233. package/dist/theme/nav-link.module.css +7 -0
  234. package/dist/theme-lab/derive-controls.d.ts.map +1 -1
  235. package/dist/tokens/index.d.ts +7 -16
  236. package/dist/tokens/index.d.ts.map +1 -1
  237. package/dist/tokens/index.js +23 -28
  238. package/dist/tokens/palette.d.ts +5 -27
  239. package/dist/tokens/palette.d.ts.map +1 -1
  240. package/dist/tokens/palette.js +8 -30
  241. package/dist/tokens/size-classes.d.ts +40 -0
  242. package/dist/tokens/size-classes.d.ts.map +1 -0
  243. package/dist/tokens/size-classes.js +29 -0
  244. package/dist/tokens.css +8 -4
  245. package/llms.txt +3 -3
  246. package/package.json +18 -17
  247. package/src/agent-chat/composer.tsx +74 -47
  248. package/src/agent-chat/thread-workspace.tsx +8 -11
  249. package/src/agent-chat/use-keyboard-inset.ts +68 -0
  250. package/src/charts/cursor/touch-pin.ts +138 -0
  251. package/src/charts/hooks/use-discrete-cursor.ts +335 -0
  252. package/src/charts/hooks/use-hysteresis.ts +26 -0
  253. package/src/charts/hooks/useBandPlot.ts +90 -27
  254. package/src/charts/hooks/useChartCursor.ts +211 -26
  255. package/src/charts/index.ts +0 -8
  256. package/src/charts/kinds/BandStrip.tsx +25 -61
  257. package/src/charts/kinds/Donut.tsx +153 -62
  258. package/src/charts/kinds/DualPanel.tsx +21 -26
  259. package/src/charts/kinds/Heatmap.tsx +61 -22
  260. package/src/charts/kinds/MirroredBars.tsx +23 -65
  261. package/src/charts/kinds/band-chrome.tsx +91 -0
  262. package/src/charts/kinds/donut-layout.ts +82 -0
  263. package/src/charts/layout/auto-margin.ts +24 -5
  264. package/src/charts/layout/end-labels.ts +135 -0
  265. package/src/charts/primitives/Axes.tsx +112 -21
  266. package/src/charts/primitives/CartesianChart.tsx +282 -111
  267. package/src/charts/primitives/ChartCard.tsx +119 -48
  268. package/src/charts/primitives/ChartFrame.tsx +204 -60
  269. package/src/charts/primitives/ChartLegend.tsx +115 -78
  270. package/src/charts/primitives/ChartTooltip.tsx +24 -3
  271. package/src/charts/primitives/Crosshair.tsx +2 -2
  272. package/src/charts/primitives/HoverOverlay.module.css +13 -0
  273. package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  274. package/src/charts/primitives/HoverOverlay.tsx +62 -20
  275. package/src/charts/primitives/chart-card-context.ts +46 -0
  276. package/src/charts/primitives/chart-frame-layout.ts +207 -182
  277. package/src/charts/primitives/chart-layout.ts +318 -0
  278. package/src/charts/primitives/chart-tier.tsx +66 -21
  279. package/src/charts/primitives/legend-disclosure.tsx +177 -0
  280. package/src/charts/series.ts +4 -1
  281. package/src/charts/utils/ticks.ts +95 -19
  282. package/src/common/errors.ts +25 -1
  283. package/src/common/hit-floor.ts +9 -0
  284. package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
  285. package/src/common/use-measured-widths.ts +88 -0
  286. package/src/common/use-media-query.ts +56 -0
  287. package/src/common/validate.ts +17 -4
  288. package/src/content/article-layout.module.css +45 -4
  289. package/src/content/article-layout.module.css.d.ts +3 -0
  290. package/src/content/article-layout.tsx +60 -5
  291. package/src/content/code-block.module.css +32 -2
  292. package/src/content/code-block.tsx +47 -3
  293. package/src/content/prose.module.css +13 -8
  294. package/src/controls/actions.module.css +14 -1
  295. package/src/controls/actions.module.css.d.ts +1 -0
  296. package/src/controls/actions.tsx +223 -75
  297. package/src/controls/controls.module.css +13 -31
  298. package/src/controls/enum-filter.tsx +2 -2
  299. package/src/controls/filter-context.tsx +11 -0
  300. package/src/controls/filter-pill.tsx +1 -0
  301. package/src/controls/index.ts +0 -1
  302. package/src/controls/multi-select-filter.tsx +2 -2
  303. package/src/controls/sync-button.module.css +2 -1
  304. package/src/dashboard/section.module.css +1 -26
  305. package/src/dashboard/section.tsx +1 -0
  306. package/src/dashboard/stat-card.module.css +6 -9
  307. package/src/dashboard/stat-card.tsx +6 -3
  308. package/src/dashboard/stat-group.module.css +33 -66
  309. package/src/dashboard/widget-grid.module.css +11 -38
  310. package/src/dashboard/widget-grid.tsx +7 -6
  311. package/src/dashboard/widget-header.module.css +145 -133
  312. package/src/dashboard/widget-header.module.css.d.ts +2 -0
  313. package/src/dashboard/widget-header.tsx +45 -11
  314. package/src/data/data-table-state.ts +218 -0
  315. package/src/data/data-table.module.css +94 -0
  316. package/src/data/data-table.module.css.d.ts +11 -0
  317. package/src/data/data-table.tsx +547 -299
  318. package/src/data/virtual-list.tsx +31 -0
  319. package/src/forms/form-layout.module.css +20 -5
  320. package/src/forms/form-layout.module.css.d.ts +1 -0
  321. package/src/forms/form-layout.tsx +12 -10
  322. package/src/guard/index.ts +188 -3
  323. package/src/guard/types.ts +3 -2
  324. package/src/index.ts +2 -0
  325. package/src/notifications/center.tsx +3 -0
  326. package/src/provider/index.tsx +60 -7
  327. package/src/shell/app-brand.module.css +2 -2
  328. package/src/shell/app-brand.tsx +1 -0
  329. package/src/shell/app-header.module.css +18 -8
  330. package/src/shell/app-main.module.css +54 -3
  331. package/src/shell/app-main.module.css.d.ts +1 -0
  332. package/src/shell/app-mobile-nav.module.css +16 -4
  333. package/src/shell/app-sidebar.module.css +4 -6
  334. package/src/shell/aside-docking.ts +54 -0
  335. package/src/shell/index.tsx +178 -129
  336. package/src/shell/mobile-nav-model.ts +1 -1
  337. package/src/shell/page-aside.tsx +70 -21
  338. package/src/shell/page-bar.module.css +10 -34
  339. package/src/shell/page-bar.tsx +10 -5
  340. package/src/shell/use-breakpoint.ts +9 -45
  341. package/src/shell/use-size-class.ts +31 -0
  342. package/src/state/persisted-or-local.ts +10 -4
  343. package/src/state/persisted.ts +76 -17
  344. package/src/styles.css +41 -0
  345. package/src/surfaces.ts +9 -1
  346. package/src/theme/controls.module.css +7 -0
  347. package/src/theme/ctl-theme.tsx +15 -3
  348. package/src/theme/floating.module.css +37 -0
  349. package/src/theme/floating.module.css.d.ts +3 -0
  350. package/src/theme/index.ts +46 -8
  351. package/src/theme/nav-link.module.css +7 -0
  352. package/src/theme-lab/derive-controls.tsx +2 -0
  353. package/src/tokens/index.ts +20 -30
  354. package/src/tokens/palette.ts +6 -28
  355. package/src/tokens/size-classes.ts +46 -0
  356. package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
  357. /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Group } from "@visx/group";
3
- import { memo, useMemo, useState } from "react";
3
+ import { memo, useMemo } from "react";
4
4
  import { assertRequiredProps } from "../../common/validate.js";
5
5
  import {
6
6
  ChartTooltipFloat,
@@ -9,7 +9,8 @@ import {
9
9
  TooltipRow
10
10
  } from "../primitives/ChartTooltip.js";
11
11
  import { ChartFrame } from "../primitives/ChartFrame.js";
12
- import { useChartTierMetrics } from "../primitives/chart-tier.js";
12
+ import { useChartMetrics } from "../primitives/chart-tier.js";
13
+ import { useDiscreteCursor } from "../hooks/use-discrete-cursor.js";
13
14
  import { maxTextWidth } from "../utils/measure-text.js";
14
15
  import { thinLabels, xLabelPxFor } from "../utils/ticks.js";
15
16
  import { VX, alpha } from "../../tokens/index.js";
@@ -75,8 +76,7 @@ function HeatmapPlot(props) {
75
76
  legend
76
77
  } = props;
77
78
  const { width, height } = plot;
78
- const { axisFont } = useChartTierMetrics();
79
- const [tip, setTip] = useState(null);
79
+ const { axisFont } = useChartMetrics();
80
80
  const rows = useMemo(() => rowsProp ?? firstSeen(data, getRow), [rowsProp, data, getRow]);
81
81
  const cols = useMemo(() => colsProp ?? firstSeen(data, getCol), [colsProp, data, getCol]);
82
82
  const { lookup, max } = useMemo(() => {
@@ -89,6 +89,27 @@ function HeatmapPlot(props) {
89
89
  }
90
90
  return { lookup: map, max: m };
91
91
  }, [data, getRow, getCol, getValue]);
92
+ const cellTargets = useMemo(() => {
93
+ const out = [];
94
+ for (const row of rows) {
95
+ for (const col of cols) out.push({ row, col, value: lookup.get(cellKey(row, col)) });
96
+ }
97
+ return out;
98
+ }, [rows, cols, lookup]);
99
+ const cursor = useDiscreteCursor({
100
+ targets: cellTargets,
101
+ getKey: (c) => cellKey(c.row, c.col),
102
+ columns: cols.length,
103
+ ariaLabel: "Heatmap cells \u2014 use arrow keys to navigate, Escape to dismiss"
104
+ });
105
+ const activeTip = cursor.tip !== null && cursor.tip.target.value !== void 0 ? {
106
+ anchor: cursor.tip.anchor,
107
+ cell: {
108
+ row: cursor.tip.target.row,
109
+ col: cursor.tip.target.col,
110
+ value: cursor.tip.target.value
111
+ }
112
+ } : null;
92
113
  const rowLabels = useMemo(() => rows.map(rowLabel), [rows, rowLabel]);
93
114
  const colLabels = useMemo(() => cols.map(colLabel), [cols, colLabel]);
94
115
  const legendH = legend ? LEGEND_H + LEGEND_LABEL_H : 0;
@@ -106,20 +127,22 @@ function HeatmapPlot(props) {
106
127
  () => thinLabels(rowLabels, cellH, labelLineHeight(axisFont)),
107
128
  [rowLabels, cellH, axisFont]
108
129
  );
109
- const show = (row, col, value, event) => {
110
- setTip({ row, col, value, anchor: { x: event.clientX, y: event.clientY } });
111
- };
112
- const hide = () => setTip(null);
113
130
  return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
114
- /* @__PURE__ */ jsxs("svg", { width, height, children: [
131
+ /* @__PURE__ */ jsxs("svg", { width, height, ...cursor.hostProps, children: [
115
132
  /* @__PURE__ */ jsx(Group, { left: PAD_LEFT, top: PAD_TOP, children: rows.flatMap(
116
133
  (row, ri) => cols.map((col, ci) => {
117
134
  const value = lookup.get(cellKey(row, col));
118
135
  const has = value !== void 0;
119
136
  const cellFill = has ? alpha(color, intensity(value, max)) : alpha(VX.neutral, 0.04);
137
+ const target = { row, col, value };
138
+ const isActive = cursor.tip?.target.row === row && cursor.tip?.target.col === col;
120
139
  return /* @__PURE__ */ jsx(
121
140
  "rect",
122
141
  {
142
+ id: cursor.optionId(target),
143
+ role: "option",
144
+ "aria-selected": isActive,
145
+ "aria-label": `${rowLabels[ri]}: ${colLabels[ci]}: ${value !== void 0 ? formatValue(value) : "no data"}`,
123
146
  x: ci * cellW + cellGap / 2,
124
147
  y: ri * cellH + cellGap / 2,
125
148
  width: Math.max(0, cellW - cellGap),
@@ -127,9 +150,7 @@ function HeatmapPlot(props) {
127
150
  rx: cellRadius,
128
151
  fill: cellFill,
129
152
  style: { cursor: has ? "pointer" : "default" },
130
- onPointerMove: (e) => has && show(row, col, value, e),
131
- onPointerLeave: hide,
132
- onPointerCancel: hide
153
+ ...has ? cursor.pointerProps(target) : {}
133
154
  },
134
155
  cellKey(row, col)
135
156
  );
@@ -197,8 +218,8 @@ function HeatmapPlot(props) {
197
218
  )
198
219
  ] })
199
220
  ] }),
200
- tip !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tip.anchor, children: [
201
- /* @__PURE__ */ jsx(TooltipHeader, { date: rowLabel(tip.row), label: colLabel(tip.col) }),
221
+ activeTip !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: activeTip.anchor, children: [
222
+ /* @__PURE__ */ jsx(TooltipHeader, { date: rowLabel(activeTip.cell.row), label: colLabel(activeTip.cell.col) }),
202
223
  /* @__PURE__ */ jsxs(TooltipBody, { children: [
203
224
  /* @__PURE__ */ jsx(
204
225
  TooltipRow,
@@ -206,10 +227,10 @@ function HeatmapPlot(props) {
206
227
  color: alpha(color, 0.9),
207
228
  shape: "bar",
208
229
  label: "Value",
209
- value: formatValue(tip.value)
230
+ value: formatValue(activeTip.cell.value)
210
231
  }
211
232
  ),
212
- renderTooltip?.(tip)
233
+ renderTooltip?.(activeTip.cell)
213
234
  ] })
214
235
  ] })
215
236
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"MirroredBars.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/MirroredBars.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAIvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAUrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAG/D,gGAAgG;AAChG,MAAM,MAAM,eAAe,GAAG;IAC5B;mEAC+D;IAC/D,GAAG,EAAE,MAAM,CAAA;IACX;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB;sBACkB;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;oFACgF;IAChF,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,WAAW,GAAG;IAC/C,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,sFAAsF;IACtF,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;;;;;OAKG;IACH,MAAM,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACjC,yCAAyC;IACzC,EAAE,EAAE,eAAe,CAAA;IACnB,yCAAyC;IACzC,IAAI,EAAE,eAAe,CAAA;IACrB;8FAC0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACpC;kCAC8B;IAC9B,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAChC;kGAC8F;IAC9F,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACjC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAA;IAC/E,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IAClB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IACtC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC;gFAC4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;4FACwF;IACxF,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AA8BD;;;;;;;;;;;;;GAaG;AACH,iBAAS,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,+BAwBxD;AAoVD,eAAO,MAAM,YAAY,EAAuB,OAAO,iBAAiB,CAAA"}
1
+ {"version":3,"file":"MirroredBars.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/MirroredBars.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAIvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAIrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAI/D,gGAAgG;AAChG,MAAM,MAAM,eAAe,GAAG;IAC5B;mEAC+D;IAC/D,GAAG,EAAE,MAAM,CAAA;IACX;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB;sBACkB;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;oFACgF;IAChF,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,WAAW,GAAG;IAC/C,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,sFAAsF;IACtF,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;;;;;OAKG;IACH,MAAM,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACjC,yCAAyC;IACzC,EAAE,EAAE,eAAe,CAAA;IACnB,yCAAyC;IACzC,IAAI,EAAE,eAAe,CAAA;IACrB;8FAC0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACpC;kCAC8B;IAC9B,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAChC;kGAC8F;IAC9F,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACjC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAA;IAC/E,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IAClB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IACtC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC;gFAC4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;4FACwF;IACxF,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AA8BD;;;;;;;;;;;;;GAaG;AACH,iBAAS,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,+BAwBxD;AA+SD,eAAO,MAAM,YAAY,EAAuB,OAAO,iBAAiB,CAAA"}
@@ -6,19 +6,14 @@ import { assertRequiredProps } from "../../common/validate.js";
6
6
  import { VX, alpha } from "../../tokens/index.js";
7
7
  import { useBandPlot } from "../hooks/useBandPlot.js";
8
8
  import { probeAxisLabels } from "../layout/auto-margin.js";
9
- import { AxisBottomDate, AxisLeftNumeric } from "../primitives/Axes.js";
9
+ import { AxisLeftNumeric } from "../primitives/Axes.js";
10
10
  import { ChartFrame, resolveLegend } from "../primitives/ChartFrame.js";
11
- import {
12
- ChartTooltipFloat,
13
- TooltipBody,
14
- TooltipHeader,
15
- TooltipRow
16
- } from "../primitives/ChartTooltip.js";
11
+ import { TooltipRows } from "../primitives/ChartTooltip.js";
17
12
  import { Crosshair } from "../primitives/Crosshair.js";
18
13
  import { HatchPattern, hatchFill, hatchSizeFor } from "../primitives/HatchPattern.js";
19
- import { HoverOverlay } from "../primitives/HoverOverlay.js";
20
14
  import { deriveTooltipRows } from "../series.js";
21
15
  import { fmtAxisDate } from "../utils/format.js";
16
+ import { BandAxisOverlay, BandTooltip, clampFraction } from "./band-chrome.js";
22
17
  const DEFAULT_UP_FRACTION = 0.35;
23
18
  const DEFAULT_PANE_TICKS = 3;
24
19
  function assertPaneKey(side, key, resolved, series) {
@@ -107,7 +102,7 @@ function MirroredBarsPlot(props) {
107
102
  ...marginOverride !== void 0 && { margin: marginOverride },
108
103
  ...tooltip !== void 0 && { tooltip }
109
104
  });
110
- const { bands, margin, plotWidth, scale, cursor, point, crosshairX, step, bandWidth } = band;
105
+ const { bands, margin, plotWidth, point, crosshairX, step, bandWidth } = band;
111
106
  const barBand = Math.max(plot.height - margin.top - margin.bottom, 2);
112
107
  const upHeight = Math.max(Math.round(barBand * upFraction), 1);
113
108
  const downHeight = Math.max(barBand - upHeight, 1);
@@ -198,8 +193,6 @@ function MirroredBarsPlot(props) {
198
193
  hatchId
199
194
  ]);
200
195
  if (plotWidth <= 0 || bands.length === 0) return null;
201
- const cfg = tooltip === false ? void 0 : tooltip;
202
- const badge = cfg?.label === void 0 || point === null ? null : cfg.label(point);
203
196
  const visibleSeries = series.filter((s) => !hidden.has(s.key)).map((s) => {
204
197
  if (s.formatValue !== void 0) return s;
205
198
  if (s.key === up.key) return { ...s, formatValue: (v) => up.format(v) };
@@ -208,108 +201,72 @@ function MirroredBarsPlot(props) {
208
201
  });
209
202
  const rows = point === null ? [] : deriveTooltipRows(visibleSeries, point, down.format);
210
203
  return /* @__PURE__ */ jsxs(Fragment, { children: [
211
- /* @__PURE__ */ jsxs("svg", { ref: band.svgRef, width: plot.width, height: plot.height, children: [
212
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
213
- HatchPattern,
214
- {
215
- id: hatchId,
216
- color: absentColor,
217
- opacity: 0.7,
218
- size: hatchSizeFor(bandWidth)
219
- }
220
- ) }),
221
- /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
222
- bars,
223
- upVisible && /* @__PURE__ */ jsx(
224
- AxisLeftNumeric,
225
- {
226
- scale: upAxisScale,
227
- numTicks: upTicks,
228
- tickFormat: (v) => up.format(Number(v))
229
- }
230
- ),
231
- downVisible && /* @__PURE__ */ jsx(Group, { top: baseline, children: /* @__PURE__ */ jsx(
232
- AxisLeftNumeric,
233
- {
234
- scale: downScale,
235
- numTicks: downTicks,
236
- tickFormat: (v) => down.format(Number(v))
237
- }
238
- ) }),
239
- /* @__PURE__ */ jsx(
240
- "line",
241
- {
242
- x1: 0,
243
- x2: plotWidth,
244
- y1: baseline,
245
- y2: baseline,
246
- stroke: VX.axisStroke,
247
- strokeWidth: 1
248
- }
249
- ),
250
- crosshairX !== null && /* @__PURE__ */ jsx(Crosshair, { x: crosshairX, top: 0, bottom: barBand }),
251
- /* @__PURE__ */ jsx(
252
- AxisBottomDate,
253
- {
254
- top: barBand,
255
- scale,
256
- tickValues: band.tickValues,
257
- tickFormat: (v) => formatX(String(v))
258
- }
259
- ),
260
- /* @__PURE__ */ jsx(
261
- HoverOverlay,
262
- {
263
- width: plotWidth,
264
- height: barBand,
265
- onMove: cursor.onPointerMove,
266
- onLeave: cursor.onPointerLeave,
267
- onKeyDown: cursor.onKeyDown,
268
- onBlur: cursor.onBlur,
269
- valueMax: Math.max(bands.length - 1, 0),
270
- ...point !== null && {
271
- valueNow: bands.indexOf(point),
272
- valueText: formatX(getX(point))
273
- },
274
- ...ariaLabel !== void 0 && { ariaLabel }
275
- }
276
- )
277
- ] })
278
- ] }),
279
- band.showTooltip && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: band.tooltipAnchor, ariaLive: band.ariaLive, children: [
280
- /* @__PURE__ */ jsx(
281
- TooltipHeader,
282
- {
283
- date: getX(point),
284
- ...cfg?.formatHeader !== void 0 && {
285
- format: (key) => cfg.formatHeader?.(key, point) ?? key
286
- },
287
- ...badge !== null && { label: badge.text, labelColor: badge.color }
288
- }
289
- ),
290
- /* @__PURE__ */ jsxs(TooltipBody, { children: [
291
- cfg?.prependRows?.(point, { hidden }),
292
- rows.map((row) => /* @__PURE__ */ jsx(
293
- TooltipRow,
294
- {
295
- color: row.color,
296
- label: row.label,
297
- value: row.value,
298
- shape: row.shape,
299
- dashed: row.dashed,
300
- ...row.strokeWidth !== void 0 && { strokeWidth: row.strokeWidth }
301
- },
302
- row.key
303
- )),
304
- cfg?.extraRows?.(point, { hidden })
305
- ] })
306
- ] })
204
+ /* @__PURE__ */ jsxs(
205
+ "svg",
206
+ {
207
+ ref: band.svgRef,
208
+ width: plot.width,
209
+ height: plot.height,
210
+ style: { touchAction: "pan-y" },
211
+ children: [
212
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
213
+ HatchPattern,
214
+ {
215
+ id: hatchId,
216
+ color: absentColor,
217
+ opacity: 0.7,
218
+ size: hatchSizeFor(bandWidth)
219
+ }
220
+ ) }),
221
+ /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
222
+ bars,
223
+ upVisible && band.yPlacement !== "none" && /* @__PURE__ */ jsx(
224
+ AxisLeftNumeric,
225
+ {
226
+ scale: upAxisScale,
227
+ numTicks: upTicks,
228
+ tickFormat: (v) => up.format(Number(v)),
229
+ inside: band.yPlacement === "inside"
230
+ }
231
+ ),
232
+ downVisible && band.yPlacement !== "none" && /* @__PURE__ */ jsx(Group, { top: baseline, children: /* @__PURE__ */ jsx(
233
+ AxisLeftNumeric,
234
+ {
235
+ scale: downScale,
236
+ numTicks: downTicks,
237
+ tickFormat: (v) => down.format(Number(v)),
238
+ inside: band.yPlacement === "inside"
239
+ }
240
+ ) }),
241
+ /* @__PURE__ */ jsx(
242
+ "line",
243
+ {
244
+ x1: 0,
245
+ x2: plotWidth,
246
+ y1: baseline,
247
+ y2: baseline,
248
+ stroke: VX.axisStroke,
249
+ strokeWidth: 1
250
+ }
251
+ ),
252
+ crosshairX !== null && /* @__PURE__ */ jsx(Crosshair, { x: crosshairX, top: 0, bottom: barBand }),
253
+ /* @__PURE__ */ jsx(
254
+ BandAxisOverlay,
255
+ {
256
+ band,
257
+ height: barBand,
258
+ getX,
259
+ formatX,
260
+ ariaLabel
261
+ }
262
+ )
263
+ ] })
264
+ ]
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsx(BandTooltip, { band, tooltip, getX, hidden, children: /* @__PURE__ */ jsx(TooltipRows, { rows }) })
307
268
  ] });
308
269
  }
309
- function clampFraction(value) {
310
- if (value === void 0 || !Number.isFinite(value)) return 0;
311
- return Math.min(Math.max(value, 0), 1);
312
- }
313
270
  function finiteOrNull(v) {
314
271
  return v === null || v === void 0 || Number.isNaN(v) ? null : v;
315
272
  }
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { BandPlot, BandTooltipConfig } from '../hooks/useBandPlot.js';
3
+ /**
4
+ * The chrome `BandStrip` and `MirroredBars` share around their own marks — the slot axis, the
5
+ * keyboard/pointer overlay and the tooltip shell — so the two `useBandPlot` kinds cannot drift in
6
+ * any of them. Internal; neither is exported from a barrel.
7
+ *
8
+ * theme-allow-file hand-rolled-plot — the slot axis and overlay of the two declared banded
9
+ * exceptions (`BandStrip`, `MirroredBars`), which carry the same waiver for the same reason.
10
+ */
11
+ /** A 0..1 share. Non-finite (a 0/0 fold count) reads as "nothing is absent" — the conservative
12
+ * end, since the alternative is a band that does not render at all. */
13
+ export declare function clampFraction(value: number | undefined): number;
14
+ /** The slot axis under a `height`-tall band row, plus the hover/keyboard overlay over it. */
15
+ export declare function BandAxisOverlay<T>({ band, height, getX, formatX, ariaLabel, }: {
16
+ band: BandPlot<T>;
17
+ height: number;
18
+ getX: (d: T) => string;
19
+ formatX: (key: string) => string;
20
+ ariaLabel: string | undefined;
21
+ }): import("react").JSX.Element;
22
+ /** The tooltip shell: header (+ badge), the consumer's prepended rows, the kind's own derived
23
+ * `children` rows, the consumer's extra rows. Renders nothing off-hover or with `tooltip: false`. */
24
+ export declare function BandTooltip<T>({ band, tooltip, getX, hidden, children, }: {
25
+ band: BandPlot<T>;
26
+ tooltip: BandTooltipConfig<T> | false | undefined;
27
+ getX: (d: T) => string;
28
+ hidden: ReadonlySet<string>;
29
+ children: ReactNode;
30
+ }): import("react").JSX.Element | null;
31
+ //# sourceMappingURL=band-chrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"band-chrome.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/band-chrome.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAKvE;;;;;;;GAOG;AAEH;uEACuE;AACvE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG/D;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,CAAC,EAAE,EACjC,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,GACV,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B,+BAkBA;AAED;qGACqG;AACrG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAC7B,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IACjB,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,SAAS,CAAA;IACjD,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3B,QAAQ,EAAE,SAAS,CAAA;CACpB,sCAqBA"}
@@ -0,0 +1,71 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { AxisBottomDate } from "../primitives/Axes.js";
3
+ import { ChartTooltipFloat, TooltipBody, TooltipHeader } from "../primitives/ChartTooltip.js";
4
+ import { cursorSliderProps, HoverOverlay } from "../primitives/HoverOverlay.js";
5
+ function clampFraction(value) {
6
+ if (value === void 0 || !Number.isFinite(value)) return 0;
7
+ return Math.min(Math.max(value, 0), 1);
8
+ }
9
+ function BandAxisOverlay({
10
+ band,
11
+ height,
12
+ getX,
13
+ formatX,
14
+ ariaLabel
15
+ }) {
16
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
17
+ /* @__PURE__ */ jsx(
18
+ AxisBottomDate,
19
+ {
20
+ top: height,
21
+ scale: band.scale,
22
+ tickValues: band.tickValues,
23
+ tickFormat: (v) => formatX(String(v)),
24
+ anchorTerminals: band.xAnchorTerminals,
25
+ ...band.xWrapWidth !== void 0 && { wrapWidth: band.xWrapWidth }
26
+ }
27
+ ),
28
+ /* @__PURE__ */ jsx(
29
+ HoverOverlay,
30
+ {
31
+ width: band.plotWidth,
32
+ height,
33
+ ...cursorSliderProps({ cursor: band.cursor, data: band.bands, getX, formatX, ariaLabel })
34
+ }
35
+ )
36
+ ] });
37
+ }
38
+ function BandTooltip({
39
+ band,
40
+ tooltip,
41
+ getX,
42
+ hidden,
43
+ children
44
+ }) {
45
+ const { point } = band;
46
+ if (!band.showTooltip || point === null) return null;
47
+ const cfg = tooltip === false ? void 0 : tooltip;
48
+ const badge = cfg?.label === void 0 ? null : cfg.label(point);
49
+ return /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: band.tooltipAnchor, ariaLive: band.ariaLive, children: [
50
+ /* @__PURE__ */ jsx(
51
+ TooltipHeader,
52
+ {
53
+ date: getX(point),
54
+ ...cfg?.formatHeader !== void 0 && {
55
+ format: (key) => cfg.formatHeader?.(key, point) ?? key
56
+ },
57
+ ...badge !== null && { label: badge.text, labelColor: badge.color }
58
+ }
59
+ ),
60
+ /* @__PURE__ */ jsxs(TooltipBody, { children: [
61
+ cfg?.prependRows?.(point, { hidden }),
62
+ children,
63
+ cfg?.extraRows?.(point, { hidden })
64
+ ] })
65
+ ] });
66
+ }
67
+ export {
68
+ BandAxisOverlay,
69
+ BandTooltip,
70
+ clampFraction
71
+ };
@@ -0,0 +1,47 @@
1
+ import type { ContainerClass } from '../../tokens/size-classes.js';
2
+ /** Aspect (frame W/H) above which the legend moves beside the ring. */
3
+ export declare const SIDE_LEGEND_ASPECT = 1.5;
4
+ /** Aspect a side layout must fall below before it stacks again — the gap stops a legend whose own
5
+ * placement changes the measured height from flipping the layout back and forth. */
6
+ export declare const STACK_LEGEND_ASPECT = 1.35;
7
+ /** Most slices kept before the rest fold into "Other" (so 7 or more slices fold). */
8
+ export declare const DONUT_MAX_SLICES = 6;
9
+ export declare const OTHER_KEY = "__other";
10
+ /** Side layout: the ring's diameter never exceeds this share of the frame width. */
11
+ export declare const SIDE_RING_MAX_WIDTH_SHARE = 0.55;
12
+ /** Space kept between the ring's outer edge and the plot box. */
13
+ export declare const RING_PADDING = 4;
14
+ export type DonutLayout = {
15
+ side: boolean;
16
+ };
17
+ /** Side legend only on a wide-enough frame (`micro`/`compact` always stack under the ring).
18
+ * `wasSide` applies hysteresis: an already-side layout stays side until W/H falls below
19
+ * {@link STACK_LEGEND_ASPECT}. */
20
+ export declare function resolveDonutLayout(input: {
21
+ frameW: number;
22
+ frameH: number;
23
+ containerClass: ContainerClass;
24
+ wasSide?: boolean;
25
+ }): DonutLayout;
26
+ /** Ring outer radius: never negative and never wider than the plot; the side layout also caps the
27
+ * diameter at {@link SIDE_RING_MAX_WIDTH_SHARE} of the frame width. */
28
+ export declare function resolveDonutRadius(input: {
29
+ plotW: number;
30
+ plotH: number;
31
+ frameW: number;
32
+ side: boolean;
33
+ }): number;
34
+ /** Share of `total` as a whole percent; an empty total is 0%. */
35
+ export declare function sharePercent(value: number, total: number): number;
36
+ /** The key for the synthesized "Other" bucket — {@link OTHER_KEY}, prefixed until no input key uses it. */
37
+ export declare function resolveOtherKey(keys: readonly string[]): string;
38
+ /** Keeps the top {@link DONUT_MAX_SLICES} by value (in original order) and sums the rest into one
39
+ * trailing "Other" entry. Non-finite and negative values count as 0. */
40
+ export declare function foldOther<T extends {
41
+ key: string;
42
+ value: number;
43
+ }>(slices: readonly T[]): {
44
+ kept: T[];
45
+ other: number | null;
46
+ };
47
+ //# sourceMappingURL=donut-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"donut-layout.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/donut-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,MAAM,CAAA;AACrC;oFACoF;AACpF,eAAO,MAAM,mBAAmB,OAAO,CAAA;AACvC,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,SAAS,YAAY,CAAA;AAClC,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,OAAO,CAAA;AAC7C,iEAAiE;AACjE,eAAO,MAAM,YAAY,IAAI,CAAA;AAE7B,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3C;;kCAEkC;AAClC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,WAAW,CAMd;AAED;uEACuE;AACvE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,OAAO,CAAA;CACd,GAAG,MAAM,CAMT;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjE;AAKD,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK/D;AAED;wEACwE;AACxE,wBAAgB,SAAS,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAChE,MAAM,EAAE,SAAS,CAAC,EAAE,GACnB;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAarC"}
@@ -0,0 +1,52 @@
1
+ const SIDE_LEGEND_ASPECT = 1.5;
2
+ const STACK_LEGEND_ASPECT = 1.35;
3
+ const DONUT_MAX_SLICES = 6;
4
+ const OTHER_KEY = "__other";
5
+ const SIDE_RING_MAX_WIDTH_SHARE = 0.55;
6
+ const RING_PADDING = 4;
7
+ function resolveDonutLayout(input) {
8
+ const { frameW, frameH, containerClass, wasSide = false } = input;
9
+ const roomy = containerClass !== "micro" && containerClass !== "compact";
10
+ if (!roomy || frameH <= 0) return { side: false };
11
+ const aspect = frameW / frameH;
12
+ return { side: wasSide ? aspect >= STACK_LEGEND_ASPECT : aspect > SIDE_LEGEND_ASPECT };
13
+ }
14
+ function resolveDonutRadius(input) {
15
+ const { plotW, plotH, frameW, side } = input;
16
+ const diameter = side ? Math.min(plotW, plotH, SIDE_RING_MAX_WIDTH_SHARE * frameW) : Math.min(plotW, plotH);
17
+ return Math.max(0, diameter / 2 - RING_PADDING);
18
+ }
19
+ function sharePercent(value, total) {
20
+ return total > 0 ? Math.round(value / total * 100) : 0;
21
+ }
22
+ const safeValue = (value) => Number.isFinite(value) && value > 0 ? value : 0;
23
+ function resolveOtherKey(keys) {
24
+ const taken = new Set(keys);
25
+ let key = OTHER_KEY;
26
+ while (taken.has(key)) key = `_${key}`;
27
+ return key;
28
+ }
29
+ function foldOther(slices) {
30
+ const safe = slices.map((s) => ({ ...s, value: safeValue(s.value) }));
31
+ if (safe.length <= DONUT_MAX_SLICES) return { kept: safe, other: null };
32
+ const top = new Set(
33
+ safe.toSorted((a, b) => b.value - a.value).slice(0, DONUT_MAX_SLICES).map((s) => s.key)
34
+ );
35
+ return {
36
+ kept: safe.filter((s) => top.has(s.key)),
37
+ other: safe.reduce((sum, s) => top.has(s.key) ? sum : sum + s.value, 0)
38
+ };
39
+ }
40
+ export {
41
+ DONUT_MAX_SLICES,
42
+ OTHER_KEY,
43
+ RING_PADDING,
44
+ SIDE_LEGEND_ASPECT,
45
+ SIDE_RING_MAX_WIDTH_SHARE,
46
+ STACK_LEGEND_ASPECT,
47
+ foldOther,
48
+ resolveDonutLayout,
49
+ resolveDonutRadius,
50
+ resolveOtherKey,
51
+ sharePercent
52
+ };
@@ -59,15 +59,20 @@ export type AutoMarginInput = {
59
59
  /** Degrees of counter-clockwise rotation applied to the x tick labels. Default 0. */
60
60
  rotate?: number;
61
61
  /** Tick-label font size. Default `VX.axisFont`. A phone-tier chart passes its own smaller size
62
- * (`chartTierMetrics().axisFont`) — the measured label must be the painted label. */
62
+ * (`chartMetrics().axisFont`) — the measured label must be the painted label. */
63
63
  fontPx?: number;
64
64
  /**
65
65
  * Per-side FLOORS. Default `VX.margin`. The phone tier passes its tightened set
66
- * (`chartTierMetrics().margin`) so a static 44px left gutter does not spend an eighth of a 360px
66
+ * (`chartMetrics().margin`) so a static 44px left gutter does not spend an eighth of a 360px
67
67
  * chart on a three-character label. The law itself does not move: a side may still only ever
68
68
  * grow past its floor.
69
69
  */
70
70
  floor?: ChartMargin;
71
+ /** X labels anchored inward at the terminals (`AxisBottomDate anchorTerminals`): the last label
72
+ * no longer centres on the right edge, so the half-label right reservation is dropped. */
73
+ anchorTerminals?: boolean;
74
+ /** Lines the tallest (wrapped) x label takes. Default 1. */
75
+ bottomLines?: number;
71
76
  /** Explicit per-side overrides — applied LAST, so an escape hatch always wins. */
72
77
  override?: Partial<ChartMargin>;
73
78
  };
@@ -1 +1 @@
1
- {"version":3,"file":"auto-margin.d.ts","sourceRoot":"","sources":["../../../src/charts/layout/auto-margin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAgC/C;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAG5E,CAAA;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAiBlC;AAUD,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;yFACqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CAChC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,GAAE,eAAoB,GAAG,WAAW,CA8BnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,sCAAsC;IACtC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;qBAEiB;IACjB,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CACzB,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,CAatD"}
1
+ {"version":3,"file":"auto-margin.d.ts","sourceRoot":"","sources":["../../../src/charts/layout/auto-margin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAgC/C;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAG5E,CAAA;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAiBlC;AAUD,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;qFACiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB;8FAC0F;IAC1F,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CAChC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,GAAE,eAAoB,GAAG,WAAW,CA4CnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,sCAAsC;IACtC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;qBAEiB;IACjB,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CACzB,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,CAatD"}
@@ -30,18 +30,27 @@ function rotatedLabelExtents(input) {
30
30
  const LOG_TICK_FORMAT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 20 });
31
31
  function autoMargin(input = {}) {
32
32
  const fontPx = input.fontPx ?? VX.axisFont;
33
- const { left = [], right = [], bottom = [], rotate = 0, floor = VX.margin, override } = input;
33
+ const {
34
+ left = [],
35
+ right = [],
36
+ bottom = [],
37
+ rotate = 0,
38
+ floor = VX.margin,
39
+ anchorTerminals = false,
40
+ bottomLines = 1,
41
+ override
42
+ } = input;
34
43
  const leftWidth = maxTextWidth(left, fontPx);
35
44
  const rightWidth = maxTextWidth(right, fontPx);
36
45
  const bottomWidth = maxTextWidth(bottom, fontPx);
37
46
  const rotated = rotatedLabelExtents({ labelWidth: bottomWidth, fontPx, rotate });
38
- const bottomExtent = rotate === 0 ? lineHeight(fontPx) + AXIS_TICK_GAP : rotated.below;
47
+ const bottomExtent = rotate === 0 ? lineHeight(fontPx) * Math.max(bottomLines, 1) + AXIS_TICK_GAP : rotated.below;
39
48
  return {
40
49
  top: override?.top ?? floor.top,
41
50
  right: override?.right ?? Math.ceil(
42
51
  Math.max(
43
52
  floor.right,
44
- right.length > 0 ? rightWidth + AXIS_TICK_GAP : rotate === 0 ? bottomWidth / 2 : 0
53
+ right.length > 0 ? rightWidth + AXIS_TICK_GAP : rotate === 0 && !anchorTerminals ? bottomWidth / 2 : 0
45
54
  )
46
55
  ),
47
56
  bottom: override?.bottom ?? Math.ceil(Math.max(floor.bottom, bottomExtent)),