basalt-ui 2.3.0 → 2.5.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 (65) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/MIGRATING.md +79 -12
  3. package/agent/rules/basalt-state.md +1 -1
  4. package/configs/lefthook.yml +4 -1
  5. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  6. package/dist/agent/ai-sdk-transport.js +4 -1
  7. package/dist/agent/index.d.ts +1 -1
  8. package/dist/agent/index.d.ts.map +1 -1
  9. package/dist/agent/run-records.d.ts +12 -3
  10. package/dist/agent/run-records.d.ts.map +1 -1
  11. package/dist/agent/run-records.js +16 -3
  12. package/dist/agent/transport.d.ts +10 -3
  13. package/dist/agent/transport.d.ts.map +1 -1
  14. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  15. package/dist/agent/use-agent-thread-runs.js +23 -7
  16. package/dist/charts/CartesianChart.d.ts.map +1 -1
  17. package/dist/charts/CartesianChart.js +6 -2
  18. package/dist/charts/DualPanel.d.ts +5 -1
  19. package/dist/charts/DualPanel.d.ts.map +1 -1
  20. package/dist/charts/DualPanel.js +46 -19
  21. package/dist/charts/Heatmap.js +2 -2
  22. package/dist/charts/Lines.js +2 -2
  23. package/dist/charts/MirroredBars.d.ts.map +1 -1
  24. package/dist/charts/MirroredBars.js +14 -11
  25. package/dist/charts/compile.d.ts +19 -2
  26. package/dist/charts/compile.d.ts.map +1 -1
  27. package/dist/charts/compile.js +192 -42
  28. package/dist/charts/marks.d.ts +8 -0
  29. package/dist/charts/marks.d.ts.map +1 -1
  30. package/dist/charts/marks.js +22 -1
  31. package/dist/charts/panel.d.ts +6 -0
  32. package/dist/charts/panel.d.ts.map +1 -1
  33. package/dist/charts/panel.js +12 -3
  34. package/dist/charts/types.d.ts +39 -1
  35. package/dist/charts/types.d.ts.map +1 -1
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/provider/index.d.ts.map +1 -1
  39. package/dist/provider/index.js +8 -2
  40. package/dist/state/fields.d.ts +14 -3
  41. package/dist/state/fields.d.ts.map +1 -1
  42. package/dist/state/fields.js +41 -9
  43. package/dist/telemetry.d.ts +58 -4
  44. package/dist/telemetry.d.ts.map +1 -1
  45. package/dist/telemetry.js +13 -5
  46. package/llms.txt +2 -2
  47. package/package.json +1 -1
  48. package/src/agent/ai-sdk-transport.ts +5 -2
  49. package/src/agent/index.ts +1 -1
  50. package/src/agent/run-records.ts +27 -3
  51. package/src/agent/transport.ts +11 -5
  52. package/src/agent/use-agent-thread-runs.ts +35 -8
  53. package/src/charts/CartesianChart.tsx +7 -2
  54. package/src/charts/DualPanel.tsx +50 -19
  55. package/src/charts/Heatmap.tsx +2 -2
  56. package/src/charts/Lines.tsx +2 -2
  57. package/src/charts/MirroredBars.tsx +13 -10
  58. package/src/charts/compile.ts +261 -46
  59. package/src/charts/marks.ts +35 -1
  60. package/src/charts/panel.tsx +18 -3
  61. package/src/charts/types.ts +37 -1
  62. package/src/index.ts +1 -1
  63. package/src/provider/index.tsx +8 -2
  64. package/src/state/fields.ts +79 -21
  65. package/src/telemetry.ts +70 -7
@@ -7,9 +7,9 @@ import { scaleUtc } from "d3-scale";
7
7
  import { useCallback, useMemo } from "react";
8
8
  import { VX, alpha } from "../tokens/index.js";
9
9
  import { resolveContainerClass } from "../tokens/size-classes.js";
10
- import { defaultFormatX, defaultNumberFormat, visibleSeries } from "./compile.js";
10
+ import { defaultFormatX, defaultNumberFormat, engineRow, seriesRow, visibleSeries } from "./compile.js";
11
11
  import { barMark, emphasis, lineMark, markersOf, thinFill } from "./marks.js";
12
- import { PanelRung, paneX, paneY, rowsOf } from "./panel.js";
12
+ import { PanelRung, chartHeightOf, paneX, paneY, rowsOf } from "./panel.js";
13
13
  import { xScaleFor } from "./scales.js";
14
14
  import { rowX } from "./types.js";
15
15
  const BAR_KEY = "__bar";
@@ -60,6 +60,8 @@ function DualPanel(props) {
60
60
  );
61
61
  const all = useMemo(() => [...lines, barSeries], [lines, barSeries]);
62
62
  const topFormat = top?.format ?? defaultNumberFormat;
