basalt-ui 1.14.0 → 1.16.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 (176) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +38 -2
  4. package/agent/rules/basalt-charts.md +279 -108
  5. package/agent/rules/basalt-mantine.md +4 -2
  6. package/agent/skills/basalt-charts/SKILL.md +114 -64
  7. package/agent/skills/basalt-design/SKILL.md +4 -3
  8. package/agent/templates/CLAUDE-block.md.tpl +9 -6
  9. package/configs/oxlint-plugin.js +152 -0
  10. package/configs/oxlint-plugin.test.ts +107 -0
  11. package/configs/oxlint.json +2 -0
  12. package/dist/charts/cursor/resolve.d.ts +33 -0
  13. package/dist/charts/cursor/resolve.d.ts.map +1 -0
  14. package/dist/charts/cursor/resolve.js +61 -0
  15. package/dist/charts/cursor/resolve.test.d.ts +2 -0
  16. package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
  17. package/dist/charts/cursor/scope.d.ts +17 -0
  18. package/dist/charts/cursor/scope.d.ts.map +1 -0
  19. package/dist/charts/cursor/scope.js +20 -0
  20. package/dist/charts/cursor/store.d.ts +31 -0
  21. package/dist/charts/cursor/store.d.ts.map +1 -0
  22. package/dist/charts/cursor/store.js +25 -0
  23. package/dist/charts/cursor/store.test.d.ts +2 -0
  24. package/dist/charts/cursor/store.test.d.ts.map +1 -0
  25. package/dist/charts/hooks/useChartCursor.d.ts +39 -0
  26. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
  27. package/dist/charts/hooks/useChartCursor.js +104 -0
  28. package/dist/charts/hooks/useChartSize.d.ts +8 -4
  29. package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
  30. package/dist/charts/index.d.ts +16 -13
  31. package/dist/charts/index.d.ts.map +1 -1
  32. package/dist/charts/index.js +38 -19
  33. package/dist/charts/kinds/Bars.d.ts +45 -54
  34. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  35. package/dist/charts/kinds/Bars.js +235 -392
  36. package/dist/charts/kinds/Bars.test.d.ts +2 -0
  37. package/dist/charts/kinds/Bars.test.d.ts.map +1 -0
  38. package/dist/charts/kinds/Donut.d.ts +5 -4
  39. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  40. package/dist/charts/kinds/Donut.js +17 -14
  41. package/dist/charts/kinds/DualPanel.d.ts +13 -0
  42. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  43. package/dist/charts/kinds/DualPanel.js +185 -92
  44. package/dist/charts/kinds/DualPanel.test.d.ts +2 -0
  45. package/dist/charts/kinds/DualPanel.test.d.ts.map +1 -0
  46. package/dist/charts/kinds/Heatmap.d.ts +15 -11
  47. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  48. package/dist/charts/kinds/Heatmap.js +38 -22
  49. package/dist/charts/kinds/MultiLine.d.ts +27 -35
  50. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  51. package/dist/charts/kinds/MultiLine.js +122 -235
  52. package/dist/charts/kinds/MultiLine.test.d.ts +2 -0
  53. package/dist/charts/kinds/MultiLine.test.d.ts.map +1 -0
  54. package/dist/charts/kinds/StackedArea.d.ts +21 -10
  55. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  56. package/dist/charts/kinds/StackedArea.js +74 -132
  57. package/dist/charts/kinds/ZonedLine.d.ts +24 -40
  58. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  59. package/dist/charts/kinds/ZonedLine.js +104 -184
  60. package/dist/charts/kinds/ZonedLine.test.d.ts +2 -0
  61. package/dist/charts/kinds/ZonedLine.test.d.ts.map +1 -0
  62. package/dist/charts/layout/auto-margin.d.ts +60 -0
  63. package/dist/charts/layout/auto-margin.d.ts.map +1 -0
  64. package/dist/charts/layout/auto-margin.js +37 -0
  65. package/dist/charts/layout/auto-margin.test.d.ts +2 -0
  66. package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
  67. package/dist/charts/primitives/CartesianChart.d.ts +161 -0
  68. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
  69. package/dist/charts/primitives/CartesianChart.js +359 -0
  70. package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
  71. package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
  72. package/dist/charts/primitives/ChartFrame.d.ts +16 -5
  73. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  74. package/dist/charts/primitives/ChartFrame.js +17 -2
  75. package/dist/charts/primitives/ChartLegend.d.ts +15 -1
  76. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  77. package/dist/charts/primitives/ChartLegend.js +25 -3
  78. package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
  79. package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
  80. package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
  81. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  82. package/dist/charts/primitives/ChartTooltip.js +45 -19
  83. package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
  84. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  85. package/dist/charts/primitives/HoverOverlay.js +22 -1
  86. package/dist/charts/primitives/XZoneRects.d.ts +54 -0
  87. package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
  88. package/dist/charts/primitives/XZoneRects.js +65 -0
  89. package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
  90. package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
  91. package/dist/charts/series.d.ts +28 -3
  92. package/dist/charts/series.d.ts.map +1 -1
  93. package/dist/charts/series.js +5 -2
  94. package/dist/charts/series.test.d.ts +2 -0
  95. package/dist/charts/series.test.d.ts.map +1 -0
  96. package/dist/charts/utils/measure-text.d.ts +25 -0
  97. package/dist/charts/utils/measure-text.d.ts.map +1 -0
  98. package/dist/charts/utils/measure-text.js +64 -0
  99. package/dist/charts/utils/measure-text.test.d.ts +2 -0
  100. package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
  101. package/dist/content/fence-block.d.ts.map +1 -1
  102. package/dist/surfaces.d.ts +1 -1
  103. package/dist/surfaces.d.ts.map +1 -1
  104. package/dist/surfaces.js +1 -1
  105. package/dist/tokens/chart-margin.test.d.ts +2 -0
  106. package/dist/tokens/chart-margin.test.d.ts.map +1 -0
  107. package/dist/tokens/index.d.ts +29 -0
  108. package/dist/tokens/index.d.ts.map +1 -1
  109. package/dist/tokens/index.js +10 -0
  110. package/dist/tokens/palette.d.ts +4 -0
  111. package/dist/tokens/palette.d.ts.map +1 -1
  112. package/dist/tokens/palette.js +3 -0
  113. package/llms.txt +3 -3
  114. package/package.json +1 -1
  115. package/src/charts/cursor/resolve.test.ts +61 -0
  116. package/src/charts/cursor/resolve.ts +102 -0
  117. package/src/charts/cursor/scope.tsx +32 -0
  118. package/src/charts/cursor/store.test.ts +46 -0
  119. package/src/charts/cursor/store.ts +53 -0
  120. package/src/charts/hooks/useChartCursor.ts +163 -0
  121. package/src/charts/hooks/useChartSize.ts +8 -4
  122. package/src/charts/index.ts +35 -14
  123. package/src/charts/kinds/Bars.test.tsx +98 -0
  124. package/src/charts/kinds/Bars.tsx +350 -467
  125. package/src/charts/kinds/Donut.tsx +31 -23
  126. package/src/charts/kinds/DualPanel.test.tsx +131 -0
  127. package/src/charts/kinds/DualPanel.tsx +301 -135
  128. package/src/charts/kinds/Heatmap.tsx +72 -47
  129. package/src/charts/kinds/MultiLine.test.tsx +37 -0
  130. package/src/charts/kinds/MultiLine.tsx +171 -290
  131. package/src/charts/kinds/StackedArea.tsx +116 -175
  132. package/src/charts/kinds/ZonedLine.test.tsx +37 -0
  133. package/src/charts/kinds/ZonedLine.tsx +149 -255
  134. package/src/charts/layout/auto-margin.test.ts +42 -0
  135. package/src/charts/layout/auto-margin.ts +110 -0
  136. package/src/charts/primitives/CartesianChart.test.tsx +160 -0
  137. package/src/charts/primitives/CartesianChart.tsx +598 -0
  138. package/src/charts/primitives/ChartFrame.test.tsx +33 -0
  139. package/src/charts/primitives/ChartFrame.tsx +40 -4
  140. package/src/charts/primitives/ChartLegend.test.tsx +78 -0
  141. package/src/charts/primitives/ChartLegend.tsx +52 -2
  142. package/src/charts/primitives/ChartTooltip.tsx +67 -37
  143. package/src/charts/primitives/HoverOverlay.tsx +39 -3
  144. package/src/charts/primitives/XZoneRects.test.tsx +123 -0
  145. package/src/charts/primitives/XZoneRects.tsx +132 -0
  146. package/src/charts/series.test.ts +112 -0
  147. package/src/charts/series.ts +33 -5
  148. package/src/charts/utils/measure-text.test.ts +36 -0
  149. package/src/charts/utils/measure-text.ts +109 -0
  150. package/src/content/fence-block.tsx +2 -3
  151. package/src/surfaces.ts +2 -1
  152. package/src/theme/spacing.test.ts +2 -0
  153. package/src/tokens/chart-margin.test.ts +39 -0
  154. package/src/tokens/density.test.ts +1 -0
  155. package/src/tokens/index.ts +36 -0
  156. package/src/tokens/palette.ts +3 -0
  157. package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
  158. package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
  159. package/dist/charts/hooks/useChartTooltip.js +0 -26
  160. package/dist/charts/hooks/useHoverSync.d.ts +0 -46
  161. package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
  162. package/dist/charts/hooks/useHoverSync.js +0 -75
  163. package/dist/charts/hover-context.d.ts +0 -21
  164. package/dist/charts/hover-context.d.ts.map +0 -1
  165. package/dist/charts/hover-context.js +0 -12
  166. package/dist/charts/hover-sync.d.ts +0 -14
  167. package/dist/charts/hover-sync.d.ts.map +0 -1
  168. package/dist/charts/hover-sync.js +0 -17
  169. package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
  170. package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
  171. package/dist/charts/primitives/ResponsiveChart.js +0 -24
  172. package/src/charts/hooks/useChartTooltip.ts +0 -31
  173. package/src/charts/hooks/useHoverSync.ts +0 -152
  174. package/src/charts/hover-context.ts +0 -28
  175. package/src/charts/hover-sync.tsx +0 -37
  176. package/src/charts/primitives/ResponsiveChart.tsx +0 -64
