basalt-ui 1.13.0 → 1.15.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 (180) 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 +260 -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/configs/oxlint-plugin.js +152 -0
  9. package/configs/oxlint-plugin.test.ts +107 -0
  10. package/configs/oxlint.json +2 -0
  11. package/dist/charts/cursor/resolve.d.ts +33 -0
  12. package/dist/charts/cursor/resolve.d.ts.map +1 -0
  13. package/dist/charts/cursor/resolve.js +61 -0
  14. package/dist/charts/cursor/resolve.test.d.ts +2 -0
  15. package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
  16. package/dist/charts/cursor/scope.d.ts +17 -0
  17. package/dist/charts/cursor/scope.d.ts.map +1 -0
  18. package/dist/charts/cursor/scope.js +20 -0
  19. package/dist/charts/cursor/store.d.ts +31 -0
  20. package/dist/charts/cursor/store.d.ts.map +1 -0
  21. package/dist/charts/cursor/store.js +25 -0
  22. package/dist/charts/cursor/store.test.d.ts +2 -0
  23. package/dist/charts/cursor/store.test.d.ts.map +1 -0
  24. package/dist/charts/hooks/useChartCursor.d.ts +39 -0
  25. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
  26. package/dist/charts/hooks/useChartCursor.js +104 -0
  27. package/dist/charts/hooks/useChartSize.d.ts +8 -4
  28. package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
  29. package/dist/charts/index.d.ts +16 -13
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/index.js +38 -19
  32. package/dist/charts/kinds/Bars.d.ts +25 -47
  33. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  34. package/dist/charts/kinds/Bars.js +230 -390
  35. package/dist/charts/kinds/Donut.d.ts +5 -4
  36. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  37. package/dist/charts/kinds/Donut.js +17 -14
  38. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  39. package/dist/charts/kinds/DualPanel.js +136 -90
  40. package/dist/charts/kinds/Heatmap.d.ts +15 -11
  41. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  42. package/dist/charts/kinds/Heatmap.js +38 -22
  43. package/dist/charts/kinds/MultiLine.d.ts +27 -35
  44. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  45. package/dist/charts/kinds/MultiLine.js +114 -237
  46. package/dist/charts/kinds/StackedArea.d.ts +21 -10
  47. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  48. package/dist/charts/kinds/StackedArea.js +74 -132
  49. package/dist/charts/kinds/ZonedLine.d.ts +24 -40
  50. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  51. package/dist/charts/kinds/ZonedLine.js +103 -184
  52. package/dist/charts/layout/auto-margin.d.ts +60 -0
  53. package/dist/charts/layout/auto-margin.d.ts.map +1 -0
  54. package/dist/charts/layout/auto-margin.js +37 -0
  55. package/dist/charts/layout/auto-margin.test.d.ts +2 -0
  56. package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
  57. package/dist/charts/primitives/CartesianChart.d.ts +143 -0
  58. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
  59. package/dist/charts/primitives/CartesianChart.js +349 -0
  60. package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
  61. package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
  62. package/dist/charts/primitives/ChartFrame.d.ts +16 -5
  63. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  64. package/dist/charts/primitives/ChartFrame.js +17 -2
  65. package/dist/charts/primitives/ChartLegend.d.ts +12 -1
  66. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  67. package/dist/charts/primitives/ChartLegend.js +21 -3
  68. package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
  69. package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
  70. package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
  71. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  72. package/dist/charts/primitives/ChartTooltip.js +45 -19
  73. package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
  74. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  75. package/dist/charts/primitives/HoverOverlay.js +22 -1
  76. package/dist/charts/primitives/XZoneRects.d.ts +40 -0
  77. package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
  78. package/dist/charts/primitives/XZoneRects.js +28 -0
  79. package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
  80. package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
  81. package/dist/charts/series.d.ts +16 -1
  82. package/dist/charts/series.d.ts.map +1 -1
  83. package/dist/charts/series.js +3 -1
  84. package/dist/charts/series.test.d.ts +2 -0
  85. package/dist/charts/series.test.d.ts.map +1 -0
  86. package/dist/charts/utils/measure-text.d.ts +25 -0
  87. package/dist/charts/utils/measure-text.d.ts.map +1 -0
  88. package/dist/charts/utils/measure-text.js +64 -0
  89. package/dist/charts/utils/measure-text.test.d.ts +2 -0
  90. package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
  91. package/dist/content/fence-block.d.ts.map +1 -1
  92. package/dist/shell/app-sidebar.d.ts +19 -1
  93. package/dist/shell/app-sidebar.d.ts.map +1 -1
  94. package/dist/shell/app-sidebar.js +30 -26
  95. package/dist/shell/app-sidebar.module.css +11 -0
  96. package/dist/shell/app-sidebar.module.css.d.ts +1 -0
  97. package/dist/shell/app-sidebar.test.d.ts +2 -0
  98. package/dist/shell/app-sidebar.test.d.ts.map +1 -0
  99. package/dist/shell/index.d.ts +10 -1
  100. package/dist/shell/index.d.ts.map +1 -1
  101. package/dist/shell/index.js +2 -0
  102. package/dist/shell/index.test.d.ts +2 -0
  103. package/dist/shell/index.test.d.ts.map +1 -0
  104. package/dist/surfaces.d.ts +1 -1
  105. package/dist/surfaces.d.ts.map +1 -1
  106. package/dist/surfaces.js +1 -1
  107. package/dist/tokens/chart-margin.test.d.ts +2 -0
  108. package/dist/tokens/chart-margin.test.d.ts.map +1 -0
  109. package/dist/tokens/index.d.ts +29 -0
  110. package/dist/tokens/index.d.ts.map +1 -1
  111. package/dist/tokens/index.js +10 -0
  112. package/dist/tokens/palette.d.ts +4 -0
  113. package/dist/tokens/palette.d.ts.map +1 -1
  114. package/dist/tokens/palette.js +3 -0
  115. package/llms.txt +3 -3
  116. package/package.json +1 -1
  117. package/src/charts/cursor/resolve.test.ts +61 -0
  118. package/src/charts/cursor/resolve.ts +102 -0
  119. package/src/charts/cursor/scope.tsx +32 -0
  120. package/src/charts/cursor/store.test.ts +46 -0
  121. package/src/charts/cursor/store.ts +53 -0
  122. package/src/charts/hooks/useChartCursor.ts +163 -0
  123. package/src/charts/hooks/useChartSize.ts +8 -4
  124. package/src/charts/index.ts +34 -14
  125. package/src/charts/kinds/Bars.tsx +327 -460
  126. package/src/charts/kinds/Donut.tsx +31 -23
  127. package/src/charts/kinds/DualPanel.tsx +228 -134
  128. package/src/charts/kinds/Heatmap.tsx +72 -47
  129. package/src/charts/kinds/MultiLine.tsx +158 -294
  130. package/src/charts/kinds/StackedArea.tsx +116 -175
  131. package/src/charts/kinds/ZonedLine.tsx +148 -255
  132. package/src/charts/layout/auto-margin.test.ts +42 -0
  133. package/src/charts/layout/auto-margin.ts +110 -0
  134. package/src/charts/primitives/CartesianChart.test.ts +99 -0
  135. package/src/charts/primitives/CartesianChart.tsx +566 -0
  136. package/src/charts/primitives/ChartFrame.test.tsx +33 -0
  137. package/src/charts/primitives/ChartFrame.tsx +40 -4
  138. package/src/charts/primitives/ChartLegend.test.tsx +48 -0
  139. package/src/charts/primitives/ChartLegend.tsx +45 -2
  140. package/src/charts/primitives/ChartTooltip.tsx +67 -37
  141. package/src/charts/primitives/HoverOverlay.tsx +39 -3
  142. package/src/charts/primitives/XZoneRects.test.tsx +63 -0
  143. package/src/charts/primitives/XZoneRects.tsx +67 -0
  144. package/src/charts/series.test.ts +61 -0
  145. package/src/charts/series.ts +18 -1
  146. package/src/charts/utils/measure-text.test.ts +36 -0
  147. package/src/charts/utils/measure-text.ts +109 -0
  148. package/src/content/fence-block.tsx +2 -3
  149. package/src/shell/app-sidebar.module.css +11 -0
  150. package/src/shell/app-sidebar.module.css.d.ts +1 -0
  151. package/src/shell/app-sidebar.test.tsx +98 -0
  152. package/src/shell/app-sidebar.tsx +22 -0
  153. package/src/shell/index.test.tsx +35 -0
  154. package/src/shell/index.tsx +11 -0
  155. package/src/surfaces.ts +2 -1
  156. package/src/theme/spacing.test.ts +2 -0
  157. package/src/tokens/chart-margin.test.ts +39 -0
  158. package/src/tokens/density.test.ts +1 -0
  159. package/src/tokens/index.ts +36 -0
  160. package/src/tokens/palette.ts +3 -0
  161. package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
  162. package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
  163. package/dist/charts/hooks/useChartTooltip.js +0 -26
  164. package/dist/charts/hooks/useHoverSync.d.ts +0 -46
  165. package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
  166. package/dist/charts/hooks/useHoverSync.js +0 -75
  167. package/dist/charts/hover-context.d.ts +0 -21
  168. package/dist/charts/hover-context.d.ts.map +0 -1
  169. package/dist/charts/hover-context.js +0 -12
  170. package/dist/charts/hover-sync.d.ts +0 -14
  171. package/dist/charts/hover-sync.d.ts.map +0 -1
  172. package/dist/charts/hover-sync.js +0 -17
  173. package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
  174. package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
  175. package/dist/charts/primitives/ResponsiveChart.js +0 -24
  176. package/src/charts/hooks/useChartTooltip.ts +0 -31
  177. package/src/charts/hooks/useHoverSync.ts +0 -152
  178. package/src/charts/hover-context.ts +0 -28
  179. package/src/charts/hover-sync.tsx +0 -37
  180. package/src/charts/primitives/ResponsiveChart.tsx +0 -64