63
+ const topTooltipFormat = top?.tooltipFormat ?? topFormat;
64
+ const barTooltipFormat = bar.tooltipFormat ?? bar.format;
63
65
  const plan = useCallback(
64
66
  ({ hidden, highlighted }) => {
65
67
  const model = xScaleFor(data.map(x), false);
@@ -145,12 +147,20 @@ function DualPanel(props) {
145
147
  ],
146
148
  scales: {
147
149
  x: { scale: xScale(), axis: false },
148
- y: paneY({ scale: topY, domain: topDomain, format: topFormat, ticks: TOP_TICKS, cls })
150
+ y: paneY({
151
+ scale: topY,
152
+ domain: topDomain,
153
+ format: topFormat,
154
+ ticks: TOP_TICKS,
155
+ cls,
156
+ height: chartHeightOf(h, topFraction, PANE_GAP)
157
+ })
149
158
  }
150
159
  };
151
160
  };
152
- const barPane = ({ width }) => {
161
+ const barPane = ({ width, height: h }) => {
153
162
  const cls = resolveContainerClass(width);
163
+ const whole = chartHeightOf(h, 1 - topFraction, PANE_GAP);
154
164
  const barRows = barOn ? rowsOf(data, x, barSeries) : [];
155
165
  const dim = emphasis(highlighted, { key: BAR_KEY });
156
166
  return {
@@ -164,13 +174,14 @@ function DualPanel(props) {
164
174
  })
165
175
  ],
166
176
  scales: {
167
- x: paneX({ scale: xScale(), model, format, cls }),
177
+ x: paneX({ scale: xScale(), model, format, cls, height: whole }),
168
178
  y: paneY({
169
179
  scale: barScale,
170
180
  domain: barDomain,
171
181
  format: bar.format,
172
182
  ticks: BAR_TICKS,
173
- cls
183
+ cls,
184
+ height: whole
174
185
  })
175
186
  }
176
187
  };
@@ -186,26 +197,42 @@ function DualPanel(props) {
186
197
  for (const s of visible) {
187
198
  const v2 = s.getValue(d);
188
199
  if (s.tooltip === false || v2 === null || !Number.isFinite(v2)) continue;
189
- out.push({
190
- label: s.note === void 0 ? s.label : `${s.label} \xB7 ${s.note}`,
191
- value: s.formatValue ? s.formatValue(v2, d) : topFormat(v2),
192
- color: s.color,
193
- ...highlighted === s.key && { active: true }
194
- });
200
+ out.push(
201
+ seriesRow(
202
+ s,
203
+ s.formatValue ? s.formatValue(v2, d) : topTooltipFormat(v2),
204
+ highlighted === s.key
205
+ )
206
+ );
195
207
  }
196
208
  const v = barOn ? bar.getValue(d) : null;
197
209
  if (v !== null && Number.isFinite(v))
198
- out.push({
199
- label: bar.label,
200
- value: bar.formatValue ? bar.formatValue(v, d) : bar.format(v),
201
- color: v >= 0 ? bar.positive : bar.negative,
202
- ...highlighted === BAR_KEY && { active: true }
203
- });
210
+ out.push(
211
+ engineRow({
212
+ label: bar.label,
213
+ value: bar.formatValue ? bar.formatValue(v, d) : barTooltipFormat(v),
214
+ color: v >= 0 ? bar.positive : bar.negative,
215
+ active: highlighted === BAR_KEY
216
+ })
217
+ );
204
218
  return out;
205
219
  }
206
220
  };
207
221
  },
208
- [data, x, lines, barSeries, top, fillBetween, bar, topFraction, topFormat, formatX]
222
+ [
223
+ data,
224
+ x,
225
+ lines,
226
+ barSeries,
227
+ top,
228
+ fillBetween,
229
+ bar,
230
+ topFraction,
231
+ topFormat,
232
+ topTooltipFormat,
233
+ barTooltipFormat,
234
+ formatX
235
+ ]
209
236
  );
210
237
  return /* @__PURE__ */ jsx(PanelRung, { ...rung, series: all, plan });
211
238
  }
@@ -9,7 +9,7 @@ import { scaleQuantize } from "d3-scale";
9
9
  import { useId, useMemo } from "react";
10
10
  import { alpha, VX } from "../tokens/index.js";
11
11
  import { resolveContainerClass } from "../tokens/size-classes.js";
12
- import { strideTicks } from "./compile.js";
12
+ import { engineRow, strideTicks } from "./compile.js";
13
13
  import { ChartEngine, idPrefixOf } from "./host.js";
14
14
  import { useCoarsePointer } from "../common/use-media-query.js";
15
15
  import { HEIGHT_MAX, HEIGHT_MIN } from "./box.js";
@@ -176,7 +176,7 @@ function Heatmap(props) {
176
176
  ...point.color && { color: point.color }
177
177
  }
178
178
  ];
179
- return { title: header(c), rows: [...value, ...extra] };
179
+ return { title: header(c), rows: [...value, ...extra].map(engineRow) };
180
180
  }
181
181
  } : false
182
182
  };
@@ -8,7 +8,7 @@ import { VX } from "../tokens/index.js";
8
8
  import { CartesianRung } from "./CartesianChart.js";
9
9
  import { areaGradientId } from "./compile.js";
10
10
  import { chartCurve } from "./curves.js";
