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,7 +1,8 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useState } from "react";
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
2
  import { alpha, VX } from "../../tokens/index.js";
4
- import { useChartTierMetrics } from "./chart-tier.js";
3
+ import { DOTS_HIT_GAP, LEGEND_DOT_SIZE, LEGEND_ROW_GAP, orderEntries } from "./chart-frame-layout.js";
4
+ import { useChartMetrics, useCoarsePointer } from "./chart-tier.js";
5
+ import { LegendDisclosure, useLegendDisclosure } from "./legend-disclosure.js";
5
6
  const LEGEND_ITEM_BASE = {
6
7
  display: "flex",
7
8
  alignItems: "center",
@@ -20,33 +21,26 @@ const LEGEND_ITEM_BUTTON = {
20
21
  color: "inherit",
21
22
  textAlign: "inherit"
22
23
  };
23
- const GROUP_ORDER = ["series", "overlay", "reference"];
24
24
  const NO_HIDDEN = /* @__PURE__ */ new Set();
25
- const roleOf = (item) => item.role ?? "series";
26
- function orderEntries(items, groups) {
27
- if (!groups) return { entries: items, dividerAfter: /* @__PURE__ */ new Set() };
28
- const dividerAfter = /* @__PURE__ */ new Set();
29
- const entries = [];
30
- const nonEmptyGroups = GROUP_ORDER.map(
31
- (role) => items.filter((item) => roleOf(item) === role)
32
- ).filter((group) => group.length > 0);
33
- nonEmptyGroups.forEach((group, i) => {
34
- entries.push(...group);
35
- if (i < nonEmptyGroups.length - 1) dividerAfter.add(entries.length - 1);
36
- });
37
- return { entries, dividerAfter };
38
- }
39
- function wrapperStyle(placement, fontSize) {
25
+ function wrapperStyle(placement, fontSize, dots) {
40
26
  const vertical = placement === "left" || placement === "right";
27
+ const gap = dots ? DOTS_HIT_GAP : VX.legendGap;
41
28
  return {
42
29
  display: "flex",
43
30
  flexDirection: vertical ? "column" : "row",
44
31
  flexWrap: vertical ? "nowrap" : "wrap",
45
32
  alignItems: vertical ? "flex-start" : "center",
46
- justifyContent: vertical ? "flex-start" : "center",
47
- columnGap: VX.legendGap,
48
- rowGap: VX.legendGap,
49
- padding: "8px 0 2px",
33
+ justifyContent: "flex-start",
34
+ columnGap: gap,
35
+ // Always the tight row gap (R2C-9): the wider dots COLUMN gap is a hit-overlap cap, not
36
+ // breathing room between wrapped rows — a dots-mode header legend is 1 row by law
37
+ // (`LEGEND_ROWS.header` in `chart-layout.ts`) anyway, so this only ever shows in the band.
38
+ rowGap: LEGEND_ROW_GAP,
39
+ // Caps each entry's `[data-basalt-hit]` overlay at the real inter-entry gap (styles.css).
40
+ ...{ "--vx-hit-gap": `${gap}px` },
41
+ // A dots-mode row has no text baseline to breathe around — the header-fold law (wave 8) is
42
+ // the reason this row exists at all, so it gets the same tight treatment.
43
+ padding: dots ? "4px 0 2px" : "8px 0 2px",
50
44
  fontSize,
51
45
  color: VX.muted
52
46
  };
@@ -145,6 +139,21 @@ const LEGEND_CHILD_STYLE = {
145
139
  fontSize: "0.85em",
146
140
  opacity: 0.75
147
141
  };
142
+ function LegendDot({ item }) {
143
+ return /* @__PURE__ */ jsx(
144
+ "span",
145
+ {
146
+ style: {
147
+ width: LEGEND_DOT_SIZE,
148
+ height: LEGEND_DOT_SIZE,
149
+ borderRadius: "50%",
150
+ // theme-allow raw-surface — a circle shape, not a surface corner
151
+ backgroundColor: item.color,
152
+ flexShrink: 0
153
+ }
154
+ }
155
+ );
156
+ }
148
157
  function LegendChildSwatch({ item }) {
149
158
  if (item.shape === "bar") {
150
159
  return /* @__PURE__ */ jsx(
@@ -193,59 +202,57 @@ function ChartLegend({
193
202
  placement = "bottom",
194
203
  groups = false,
195
204
  maxRows,
205
+ mode = "chips",
196
206
  className,
197
207
  style
198
208
  }) {
199
- const tier = useChartTierMetrics();
200
- const [expanded, setExpanded] = useState(false);
201
- const handleEnter = (e) => {
202
- const key = e.currentTarget.dataset["legendKey"];
203
- if (key !== void 0) onHighlight?.(key);
204
- };
209
+ const tier = useChartMetrics();
210
+ const coarse = useCoarsePointer();
211
+ const { open, panelId, openFrom, close, panel } = useLegendDisclosure(coarse);
212
+ const handleEnter = (key) => onHighlight?.(key);
205
213
  const handleLeave = () => onHighlight?.(null);
206
- const handleToggle = (e) => {
207
- const key = e.currentTarget.dataset["legendKey"];
208
- if (key !== void 0) onToggle?.(key);
209
- };
210
214
  const idPrefix = chartId ? `${chartId}-` : "";
211
215
  const vertical = placement === "left" || placement === "right";
212
216
  const { entries, dividerAfter } = orderEntries(items, groups);
213
- const cap = maxRows ?? tier.legendMaxRows;
214
- const rolledUp = cap === void 0 ? 0 : Math.max(entries.length - cap, 0);
215
- const visible = rolledUp > 0 && !expanded ? entries.slice(0, cap) : entries;
216
- const nodes = [];
217
- visible.forEach((item, i) => {
218
- nodes.push(
219
- /* @__PURE__ */ jsxs(
220
- "button",
221
- {
222
- type: "button",
223
- "data-legend-key": item.key,
224
- "aria-label": item.note ? `${item.label} \u2014 ${item.note}` : item.label,
225
- ...onToggle !== void 0 && { "aria-pressed": !hidden.has(item.key) },
226
- style: {
227
- ...LEGEND_ITEM_BUTTON,
228
- cursor: onToggle === void 0 ? "default" : "pointer",
229
- // Toggled-off wins over hover-dimming: a hidden series must read as hidden even while
230
- // it is the one being hovered.
231
- opacity: hidden.has(item.key) ? 0.35 : highlighted === null || highlighted === item.key ? 1 : 0.3,
232
- textDecoration: hidden.has(item.key) ? "line-through" : "none"
233
- },
234
- ...onToggle !== void 0 && { onClick: handleToggle },
235
- onMouseEnter: handleEnter,
236
- onMouseLeave: handleLeave,
237
- onFocus: handleEnter,
238
- onBlur: handleLeave,
239
- children: [
240
- /* @__PURE__ */ jsx(LegendSwatch, { item, idPrefix }),
241
- /* @__PURE__ */ jsx("span", { children: item.label }),
242
- item.note ? /* @__PURE__ */ jsx("span", { style: LEGEND_NOTE_STYLE, children: item.note }) : null,
243
- item.children?.map((child) => /* @__PURE__ */ jsx(LegendChild, { item: child }, child.key))
244
- ]
217
+ const rolledUp = maxRows === void 0 ? 0 : Math.max(entries.length - maxRows, 0);
218
+ const visible = rolledUp > 0 ? entries.slice(0, maxRows) : entries;
219
+ const entryButton = (item, inPanel = false) => {
220
+ const isDotTrigger = !inPanel && mode === "dots";
221
+ return /* @__PURE__ */ jsx(
222
+ "button",
223
+ {
224
+ type: "button",
225
+ "data-basalt-hit": true,
226
+ ...!inPanel && { "data-legend-key": item.key },
227
+ "aria-label": item.note ? `${item.label} \u2014 ${item.note}` : item.label,
228
+ ...isDotTrigger && { title: item.label, "aria-haspopup": "dialog" },
229
+ ...isDotTrigger ? { "aria-expanded": open, "aria-controls": panelId } : onToggle !== void 0 && { "aria-pressed": !hidden.has(item.key) },
230
+ style: {
231
+ ...LEGEND_ITEM_BUTTON,
232
+ cursor: onToggle === void 0 && !isDotTrigger ? "default" : "pointer",
233
+ // Toggled-off wins over hover-dimming: a hidden series must read as hidden even while
234
+ // it is the one being hovered.
235
+ opacity: hidden.has(item.key) ? 0.35 : highlighted === null || highlighted === item.key ? 1 : 0.3,
236
+ textDecoration: hidden.has(item.key) ? "line-through" : "none"
245
237
  },
246
- item.key
247
- )
238
+ ...isDotTrigger ? { onClick: (e) => openFrom(e.currentTarget) } : onToggle !== void 0 && { onClick: () => onToggle(item.key) },
239
+ onMouseEnter: () => handleEnter(item.key),
240
+ onMouseLeave: handleLeave,
241
+ onFocus: () => handleEnter(item.key),
242
+ onBlur: handleLeave,
243
+ children: !inPanel && mode === "dots" ? /* @__PURE__ */ jsx(LegendDot, { item }) : /* @__PURE__ */ jsxs(Fragment, { children: [
244
+ /* @__PURE__ */ jsx(LegendSwatch, { item, idPrefix: inPanel ? `${idPrefix}disclosure-` : idPrefix }),
245
+ /* @__PURE__ */ jsx("span", { children: item.label }),
246
+ item.note ? /* @__PURE__ */ jsx("span", { style: LEGEND_NOTE_STYLE, children: item.note }) : null,
247
+ item.children?.map((child) => /* @__PURE__ */ jsx(LegendChild, { item: child }, child.key))
248
+ ] })
249
+ },
250
+ item.key
248
251
  );
252
+ };
253
+ const nodes = [];
254
+ visible.forEach((item, i) => {
255
+ nodes.push(entryButton(item));
249
256
  if (dividerAfter.has(i))
250
257
  nodes.push(/* @__PURE__ */ jsx(LegendDivider, { columnLayout: vertical }, `divider-${item.key}`));
251
258
  });
@@ -255,21 +262,27 @@ function ChartLegend({
255
262
  "button",
256
263
  {
257
264
  type: "button",
258
- "aria-expanded": expanded,
259
- onClick: () => setExpanded((prev) => !prev),
265
+ "data-basalt-hit": true,
266
+ "aria-expanded": open,
267
+ "aria-haspopup": "dialog",
268
+ "aria-controls": panelId,
269
+ onClick: (e) => open ? close() : openFrom(e.currentTarget),
260
270
  style: { ...LEGEND_ITEM_BUTTON, cursor: "pointer", textDecoration: "underline" },
261
- children: expanded ? "Show less" : `+${rolledUp} more`
271
+ children: `All ${entries.length}`
262
272
  },
263
273
  "legend-more"
264
274
  )
265
275
  );
266
276
  }
267
- return /* @__PURE__ */ jsx(
277
+ return /* @__PURE__ */ jsxs(
268
278
  "div",
269
279
  {
270
280
  ...className !== void 0 && { className },
271
- style: { ...wrapperStyle(placement, tier.legendFontSize), ...style },
272
- children: nodes
281
+ style: { ...wrapperStyle(placement, tier.legendFontSize, mode === "dots"), ...style },
282
+ children: [
283
+ nodes,
284
+ /* @__PURE__ */ jsx(LegendDisclosure, { ...panel, fontSize: tier.legendFontSize, children: entries.map((item) => entryButton(item, true)) })
285
+ ]
273
286
  }
274
287
  );
275
288
  }
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { TooltipRowData } from '../series.js';
2
3
  /** Tooltip header — shows formatted date + optional right-aligned label with color. */
3
4
  export declare function TooltipHeader({ date, label, labelColor, format, }: {
4
5
  date: string;
@@ -21,6 +22,10 @@ export declare function TooltipRow({ color, label, value, valueColor, shape, str
21
22
  /** Render the line swatch as dashed (only applies to shape='line'). */
22
23
  dashed?: boolean;
23
24
  }): import("react").JSX.Element;
25
+ /** The derived rows (`deriveTooltipRows`), one {@link TooltipRow} each. */
26
+ export declare function TooltipRows({ rows }: {
27
+ rows: readonly TooltipRowData[];
28
+ }): import("react").JSX.Element[];
24
29
  export declare function TooltipBody({ children }: {
25
30
  children: ReactNode;
26
31
  }): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCAsDA"}
1
+ {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAIrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAoB/C,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,iCAYxE;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA2DA"}
@@ -3,11 +3,11 @@ import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { VX } from "../../tokens/index.js";
5
5
  import { fmtTooltipDate } from "../utils/format.js";
6
- import { useChartTierMetrics } from "./chart-tier.js";
6
+ import { useChartMetrics } from "./chart-tier.js";
7
7
  const TOOLTIP_STYLES = {
8
8
  position: "fixed",
9
9
  pointerEvents: "none",
10
- zIndex: 9999,
10
+ zIndex: VX.zIndexFloating,
11
11
  backgroundColor: VX.surface.panel,
12
12
  borderRadius: "var(--vx-radius-floating)",
13
13
  padding: "0",
@@ -114,6 +114,20 @@ function TooltipRow({
114
114
  }
115
115
  );
116
116
  }
117
+ function TooltipRows({ rows }) {
118
+ return rows.map((row) => /* @__PURE__ */ jsx(
119
+ TooltipRow,
120
+ {
121
+ color: row.color,
122
+ label: row.label,
123
+ value: row.value,
124
+ shape: row.shape,
125
+ dashed: row.dashed,
126
+ ...row.strokeWidth !== void 0 && { strokeWidth: row.strokeWidth }
127
+ },
128
+ row.key
129
+ ));
130
+ }
117
131
  function TooltipBody({ children }) {
118
132
  return /* @__PURE__ */ jsx("div", { style: { padding: "5px 0" }, children });
119
133
  }
@@ -132,7 +146,7 @@ function ChartTooltipFloat({
132
146
  const ref = useRef(null);
133
147
  const [box, setBox] = useState(null);
134
148
  const isClient = useIsClient();
135
- const { tooltipMinWidth } = useChartTierMetrics();
149
+ const { tooltipMinWidth } = useChartMetrics();
136
150
  useLayoutEffect(() => {
137
151
  const el = ref.current;
138
152
  if (el === null) return;
@@ -147,6 +161,7 @@ function ChartTooltipFloat({
147
161
  const height = box?.height ?? 0;
148
162
  const vw = window.innerWidth;
149
163
  const vh = window.innerHeight;
164
+ if (anchor.y < 0 || anchor.y > vh) return null;
150
165
  const flipsLeft = anchor.x + TOOLTIP_GAP + width + VIEWPORT_MARGIN > vw;
151
166
  const left = flipsLeft ? Math.max(VIEWPORT_MARGIN, anchor.x - TOOLTIP_GAP - width) : anchor.x + TOOLTIP_GAP;
152
167
  const top = Math.min(
@@ -177,5 +192,6 @@ export {
177
192
  ChartTooltipFloat,
178
193
  TooltipBody,
179
194
  TooltipHeader,
180
- TooltipRow
195
+ TooltipRow,
196
+ TooltipRows
181
197
  };
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { VX } from "../../tokens/index.js";
3
- import { useChartTierMetrics } from "./chart-tier.js";
3
+ import { useChartMetrics } from "./chart-tier.js";
4
4
  function Crosshair({
5
5
  x,
6
6
  top,
@@ -14,7 +14,7 @@ function SeriesDot({
14
14
  color,
15
15
  r
16
16
  }) {
17
- const { dotR } = useChartTierMetrics();
17
+ const { dotR } = useChartMetrics();
18
18
  return /* @__PURE__ */ jsx("circle", { cx, cy, r: r ?? dotR, fill: color, stroke: VX.dotStroke, strokeWidth: 2 });
19
19
  }
20
20
  export {
@@ -1,15 +1,15 @@
1
1
  import type { FocusEventHandler, KeyboardEventHandler, PointerEventHandler } from 'react';
2
- /**
3
- * Transparent <rect> that captures pointer events (mouse + touch + pen) for tooltip + crosshair
4
- * sync.
5
- *
6
- * With `onKeyDown` it also becomes the chart's keyboard affordance: the rect takes focus and
7
- * ←/→ scrub the cursor, so a chart's values are reachable without a pointer.
8
- */
9
- export declare function HoverOverlay({ width, height, onMove, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: {
2
+ import type { ChartCursor } from '../hooks/useChartCursor.js';
3
+ /** Props of {@link HoverOverlay}. */
4
+ export type HoverOverlayProps = {
10
5
  width: number;
11
6
  height: number;
12
7
  onMove: PointerEventHandler<SVGRectElement>;
8
+ /** Coarse-pointer tap: resolves + shows the tooltip immediately, with no preceding `pointermove`
9
+ * (`docs/CHARTS-SPEC.md` §4, Tooltip). The press is provisional; `onUp` commits it. */
10
+ onDown?: PointerEventHandler<SVGRectElement>;
11
+ /** Commits the provisional touch press so the pin survives the lift. */
12
+ onUp?: PointerEventHandler<SVGRectElement>;
13
13
  onLeave: PointerEventHandler<SVGRectElement>;
14
14
  /** Present = the overlay is focusable and scrubs on ←/→. */
15
15
  onKeyDown?: KeyboardEventHandler<SVGRectElement>;
@@ -21,5 +21,25 @@ export declare function HoverOverlay({ width, height, onMove, onLeave, onKeyDown
21
21
  valueMax?: number;
22
22
  /** Human-readable label for the focused point (the formatted x key). */
23
23
  valueText?: string;
24
- }): import("react").JSX.Element;
24
+ };
25
+ /**
26
+ * Transparent <rect> that captures pointer events (mouse + touch + pen) for tooltip + crosshair
27
+ * sync.
28
+ *
29
+ * With `onKeyDown` it also becomes the chart's keyboard affordance: the rect takes focus and
30
+ * ←/→ scrub the cursor, so a chart's values are reachable without a pointer.
31
+ */
32
+ export declare function HoverOverlay({ width, height, onMove, onDown, onUp, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: HoverOverlayProps): import("react").JSX.Element;
33
+ /**
34
+ * Everything a FOCUSABLE overlay over indexed data takes besides its rect: the cursor's pointer and
35
+ * keyboard handlers plus the slider label/position. `CartesianChart`, `DualPanel`'s top pane and
36
+ * the banded kinds all wire it identically, so it is stated once.
37
+ */
38
+ export declare function cursorSliderProps<T>(input: {
39
+ cursor: ChartCursor<T>;
40
+ data: readonly T[];
41
+ getX: (d: T) => string;
42
+ formatX: (key: string) => string;
43
+ ariaLabel: string | undefined;
44
+ }): Omit<HoverOverlayProps, 'width' | 'height'>;
25
45
  //# sourceMappingURL=HoverOverlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,+BA8BA"}
1
+ {"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAG1D,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C;2FACuF;IACvF,MAAM,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,wEAAwE;IACxE,IAAI,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BAiCnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE;IAC1C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IACtB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,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,GAAG,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAc9C"}
@@ -1,8 +1,11 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import styles from "./HoverOverlay.module.css";
2
3
  function HoverOverlay({
3
4
  width,
4
5
  height,
5
6
  onMove,
7
+ onDown,
8
+ onUp,
6
9
  onLeave,
7
10
  onKeyDown,
8
11
  onBlur,
@@ -11,34 +14,58 @@ function HoverOverlay({
11
14
  valueMax,
12
15
  valueText
13
16
  }) {
14
- return /* @__PURE__ */ jsx(
15
- "rect",
16
- {
17
- width,
18
- height,
19
- fill: "transparent",
20
- ...onKeyDown !== void 0 && {
21
- tabIndex: 0,
22
- // `slider`, not `application`: `application` would drop this node out of the screen
23
- // reader's normal browse mode entirely, which is a far bigger hammer than an arrow-key
24
- // scrub needs. A slider announces both the affordance and the focused point.
25
- role: "slider",
26
- "aria-label": ariaLabel ?? "Chart data \u2014 use arrow keys to scrub",
27
- "aria-orientation": "horizontal",
28
- "aria-valuemin": 0,
29
- ...valueMax !== void 0 && { "aria-valuemax": valueMax },
30
- ...valueNow !== void 0 && { "aria-valuenow": valueNow },
31
- ...valueText !== void 0 && { "aria-valuetext": valueText },
32
- onKeyDown,
33
- ...onBlur !== void 0 && { onBlur }
34
- },
35
- style: { touchAction: "pan-y" },
36
- onPointerMove: onMove,
37
- onPointerLeave: onLeave,
38
- onPointerCancel: onLeave
39
- }
17
+ return (
18
+ // `touch-action: pan-y` lives on the plot's `<svg>` (each kind's own), NOT here: Chrome ignores
19
+ // `touch-action` on an SVG child `<rect>`, so the browser claims a horizontal pan and cancels
20
+ // the pointer (`R2C-2`). The `overlay` class owns the focus ring: no UA outline on tap, a VX
21
+ // accent inset under `:focus-visible` (`R2C-15`).
22
+ /* @__PURE__ */ jsx(
23
+ "rect",
24
+ {
25
+ className: styles.overlay,
26
+ width,
27
+ height,
28
+ fill: "transparent",
29
+ ...onKeyDown !== void 0 && {
30
+ tabIndex: 0,
31
+ // `slider`, not `application`: `application` would drop this node out of the screen
32
+ // reader's normal browse mode entirely, which is a far bigger hammer than an arrow-key
33
+ // scrub needs. A slider announces both the affordance and the focused point.
34
+ role: "slider",
35
+ "aria-label": ariaLabel ?? "Chart data \u2014 use arrow keys to scrub",
36
+ "aria-orientation": "horizontal",
37
+ "aria-valuemin": 0,
38
+ ...valueMax !== void 0 && { "aria-valuemax": valueMax },
39
+ ...valueNow !== void 0 && { "aria-valuenow": valueNow },
40
+ ...valueText !== void 0 && { "aria-valuetext": valueText },
41
+ onKeyDown,
42
+ ...onBlur !== void 0 && { onBlur }
43
+ },
44
+ onPointerMove: onMove,
45
+ ...onDown !== void 0 && { onPointerDown: onDown },
46
+ ...onUp !== void 0 && { onPointerUp: onUp },
47
+ onPointerLeave: onLeave,
48
+ onPointerCancel: onLeave
49
+ }
50
+ )
40
51
  );
41
52
  }
53
+ function cursorSliderProps(input) {
54
+ const { cursor, data, getX, formatX, ariaLabel } = input;
55
+ const { point } = cursor;
56
+ return {
57
+ onMove: cursor.onPointerMove,
58
+ onDown: cursor.onPointerDown,
59
+ onUp: cursor.onPointerUp,
60
+ onLeave: cursor.onPointerLeave,
61
+ onKeyDown: cursor.onKeyDown,
62
+ onBlur: cursor.onBlur,
63
+ valueMax: Math.max(data.length - 1, 0),
64
+ ...point !== null && { valueNow: data.indexOf(point), valueText: formatX(getX(point)) },
65
+ ...ariaLabel !== void 0 && { ariaLabel }
66
+ };
67
+ }
42
68
  export {
43
- HoverOverlay
69
+ HoverOverlay,
70
+ cursorSliderProps
44
71
  };
@@ -0,0 +1,13 @@
1
+ /*
2
+ * `HoverOverlay` — the transparent plot rect every cartesian kind scrubs through. A tap focuses it
3
+ * on pointerdown, so the UA's own outline would ring the whole plot on every phone tap (`R2C-15`);
4
+ * keyboard focus keeps a VX accent inset instead.
5
+ */
6
+ .overlay {
7
+ outline: none;
8
+ }
9
+
10
+ .overlay:focus-visible {
11
+ outline: 1px solid var(--vx-accent);
12
+ outline-offset: -1px;
13
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Typed CSS-module declaration — named string props, so dot access stays verbatim under the
3
+ * package's strict flags.
4
+ */
5
+ declare const classes: {
6
+ readonly overlay: string
7
+ }
8
+ export default classes
@@ -0,0 +1,33 @@
1
+ import type { Dispatch } from 'react';
2
+ /** One frame claiming or releasing the card's header legend slot. */
3
+ export type LegendSlotAction = {
4
+ type: 'claim' | 'release';
5
+ id: string;
6
+ };
7
+ /**
8
+ * The ONE owner of the legend-slot protocol (`docs/waves/PLAN.md` wave 3, P2-9): claimants in claim
9
+ * order, and the head owns the slot. A later claimant is denied simply by not being the head, and
10
+ * it takes over the moment everything ahead of it releases — no retry signal, no ownership mirrored
11
+ * in a ref. Returns the same array on a no-op so a repeated claim/release never re-renders the card.
12
+ */
13
+ export declare function legendSlotReducer(claimants: readonly string[], { type, id }: LegendSlotAction): readonly string[];
14
+ /**
15
+ * `ChartCard`'s internal context: `inCard` (a frame inside a card treats its computed height as a
16
+ * MINIMUM and grows into the card body), the legend slot — the empty header-row box a chart will portal its legend into
17
+ * (`docs/CHARTS-SPEC.md` §5, Legend), so a card has one chrome band, not two — and `short`, the same
18
+ * `measuredHeight < CARD_SHORT_HEIGHT` flag `ChartCard` already computes for the header-fold law
19
+ * (`data-basalt-card-short`), threaded through so `ChartFrame` can fold its header-slot legend to
20
+ * 'dots' before it eats the plot floor (`docs/CHARTS-SPEC.md` §5, wave 11). `null`/`false` until the slot
21
+ * mounts, and outside any card. Deliberately not exported from any barrel.
22
+ *
23
+ * `legendClaimants` is {@link legendSlotReducer}'s queue (its head owns the slot) and
24
+ * `dispatchLegendSlot` feeds it. Outside any card there is no slot, so nothing ever claims.
25
+ */
26
+ export declare const ChartCardContext: import("react").Context<{
27
+ legendSlot: HTMLDivElement | null;
28
+ inCard: boolean;
29
+ short: boolean;
30
+ legendClaimants: readonly string[];
31
+ dispatchLegendSlot: Dispatch<LegendSlotAction>;
32
+ }>;
33
+ //# sourceMappingURL=chart-card-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-card-context.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-card-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErC,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAExE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,gBAAgB,GAC7B,SAAS,MAAM,EAAE,CAInB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB;gBACf,cAAc,GAAG,IAAI;YACzB,OAAO;WACR,OAAO;qBACG,SAAS,MAAM,EAAE;wBACd,QAAQ,CAAC,gBAAgB,CAAC;EAO9C,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { createContext } from "react";
2
+ function legendSlotReducer(claimants, { type, id }) {
3
+ const held = claimants.includes(id);
4
+ if (type === "claim") return held ? claimants : [...claimants, id];
5
+ return held ? claimants.filter((c) => c !== id) : claimants;
6
+ }
7
+ const ChartCardContext = createContext({
8
+ legendSlot: null,
9
+ inCard: false,
10
+ short: false,
11
+ legendClaimants: [],
12
+ dispatchLegendSlot: () => {
13
+ }
14
+ });
15
+ export {
16
+ ChartCardContext,
17
+ legendSlotReducer
18
+ };