@@ -1,66 +1,58 @@
1
- import type { ReactNode } from 'react';
1
+ import type { CartesianTooltipConfig, AxisConfig } from '../primitives/CartesianChart.js';
2
+ import type { XZoneSpec } from '../primitives/XZoneRects.js';
2
3
  import type { ZoneSpec } from '../primitives/ZoneRects.js';
3
4
  import type { ChartLegendConfig, ChartSeries } from '../series.js';
4
5
  export type MultiLineProps<T> = {
5
6
  data: T[];
6
- /** Fixed height in pixels, forwarded to the internal `ChartFrame`. Default 240. */
7
+ /** Fixed height in pixels, forwarded to `CartesianChart`. Default 240. */
7
8
  height?: number;
8
9
  chartId: string;
9
10
  /** Extracts the x-axis category (date string) from a data point. */
10
11
  getX: (d: T) => string;
11
- /** 1+ line series sharing one y-axis — the single source of truth for color, dash, legend, and
12
- * tooltip rows. */
12
+ /** 1+ line series sharing the y-axis(es) — the single source of truth for color, dash, legend,
13
+ * and tooltip rows. A series opts into the right axis with `axis: 'right'`. */
13
14
  series: ChartSeries<T>[];
14
- /** Fixed y-domain (e.g. [-2.5, 2.5]) or 'auto' to compute from all series. */
15
- yDomain: [number, number] | 'auto';
16
- /** When 'auto': upper bound is at least this value. */
17
- yAutoMaxFloor?: number;
18
- /** When 'auto': lower bound is at most this value. Default 0. Pass `Infinity` to disable the
19
- * forced zero baseline — the lower bound then pads DOWN from the raw data minimum instead. */
20
- yAutoMinCeil?: number;
21
- /** When 'auto': padding multiplier applied away from the domain. Default 1.1. The lower bound
22
- * always pads DOWN from `min(dataMin, yAutoMinCeil)` (never up, which would clip data) — see
23
- * {@link padAutoLower}. */
24
- yAutoPad?: number;
25
- /** Horizontal value-range overlays (target zones), rendered behind the lines on the left scale. */
15
+ /** Left y-axis. Collapses the old `yDomain` / `yAutoMaxFloor` / `yAutoMinCeil` / `yAutoPad` /
16
+ * `numTicksY` / `formatYTick` prop soup into one object. */
17
+ y?: AxisConfig<T>;
18
+ /** Right y-axis. Passing it is what makes the chart dual-axis — the margin widens by
19
+ * measurement, and `axis: 'right'` series read against it. */
20
+ y2?: AxisConfig<T>;
21
+ /** Horizontal value-range overlays (target zones), rendered behind the lines. */
26
22
  zones?: ZoneSpec[];
23
+ /** Vertical x-range overlays (time windows), rendered behind the lines. Bounds are `getX`
24
+ * domain keys. */
25
+ xZones?: XZoneSpec[];
27
26
  /** Horizontal reference lines. Solid by default; set dashed: true for a dashed line. */
28
27
  refLines?: {
29
28
  value: number;
30
29
  color: string;
31
30
  dashed?: boolean;
32
31
  }[];
33
- numTicksX?: number;
34
- numTicksY?: number;
35
- /** Tooltip + (per-series) value formatter. */
36
- formatValue: (v: number) => string;
37
- /** Y-axis tick label formatter. Falls back to the scale's default. */
38
- formatYTick?: (v: number) => string;
39
- /** Label shown at the right of the tooltip header (e.g. status badge). */
40
- tooltipLabel?: (d: T) => {
41
- text: string;
42
- color: string;
43
- } | null;
44
- /** Extra tooltip rows rendered after the per-series rows. */
45
- renderExtraTooltipRows?: (d: T) => ReactNode;
32
+ /** Exact number of x ticks. Default: as many as fit. */
33
+ xTicks?: number;
34
+ /** Tooltip config — `label` for a right-aligned header badge, `extraRows` for rows appended
35
+ * after the derived per-series rows. `false` disables the tooltip entirely. */
36
+ tooltip?: CartesianTooltipConfig<T> | false;
46
37
  /** Marker glyph for per-point markers. Default 'circle'. */
47
38
  markerShape?: 'circle' | 'star';
48
- /** Legend config forwarded to `ChartFrame`; `false` disables the legend (sparkline escape).
39
+ /** Legend config forwarded to `CartesianChart`; `false` disables the legend (sparkline escape).
49
40
  * Default `{ placement: 'bottom' }`. */
50
41
  legend?: ChartLegendConfig | false;
51
- /** Accessible text alternative, forwarded to `ChartFrame` as `aria-label` (+ `role="img"`). */
42
+ /** Accessible text alternative, forwarded to `CartesianChart` as `aria-label` (+ `role="img"`). */
52
43
  ariaLabel?: string;
53
- /** Forwarded to `ChartFrame` — see `ChartPending`'s JSDoc for the three-state rationale. */
44
+ /** Forwarded to `CartesianChart` — see `ChartPending`'s JSDoc for the three-state rationale. */
54
45
  isPending?: boolean;
55
46
  };