@@ -2,8 +2,9 @@
2
2
  * `./charts` — Mantine-free visx chart system (ZERO `@mantine/*` imports, lint-enforced).
3
3
  *
4
4
  * Re-exports the framework token layer (so chart consumers have one import surface), the chart
5
- * theme context, the shared-cursor hover context, the primitives, kinds, sparklines, hooks, utils,
6
- * and a curated set of raw @visx primitives for bespoke charts.
5
+ * theme context, the shared cursor (module-level store — charts sync with no provider; see
6
+ * `docs/CHARTS-SPEC.md` §3), the primitives, kinds, sparklines, hooks, utils, and a curated set of
7
+ * raw @visx primitives for bespoke charts.
7
8
  *
8
9
  * The framework ships ONLY generic primitives + framework palette data — no domain series tree
9
10
  * (apps rebuild that app-side with `seriesTokens` / `groupTokens` against their own series maps).
@@ -20,15 +21,22 @@ export {
20
21
  seriesTokens,
21
22
  defineSeries,
22
23
  groupTokens,
24
+ chartMargin,
25
+ type ChartMargin,
23
26
  } from '../tokens'
24
27
 
25
28
  // ── Design seam type (type-only; erased at runtime — no @mantine value) ─
26
29
  export type { SeriesKey } from '../register'
