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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +38 -2
- package/agent/rules/basalt-charts.md +279 -108
- package/agent/rules/basalt-mantine.md +4 -2
- package/agent/skills/basalt-charts/SKILL.md +114 -64
- package/agent/skills/basalt-design/SKILL.md +4 -3
- package/agent/templates/CLAUDE-block.md.tpl +9 -6
- package/configs/oxlint-plugin.js +152 -0
- package/configs/oxlint-plugin.test.ts +107 -0
- package/configs/oxlint.json +2 -0
- package/dist/charts/cursor/resolve.d.ts +33 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -0
- package/dist/charts/cursor/resolve.js +61 -0
- package/dist/charts/cursor/resolve.test.d.ts +2 -0
- package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
- package/dist/charts/cursor/scope.d.ts +17 -0
- package/dist/charts/cursor/scope.d.ts.map +1 -0
- package/dist/charts/cursor/scope.js +20 -0
- package/dist/charts/cursor/store.d.ts +31 -0
- package/dist/charts/cursor/store.d.ts.map +1 -0
- package/dist/charts/cursor/store.js +25 -0
- package/dist/charts/cursor/store.test.d.ts +2 -0
- package/dist/charts/cursor/store.test.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.d.ts +39 -0
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.js +104 -0
- package/dist/charts/hooks/useChartSize.d.ts +8 -4
- package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
- package/dist/charts/index.d.ts +16 -13
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +38 -19
- package/dist/charts/kinds/Bars.d.ts +45 -54
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +235 -392
- package/dist/charts/kinds/Bars.test.d.ts +2 -0
- package/dist/charts/kinds/Bars.test.d.ts.map +1 -0
- package/dist/charts/kinds/Donut.d.ts +5 -4
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +17 -14
- package/dist/charts/kinds/DualPanel.d.ts +13 -0
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +185 -92
- package/dist/charts/kinds/DualPanel.test.d.ts +2 -0
- package/dist/charts/kinds/DualPanel.test.d.ts.map +1 -0
- package/dist/charts/kinds/Heatmap.d.ts +15 -11
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +38 -22
- package/dist/charts/kinds/MultiLine.d.ts +27 -35
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +122 -235
- package/dist/charts/kinds/MultiLine.test.d.ts +2 -0
- package/dist/charts/kinds/MultiLine.test.d.ts.map +1 -0
- package/dist/charts/kinds/StackedArea.d.ts +21 -10
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +74 -132
- package/dist/charts/kinds/ZonedLine.d.ts +24 -40
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +104 -184
- package/dist/charts/kinds/ZonedLine.test.d.ts +2 -0
- package/dist/charts/kinds/ZonedLine.test.d.ts.map +1 -0
- package/dist/charts/layout/auto-margin.d.ts +60 -0
- package/dist/charts/layout/auto-margin.d.ts.map +1 -0
- package/dist/charts/layout/auto-margin.js +37 -0
- package/dist/charts/layout/auto-margin.test.d.ts +2 -0
- package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.d.ts +161 -0
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.js +359 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartFrame.d.ts +16 -5
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +17 -2
- package/dist/charts/primitives/ChartLegend.d.ts +15 -1
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +25 -3
- package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +45 -19
- package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +22 -1
- package/dist/charts/primitives/XZoneRects.d.ts +54 -0
- package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
- package/dist/charts/primitives/XZoneRects.js +65 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
- package/dist/charts/series.d.ts +28 -3
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +5 -2
- package/dist/charts/series.test.d.ts +2 -0
- package/dist/charts/series.test.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.d.ts +25 -0
- package/dist/charts/utils/measure-text.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.js +64 -0
- package/dist/charts/utils/measure-text.test.d.ts +2 -0
- package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
- package/dist/content/fence-block.d.ts.map +1 -1
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/chart-margin.test.d.ts +2 -0
- package/dist/tokens/chart-margin.test.d.ts.map +1 -0
- package/dist/tokens/index.d.ts +29 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/palette.d.ts +4 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +3 -0
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/charts/cursor/resolve.test.ts +61 -0
- package/src/charts/cursor/resolve.ts +102 -0
- package/src/charts/cursor/scope.tsx +32 -0
- package/src/charts/cursor/store.test.ts +46 -0
- package/src/charts/cursor/store.ts +53 -0
- package/src/charts/hooks/useChartCursor.ts +163 -0
- package/src/charts/hooks/useChartSize.ts +8 -4
- package/src/charts/index.ts +35 -14
- package/src/charts/kinds/Bars.test.tsx +98 -0
- package/src/charts/kinds/Bars.tsx +350 -467
- package/src/charts/kinds/Donut.tsx +31 -23
- package/src/charts/kinds/DualPanel.test.tsx +131 -0
- package/src/charts/kinds/DualPanel.tsx +301 -135
- package/src/charts/kinds/Heatmap.tsx +72 -47
- package/src/charts/kinds/MultiLine.test.tsx +37 -0
- package/src/charts/kinds/MultiLine.tsx +171 -290
- package/src/charts/kinds/StackedArea.tsx +116 -175
- package/src/charts/kinds/ZonedLine.test.tsx +37 -0
- package/src/charts/kinds/ZonedLine.tsx +149 -255
- package/src/charts/layout/auto-margin.test.ts +42 -0
- package/src/charts/layout/auto-margin.ts +110 -0
- package/src/charts/primitives/CartesianChart.test.tsx +160 -0
- package/src/charts/primitives/CartesianChart.tsx +598 -0
- package/src/charts/primitives/ChartFrame.test.tsx +33 -0
- package/src/charts/primitives/ChartFrame.tsx +40 -4
- package/src/charts/primitives/ChartLegend.test.tsx +78 -0
- package/src/charts/primitives/ChartLegend.tsx +52 -2
- package/src/charts/primitives/ChartTooltip.tsx +67 -37
- package/src/charts/primitives/HoverOverlay.tsx +39 -3
- package/src/charts/primitives/XZoneRects.test.tsx +123 -0
- package/src/charts/primitives/XZoneRects.tsx +132 -0
- package/src/charts/series.test.ts +112 -0
- package/src/charts/series.ts +33 -5
- package/src/charts/utils/measure-text.test.ts +36 -0
- package/src/charts/utils/measure-text.ts +109 -0
- package/src/content/fence-block.tsx +2 -3
- package/src/surfaces.ts +2 -1
- package/src/theme/spacing.test.ts +2 -0
- package/src/tokens/chart-margin.test.ts +39 -0
- package/src/tokens/density.test.ts +1 -0
- package/src/tokens/index.ts +36 -0
- package/src/tokens/palette.ts +3 -0
- package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
- package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
- package/dist/charts/hooks/useChartTooltip.js +0 -26
- package/dist/charts/hooks/useHoverSync.d.ts +0 -46
- package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
- package/dist/charts/hooks/useHoverSync.js +0 -75
- package/dist/charts/hover-context.d.ts +0 -21
- package/dist/charts/hover-context.d.ts.map +0 -1
- package/dist/charts/hover-context.js +0 -12
- package/dist/charts/hover-sync.d.ts +0 -14
- package/dist/charts/hover-sync.d.ts.map +0 -1
- package/dist/charts/hover-sync.js +0 -17
- package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
- package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
- package/dist/charts/primitives/ResponsiveChart.js +0 -24
- package/src/charts/hooks/useChartTooltip.ts +0 -31
- package/src/charts/hooks/useHoverSync.ts +0 -152
- package/src/charts/hover-context.ts +0 -28
- package/src/charts/hover-sync.tsx +0 -37
- package/src/charts/primitives/ResponsiveChart.tsx +0 -64
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GridRows } from "@visx/grid";
|
|
3
|
+
import { Group } from "@visx/group";
|
|
4
|
+
import { scaleLinear, scalePoint } from "@visx/scale";
|
|
5
|
+
import { useMemo, useRef, useState } from "react";
|
|
6
|
+
import { VX } from "../../tokens/index.js";
|
|
7
|
+
import { useChartCursor } from "../hooks/useChartCursor.js";
|
|
8
|
+
import { autoMargin, probeAxisLabels } from "../layout/auto-margin.js";
|
|
9
|
+
import { deriveTooltipRows } from "../series.js";
|
|
10
|
+
import { padAutoLower } from "../utils/domain.js";
|
|
11
|
+
import { fmtAxisDate } from "../utils/format.js";
|
|
12
|
+
import { smartTicks, smartTicksEvery } from "../utils/ticks.js";
|
|
13
|
+
import { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric } from "./Axes.js";
|
|
14
|
+
import { ChartFrame, resolveLegend } from "./ChartFrame.js";
|
|
15
|
+
import { ChartTooltipFloat, TooltipBody, TooltipHeader, TooltipRow } from "./ChartTooltip.js";
|
|
16
|
+
import { Crosshair, SeriesDot } from "./Crosshair.js";
|
|
17
|
+
import { HoverOverlay } from "./HoverOverlay.js";
|
|
18
|
+
import { XZoneRects } from "./XZoneRects.js";
|
|
19
|
+
import { ZoneRects } from "./ZoneRects.js";
|
|
20
|
+
const DEFAULT_TICKS = 5;
|
|
21
|
+
const DEFAULT_AUTO_PAD = 1.1;
|
|
22
|
+
function resolveAxisDomain(cfg, data, series, extraBounds = []) {
|
|
23
|
+
const domain = cfg?.domain ?? "auto";
|
|
24
|
+
if (Array.isArray(domain)) return domain;
|
|
25
|
+
if (typeof domain === "function") return domain(data, series);
|
|
26
|
+
let min = Infinity;
|
|
27
|
+
let max = -Infinity;
|
|
28
|
+
for (const d of data) {
|
|
29
|
+
for (const s of series) {
|
|
30
|
+
const v = s.getValue(d);
|
|
31
|
+
if (v === null) continue;
|
|
32
|
+
if (v < min) min = v;
|
|
33
|
+
if (v > max) max = v;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const v of extraBounds) {
|
|
37
|
+
if (!Number.isFinite(v)) continue;
|
|
38
|
+
if (v < min) min = v;
|
|
39
|
+
if (v > max) max = v;
|
|
40
|
+
}
|
|
41
|
+
if (min === Infinity) return [0, cfg?.autoMaxFloor ?? 1];
|
|
42
|
+
const pad = cfg?.autoPad ?? DEFAULT_AUTO_PAD;
|
|
43
|
+
const ceil = cfg?.autoMinCeil ?? 0;
|
|
44
|
+
const paddedMax = max > 0 ? max * pad : max < 0 ? max / pad : 0;
|
|
45
|
+
const upper = Math.max(paddedMax, cfg?.autoMaxFloor ?? -Infinity);
|
|
46
|
+
const lower = padAutoLower(Math.min(min, ceil), pad);
|
|
47
|
+
if (lower === upper) return [Math.min(lower, 0), upper === 0 ? 1 : upper * pad];
|
|
48
|
+
return [lower, upper];
|
|
49
|
+
}
|
|
50
|
+
function CartesianChart({
|
|
51
|
+
data,
|
|
52
|
+
chartId,
|
|
53
|
+
getX,
|
|
54
|
+
series,
|
|
55
|
+
y,
|
|
56
|
+
y2,
|
|
57
|
+
xTicks,
|
|
58
|
+
formatX = fmtAxisDate,
|
|
59
|
+
zones,
|
|
60
|
+
xZones,
|
|
61
|
+
refLines,
|
|
62
|
+
height,
|
|
63
|
+
aspectRatio,
|
|
64
|
+
fill,
|
|
65
|
+
legend,
|
|
66
|
+
tooltip,
|
|
67
|
+
margin: marginOverride,
|
|
68
|
+
cursorValue,
|
|
69
|
+
ariaLabel,
|
|
70
|
+
isPending,
|
|
71
|
+
children
|
|
72
|
+
}) {
|
|
73
|
+
const [highlighted, setHighlighted] = useState(null);
|
|
74
|
+
return /* @__PURE__ */ jsx(
|
|
75
|
+
ChartFrame,
|
|
76
|
+
{
|
|
77
|
+
series,
|
|
78
|
+
chartId,
|
|
79
|
+
...height !== void 0 && { height },
|
|
80
|
+
...aspectRatio !== void 0 && { aspectRatio },
|
|
81
|
+
...fill !== void 0 && { fill },
|
|
82
|
+
...ariaLabel !== void 0 && { ariaLabel },
|
|
83
|
+
...isPending !== void 0 && { isPending },
|
|
84
|
+
legend: resolveLegend(legend, { highlighted, onHighlight: setHighlighted }),
|
|
85
|
+
children: (plot) => /* @__PURE__ */ jsx(
|
|
86
|
+
CartesianPlot,
|
|
87
|
+
{
|
|
88
|
+
data,
|
|
89
|
+
chartId,
|
|
90
|
+
getX,
|
|
91
|
+
series,
|
|
92
|
+
...y !== void 0 && { y },
|
|
93
|
+
...y2 !== void 0 && { y2 },
|
|
94
|
+
...xTicks !== void 0 && { xTicks },
|
|
95
|
+
formatX,
|
|
96
|
+
...zones !== void 0 && { zones },
|
|
97
|
+
...xZones !== void 0 && { xZones },
|
|
98
|
+
...refLines !== void 0 && { refLines },
|
|
99
|
+
...tooltip !== void 0 && { tooltip },
|
|
100
|
+
...marginOverride !== void 0 && { marginOverride },
|
|
101
|
+
...cursorValue !== void 0 && { cursorValue },
|
|
102
|
+
...ariaLabel !== void 0 && { ariaLabel },
|
|
103
|
+
plot,
|
|
104
|
+
highlighted,
|
|
105
|
+
children
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function CartesianPlot({
|
|
112
|
+
data,
|
|
113
|
+
chartId,
|
|
114
|
+
getX,
|
|
115
|
+
series,
|
|
116
|
+
y,
|
|
117
|
+
y2,
|
|
118
|
+
xTicks,
|
|
119
|
+
formatX = fmtAxisDate,
|
|
120
|
+
zones,
|
|
121
|
+
xZones,
|
|
122
|
+
refLines,
|
|
123
|
+
tooltip,
|
|
124
|
+
marginOverride,
|
|
125
|
+
cursorValue,
|
|
126
|
+
ariaLabel,
|
|
127
|
+
plot,
|
|
128
|
+
highlighted,
|
|
129
|
+
children
|
|
130
|
+
}) {
|
|
131
|
+
const { hidden } = plot;
|
|
132
|
+
const visible = useMemo(() => series.filter((s) => !hidden.has(s.key)), [series, hidden]);
|
|
133
|
+
const leftSeries = useMemo(() => visible.filter((s) => s.axis !== "right"), [visible]);
|
|
134
|
+
const rightSeries = useMemo(() => visible.filter((s) => s.axis === "right"), [visible]);
|
|
135
|
+
const keys = useMemo(() => data.map(getX), [data, getX]);
|
|
136
|
+
const [leftBounds, rightBounds] = useMemo(() => {
|
|
137
|
+
const left = [];
|
|
138
|
+
const right = [];
|
|
139
|
+
for (const z of zones ?? []) (z.axisSide === "right" ? right : left).push(z.from, z.to);
|
|
140
|
+
for (const l of refLines ?? []) (l.axis === "right" ? right : left).push(l.value);
|
|
141
|
+
return [left, right];
|
|
142
|
+
}, [zones, refLines]);
|
|
143
|
+
const leftDomain = useMemo(
|
|
144
|
+
() => resolveAxisDomain(y, data, leftSeries, leftBounds),
|
|
145
|
+
[y, data, leftSeries, leftBounds]
|
|
146
|
+
);
|
|
147
|
+
const rightDomain = useMemo(
|
|
148
|
+
() => y2 === void 0 ? null : resolveAxisDomain(y2, data, rightSeries, rightBounds),
|
|
149
|
+
[y2, data, rightSeries, rightBounds]
|
|
150
|
+
);
|
|
151
|
+
const leftTicks = y?.ticks ?? DEFAULT_TICKS;
|
|
152
|
+
const rightTicks = y2?.ticks ?? DEFAULT_TICKS;
|
|
153
|
+
const { labels: leftLabels, format: leftFormat } = useMemo(
|
|
154
|
+
() => probeAxisLabels({
|
|
155
|
+
domain: leftDomain,
|
|
156
|
+
ticks: leftTicks,
|
|
157
|
+
nice: y?.nice ?? false,
|
|
158
|
+
...y?.format !== void 0 && { format: y.format }
|
|
159
|
+
}),
|
|
160
|
+
[leftDomain, leftTicks, y?.format, y?.nice]
|
|
161
|
+
);
|
|
162
|
+
const { labels: rightLabels, format: rightFormat } = useMemo(
|
|
163
|
+
() => rightDomain === null ? { labels: [], format: (v) => String(v) } : probeAxisLabels({
|
|
164
|
+
domain: rightDomain,
|
|
165
|
+
ticks: rightTicks,
|
|
166
|
+
nice: y2?.nice ?? false,
|
|
167
|
+
...y2?.format !== void 0 && { format: y2.format }
|
|
168
|
+
}),
|
|
169
|
+
[rightDomain, rightTicks, y2?.format, y2?.nice]
|
|
170
|
+
);
|
|
171
|
+
const xLabels = useMemo(() => keys.map(formatX), [keys, formatX]);
|
|
172
|
+
const margin = useMemo(
|
|
173
|
+
() => autoMargin({
|
|
174
|
+
left: leftLabels,
|
|
175
|
+
right: rightLabels,
|
|
176
|
+
bottom: xLabels,
|
|
177
|
+
...marginOverride !== void 0 && { override: marginOverride }
|
|
178
|
+
}),
|
|
179
|
+
[leftLabels, rightLabels, xLabels, marginOverride]
|
|
180
|
+
);
|
|
181
|
+
const xMax = Math.max(plot.width - margin.left - margin.right, 0);
|
|
182
|
+
const yMax = Math.max(plot.height - margin.top - margin.bottom, 0);
|
|
183
|
+
const xScale = useMemo(
|
|
184
|
+
() => scalePoint({ domain: keys, range: [0, xMax], padding: 0.5 }),
|
|
185
|
+
[keys, xMax]
|
|
186
|
+
);
|
|
187
|
+
const yScale = useMemo(
|
|
188
|
+
() => scaleLinear({
|
|
189
|
+
domain: leftDomain,
|
|
190
|
+
range: [yMax, 0],
|
|
191
|
+
...y?.nice === true && { nice: true }
|
|
192
|
+
}),
|
|
193
|
+
[leftDomain, yMax, y?.nice]
|
|
194
|
+
);
|
|
195
|
+
const y2Scale = useMemo(
|
|
196
|
+
() => rightDomain === null ? null : scaleLinear({
|
|
197
|
+
domain: rightDomain,
|
|
198
|
+
range: [yMax, 0],
|
|
199
|
+
...y2?.nice === true && { nice: true }
|
|
200
|
+
}),
|
|
201
|
+
[rightDomain, yMax, y2?.nice]
|
|
202
|
+
);
|
|
203
|
+
const tickValues = useMemo(
|
|
204
|
+
() => xTicks === void 0 ? smartTicks(keys, xMax) : smartTicksEvery(keys, xTicks),
|
|
205
|
+
[keys, xMax, xTicks]
|
|
206
|
+
);
|
|
207
|
+
const cursor = useChartCursor({
|
|
208
|
+
data,
|
|
209
|
+
chartId,
|
|
210
|
+
getKey: getX,
|
|
211
|
+
xScale,
|
|
212
|
+
marginLeft: margin.left
|
|
213
|
+
});
|
|
214
|
+
const svgRef = useRef(null);
|
|
215
|
+
const tooltipEnabled = tooltip !== false;
|
|
216
|
+
const tooltipCfg = tooltip === false ? void 0 : tooltip;
|
|
217
|
+
const point = cursor.point;
|
|
218
|
+
const cursorX = point === null ? null : xScale(getX(point)) ?? null;
|
|
219
|
+
const badge = tooltipCfg?.label === void 0 || point === null ? null : tooltipCfg.label(point);
|
|
220
|
+
const tooltipSeries = useMemo(
|
|
221
|
+
() => visible.map(
|
|
222
|
+
(s) => s.formatValue === void 0 ? { ...s, formatValue: s.axis === "right" ? rightFormat : leftFormat } : s
|
|
223
|
+
),
|
|
224
|
+
[visible, leftFormat, rightFormat]
|
|
225
|
+
);
|
|
226
|
+
const rows = tooltipEnabled && point !== null ? deriveTooltipRows(tooltipSeries, point, leftFormat) : [];
|
|
227
|
+
const anchorX = tooltipCfg?.follow === false && point !== null && cursorX !== null ? cursorX : null;
|
|
228
|
+
const svgRect = anchorX === null ? void 0 : svgRef.current?.getBoundingClientRect();
|
|
229
|
+
const tooltipAnchor = svgRect === void 0 || anchorX === null ? cursor.anchor : { x: svgRect.left + margin.left + anchorX, y: svgRect.top + margin.top };
|
|
230
|
+
const ctx = {
|
|
231
|
+
data,
|
|
232
|
+
visible,
|
|
233
|
+
hidden,
|
|
234
|
+
xScale,
|
|
235
|
+
yScale,
|
|
236
|
+
y2Scale,
|
|
237
|
+
xMax,
|
|
238
|
+
yMax,
|
|
239
|
+
margin,
|
|
240
|
+
cursorPoint: point,
|
|
241
|
+
highlighted
|
|
242
|
+
};
|
|
243
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx("svg", { ref: svgRef, width: plot.width, height: plot.height, children: /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
|
|
245
|
+
(y?.grid ?? true) && /* @__PURE__ */ jsx(GridRows, { scale: yScale, width: xMax, stroke: VX.grid, numTicks: leftTicks }),
|
|
246
|
+
xZones !== void 0 && /* @__PURE__ */ jsx(XZoneRects, { zones: xZones, height: yMax, xScale }),
|
|
247
|
+
zones !== void 0 && /* @__PURE__ */ jsx(ZoneRects, { zones, width: xMax, leftScale: yScale, rightScale: y2Scale }),
|
|
248
|
+
children(ctx),
|
|
249
|
+
refLines?.map((line) => {
|
|
250
|
+
const scale = line.axis === "right" && y2Scale !== null ? y2Scale : yScale;
|
|
251
|
+
return /* @__PURE__ */ jsx(
|
|
252
|
+
"line",
|
|
253
|
+
{
|
|
254
|
+
x1: 0,
|
|
255
|
+
x2: xMax,
|
|
256
|
+
y1: scale(line.value),
|
|
257
|
+
y2: scale(line.value),
|
|
258
|
+
stroke: line.color,
|
|
259
|
+
strokeWidth: 1,
|
|
260
|
+
strokeDasharray: line.dashed === true ? VX.dashArray : void 0
|
|
261
|
+
},
|
|
262
|
+
`${line.axis ?? "left"}-${line.value}`
|
|
263
|
+
);
|
|
264
|
+
}),
|
|
265
|
+
tooltipEnabled && point !== null && cursorX !== null && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx(Crosshair, { x: cursorX, top: 0, bottom: yMax }),
|
|
267
|
+
visible.map((s) => {
|
|
268
|
+
if (s.mark === "bar") return null;
|
|
269
|
+
const value = cursorValue === void 0 ? s.getValue(point) : cursorValue(point, s, visible);
|
|
270
|
+
if (value === null) return null;
|
|
271
|
+
const scale = s.axis === "right" && y2Scale !== null ? y2Scale : yScale;
|
|
272
|
+
const marker = s.getMarker?.(point);
|
|
273
|
+
return /* @__PURE__ */ jsx(
|
|
274
|
+
SeriesDot,
|
|
275
|
+
{
|
|
276
|
+
cx: cursorX,
|
|
277
|
+
cy: scale(value),
|
|
278
|
+
color: marker?.color ?? s.color,
|
|
279
|
+
...marker?.r !== void 0 && { r: marker.r }
|
|
280
|
+
},
|
|
281
|
+
s.key
|
|
282
|
+
);
|
|
283
|
+
})
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ jsx(
|
|
286
|
+
AxisLeftNumeric,
|
|
287
|
+
{
|
|
288
|
+
scale: yScale,
|
|
289
|
+
numTicks: leftTicks,
|
|
290
|
+
tickFormat: (v) => leftFormat(Number(v))
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
y2Scale !== null && /* @__PURE__ */ jsx(
|
|
294
|
+
AxisRightNumeric,
|
|
295
|
+
{
|
|
296
|
+
scale: y2Scale,
|
|
297
|
+
left: xMax,
|
|
298
|
+
numTicks: rightTicks,
|
|
299
|
+
tickFormat: (v) => rightFormat(Number(v))
|
|
300
|
+
}
|
|
301
|
+
),
|
|
302
|
+
/* @__PURE__ */ jsx(
|
|
303
|
+
AxisBottomDate,
|
|
304
|
+
{
|
|
305
|
+
top: yMax,
|
|
306
|
+
scale: xScale,
|
|
307
|
+
tickValues,
|
|
308
|
+
tickFormat: (v) => formatX(String(v))
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsx(
|
|
312
|
+
HoverOverlay,
|
|
313
|
+
{
|
|
314
|
+
width: xMax,
|
|
315
|
+
height: yMax,
|
|
316
|
+
onMove: cursor.onPointerMove,
|
|
317
|
+
onLeave: cursor.onPointerLeave,
|
|
318
|
+
onKeyDown: cursor.onKeyDown,
|
|
319
|
+
onBlur: cursor.onBlur,
|
|
320
|
+
valueMax: Math.max(data.length - 1, 0),
|
|
321
|
+
...point !== null && {
|
|
322
|
+
valueNow: data.indexOf(point),
|
|
323
|
+
valueText: formatX(getX(point))
|
|
324
|
+
},
|
|
325
|
+
...ariaLabel !== void 0 && { ariaLabel }
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
] }) }),
|
|
329
|
+
tooltipEnabled && cursor.isSource && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tooltipAnchor, children: [
|
|
330
|
+
/* @__PURE__ */ jsx(
|
|
331
|
+
TooltipHeader,
|
|
332
|
+
{
|
|
333
|
+
date: getX(point),
|
|
334
|
+
...badge !== null && { label: badge.text, labelColor: badge.color }
|
|
335
|
+
}
|
|
336
|
+
),
|
|
337
|
+
/* @__PURE__ */ jsxs(TooltipBody, { children: [
|
|
338
|
+
tooltipCfg?.prependRows?.(point, { visible, hidden }),
|
|
339
|
+
rows.map((row) => /* @__PURE__ */ jsx(
|
|
340
|
+
TooltipRow,
|
|
341
|
+
{
|
|
342
|
+
color: row.color,
|
|
343
|
+
label: row.label,
|
|
344
|
+
value: row.value,
|
|
345
|
+
shape: row.shape,
|
|
346
|
+
dashed: row.dashed,
|
|
347
|
+
...row.strokeWidth !== void 0 && { strokeWidth: row.strokeWidth }
|
|
348
|
+
},
|
|
349
|
+
row.key
|
|
350
|
+
)),
|
|
351
|
+
tooltipCfg?.extraRows?.(point, { visible, hidden })
|
|
352
|
+
] })
|
|
353
|
+
] })
|
|
354
|
+
] });
|
|
355
|
+
}
|
|
356
|
+
export {
|
|
357
|
+
CartesianChart,
|
|
358
|
+
resolveAxisDomain
|
|
359
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CartesianChart.test.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/CartesianChart.test.tsx"],"names":[],"mappings":""}
|
|
@@ -12,6 +12,13 @@ export type ChartFrameLegend = {
|
|
|
12
12
|
/** Hover-dim wiring lifted from the kind — optional. */
|
|
13
13
|
highlighted?: string | null;
|
|
14
14
|
onHighlight?: (key: string | null) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Clicking a legend entry hides that series. Defaults to true whenever there is more than one
|
|
17
|
+
* entry to toggle between — hiding the only series a chart draws is never useful. The hidden
|
|
18
|
+
* key set reaches the marks through the `children` render prop, so a hidden series leaves the
|
|
19
|
+
* plot, the tooltip, and the auto domain together (`docs/CHARTS-SPEC.md` §5).
|
|
20
|
+
*/
|
|
21
|
+
toggle?: boolean;
|
|
15
22
|
};
|
|
16
23
|
export type ChartFrameProps = {
|
|
17
24
|
/** Series identity — drives the derived legend. Pass the SAME array the kind draws + tooltips from. */
|
|
@@ -41,11 +48,15 @@ export type ChartFrameProps = {
|
|
|
41
48
|
* kind composing `ChartFrame` should accept and forward this from its own props.
|
|
42
49
|
*/
|
|
43
50
|
ariaLabel?: string;
|
|
44
|
-
/** Draw the SVG marks given the plot rect that already excludes the legend band
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
/** Draw the SVG marks given the plot rect that already excludes the legend band, plus the set
|
|
52
|
+
* of series keys the legend has toggled off. */
|
|
53
|
+
children: (plot: PlotRect) => ReactNode;
|
|
54
|
+
};
|
|
55
|
+
/** What `ChartFrame` hands its child: the resolved plot rect plus legend-toggle state. */
|
|
56
|
+
export type PlotRect = {
|
|
57
|
+
width: number;
|
|
58
|
+
height: number;
|
|
59
|
+
hidden: ReadonlySet<string>;
|
|
49
60
|
};
|
|
50
61
|
/** A kind's own hover-dim wiring, merged into the resolved `ChartFrameLegend`. Kinds without
|
|
51
62
|
* per-series highlight state (single-series `ZonedLine`, `DualPanel`) omit this. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAIrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAOhF;0EAC0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wBAAwB;IACxB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,uGAAuG;IACvG,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IAC9B,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IACjC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;oDACgD;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,SAAS,CAAA;CACxC,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC5B,CAAA;AAcD;oFACoF;AACpF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,iBAAiB,GAAG,KAAK,GAAG,SAAS,EAC7C,KAAK,CAAC,EAAE,qBAAqB,GAC5B,gBAAgB,GAAG,KAAK,CAS1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,WAAW,EACX,IAAY,EACZ,QAA4B,EAC5B,OAAO,EACP,MAAW,EACX,SAAS,EACT,SAAiB,EACjB,QAAQ,GACT,EAAE,eAAe,GAAG,SAAS,CAsE7B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
2
3
|
import { useChartSize } from "../hooks/useChartSize.js";
|
|
3
4
|
import { deriveLegend } from "../series.js";
|
|
4
5
|
import { ChartLegend } from "./ChartLegend.js";
|
|
@@ -21,6 +22,7 @@ function resolveLegend(config, hover) {
|
|
|
21
22
|
placement: config?.placement ?? "bottom",
|
|
22
23
|
...config?.groups !== void 0 && { groups: config.groups },
|
|
23
24
|
...config?.maxRows !== void 0 && { maxRows: config.maxRows },
|
|
25
|
+
...config?.toggle !== void 0 && { toggle: config.toggle },
|
|
24
26
|
...hover !== void 0 && { highlighted: hover.highlighted, onHighlight: hover.onHighlight }
|
|
25
27
|
};
|
|
26
28
|
}
|
|
@@ -38,6 +40,15 @@ function ChartFrame({
|
|
|
38
40
|
}) {
|
|
39
41
|
const { ref: containerRef, width: containerW, height: containerH } = useChartSize();
|
|
40
42
|
const { ref: legendRef, width: legendW, height: legendH } = useChartSize();
|
|
43
|
+
const [hidden, setHidden] = useState(() => /* @__PURE__ */ new Set());
|
|
44
|
+
const toggleKey = useCallback((key) => {
|
|
45
|
+
setHidden((prev) => {
|
|
46
|
+
const next = new Set(prev);
|
|
47
|
+
if (next.has(key)) next.delete(key);
|
|
48
|
+
else next.add(key);
|
|
49
|
+
return next;
|
|
50
|
+
});
|
|
51
|
+
}, []);
|
|
41
52
|
const placement = legend === false ? "bottom" : legend.placement ?? "bottom";
|
|
42
53
|
const vertical = placement === "left" || placement === "right";
|
|
43
54
|
const legendVisible = legend !== false && !isPending;
|
|
@@ -48,11 +59,15 @@ function ChartFrame({
|
|
|
48
59
|
width: Math.max(containerW - sideLegendWidth, minWidth),
|
|
49
60
|
height: resolvedHeight - topBottomLegendHeight
|
|
50
61
|
};
|
|
62
|
+
const legendItems = legend === false ? [] : deriveLegend(series);
|
|
63
|
+
const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1);
|
|
51
64
|
const legendNode = legend === false || isPending ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
|
|
52
65
|
ChartLegend,
|
|
53
66
|
{
|
|
54
|
-
items:
|
|
67
|
+
items: legendItems,
|
|
55
68
|
placement,
|
|
69
|
+
hidden,
|
|
70
|
+
...togglable && { onToggle: toggleKey },
|
|
56
71
|
...chartId !== void 0 && { chartId },
|
|
57
72
|
...legend.groups !== void 0 && { groups: legend.groups },
|
|
58
73
|
...legend.maxRows !== void 0 && { maxRows: legend.maxRows },
|
|
@@ -69,7 +84,7 @@ function ChartFrame({
|
|
|
69
84
|
...isPending && { "aria-busy": "true" },
|
|
70
85
|
children: [
|
|
71
86
|
legendNode !== null && (placement === "top" || placement === "left") && legendNode,
|
|
72
|
-
plot.width > 0 && plot.height > 0 && (isPending ? /* @__PURE__ */ jsx(ChartPending, { width: plot.width, height: plot.height }) : children(plot)),
|
|
87
|
+
plot.width > 0 && plot.height > 0 && (isPending ? /* @__PURE__ */ jsx(ChartPending, { width: plot.width, height: plot.height }) : children({ ...plot, hidden })),
|
|
73
88
|
legendNode !== null && (placement === "bottom" || placement === "right") && legendNode
|
|
74
89
|
]
|
|
75
90
|
}
|
|
@@ -10,11 +10,17 @@ export type LegendEntry = {
|
|
|
10
10
|
dashed?: boolean;
|
|
11
11
|
/** bar swatch opacity — honored instead of a hardcoded value, so it cannot lie about the fill. */
|
|
12
12
|
fillOpacity?: number;
|
|
13
|
+
/** line/splitLine swatch stroke opacity — parity with `fillOpacity` above, so a dimmed
|
|
14
|
+
* companion line's swatch cannot lie about its own opacity either. */
|
|
15
|
+
strokeOpacity?: number;
|
|
13
16
|
/** Drives `groups` rendering (series → hairline divider → overlay → reference). */
|
|
14
17
|
role?: SeriesRole;
|
|
15
18
|
/** Companions folded under this entry via `parent` (`deriveLegend`) — rendered as compact,
|
|
16
19
|
* subordinate sub-entries beside the parent's label instead of vanishing. */
|
|
17
20
|
children?: LegendEntry[];
|
|
21
|
+
/** Short qualifier rendered after the label in muted text — e.g. a flat-at-zero series that is
|
|
22
|
+
* invisible in the plot. */
|
|
23
|
+
note?: string;
|
|
18
24
|
};
|
|
19
25
|
/**
|
|
20
26
|
* Shared legend for all non-sparkline charts. `highlighted`/`onHighlight` are optional — omit for
|
|
@@ -23,11 +29,19 @@ export type LegendEntry = {
|
|
|
23
29
|
* reference bands instead of one flat smear. `maxRows` caps the number of rendered entries and
|
|
24
30
|
* rolls the remainder into a `+N more` chip (row-accurate wrap measurement is `ChartFrame`'s job —
|
|
25
31
|
* this is an entry-count cap, a deliberate simplification until that primitive lands).
|
|
32
|
+
*
|
|
33
|
+
* `onToggle` makes an entry a real toggle: clicking it hides the series in the plot, the tooltip,
|
|
34
|
+
* and the auto y-domain together (`ChartFrame` owns the `hidden` set — see `docs/CHARTS-SPEC.md`
|
|
35
|
+
* §5). Omit it for a static legend; a hidden entry is announced via `aria-pressed`.
|
|
26
36
|
*/
|
|
27
|
-
export declare function ChartLegend({ items, highlighted, onHighlight, chartId, placement, groups, maxRows, }: {
|
|
37
|
+
export declare function ChartLegend({ items, highlighted, onHighlight, hidden, onToggle, chartId, placement, groups, maxRows, }: {
|
|
28
38
|
items: LegendEntry[];
|
|
29
39
|
highlighted?: string | null;
|
|
30
40
|
onHighlight?: (key: string | null) => void;
|
|
41
|
+
/** Keys currently toggled off. */
|
|
42
|
+
hidden?: ReadonlySet<string>;
|
|
43
|
+
/** Present = entries are clickable toggles. */
|
|
44
|
+
onToggle?: (key: string) => void;
|
|
31
45
|
chartId?: string;
|
|
32
46
|
placement?: LegendPlacement;
|
|
33
47
|
groups?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartLegend.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartLegend.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE5D,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,WAAW,CAAA;IAC9C,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;iFAC6E;IAC7E,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"ChartLegend.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartLegend.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE5D,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,WAAW,CAAA;IAC9C,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;0EACsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mFAAmF;IACnF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;iFAC6E;IAC7E,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;IACxB;gCAC4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAgOD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,WAAkB,EAClB,WAAW,EACX,MAAkB,EAClB,QAAQ,EACR,OAAY,EACZ,SAAoB,EACpB,MAAc,EACd,OAAO,GACR,EAAE;IACD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1C,kCAAkC;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC5B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,+BAmEA"}
|
|
@@ -19,6 +19,7 @@ const LEGEND_ITEM_BUTTON = {
|
|
|
19
19
|
textAlign: "inherit"
|
|
20
20
|
};
|
|
21
21
|
const GROUP_ORDER = ["series", "overlay", "reference"];
|
|
22
|
+
const NO_HIDDEN = /* @__PURE__ */ new Set();
|
|
22
23
|
const roleOf = (item) => item.role ?? "series";
|
|
23
24
|
function orderEntries(items, groups) {
|
|
24
25
|
if (!groups) return { entries: items, dividerAfter: /* @__PURE__ */ new Set() };
|
|
@@ -69,6 +70,7 @@ function LegendSwatch({ item, idPrefix }) {
|
|
|
69
70
|
y2: 7,
|
|
70
71
|
stroke: item.color,
|
|
71
72
|
strokeWidth: item.strokeWidth ?? 2.5,
|
|
73
|
+
strokeOpacity: item.strokeOpacity ?? 1,
|
|
72
74
|
strokeDasharray: item.dashed ? VX.dashArray : void 0
|
|
73
75
|
}
|
|
74
76
|
),
|
|
@@ -81,6 +83,7 @@ function LegendSwatch({ item, idPrefix }) {
|
|
|
81
83
|
y2: 7,
|
|
82
84
|
stroke: item.secondColor,
|
|
83
85
|
strokeWidth: item.strokeWidth ?? 2.5,
|
|
86
|
+
strokeOpacity: item.strokeOpacity ?? 1,
|
|
84
87
|
strokeDasharray: item.dashed ? VX.dashArray : void 0
|
|
85
88
|
}
|
|
86
89
|
)
|
|
@@ -123,11 +126,15 @@ function LegendSwatch({ item, idPrefix }) {
|
|
|
123
126
|
y2: 3,
|
|
124
127
|
stroke: item.color,
|
|
125
128
|
strokeWidth: item.strokeWidth ?? 3,
|
|
129
|
+
strokeOpacity: item.strokeOpacity ?? 1,
|
|
126
130
|
strokeLinecap: "round",
|
|
127
131
|
strokeDasharray: item.dashed ? VX.dashArray : void 0
|
|
128
132
|
}
|
|
129
133
|
) });
|
|
130
134
|
}
|
|
135
|
+
const LEGEND_NOTE_STYLE = {
|
|
136
|
+
opacity: 0.75
|
|
137
|
+
};
|
|
131
138
|
const LEGEND_CHILD_STYLE = {
|
|
132
139
|
display: "flex",
|
|
133
140
|
alignItems: "center",
|
|
@@ -162,6 +169,7 @@ function LegendChildSwatch({ item }) {
|
|
|
162
169
|
y2: 5,
|
|
163
170
|
stroke: item.color,
|
|
164
171
|
strokeWidth: item.strokeWidth ?? 2,
|
|
172
|
+
strokeOpacity: item.strokeOpacity ?? 1,
|
|
165
173
|
strokeDasharray: item.dashed ? VX.dashArray : void 0
|
|
166
174
|
}
|
|
167
175
|
) });
|
|
@@ -169,13 +177,16 @@ function LegendChildSwatch({ item }) {
|
|
|
169
177
|
function LegendChild({ item }) {
|
|
170
178
|
return /* @__PURE__ */ jsxs("span", { style: LEGEND_CHILD_STYLE, children: [
|
|
171
179
|
/* @__PURE__ */ jsx(LegendChildSwatch, { item }),
|
|
172
|
-
/* @__PURE__ */ jsx("span", { children: item.label })
|
|
180
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
181
|
+
item.note ? /* @__PURE__ */ jsx("span", { children: item.note }) : null
|
|
173
182
|
] });
|
|
174
183
|
}
|
|
175
184
|
function ChartLegend({
|
|
176
185
|
items,
|
|
177
186
|
highlighted = null,
|
|
178
187
|
onHighlight,
|
|
188
|
+
hidden = NO_HIDDEN,
|
|
189
|
+
onToggle,
|
|
179
190
|
chartId = "",
|
|
180
191
|
placement = "bottom",
|
|
181
192
|
groups = false,
|
|
@@ -186,6 +197,10 @@ function ChartLegend({
|
|
|
186
197
|
if (key !== void 0) onHighlight?.(key);
|
|
187
198
|
};
|
|
188
199
|
const handleLeave = () => onHighlight?.(null);
|
|
200
|
+
const handleToggle = (e) => {
|
|
201
|
+
const key = e.currentTarget.dataset["legendKey"];
|
|
202
|
+
if (key !== void 0) onToggle?.(key);
|
|
203
|
+
};
|
|
189
204
|
const idPrefix = chartId ? `${chartId}-` : "";
|
|
190
205
|
const vertical = placement === "left" || placement === "right";
|
|
191
206
|
const { entries, dividerAfter } = orderEntries(items, groups);
|
|
@@ -199,11 +214,17 @@ function ChartLegend({
|
|
|
199
214
|
{
|
|
200
215
|
type: "button",
|
|
201
216
|
"data-legend-key": item.key,
|
|
202
|
-
"aria-label": item.label,
|
|
217
|
+
"aria-label": item.note ? `${item.label} \u2014 ${item.note}` : item.label,
|
|
218
|
+
...onToggle !== void 0 && { "aria-pressed": !hidden.has(item.key) },
|
|
203
219
|
style: {
|
|
204
220
|
...LEGEND_ITEM_BUTTON,
|
|
205
|
-
|
|
221
|
+
cursor: onToggle === void 0 ? "default" : "pointer",
|
|
222
|
+
// Toggled-off wins over hover-dimming: a hidden series must read as hidden even while
|
|
223
|
+
// it is the one being hovered.
|
|
224
|
+
opacity: hidden.has(item.key) ? 0.35 : highlighted === null || highlighted === item.key ? 1 : 0.3,
|
|
225
|
+
textDecoration: hidden.has(item.key) ? "line-through" : "none"
|
|
206
226
|
},
|
|
227
|
+
...onToggle !== void 0 && { onClick: handleToggle },
|
|
207
228
|
onMouseEnter: handleEnter,
|
|
208
229
|
onMouseLeave: handleLeave,
|
|
209
230
|
onFocus: handleEnter,
|
|
@@ -211,6 +232,7 @@ function ChartLegend({
|
|
|
211
232
|
children: [
|
|
212
233
|
/* @__PURE__ */ jsx(LegendSwatch, { item, idPrefix }),
|
|
213
234
|
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
235
|
+
item.note ? /* @__PURE__ */ jsx("span", { style: LEGEND_NOTE_STYLE, children: item.note }) : null,
|
|
214
236
|
item.children?.map((child) => /* @__PURE__ */ jsx(LegendChild, { item: child }, child.key))
|
|
215
237
|
]
|
|
216
238
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartLegend.test.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartLegend.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/** Theme-aware tooltip container styles — use with useChartTooltip(). */
|
|
3
|
-
export declare function useTooltipStyles(): CSSProperties;
|
|
4
|
-
/**
|
|
5
|
-
* Outer tooltip shell. Renders nothing when tip is null. Renders to `document.body` via a
|
|
6
|
-
* portal, so it may be authored anywhere in the tree, including inside `<svg>`.
|
|
7
|
-
*/
|
|
8
|
-
export declare function ChartTooltip({ tip, tooltipRef, styles, children, }: {
|
|
9
|
-
tip: {
|
|
10
|
-
x: number;
|
|
11
|
-
y: number;
|
|
12
|
-
} | null;
|
|
13
|
-
tooltipRef?: RefObject<HTMLDivElement | null>;
|
|
14
|
-
styles: CSSProperties;
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
}): import("react").ReactPortal | null;
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
17
2
|
/** Tooltip header — shows formatted date + optional right-aligned label with color. */
|
|
18
3
|
export declare function TooltipHeader({ date, label, labelColor, }: {
|
|
19
4
|
date: string;
|
|
@@ -34,4 +19,19 @@ export declare function TooltipRow({ color, label, value, valueColor, shape, str
|
|
|
34
19
|
export declare function TooltipBody({ children }: {
|
|
35
20
|
children: ReactNode;
|
|
36
21
|
}): import("react").JSX.Element;
|
|
22
|
+
/**
|
|
23
|
+
* Floating tooltip positioned against a viewport-space anchor, with flip + clamp handled once for
|
|
24
|
+
* every chart instead of per call site (this used to be recomputed inside each chart's own `useChartTooltip`, and only for
|
|
25
|
+
* charts that remembered to use it).
|
|
26
|
+
*
|
|
27
|
+
* The box is measured after mount via `useLayoutEffect`, so the first paint of a given tooltip is
|
|
28
|
+
* hidden rather than misplaced — a tooltip that flashes at the wrong corner reads as a bug.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ChartTooltipFloat({ anchor, children, }: {
|
|
31
|
+
anchor: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
} | null;
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}): import("react").ReactPortal | null;
|
|
37
37
|
//# sourceMappingURL=ChartTooltip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
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,GACX,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,+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;AAOD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA4CA"}
|