56
47
  /**
57
- * N series sharing one y-axis, with optional zones/reference lines, per-point markers
48
+ * N series sharing one or two y-axes, with optional zones/reference lines, per-point markers
58
49
  * (PR stars / status dots), a dashed companion line per series, legend-hover dimming, and a
59
50
  * shared-cursor tooltip. Generalizes the multi-line argo charts (e1RM trend, relative
60
51
  * progression, training load, fitness trends).
61
52
  *
62
- * Composes `ChartFrame` for measuring + the derived legend — `series` is the single array that
63
- * drives the plotted lines, the legend, and the tooltip rows.
53
+ * Composes `CartesianChart` for measuring, scales, grid, axes, cursor, and tooltip — this kind
54
+ * draws ONLY the marks (lines + per-point markers). `series` is the single array that drives the
55
+ * plotted lines, the legend, and the tooltip rows.
64
56
  *
65
57
  * X-axis is built from the full `data` array so the calendar is preserved even when a series
66
58
  * has nulls; each series line skips null points (creating visual gaps).
@@ -1 +1 @@
1
- {"version":3,"file":"MultiLine.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/MultiLine.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAatC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAGvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAK/D,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;uBACmB;IACnB,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,8EAA8E;IAC9E,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAClC,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;kGAC8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;+BAE2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mGAAmG;IACnG,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;IAClB,wFAAwF;IACxF,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8CAA8C;IAC9C,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,sEAAsE;IACtE,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D,6DAA6D;IAC7D,sBAAsB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,CAAA;IAC5C,4DAA4D;IAC5D,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IAC/B;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAgBD;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAiClD;AAkPD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
1
+ {"version":3,"file":"MultiLine.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/MultiLine.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAe,MAAM,8BAA8B,CAAA;AAEnG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAG/D,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;mFAC+E;IAC/E,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB;gEAC4D;IAC5D,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IACjB;kEAC8D;IAC9D,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;IAClB;sBACkB;IAClB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAA;IACpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC/D,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;mFAC+E;IAC/E,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IAC3C,4DAA4D;IAC5D,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IAC/B;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,mGAAmG;IACnG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AA4BD;;;;;;;;;;;;GAYG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAgDlD;AAoGD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
@@ -1,27 +1,10 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { curveMonotoneX } from "@visx/curve";
3
- import { GridRows } from "@visx/grid";
4
- import { Group } from "@visx/group";
5
- import { scaleLinear, scalePoint } from "@visx/scale";
6
3
  import { LinePath } from "@visx/shape";
7
- import { memo, useMemo, useState } from "react";
8
- import { AxisBottomDate, AxisLeftNumeric } from "../primitives/Axes.js";
9
- import {
10
- ChartTooltip,
11
- TooltipBody,
12
- TooltipHeader,
13
- TooltipRow,
14
- useTooltipStyles
15
- } from "../primitives/ChartTooltip.js";
16
- import { ChartFrame, resolveLegend } from "../primitives/ChartFrame.js";
17
- import { Crosshair, SeriesDot } from "../primitives/Crosshair.js";
18
- import { HoverOverlay } from "../primitives/HoverOverlay.js";
19
- import { ZoneRects } from "../primitives/ZoneRects.js";
20
- import { useHoverSync } from "../hooks/useHoverSync.js";
21
- import { deriveTooltipRows, LINE_OVERLAY_STROKE_WIDTH } from "../series.js";
4
+ import { memo, useMemo } from "react";
5
+ import { CartesianChart } from "../primitives/CartesianChart.js";
6
+ import { LINE_OVERLAY_STROKE_WIDTH } from "../series.js";
22
7
  import { VX } from "../../tokens/index.js";
23
- import { padAutoLower } from "../utils/domain.js";
24
- import { smartTicks, smartTicksEvery } from "../utils/ticks.js";
25
8
  const STAR_R = 6;
26
9
  function starPath(cx, cy, r) {
27
10
  const inner = r * 0.4;
@@ -33,241 +16,135 @@ function starPath(cx, cy, r) {
33
16
  }
34
17
  return `M${pts.join("L")}Z`;
35
18
  }
36
- function MultiLineInner(props) {
37
- const { series, chartId, height, legend, ariaLabel, isPending } = props;
38
- const [highlightedKey, setHighlightedKey] = useState(null);
39
- const styledSeries = useMemo(
40
- () => series.map((s) => ({ ...s, strokeWidth: s.strokeWidth ?? LINE_OVERLAY_STROKE_WIDTH })),
41
- [series]
42
- );
43
- return /* @__PURE__ */ jsx(
44
- ChartFrame,
45
- {
46
- series: styledSeries,
47
- chartId,
48
- ...height !== void 0 && { height },
49
- ...ariaLabel !== void 0 && { ariaLabel },
50
- ...isPending !== void 0 && { isPending },
51
- legend: resolveLegend(legend, {
52
- highlighted: highlightedKey,
53
- onHighlight: setHighlightedKey
54
- }),
55
- children: (plot) => /* @__PURE__ */ jsx(
56
- MultiLinePlot,
57
- {
58
- ...props,
59
- series: styledSeries,
60
- plot,
61
- highlightedKey
62
- }
63
- )
64
- }
65
- );
19
+ function seriesPoints(series, data) {
20
+ const pts = [];
21
+ for (const d of data) {
22
+ const v = series.getValue(d);
23
+ if (v !== null && v !== void 0 && !Number.isNaN(v)) pts.push({ __d: d, __y: v });
24
+ }
25
+ return pts;
66
26
  }