27
30
 
28
- // ── Chart theme + hover context ──────────────────────────────────────────
31
+ // ── Chart theme + shared cursor ──────────────────────────────────────────
29
32
  export { VxThemeProvider, useVxTheme, type VxTheme } from './theme'
30
- export { HoverContext, DEFAULT_NO_OP_SET_HOVER, type HoverCtx } from './hover-context'
31
- export { ChartHoverSync, type ChartHoverSyncProps } from './hover-sync'
33
+ export { ChartCursorScope, type ChartCursorScopeProps, useCursorState } from './cursor/scope'
34
+ export {
35
+ createCursorStore,
36
+ globalCursorStore,
37
+ type CursorState,
38
+ type CursorStore,
39
+ } from './cursor/store'
32
40
 
33
41
  // ── Series descriptor (the legend/tooltip single source of truth) ────────
34
42
  export {
@@ -44,8 +52,22 @@ export {
44
52
  } from './series'
45
53
 
46
54
  // ── Primitives ───────────────────────────────────────────────────────────
47
- export { ResponsiveChart, type ResponsiveChartProps } from './primitives/ResponsiveChart'
48
- export { ChartFrame, type ChartFrameProps, type ChartFrameLegend } from './primitives/ChartFrame'
55
+ export {
56
+ CartesianChart,
57
+ resolveAxisDomain,
58
+ type CartesianChartProps,
59
+ type CartesianTooltipConfig,
60
+ type CartesianTooltipRowContext,
61
+ type AxisConfig,
62
+ type PlotContext,
63
+ } from './primitives/CartesianChart'
64
+ export {
65
+ ChartFrame,
66
+ type ChartFrameProps,
67
+ type ChartFrameLegend,
68
+ type PlotRect,
69
+ resolveLegend,
70
+ } from './primitives/ChartFrame'
49
71
  export {
50
72
  ChartCenter,
51
73
  type ChartCenterProps,
@@ -56,25 +78,26 @@ export { Crosshair, SeriesDot } from './primitives/Crosshair'
56
78
  export { ChartCard } from './primitives/ChartCard'
57
79
  export { ChartLegend, type LegendEntry } from './primitives/ChartLegend'
58
80
  export {
59
- ChartTooltip,
81
+ ChartTooltipFloat,
60
82
  TooltipHeader,
61
83
  TooltipRow,
62
84
  TooltipBody,
63
- useTooltipStyles,
64
85
  } from './primitives/ChartTooltip'
65
86
  export { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric } from './primitives/Axes'
66
87
  export { HoverOverlay } from './primitives/HoverOverlay'
67
88
  export { ZoneRects, type ZoneSpec } from './primitives/ZoneRects'
89
+ export { XZoneRects, type XZoneSpec } from './primitives/XZoneRects'
68
90
  export { AreaGradient, areaFillUrl } from './primitives/AreaGradient'
69
91
 
70
92
  // ── Hooks ────────────────────────────────────────────────────────────────
71
- export { useChartTooltip, type TooltipState } from './hooks/useChartTooltip'
72
- export { useHoverSync } from './hooks/useHoverSync'
93
+ export { useChartCursor, type ChartCursor, type CursorAnchor } from './hooks/useChartCursor'
73
94
  export { useChartSize, type UseChartSizeResult, type ChartSize } from './hooks/useChartSize'
74
95
 
75
96
  // ── Utils ────────────────────────────────────────────────────────────────
76
97
  export { fmtAxisDate, fmtTooltipDate } from './utils/format'
77
- export { smartTicks } from './utils/ticks'
98
+ export { smartTicks, smartTicksEvery } from './utils/ticks'
99
+ export { autoMargin, probeAxisLabels, type AutoMarginInput } from './layout/auto-margin'
100
+ export { measureText, maxTextWidth } from './utils/measure-text'
78
101
 
79
102
  // ── Kind components (owned by a sibling agent under ./kinds) ──────────────
80
103
  export {
@@ -83,7 +106,6 @@ export {
83
106
  type ZonedLineZone,
84
107
  type ZonedLineThreshold,
85
108
  type ZonedLineRefLine,
86
- type ZonedLineTooltipLabel,
87
109
  } from './kinds/ZonedLine'
88
110
 
89
111
  export {
@@ -93,7 +115,6 @@ export {
93
115
  type BarsLine,
94
116
  type BarsZone,
95
117
  type BarsRefLine,
96
- type BarsAxisConfig,
97
118
  } from './kinds/Bars'
98
119
 
99
120
  export { StackedArea, type StackedAreaProps } from './kinds/StackedArea'
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `Bars` — the `tooltip: false` seam threaded from `SeriesStyle.tooltip` through `BarsBar`/
3
+ * `BarsLine` (replacing the removed per-kind `hideBarTooltipRows`), plus the `BarsBar<T>`
4
+ * generic-default assignability guarantee. Real DOM harness (`tests/setup/dom.ts`): the cursor
5
+ * only advances via `useChartCursor`'s keyboard path (`ArrowRight` on the overlay's `role="slider"`),
6
+ * which is deterministic regardless of happy-dom's zeroed layout geometry — unlike the pointer path,
7
+ * which needs a real `getBoundingClientRect`.
8
+ */
9
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
10
+ import { describe, expect, test } from 'bun:test'
11
+ import { Bars } from './Bars'
12
+ import type { BarsBar } from './Bars'
13
+
14
+ type Row = { date: string; shown: number; quiet: number; trend: number }
15
+
16
+ const rows: Row[] = [{ date: '2026-08-01', shown: 10, quiet: 5, trend: 7 }]
17
+
18
+ describe('Bars — tooltip: false drops only that row', () => {
19
+ test('a quiet bar is drawn and legended but produces no tooltip row', async () => {
20
+ render(
21
+ <Bars<Row>
22
+ data={rows}
23
+ chartId="bars-tooltip"
24
+ getX={(d) => d.date}
25
+ getValue={(d, key) => d[key as 'shown' | 'quiet']}
26
+ positiveBars={[
27
+ { key: 'shown', label: 'Shown', color: '#111' },
28
+ { key: 'quiet', label: 'Quiet', color: '#222', tooltip: false },
29
+ ]}
30
+ />,
31
+ )
32
+
33
+ // Both series are legended regardless of the tooltip flag.
34
+ expect(screen.getByText('Shown')).toBeTruthy()
35
+ expect(screen.getByText('Quiet')).toBeTruthy()
36
+
37
+ const slider = screen.getByRole('slider')
38
+ fireEvent.keyDown(slider, { key: 'ArrowRight' })
39
+
40
+ // The tooltip row duplicates 'Shown' (legend + row); 'Quiet' stays legend-only. The tooltip
41
+ // anchor is set through a `requestAnimationFrame` coalesce, so wait for the SECOND 'Shown' to
42
+ // land rather than resolving on the legend's already-present first one.
43
+ await waitFor(() => expect(screen.getAllByText('Shown')).toHaveLength(2))
44
+ expect(screen.getAllByText('Quiet')).toHaveLength(1)
45
+ })
46
+ })
47
+
48
+ describe('Bars — lines[].tooltip: false drops only that row', () => {
49
+ test('a quiet line is drawn and legended but produces no tooltip row', async () => {
50
+ render(
51
+ <Bars<Row>
52
+ data={rows}
53
+ chartId="bars-line-tooltip"
54
+ getX={(d) => d.date}
55
+ getValue={(d, key) => d[key as 'shown' | 'quiet' | 'trend']}
56
+ positiveBars={[{ key: 'shown', label: 'Shown', color: '#111' }]}
57
+ lines={[{ key: 'trend', label: 'Trend', color: '#333', tooltip: false }]}
58
+ />,
59
+ )
60
+
61
+ // Both series are legended regardless of the tooltip flag.
62
+ expect(screen.getByText('Shown')).toBeTruthy()
63
+ expect(screen.getByText('Trend')).toBeTruthy()
64
+
65
+ const slider = screen.getByRole('slider')
66
+ fireEvent.keyDown(slider, { key: 'ArrowRight' })
67
+
68
+ // The tooltip row duplicates 'Shown' (legend + row); 'Trend' stays legend-only.
69
+ await waitFor(() => expect(screen.getAllByText('Shown')).toHaveLength(2))
70
+ expect(screen.getAllByText('Trend')).toHaveLength(1)
71
+ })
72
+ })
73
+
74
+ describe('Bars — lines[].strokeOpacity dims the plotted stroke', () => {
75
+ test('the line path carries the configured stroke-opacity attribute', () => {
76
+ const { container } = render(
77
+ <Bars<Row>
78
+ data={rows}
79
+ chartId="bars-line-opacity"
80
+ getX={(d) => d.date}
81
+ getValue={(d, key) => d[key as 'shown' | 'quiet' | 'trend']}
82
+ positiveBars={[{ key: 'shown', label: 'Shown', color: '#111' }]}
83
+ lines={[{ key: 'trend', label: 'Trend', color: '#333', strokeOpacity: 0.4 }]}
84
+ />,
85
+ )
86
+
87
+ const path = container.querySelector('path[stroke="#333"]')
88
+ expect(path?.getAttribute('stroke-opacity')).toBe('0.4')
89
+ })
90
+ })
91
+
92
+ describe('BarsBar<T> — the `= unknown` default stays assignable', () => {
93
+ test('an existing untyped BarsBar[] array is assignable to BarsBar<Row>[] contravariantly', () => {
94
+ const bars: BarsBar[] = [{ key: 'shown', label: 'Shown', color: '#111' }]
95
+ const typed: BarsBar<Row>[] = bars
96
+ expect(typed).toBe(bars)
97
+ })
98
+ })