11
- import { lineMark, markersOf, yScaleOf } from "./marks.js";
11
+ import { drawnRows, lineMark, markersOf, yScaleOf } from "./marks.js";
12
12
  import { rowX } from "./types.js";
13
13
  const MAX_END_LABELS = 3;
14
14
  const END_LABEL_MIN_GAP = 13;
@@ -80,7 +80,7 @@ function Lines(props) {
80
80
  if (!s.area) continue;
81
81
  out.push(
82
82
  decorative(
83
- areaY(plot.rows(s.key), {
83
+ areaY(drawnRows(plot, s), {
84
84
  id: `${s.key}-area`,
85
85
  x: rowX,
86
86
  y2: "y",
@@ -1 +1 @@
1
- {"version":3,"file":"MirroredBars.d.ts","sourceRoot":"","sources":["../../src/charts/MirroredBars.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AASzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAW,MAAM,SAAS,CAAA;AAEhF,4BAA4B;AAC5B,MAAM,MAAM,eAAe,GAAG;IAC5B;8EAC0E;IAC1E,GAAG,EAAE,MAAM,CAAA;IACX,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACjC,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,2DAA2D;IAC3D,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9B,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,IAAI,CACtC,WAAW,CAAC,CAAC,CAAC,EACd,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAChG,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CAChE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,WAAW,GACX,MAAM,GACN,GAAG,GACH,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,WAAW,GACX,OAAO,CACV,GAAG;IACF,qEAAqE;IACrE,MAAM,EAAE,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,gCAAgC;IAChC,EAAE,EAAE,eAAe,CAAA;IACnB,gCAAgC;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;IAC9C;;;OAGG;IACH,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;KAAE,GAAG,SAAS,CAAA;IAC/E,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAA;CAC7E,CAAA;AAgDD,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EACvD,KAAK,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,YAAY,CAuKd"}
1
+ {"version":3,"file":"MirroredBars.d.ts","sourceRoot":"","sources":["../../src/charts/MirroredBars.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AASzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAW,MAAM,SAAS,CAAA;AAEhF,4BAA4B;AAC5B,MAAM,MAAM,eAAe,GAAG;IAC5B;8EAC0E;IAC1E,GAAG,EAAE,MAAM,CAAA;IACX,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACjC,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,2DAA2D;IAC3D,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9B,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,IAAI,CACtC,WAAW,CAAC,CAAC,CAAC,EACd,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAChG,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CAChE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,WAAW,GACX,MAAM,GACN,GAAG,GACH,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,WAAW,GACX,OAAO,CACV,GAAG;IACF,qEAAqE;IACrE,MAAM,EAAE,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,gCAAgC;IAChC,EAAE,EAAE,eAAe,CAAA;IACnB,gCAAgC;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;IAClD,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;IAC9C;;;OAGG;IACH,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;KAAE,GAAG,SAAS,CAAA;IAC/E,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAA;CAC7E,CAAA;AAgDD,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EACvD,KAAK,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,YAAY,CA0Kd"}
@@ -6,9 +6,9 @@ import { scaleLinear } from "@tanstack/charts/scales/linear";
6
6
  import { useCallback, useMemo } from "react";
7
7
  import { VX, alpha } from "../tokens/index.js";
8
8
  import { resolveContainerClass } from "../tokens/size-classes.js";
9
- import { defaultFormatX, defaultNumberFormat, visibleSeries } from "./compile.js";
9
+ import { defaultFormatX, defaultNumberFormat, seriesRow, visibleSeries } from "./compile.js";
10
10
  import { barMark, emphasis } from "./marks.js";
11
- import { PanelRung, paneX, paneY, rowsOf } from "./panel.js";
11
+ import { PanelRung, chartHeightOf, paneX, paneY, rowsOf } from "./panel.js";
12
12
  import { xScaleFor } from "./scales.js";
13
13
  import { rowX } from "./types.js";
14
14
  const DEFAULT_UP_FRACTION = 0.35;
@@ -69,8 +69,9 @@ function MirroredBars(props) {
69
69
  const downMax = paneMax(down, downSeries, drawn, downOn);
70
70
  const bandWidth = 2 * model.halfStep * BAND_FILL;
71
71
  const bandScale = () => xScaleFor(drawn.map(x), true).scale;
72
- const pane = (side, cfg, s, on, max) => ({ width: w }) => {
72
+ const pane = (side, cfg, s, on, max) => ({ width: w, height: h }) => {
73
73
  const cls = resolveContainerClass(w);
74
+ const whole = chartHeightOf(h, side === "up" ? upFraction : 1 - upFraction, 0);
74
75
  const domain = side === "up" ? [0, max] : [max, 0];
75
76
  const rows = on ? rowsOf(drawn, x, s) : [];
76
77
  const opacity = (d) => (getBarOpacity?.(d) ?? BAR_OPACITY) * emphasis(highlighted, s);
@@ -137,13 +138,14 @@ function MirroredBars(props) {
137
138
  )
138
139
  ],
139
140
  scales: {
140
- x: side === "down" ? paneX({ scale: bandScale(), model, format, cls }) : { scale: bandScale(), axis: false },
141
+ x: side === "down" ? paneX({ scale: bandScale(), model, format, cls, height: whole }) : { scale: bandScale(), axis: false },
141
142
  y: paneY({
142
143
  scale: scaleLinear().domain(domain),
143
144
  domain,
144
145
  format: cfg.format,
145
146
  ticks: cfg.ticks ?? DEFAULT_PANE_TICKS,
146
- cls
147
+ cls,
148
+ height: whole
147
149
  }),
148
150
  zx: { channel: "x", scale: scaleLinear().domain([0, 1]), axis: false }
149
151
  }
@@ -161,12 +163,13 @@ function MirroredBars(props) {
161
163
  const v = s.getValue(d);
162
164
  if (s.tooltip === false || v === null || !Number.isFinite(v)) continue;
163
165
  const fallback = s.key === up.key ? up.format : s.key === down.key ? down.format : defaultNumberFormat;
164
- out.push({
165
- label: s.note === void 0 ? s.label : `${s.label} \xB7 ${s.note}`,
166
- value: s.formatValue ? s.formatValue(v, d) : fallback(v),
167
- color: s.color,
168
- ...highlighted === s.key && { active: true }
169
- });
166
+ out.push(
167
+ seriesRow(
168
+ s,
169
+ s.formatValue ? s.formatValue(v, d) : fallback(v),
170
+ highlighted === s.key
171
+ )
172
+ );
170
173
  }
171
174
  return out;
172
175
  }
@@ -1,7 +1,7 @@
1
- import type { ChartGradient, ChartPositionScaleOptions, ChartValue, ResponsiveChartDefinition } from '@tanstack/charts/types/core';
1
+ import type { ChartGradient, ChartPositionScaleOptions, ChartTooltipRow as EngineTooltipRow, ChartValue, ResponsiveChartDefinition } from '@tanstack/charts/types/core';
2
2
  import type { ContainerClass } from '../tokens/size-classes.js';
3
3
  import type { XScaleModel } from './scales.js';
4
- import type { AxisConfig, CartesianChartProps, ChartSeries, ChartX, ZoneSpec } from './types.js';
4
+ import type { AxisConfig, CartesianChartProps, ChartSeries, ChartTooltipRow, ChartX, RefLine, ZoneSpec } from './types.js';
5
5
  /**
6
6
  * The compiled definition. Annotated on purpose: TanStack's inferred definition type names internal
7
7
  * modules (`dist/scene`, `cursor-host-contract`) and fails declaration emit (TS2883 / TS4058).
@@ -35,6 +35,19 @@ export declare function utcHeaderDate(d: Date): string;
35
35
  */
36
36
  export declare function dateTicks(domain: readonly [Date, Date], count: number, stepMs: number): Date[];
37
37
  export declare function defaultFormatX(v: ChartValue): string;
38
+ /**
39
+ * A tooltip swatch is the engine's `background` shorthand, so a dashed one is a gradient: a 2px
40
+ * dashed line across the swatch's middle, in the row color.
41
+ */
42
+ export declare const dashedSwatch: (color: string) => string;
43
+ /** A basalt tooltip row as the engine's: the swatch shape folded into the row color. */
44
+ export declare function engineRow(row: ChartTooltipRow & {
45
+ active?: boolean | undefined;
46
+ }): EngineTooltipRow;
47
+ /** A series row of a tooltip: the label with its note, the swatch following the series' dash. */
48
+ export declare function seriesRow(s: Pick<ChartSeries<never>, 'label' | 'note' | 'color' | 'dash'>, value: string, active: boolean): EngineTooltipRow;
49
+ /** Legend-only entries for the reference lines that name themselves, in line order. */
50
+ export declare function refLegendSeries<T>(refLines: readonly RefLine[] | undefined): ChartSeries<T>[];
38
51
  /** The visible series: not toggled off, and not a companion whose parent is. */
39
52
  export declare function visibleSeries<T>(series: readonly ChartSeries<T>[], hidden: ReadonlySet<string>): ChartSeries<T>[];
40
53
  type YAxis = {
@@ -47,6 +60,8 @@ export declare function yGuide<T>(opts: {
47
60
  cls: ContainerClass;
48
61
  side: 'left' | 'right';
49
62
  grid: boolean;
63
+ /** The whole chart's height: decides inside or gutter labels. */
64
+ height: number;
50
65
  }): Omit<ChartPositionScaleOptions<number>, 'scale'>;
51
66
  /**
52
67
  * Every n-th value, n the smallest stride that leaves at most the class's x tick count (the same
@@ -59,6 +74,8 @@ export declare function xGuide<X extends ChartX>(opts: {
59
74
  xTicks: number | readonly X[] | undefined;
60
75
  format: (v: ChartValue) => string;
61
76
  cls: ContainerClass;
77
+ /** The whole chart's height: decides whether the left margin is locked to zero. */
78
+ height: number;
62
79
  }): Omit<ChartPositionScaleOptions<ChartValue>, 'scale'>;
63
80
  /** Gradient resource id for a series' area fill; the host's `idPrefix` scopes it per chart. */
64
81
  export declare const areaGradientId: (index: number) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/charts/compile.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAEV,aAAa,EAEb,yBAAyB,EAEzB,UAAU,EACV,yBAAyB,EAE1B,MAAM,6BAA6B,CAAA;AAGpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAI5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EAEnB,WAAW,EAEX,MAAM,EAEN,QAAQ,EACT,MAAM,SAAS,CAAA;AAEhB;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,yBAAyB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;AAE/F,gGAAgG;AAChG,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,IAAI,CACpD,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,MAAM,GACN,GAAG,GACH,QAAQ,GACR,OAAO,GACP,GAAG,GACH,IAAI,GACJ,QAAQ,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,UAAU,GACV,aAAa,GACb,kBAAkB,GAClB,SAAS,CACZ,GAAG;IACF,8FAA8F;IAC9F,OAAO,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC5B,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,wDAAwD;IACxD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,qFAAqF;IACrF,KAAK,CAAC,EAAE,SAAS,GAAG,YAAY,CAAA;CACjC,CAAA;AAcD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAMD,uGAAuG;AACvG,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAE3C;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAI7C;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CAK9F;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAIpD;AAKD,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,CAAC,EAC7B,MAAM,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,EACjC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,WAAW,CAAC,CAAC,CAAC,EAAE,CAIlB;AAED,KAAK,KAAK,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,CAAA;AAEjF,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE;IAC9B,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IAC9B,IAAI,EAAE,KAAK,CAAA;IACX,GAAG,EAAE,cAAc,CAAA;IACnB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;CACd,GAAG,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CA4BnD;AAeD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,cAAc,GAAG,CAAC,EAAE,CAG7E;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE;IAC7C,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IACrB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,SAAS,CAAA;IACzC,MAAM,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,CAAA;IACjC,GAAG,EAAE,cAAc,CAAA;CACpB,GAAG,IAAI,CAAC,yBAAyB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAmBvD;AAED,+FAA+F;AAC/F,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MAAgC,CAAA;AAE/E,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAYxE;AAKD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GACjC,aAAa,GAAG,IAAI,CAuBtB;AAsUD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EAClD,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAC1B,mBAAmB,CAwDrB"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/charts/compile.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,16 @@
1
1
  import { crosshair } from "@tanstack/charts/crosshair";
2
2
  import { decorative } from "@tanstack/charts/mark/decorative";
3
3
  import { rect } from "@tanstack/charts/rect";
4
- import { ruleY } from "@tanstack/charts/rule";
4
+ import { ruleX, ruleY } from "@tanstack/charts/rule";
5
5
  import { scaleLinear } from "@tanstack/charts/scales/linear";
6
6
  import { scaleUtc } from "d3-scale";
7
7
  import { tooltip as tooltipHost } from "@tanstack/charts/tooltip";
8
8
  import { portal } from "@tanstack/charts/tooltip/portal";
9
+ import { SHORT_CHART_HEIGHT } from "./box.js";
9
10
  import { fmtCompact } from "../format/number.js";
10
11
  import { VX } from "../tokens/index.js";
11
12
  import { cursorBinding, sourceOnlyFocus } from "./cursor.js";
13
+ import { cursorDots } from "./marks.js";
12
14
  import { resolveContainerClass } from "../tokens/size-classes.js";
13
15
  import { resolveYDomain, xScaleFor, xZoneFractions, yScaleFor } from "./scales.js";
14
16
  import { AXIS_LINE, CHART_THEME, CROSSHAIR, DASH, GRID_LINE } from "./theme.js";
@@ -20,6 +22,7 @@ const LADDER = {
20
22
  };
21
23
  const INSIDE_LABEL_DX = 4;
22
24
  const INSIDE_LABEL_DY = -6;
25
+ const labelsInside = (cls, height) => cls === "compact" && height > 0 && height < SHORT_CHART_HEIGHT;
23
26
  function defaultNumberFormat(v) {
24
27
  return fmtCompact(v, { digits: Math.abs(v) >= 1e3 ? 1 : 2 });
25
28
  }
@@ -47,18 +50,65 @@ function defaultFormatX(v) {
47
50
  return String(v);
48
51
  }
49
52
  const clampTo = (v, [lo, hi]) => Math.min(Math.max(v, Math.min(lo, hi)), Math.max(lo, hi));
53
+ const dashedSwatch = (color) => `repeating-linear-gradient(90deg, ${color} 0 2px, transparent 2px 4px) center / 100% 2px no-repeat`;
54
+ function engineRow(row) {
55
+ const { label, value, color, swatch, active } = row;
56
+ return {
57
+ label,
58
+ value,
59
+ ...color !== void 0 && { color: swatch === "dashed" ? dashedSwatch(color) : color },
60
+ ...active && { active }
61
+ };
62
+ }
63
+ function seriesRow(s, value, active) {
64
+ return engineRow({
65
+ label: s.note === void 0 ? s.label : `${s.label} \xB7 ${s.note}`,
66
+ value,
67
+ color: s.color,
68
+ swatch: s.dash === "dashed" ? "dashed" : "solid",
69
+ active
70
+ });
71
+ }
72
+ const REF_KEY_PREFIX = "__ref-";
73
+ const refLegendKey = (index) => `${REF_KEY_PREFIX}${index}`;
74
+ const SWATCH_SOLID = /* @__PURE__ */ new Map([
75
+ [VX.goodRef, VX.goodSolid],
76
+ [VX.badRef, VX.badSolid],
77
+ [VX.warnRef, VX.warnSolid],
78
+ // The tinted status fills are as translucent, and a consumer may pass either as a line color.
79
+ [VX.good, VX.goodSolid],
80
+ [VX.goodSoft, VX.goodSolid],
81
+ [VX.bad, VX.badSolid],
82
+ [VX.warn, VX.warnSolid]
83
+ ]);
84
+ function refLegendSeries(refLines) {
85
+ return (refLines ?? []).flatMap(
86
+ (r, i) => r.legend === void 0 ? [] : [
87
+ {
88
+ key: refLegendKey(i),
89
+ label: r.legend.label,
90
+ color: SWATCH_SOLID.get(r.color) ?? r.color,
91
+ role: "reference",
92
+ tooltip: false,
93
+ getValue: () => null,
94
+ ...r.legend.mark !== void 0 && { mark: r.legend.mark },
95
+ ...r.dashed && { dash: "dashed" }
96
+ }
97
+ ]
98
+ );
99
+ }
50
100
  function visibleSeries(series, hidden) {
51
101
  return series.filter(
52
102
  (s) => !hidden.has(s.key) && !(s.parent !== void 0 && hidden.has(s.parent))
53
103
  );
54
104
  }
55
105
  function yGuide(opts) {
56
- const { cfg, axis, cls, side, grid } = opts;
106
+ const { cfg, axis, cls, side, grid, height } = opts;
57
107
  const ladder = LADDER[cls];
58
108
  const ticks = typeof cfg?.ticks === "object" ? { values: cfg.ticks } : { count: typeof cfg?.ticks === "number" ? cfg.ticks : ladder.yTicks };
59
109
  const gridLine = grid ? { grid: GRID_LINE } : {};
60
110
  if (cls === "micro") return { ...gridLine, axis: false };
61
- const inside = cls === "compact";
111
+ const inside = labelsInside(cls, height);
62
112
  return {
63
113
  ...gridLine,
64
114
  ...side === "right" && { side: "right" },
@@ -91,8 +141,9 @@ function strideTicks(values, cls) {
91
141
  return values.filter((_, i) => i % stride === 0);
92
142
  }
93
143
  function xGuide(opts) {
94
- const { model, xTicks, format, cls } = opts;
144
+ const { model, xTicks, format, cls, height } = opts;
95
145
  const ladder = LADDER[cls];
146
+ const inside = labelsInside(cls, height);
96
147
  const ticks = xTickPolicy(model, xTicks ?? ladder.xTicks);
97
148
  return {
98
149
  axis: {
@@ -102,8 +153,8 @@ function xGuide(opts) {
102
153
  fontSize: ladder.font,
103
154
  opacity: 1,
104
155
  thin: true,
105
- // A locked-zero left margin (compact) must not clip the first label.
106
- ...cls === "compact" && {
156
+ // A locked-zero left margin (inside labels) must not clip the first label.
157
+ ...inside && {
107
158
  anchor: ({ index }) => index === 0 ? "start" : void 0
108
159
  }
109
160
  }
@@ -241,27 +292,73 @@ function resolveAxes(input, visible) {
241
292
  scaleId: (side) => side === "right" && hasY2 ? "y2" : "y"
242
293
  };
243
294
  }
295
+ const edgeSides = (edge, lower, upper) => ({
296
+ lower: edge !== void 0 && (edge.side === lower || edge.side === "both"),
297
+ upper: edge !== void 0 && (edge.side === upper || edge.side === "both")
298
+ });
299
+ const edgePaint = (edge) => ({
300
+ stroke: edge.color,
301
+ strokeOpacity: 1,
302
+ strokeWidth: edge.width ?? 1,
303
+ ...edge.dashed && { strokeDasharray: DASH }
304
+ });
305
+ const edgeRule = (position, [lo, hi], make) => position !== null && position > lo && position < hi ? [make(position)] : [];
244
306
  function underlayMarks(input, model, axes) {
245
307
  const fractionRows = [{ f0: 0, f1: 1 }];
246
- const zones = (input.zones ?? []).map((z, i) => {
308
+ const zones = (input.zones ?? []).flatMap((z, i) => {
247
309
  const domain = axes.axisOf(z.axisSide).domain;
248
- return decorative(
249
- rect(fractionRows, {
250
- id: `zone-${i}`,
251
- x1: "f0",
252
- x2: "f1",
253
- y1: () => clampTo(z.from, domain),
254
- y2: () => clampTo(z.to, domain),
255
- xScale: "zx",
256
- yScale: axes.scaleId(z.axisSide),
257
- fill: z.fill,
258
- inset: 0
259
- })
310
+ const [lo, hi] = [Math.min(...domain), Math.max(...domain)];
311
+ const rule = (value, name) => edgeRule(
312
+ value,
313
+ [lo, hi],
314
+ (at) => decorative(
315
+ ruleY([at], {
316
+ id: `zone-${i}-${name}`,
317
+ yScale: axes.scaleId(z.axisSide),
318
+ ...z.edge && edgePaint(z.edge)
319
+ })
320
+ )
260
321
  );
322
+ const sides = edgeSides(z.edge, "bottom", "top");
323
+ return [
324
+ decorative(
325
+ rect(fractionRows, {
326
+ id: `zone-${i}`,
327
+ x1: "f0",
328
+ x2: "f1",
329
+ y1: () => clampTo(z.from, domain),
330
+ y2: () => clampTo(z.to, domain),
331
+ xScale: "zx",
332
+ yScale: axes.scaleId(z.axisSide),
333
+ fill: z.fill,
334
+ inset: 0
335
+ })
336
+ ),
337
+ ...sides.lower ? rule(Math.min(z.from, z.to), "bottom") : [],
338
+ ...sides.upper ? rule(Math.max(z.from, z.to), "top") : []
339
+ ];
261
340
  });
262
341
  const xZones = (input.xZones ?? []).flatMap((z, i) => {
263
342
  const f = xZoneFractions(z, model);
264
343
  if (!f) return [];
344
+ const sides = edgeSides(z.edge, "start", "end");
345
+ const widen = z.align === "edge" ? model.halfStep : 0;
346
+ const bound = (value, sign) => {
347
+ const fraction = value === void 0 ? null : model.fraction(value);
348
+ return fraction === null ? null : fraction + sign * widen;
349
+ };
350
+ const rule = (value, sign, name) => edgeRule(
351
+ bound(value, sign),
352
+ [0, 1],
353
+ (at) => decorative(
354
+ ruleX([at], {
355
+ id: `xzone-${i}-${name}`,
356
+ x: (v) => v,
357
+ xScale: "zx",
358
+ ...z.edge && edgePaint(z.edge)
359
+ })
360
+ )
361
+ );
265
362
  return [
266
363
  decorative(
267
364
  rect([{ f0: f[0], f1: f[1] }], {
@@ -274,32 +371,47 @@ function underlayMarks(input, model, axes) {
274
371
  fill: z.fill,
275
372
  inset: 0
276
373
  })
277
- )
374
+ ),
375
+ // An omitted bound is the plot edge: nothing to draw a rule on.
376
+ ...sides.lower ? rule(z.from, -1, "start") : [],
377
+ ...sides.upper ? rule(z.to, 1, "end") : []
278
378
  ];
279
379
  });
280
380
  return [...zones, ...xZones];
281
381
  }
282
- const refLineMarks = (input, axes) => (input.refLines ?? []).map(
283
- (r, i) => ruleY([r.value], {
284
- id: `ref-${i}`,
285
- stroke: r.color,
286
- strokeOpacity: 1,
287
- strokeWidth: 1,
288
- yScale: axes.scaleId(r.axis),
289
- ...r.dashed && { strokeDasharray: DASH }
290
- })
382
+ const refLineMarks = (input, axes) => (input.refLines ?? []).flatMap(
383
+ (r, i) => (
384
+ // A line that names itself in the legend can be toggled off there.
385
+ input.hidden?.has(refLegendKey(i)) ? [] : [
386
+ ruleY([r.value], {
387
+ id: `ref-${i}`,
388
+ stroke: r.color,
389
+ strokeOpacity: 1,
390
+ strokeWidth: 1,
391
+ yScale: axes.scaleId(r.axis),
392
+ ...r.dashed && { strokeDasharray: DASH }
393
+ })
394
+ ]
395
+ )
291
396
  );
292
397
  function buildScales(opts) {
293
- const { input, model, axes, formatX, cls, zoneScale } = opts;
398
+ const { input, model, axes, formatX, cls, height, zoneScale } = opts;
294
399
  const { left, right } = axes;
295
400
  return {
296
401
  x: {
297
402
  scale: model.scale,
298
- ...xGuide({ model, xTicks: input.xTicks, format: formatX, cls })
403
+ ...xGuide({ model, xTicks: input.xTicks, format: formatX, cls, height })
299
404
  },
300
405
  y: {
301
406
  scale: yScaleFor(input.y, left.domain),
302
- ...yGuide({ cfg: input.y, axis: left, cls, side: "left", grid: input.y?.grid ?? true })
407
+ ...yGuide({
408
+ cfg: input.y,
409
+ axis: left,
410
+ cls,
411
+ height,
412
+ side: "left",
413
+ grid: input.y?.grid ?? true
414
+ })
303
415
  },
304
416
  ...right && {
305
417
  y2: {
@@ -309,6 +421,7 @@ function buildScales(opts) {
309
421
  cfg: input.y2,
310
422
  axis: right,
311
423
  cls,
424
+ height,
312
425
  side: "right",
313
426
  grid: input.y2?.grid ?? false
314
427
  })
@@ -342,6 +455,17 @@ function rowsOf(input, visibleKeys) {
342
455
  });
343
456
  };
344
457
  }
458
+ function cursorDotMarks(input, visible, axes) {
459
+ return visible.flatMap((s) => {
460
+ const { cursorValue } = s;
461
+ if (!cursorValue) return [];
462
+ const rows = input.data.flatMap((d) => {
463
+ const y = cursorValue(d);
464
+ return y === null || !Number.isFinite(y) ? [] : [{ x: input.x(d), y, series: s.key, datum: d }];
465
+ });
466
+ return [cursorDots(s, rows, axes.onRight(s) ? "y2" : void 0)];
467
+ });
468
+ }
345
469
  function tooltipSpec(opts) {
346
470
  const { input, axes, visible, keys, visibleKeys, formatX } = opts;
347
471
  const hidden = input.series.filter((s) => !visibleKeys.has(s.key));
@@ -362,19 +486,32 @@ function tooltipSpec(opts) {
362
486
  for (const s of rowSeries) {
363
487
  const v = s.getValue(d);
364
488
  if (v === null || !Number.isFinite(v)) continue;
365
- rows.push({
366
- label: s.note === void 0 ? s.label : `${s.label} \xB7 ${s.note}`,
367
- value: s.formatValue ? s.formatValue(v, d) : axes.axisOf(axes.onRight(s) ? "right" : "left").format(v),
368
- color: s.color,
369
- ...input.highlighted === s.key && { active: true }
370
- });
489
+ rows.push(
490
+ seriesRow(
491
+ s,
492
+ s.formatValue ? s.formatValue(v, d) : axes.axisOf(axes.onRight(s) ? "right" : "left").format(v),
493
+ input.highlighted === s.key
494
+ )
495
+ );
371
496
  }
372
497
  const extra = (input.tooltip || void 0)?.rows?.(d, { visible, hidden }) ?? [];
373
- return { title: tooltipHeader(input, formatX, x, d), rows: [...rows, ...extra] };
498
+ return {
499
+ title: tooltipHeader(input, formatX, x, d),
500
+ rows: [...rows, ...extra.map(engineRow)]
501
+ };
374
502
  }
375
503
  };
376
504
  }
505
+ function guardReservedKeys(series) {
506
+ if (process.env.NODE_ENV === "production") return;
507
+ const clash = series.find((s) => s.key.startsWith(REF_KEY_PREFIX));
508
+ if (clash)
509
+ throw new Error(
510
+ `[basalt] CartesianChart: the series key "${clash.key}" is reserved (\`${REF_KEY_PREFIX}\` prefix names reference-line legend entries)`
511
+ );
512
+ }
377
513
  function compileCartesian(input) {
514
+ guardReservedKeys(input.series);
378
515
  const visible = visibleSeries(input.series, input.hidden ?? /* @__PURE__ */ new Set());
379
516
  const visibleKeys = new Set(visible.map((s) => s.key));
380
517
  const keys = new Set(input.series.map((s) => s.key));
@@ -382,6 +519,7 @@ function compileCartesian(input) {
382
519
  const axes = resolveAxes(input, visible);
383
520
  const under = underlayMarks(input, model, axes);
384
521
  const over = refLineMarks(input, axes);
522
+ const dots = cursorDotMarks(input, visible, axes);
385
523
  const rows = rowsOf(input, visibleKeys);
386
524
  const formatX = (v) => input.formatX ? input.formatX(v) : defaultFormatX(v);
387
525
  const gradients = input.series.flatMap(
@@ -402,12 +540,20 @@ function compileCartesian(input) {
402
540
  y2Domain: axes.right?.domain
403
541
  });
404
542
  return {
405
- marks: [...under, ...kindMarks, ...over, crosshair({ x: CROSSHAIR, y: false })],
406
- scales: buildScales({ input, model, axes, formatX, cls, zoneScale: under.length > 0 }),
543
+ marks: [...under, ...kindMarks, ...dots, ...over, crosshair({ x: CROSSHAIR, y: false })],
544
+ scales: buildScales({
545
+ input,
546
+ model,
547
+ axes,
548
+ formatX,
549
+ cls,
550
+ height,
551
+ zoneScale: under.length > 0
552
+ }),
407
553
  theme: CHART_THEME,
408
554
  ...gradients.length > 0 && { gradients },
409
555
  clip: true,
410
- ...cls === "compact" && { margin: { left: 0, ...axes.right && { right: 0 } } }
556
+ ...labelsInside(cls, height) && { margin: { left: 0, ...axes.right && { right: 0 } } }
411
557
  };
412
558
  },
413
559
  // Followers paint the crosshair only, unless the chart opts into following tooltips.
@@ -427,9 +573,13 @@ export {
427
573
  areaGradient,
428
574
  areaGradientId,
429
575
  compileCartesian,
576
+ dashedSwatch,
430
577
  dateTicks,
431
578
  defaultFormatX,
432
579
  defaultNumberFormat,
580
+ engineRow,
581
+ refLegendSeries,
582
+ seriesRow,
433
583
  strideTicks,
434
584
  utcAxisDate,
435
585
  utcHeaderDate,