67
- function MultiLinePlot(props) {
27
+ function MultiLineInner(props) {
68
28
  const {
69
29
  data,
70
30
  chartId,
71
31
  getX,
72
32
  series,
73
- yDomain,
74
- yAutoPad = 1.1,
75
- yAutoMaxFloor,
76
- yAutoMinCeil = 0,
77
- zones = [],
78
- refLines = [],
79
- numTicksX,
80
- numTicksY = 5,
81
- formatValue,
82
- formatYTick,
83
- tooltipLabel,
84
- renderExtraTooltipRows,
33
+ y,
34
+ y2,
35
+ zones,
36
+ xZones,
37
+ refLines,
38
+ xTicks,
39
+ tooltip,
85
40
  markerShape = "circle",
86
- plot,
87
- highlightedKey
41
+ height,
42
+ legend,
43
+ ariaLabel,
44
+ isPending
88
45
  } = props;
89
- const dimOpacity = (s) => highlightedKey === null || s.key === highlightedKey || s.parent === highlightedKey ? 1 : 0.25;
90
- const MARGIN = VX.margin;
91
- const xMax = plot.width - MARGIN.left - MARGIN.right;
92
- const yMax = plot.height - MARGIN.top - MARGIN.bottom;
93
- const xScale = useMemo(
94
- () => scalePoint({
95
- // Full calendar — axis does not compress across nulls.
96
- domain: data.map(getX),
97
- range: [0, xMax],
98
- padding: 0.3
99
- }),
100
- [data, xMax, getX]
46
+ const styledSeries = useMemo(
47
+ () => series.map((s) => ({ ...s, strokeWidth: s.strokeWidth ?? LINE_OVERLAY_STROKE_WIDTH })),
48
+ [series]
101
49
  );
102
- const yScale = useMemo(() => {
103
- if (yDomain === "auto") {
104
- let dataMax = -Infinity;
105
- let dataMin = Infinity;
106
- for (const d of data) {
107
- for (const s of series) {
108
- const v = s.getValue(d);
109
- if (v === null || v === void 0 || Number.isNaN(v)) continue;
110
- if (v > dataMax) dataMax = v;
111
- if (v < dataMin) dataMin = v;
112
- }
113
- }
114
- const safeMax = Number.isFinite(dataMax) ? dataMax : 0;
115
- const safeMin = Number.isFinite(dataMin) ? dataMin : 0;
116
- const upper = Math.max(safeMax, yAutoMaxFloor ?? safeMax) * yAutoPad;
117
- const lower = padAutoLower(Math.min(safeMin, yAutoMinCeil), yAutoPad);
118
- return scaleLinear({ domain: [lower, upper], range: [yMax, 0], nice: true });
119
- }
120
- return scaleLinear({ domain: yDomain, range: [yMax, 0] });
121
- }, [data, series, yDomain, yMax, yAutoPad, yAutoMaxFloor, yAutoMinCeil]);
122
- const tooltipStyles = useTooltipStyles();
123
- const { tip, tooltipRef, syncedPoint, isDirectHover, handleMouse, handleLeave } = useHoverSync(
50
+ return /* @__PURE__ */ jsx(
51
+ CartesianChart,
124
52
  {
125
53
  data,
126
54
  chartId,
127
- getKey: getX,
128
- xScale,
129
- marginLeft: MARGIN.left
55
+ getX,
56
+ series: styledSeries,
57
+ ...y !== void 0 && { y },
58
+ ...y2 !== void 0 && { y2 },
59
+ ...zones !== void 0 && { zones },
60
+ ...xZones !== void 0 && { xZones },
61
+ ...refLines !== void 0 && { refLines },
62
+ ...xTicks !== void 0 && { xTicks },
63
+ ...tooltip !== void 0 && { tooltip },
64
+ ...height !== void 0 && { height },
65
+ ...legend !== void 0 && { legend },
66
+ ...ariaLabel !== void 0 && { ariaLabel },
67
+ ...isPending !== void 0 && { isPending },
68
+ children: (ctx) => /* @__PURE__ */ jsx(MultiLineMarks, { getX, markerShape, ctx })
130
69
  }
131
70
  );
132
- const tickValues = useMemo(
133
- () => numTicksX ? smartTicksEvery(data.map(getX), numTicksX) : smartTicks(data.map(getX), xMax),
134
- [data, xMax, getX, numTicksX]
135
- );
136
- const seriesPts = useMemo(() => {
137
- const out = /* @__PURE__ */ new Map();
138
- for (const s of series) {
139
- const pts = [];
140
- for (const d of data) {
141
- const v = s.getValue(d);
142
- if (v !== null && v !== void 0 && !Number.isNaN(v)) pts.push({ __d: d, __y: v });
143
- }
144
- out.set(s.key, pts);
145
- }
146
- return out;
147
- }, [data, series]);
148
- const tooltipLbl = tip ? tooltipLabel?.(tip.data) ?? null : null;
71
+ }
72
+ function MultiLineMarks({
73
+ getX,
74
+ markerShape,
75
+ ctx
76
+ }) {
77
+ const { data, visible, xScale, yScale, y2Scale, highlighted } = ctx;
78
+ const pointsBySeries = useMemo(() => {
79
+ const m = /* @__PURE__ */ new Map();
80
+ for (const s of visible) m.set(s.key, seriesPoints(s, data));
81
+ return m;
82
+ }, [visible, data]);
83
+ const dimOpacity = (s) => highlighted === null || s.key === highlighted || s.parent === highlighted ? 1 : 0.25;
84
+ const scaleFor = (s) => s.axis === "right" && y2Scale !== null ? y2Scale : yScale;
149
85
  return /* @__PURE__ */ jsxs(Fragment, { children: [
150
- /* @__PURE__ */ jsx("svg", { width: plot.width, height: plot.height, children: /* @__PURE__ */ jsxs(Group, { left: MARGIN.left, top: MARGIN.top, children: [
151
- /* @__PURE__ */ jsx(GridRows, { scale: yScale, width: xMax, stroke: VX.grid, numTicks: numTicksY }),
152
- /* @__PURE__ */ jsx(ZoneRects, { zones, width: xMax, leftScale: yScale }),
153
- refLines.map((r, i) => /* @__PURE__ */ jsx(
154
- "line",
86
+ visible.map((s) => {
87
+ const valid = pointsBySeries.get(s.key) ?? [];
88
+ if (valid.length === 0) return null;
89
+ const scale = scaleFor(s);
90
+ return /* @__PURE__ */ jsx(
91
+ LinePath,
155
92
  {
156
- x1: 0,
157
- x2: xMax,
158
- y1: yScale(r.value),
159
- y2: yScale(r.value),
160
- stroke: r.color,
161
- strokeDasharray: r.dashed ? VX.dashArray : void 0
93
+ data: valid,
94
+ x: (p) => xScale(getX(p.__d)) ?? 0,
95
+ y: (p) => scale(p.__y),
96
+ stroke: s.color,
97
+ strokeWidth: s.strokeWidth ?? LINE_OVERLAY_STROKE_WIDTH,
98
+ strokeDasharray: s.dash === "dashed" ? VX.dashArray : void 0,
99
+ strokeOpacity: dimOpacity(s),
100
+ curve: curveMonotoneX
162
101
  },
163
- `ref-${i}`
164
- )),
165
- series.map((s) => {
166
- const valid = seriesPts.get(s.key) ?? [];
167
- if (valid.length === 0) return null;
168
- return /* @__PURE__ */ jsx(
169
- LinePath,
170
- {
171
- data: valid,
172
- x: (p) => xScale(getX(p.__d)) ?? 0,
173
- y: (p) => yScale(p.__y),
174
- stroke: s.color,
175
- strokeWidth: s.strokeWidth ?? VX.lineWidth,
176
- strokeDasharray: s.dash === "dashed" ? VX.dashArray : void 0,
177
- strokeOpacity: dimOpacity(s),
178
- curve: curveMonotoneX
179
- },
180
- `line-${s.key}`
102
+ `line-${s.key}`
103
+ );
104
+ }),
105
+ visible.flatMap((s) => {
106
+ const getMarker = s.getMarker;
107
+ if (!getMarker) return [];
108
+ const scale = scaleFor(s);
109
+ const op = dimOpacity(s);
110
+ const markers = [];
111
+ for (const p of pointsBySeries.get(s.key) ?? []) {
112
+ const m = getMarker(p.__d);
113
+ if (m === null) continue;
114
+ const cx = xScale(getX(p.__d)) ?? 0;
115
+ const cy = scale(p.__y);
116
+ const color = m.color ?? s.color;
117
+ const r = m.r ?? (markerShape === "star" ? STAR_R : VX.dotR);
118
+ markers.push(
119
+ markerShape === "star" ? /* @__PURE__ */ jsx(
120
+ "path",
121
+ {
122
+ d: starPath(cx, cy, r),
123
+ fill: color,
124
+ stroke: VX.dotStroke,
125
+ strokeWidth: 1.5,
126
+ fillOpacity: op,
127
+ strokeOpacity: op
128
+ },
129
+ `mk-${s.key}-${getX(p.__d)}`
130
+ ) : /* @__PURE__ */ jsx(
131
+ "circle",
132
+ {
133
+ cx,
134
+ cy,
135
+ r,
136
+ fill: color,
137
+ stroke: VX.dotStroke,
138
+ strokeWidth: 2,
139
+ fillOpacity: op,
140
+ strokeOpacity: op
141
+ },
142
+ `mk-${s.key}-${getX(p.__d)}`
143
+ )
181
144
  );
182
- }),
183
- series.flatMap((s) => {
184
- const getMarker = s.getMarker;
185
- if (!getMarker) return [];
186
- const op = dimOpacity(s);
187
- const markers = [];
188
- for (const p of seriesPts.get(s.key) ?? []) {
189
- const m = getMarker(p.__d);
190
- if (m === null) continue;
191
- const cx = xScale(getX(p.__d)) ?? 0;
192
- const cy = yScale(p.__y);
193
- const color = m.color ?? s.color;
194
- const r = m.r ?? (markerShape === "star" ? STAR_R : VX.dotR);
195
- markers.push(
196
- markerShape === "star" ? /* @__PURE__ */ jsx(
197
- "path",
198
- {
199
- d: starPath(cx, cy, r),
200
- fill: color,
201
- stroke: VX.dotStroke,
202
- strokeWidth: 1.5,
203
- fillOpacity: op,
204
- strokeOpacity: op
205
- },
206
- `mk-${s.key}-${getX(p.__d)}`
207
- ) : /* @__PURE__ */ jsx(
208
- "circle",
209
- {
210
- cx,
211
- cy,
212
- r,
213
- fill: color,
214
- stroke: VX.dotStroke,
215
- strokeWidth: 2,
216
- fillOpacity: op,
217
- strokeOpacity: op
218
- },
219
- `mk-${s.key}-${getX(p.__d)}`
220
- )
221
- );
222
- }
223
- return markers;
224
- }),
225
- syncedPoint && (() => {
226
- const sx = xScale(getX(syncedPoint)) ?? 0;
227
- return /* @__PURE__ */ jsxs(Fragment, { children: [
228
- /* @__PURE__ */ jsx(Crosshair, { x: sx, top: 0, bottom: yMax }),
229
- series.map((s) => {
230
- const v = s.getValue(syncedPoint);
231
- if (v === null || v === void 0 || Number.isNaN(v)) return null;
232
- return /* @__PURE__ */ jsx(SeriesDot, { cx: sx, cy: yScale(v), color: s.color }, `dot-${s.key}`);
233
- })
234
- ] });
235
- })(),
236
- /* @__PURE__ */ jsx(
237
- AxisLeftNumeric,
238
- {
239
- scale: yScale,
240
- numTicks: numTicksY,
241
- ...formatYTick !== void 0 && { tickFormat: formatYTick }
242
- }
243
- ),
244
- /* @__PURE__ */ jsx(AxisBottomDate, { top: yMax, scale: xScale, tickValues }),
245
- /* @__PURE__ */ jsx(HoverOverlay, { width: xMax, height: yMax, onMove: handleMouse, onLeave: handleLeave })
246
- ] }) }),
247
- /* @__PURE__ */ jsx(ChartTooltip, { tip: isDirectHover ? tip : null, tooltipRef, styles: tooltipStyles, children: tip && isDirectHover && /* @__PURE__ */ jsxs(Fragment, { children: [
248
- /* @__PURE__ */ jsx(
249
- TooltipHeader,
250
- {
251
- date: getX(tip.data),
252
- ...tooltipLbl !== null && { label: tooltipLbl.text, labelColor: tooltipLbl.color }
253
- }
254
- ),
255
- /* @__PURE__ */ jsxs(TooltipBody, { children: [
256
- deriveTooltipRows(series, tip.data, formatValue).map((row) => /* @__PURE__ */ jsx(
257
- TooltipRow,
258
- {
259
- color: row.color,
260
- label: row.label,
261
- value: row.value,
262
- shape: row.shape,
263
- dashed: row.dashed,
264
- ...row.strokeWidth !== void 0 && { strokeWidth: row.strokeWidth }
265
- },
266
- row.key
267
- )),
268
- renderExtraTooltipRows?.(tip.data)
269
- ] })
270
- ] }) })
145
+ }
146
+ return markers;
147
+ })
271
148
  ] });
272
149
  }
273
150
  const MultiLine = memo(MultiLineInner);
@@ -1,30 +1,41 @@
1
+ import type { AxisConfig } from '../primitives/CartesianChart.js';
1
2
  import type { ChartLegendConfig, ChartSeries } from '../series.js';
2
3
  export type StackedAreaProps<T> = {
3
4
  data: T[];
4
- /** Fixed height in pixels, forwarded to the internal `ChartFrame`. Default 240. */
5
+ /** Fixed height in pixels, forwarded to `CartesianChart`. Default 240. */
5
6
  height?: number;
6
7
  chartId: string;
7
8
  getX: (d: T) => string;
8
9
  /** The stacked bands, bottom to top — the single source of truth for color, legend, and tooltip
9
10
  * rows. Typically `mark: 'area'` (rendered as a `bar`-shaped legend/tooltip swatch). */
10
11
  series: ChartSeries<T>[];
11
- formatValue: (v: number) => string;
12
- yLabel?: string;
13
- numTicksY?: number;
14
- numTicksX?: number;
15
- yAutoMaxFloor?: number;
16
- /** Legend config forwarded to `ChartFrame`; `false` disables the legend (sparkline escape).
12
+ /** Axis config for ticks/format/grid. `domain` defaults to the summed per-point stack total
13
+ * (padded ×1.1, or `autoMaxFloor` if higher) rather than a per-series max — the seam a stacked
14
+ * chart needs, since the plotted quantity is the cumulative band top, not any one series' own
15
+ * value. Pass a fixed tuple or your own function to override. */
16
+ y?: AxisConfig<T>;
17
+ /** X tick count override. Default: as many as fit. */
18
+ xTicks?: number;
19
+ /** Legend config forwarded to `CartesianChart`; `false` disables the legend (sparkline escape).
17
20
  * Default `{ placement: 'bottom' }`. */
18
21
  legend?: ChartLegendConfig | false;
19
- /** Accessible text alternative, forwarded to `ChartFrame` as `aria-label` (+ `role="img"`). */
22
+ /** Accessible text alternative, forwarded as `aria-label` (+ `role="img"`). */
20
23
  ariaLabel?: string;
21
- /** Forwarded to `ChartFrame` — see `ChartPending`'s JSDoc for the three-state rationale. */
24
+ /** Forwarded to `CartesianChart` — see `ChartPending`'s JSDoc for the three-state rationale. */
22
25
  isPending?: boolean;
23
26
  };
24
27
  /**
25
28
  * Multi-series stacked-area chart with an optional derived legend (default on) and legend-hover
26
29
  * dimming. Each band's fillOpacity is dimmed when a different key is highlighted via the legend.
27
- * The tooltip renders rows in reversed (top-to-bottom) stack order so it matches the visual stack.
30
+ *
31
+ * Composes `CartesianChart` (`docs/CHARTS-SPEC.md` §2) — margin, scale, grid, axes, cursor, and
32
+ * tooltip are the primitive's job. This file draws only the `AreaStack` bands.
33
+ *
34
+ * The tooltip must render top-to-bottom (matching the visual stack read from the top), but the
35
+ * marks must stack bottom-to-top (matching `series`' own order) — the two can't share one order.
36
+ * `CartesianChart`'s `series` prop drives BOTH the derived tooltip rows and legend, so `series` is
37
+ * passed reversed to it (tooltip/legend read top-first); the marks then reverse `ctx.visible` back
38
+ * to restore the original bottom-to-top stacking order for `AreaStack`'s `keys`.
28
39
  */
29
40
  declare function StackedAreaInner<T>(props: StackedAreaProps<T>): import("react").JSX.Element;
30
41
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"StackedArea.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/StackedArea.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAI/D,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;4FACwF;IACxF,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BAmBtD;AAiJD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAA6B,OAAO,gBAAgB,CAAA"}
1
+ {"version":3,"file":"StackedArea.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/StackedArea.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,8BAA8B,CAAA;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE/D,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;4FACwF;IACxF,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB;;;qEAGiE;IACjE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IACjB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BAgEtD;AA+CD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAA6B,OAAO,gBAAgB,CAAA"}