@zuilib/charts 0.3.0 → 0.4.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/README.md +26 -7
- package/dist/chart-frame.js +1 -1
- package/dist/chart-legend.js +1 -1
- package/dist/chart-tooltip.js +1 -1
- package/dist/chart.js +5 -5
- package/dist/{chunk-SHQZRND7.js → chunk-4FWNWLEJ.js} +9 -4
- package/dist/{chunk-PV27RZSV.js → chunk-FDLNPZV6.js} +5 -3
- package/dist/{chunk-YMPZODMC.js → chunk-J2KWQJGC.js} +32 -13
- package/dist/{chunk-BFTWWI46.js → chunk-NDBDHQME.js} +2 -2
- package/dist/chunk-PPVZ3BYP.js +44 -0
- package/dist/{chunk-G4FXJJ66.js → chunk-SID6O4PY.js} +28 -7
- package/dist/forecast-chart.d.ts +28 -0
- package/dist/forecast-chart.js +20 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -5
- package/dist/registry.js +3 -3
- package/dist/tailwind.css +2 -0
- package/package.json +13 -9
- package/dist/chunk-6QTIAMA3.js +0 -169
- package/dist/chunk-MJDIQVJX.js +0 -148
package/README.md
CHANGED
|
@@ -8,14 +8,20 @@ other and to `@zuilib/data-grid` — without a hard dependency on it.
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
pnpm add @zuilib/charts
|
|
11
|
+
pnpm add @zuilib/charts
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
chart
|
|
14
|
+
React and React DOM are the required peers. Recharts, the ZUI primitives and
|
|
15
|
+
tokens install transitively. Install `recharts` yourself only when your own
|
|
16
|
+
code imports it for hand-written composition.
|
|
17
|
+
|
|
18
|
+
For a Tailwind v4 host, load the component and chart source entries once:
|
|
19
|
+
|
|
20
|
+
```css
|
|
21
|
+
@import "tailwindcss";
|
|
22
|
+
@import "@zuilib/primitives/tailwind.css";
|
|
23
|
+
@import "@zuilib/charts/tailwind.css";
|
|
24
|
+
```
|
|
19
25
|
|
|
20
26
|
## Usage
|
|
21
27
|
|
|
@@ -47,7 +53,7 @@ const spec = {
|
|
|
47
53
|
| `formats` | `Record<string, FormatterFn \| FormatterFactory>` | — custom formatter tokens the spec may name (functions stay out of the spec) |
|
|
48
54
|
| `locale` | `string` | user's locale — BCP 47 tag for formatted values |
|
|
49
55
|
| `width` / `height` | `number` | responsive — fixed pixel size for tests and SSR |
|
|
50
|
-
| `aspect` | `number` | `16/9` — responsive width/height ratio when `height` is left out |
|
|
56
|
+
| `aspect` | `number` | `16/9` — responsive width/height ratio when `height` is left out; the chart never gets shorter than 200px |
|
|
51
57
|
| `loading` | `boolean` | `false` — renders a skeleton instead of the chart |
|
|
52
58
|
| `error` | `ReactNode` | — renders an error state instead of the chart |
|
|
53
59
|
| `emptyState` | `ReactNode` | the `noData` label — renders when `rows` is empty |
|
|
@@ -58,6 +64,19 @@ const spec = {
|
|
|
58
64
|
| `ariaLabel` | `string` | the spec's title — accessible name of the figure |
|
|
59
65
|
| `className` | `string` | merged onto the container |
|
|
60
66
|
|
|
67
|
+
## Sizing
|
|
68
|
+
|
|
69
|
+
A chart fills its container. Leave `width` and `height` out and the frame
|
|
70
|
+
measures itself: the height follows `aspect` (16/9) with a 200px floor so a
|
|
71
|
+
phone-width chart keeps a readable plot; pass `height` to fix the height and
|
|
72
|
+
keep the width fluid; pass both for a fixed size (tests, SSR). The frame is
|
|
73
|
+
a CSS size container (`@container`, `min-w-0`), so it never holds a flex or
|
|
74
|
+
grid parent open at the width of a previous render — put it in any column
|
|
75
|
+
without a `min-w-0` of your own. Under 384px the value axis sizes to its
|
|
76
|
+
labels and the category axis keeps only its end ticks; the legend wraps and,
|
|
77
|
+
on touch pointers, its buttons grow to 44px hit areas. Desktop rendering
|
|
78
|
+
does not change.
|
|
79
|
+
|
|
61
80
|
## Entry points
|
|
62
81
|
|
|
63
82
|
`@zuilib/charts` (or `./chart`) is the config-driven renderer: ten chart
|
package/dist/chart-frame.js
CHANGED
package/dist/chart-legend.js
CHANGED
package/dist/chart-tooltip.js
CHANGED
package/dist/chart.js
CHANGED
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
chart_default,
|
|
5
5
|
configFromSpec,
|
|
6
6
|
crossFilterFieldOf
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-NDBDHQME.js";
|
|
8
|
+
import "./chunk-J2KWQJGC.js";
|
|
9
9
|
import "./chunk-HNJKYOPE.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-SID6O4PY.js";
|
|
11
11
|
import "./chunk-7WABBHMT.js";
|
|
12
12
|
import "./chunk-3VGLTPGL.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-FDLNPZV6.js";
|
|
14
|
+
import "./chunk-4FWNWLEJ.js";
|
|
15
15
|
import "./chunk-WVWGHGZE.js";
|
|
16
16
|
import "./chunk-UN3XPBZ5.js";
|
|
17
17
|
import "./chunk-IPOBQIYU.js";
|
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
} from "./chunk-7WJ26NHR.js";
|
|
13
13
|
|
|
14
14
|
// src/chart-legend.tsx
|
|
15
|
-
import { cn } from "@zuilib/
|
|
16
|
-
import { focusRingClasses } from "@zuilib/
|
|
15
|
+
import { cn } from "@zuilib/primitives/lib/cn";
|
|
16
|
+
import { focusRingClasses } from "@zuilib/primitives/lib/focus";
|
|
17
|
+
import { touchTargetClasses } from "@zuilib/primitives/lib/sizes";
|
|
17
18
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
19
|
function ChartLegendList({ payload, className }) {
|
|
19
20
|
const { config, labels, hiddenSeries, onLegendClick, categoryField, categories } = useChartContext();
|
|
@@ -46,7 +47,11 @@ function ChartLegendList({ payload, className }) {
|
|
|
46
47
|
"aria-label": fillLabel(labels.toggleSeries, { series: entry.name }),
|
|
47
48
|
"data-slot": "chart-legend-button",
|
|
48
49
|
className: cn(
|
|
49
|
-
"text-muted-foreground flex cursor-pointer items-center gap-1.5 rounded-sm px-1 py-0.5 text-xs transition-opacity",
|
|
50
|
+
"text-muted-foreground flex min-w-0 max-w-full cursor-pointer items-center gap-1.5 rounded-sm px-1 py-0.5 text-xs transition-opacity",
|
|
51
|
+
/* Touch: a 44px hit area; the legend reports its measured
|
|
52
|
+
height to Recharts, so the plot gives way to it. */
|
|
53
|
+
touchTargetClasses,
|
|
54
|
+
"pointer-coarse:px-2",
|
|
50
55
|
focusRingClasses,
|
|
51
56
|
off && "opacity-45"
|
|
52
57
|
),
|
|
@@ -67,7 +72,7 @@ function ChartLegendList({ payload, className }) {
|
|
|
67
72
|
style: { backgroundColor: entry.color }
|
|
68
73
|
}
|
|
69
74
|
),
|
|
70
|
-
entry.name
|
|
75
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "chart-legend-label", className: "min-w-0 text-start break-words", children: entry.name })
|
|
71
76
|
]
|
|
72
77
|
}
|
|
73
78
|
) }, entry.key);
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-7WJ26NHR.js";
|
|
7
7
|
|
|
8
8
|
// src/chart-tooltip.tsx
|
|
9
|
-
import { cn } from "@zuilib/
|
|
9
|
+
import { cn } from "@zuilib/primitives/lib/cn";
|
|
10
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
function ChartTooltipPanel({
|
|
12
12
|
active,
|
|
@@ -25,7 +25,9 @@ function ChartTooltipPanel({
|
|
|
25
25
|
{
|
|
26
26
|
"data-slot": "chart-tooltip",
|
|
27
27
|
className: cn(
|
|
28
|
-
|
|
28
|
+
/* Never wider than the frame: Recharts keeps the panel inside the
|
|
29
|
+
chart box, so this is what keeps it inside the viewport too. */
|
|
30
|
+
"bg-popover text-popover-foreground border-border min-w-32 max-w-[calc(100cqw-1rem)] rounded-md border px-2.5 py-1.5 shadow-md",
|
|
29
31
|
className
|
|
30
32
|
),
|
|
31
33
|
children: [
|
|
@@ -45,7 +47,7 @@ function ChartTooltipPanel({
|
|
|
45
47
|
style: { backgroundColor: series ? seriesColorVar(field) : entry.color }
|
|
46
48
|
}
|
|
47
49
|
),
|
|
48
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: name }),
|
|
50
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground min-w-0 break-words", children: name }),
|
|
49
51
|
/* @__PURE__ */ jsx("span", { className: "text-foreground ms-auto font-mono font-medium tabular-nums", children: format(entry.value) })
|
|
50
52
|
] }, `${field}-${index}`);
|
|
51
53
|
}) })
|
|
@@ -9,10 +9,13 @@ import {
|
|
|
9
9
|
} from "./chunk-3VGLTPGL.js";
|
|
10
10
|
import {
|
|
11
11
|
ChartTooltipPanel
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FDLNPZV6.js";
|
|
13
13
|
import {
|
|
14
14
|
ChartLegendList
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4FWNWLEJ.js";
|
|
16
|
+
import {
|
|
17
|
+
ChartContext
|
|
18
|
+
} from "./chunk-IPOBQIYU.js";
|
|
16
19
|
import {
|
|
17
20
|
cartesianGridDefaults,
|
|
18
21
|
chartColorVar,
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
import { Area, AreaChart } from "recharts";
|
|
28
31
|
|
|
29
32
|
// src/charts/cartesian.tsx
|
|
33
|
+
import { useContext } from "react";
|
|
30
34
|
import { Brush, CartesianGrid, Legend, ReferenceLine, Tooltip, XAxis, YAxis } from "recharts";
|
|
31
35
|
import { jsx } from "react/jsx-runtime";
|
|
32
36
|
function stackIdOf(series, stacked) {
|
|
@@ -35,6 +39,11 @@ function stackIdOf(series, stacked) {
|
|
|
35
39
|
function valueFormatOf(spec, stacked) {
|
|
36
40
|
return stacked === "percent" ? "percent" : spec.yAxis?.format ?? "number";
|
|
37
41
|
}
|
|
42
|
+
function useNarrowFrame() {
|
|
43
|
+
return useContext(ChartContext)?.narrow ?? false;
|
|
44
|
+
}
|
|
45
|
+
var narrowXAxisProps = { interval: "preserveStartEnd", minTickGap: 16 };
|
|
46
|
+
var narrowYAxisProps = { width: "auto" };
|
|
38
47
|
function renderXAxis(xKey, spec, formats, options) {
|
|
39
48
|
const axis = spec.xAxis ?? {};
|
|
40
49
|
const numeric = options?.numeric || axis.scale !== void 0 && axis.scale !== "band";
|
|
@@ -43,6 +52,7 @@ function renderXAxis(xKey, spec, formats, options) {
|
|
|
43
52
|
{
|
|
44
53
|
dataKey: xKey,
|
|
45
54
|
...xAxisDefaults,
|
|
55
|
+
...options?.narrow ? narrowXAxisProps : {},
|
|
46
56
|
type: numeric ? "number" : "category",
|
|
47
57
|
...axis.scale === "log" ? { scale: "log", domain: ["auto", "auto"] } : {},
|
|
48
58
|
...axis.scale === "time" ? { domain: ["dataMin", "dataMax"] } : {},
|
|
@@ -54,14 +64,16 @@ function renderXAxis(xKey, spec, formats, options) {
|
|
|
54
64
|
"x"
|
|
55
65
|
);
|
|
56
66
|
}
|
|
57
|
-
function renderYAxes(spec, formats, valueFormat, hasSecondary) {
|
|
67
|
+
function renderYAxes(spec, formats, valueFormat, hasSecondary, options) {
|
|
58
68
|
const y = spec.yAxis ?? {};
|
|
69
|
+
const narrowProps = options?.narrow ? narrowYAxisProps : {};
|
|
59
70
|
const axes = [
|
|
60
71
|
/* @__PURE__ */ jsx(
|
|
61
72
|
YAxis,
|
|
62
73
|
{
|
|
63
74
|
yAxisId: "primary",
|
|
64
75
|
...yAxisDefaults,
|
|
76
|
+
...narrowProps,
|
|
65
77
|
hide: y.hidden,
|
|
66
78
|
domain: y.domain,
|
|
67
79
|
tickCount: y.tickCount,
|
|
@@ -80,6 +92,7 @@ function renderYAxes(spec, formats, valueFormat, hasSecondary) {
|
|
|
80
92
|
yAxisId: "secondary",
|
|
81
93
|
orientation: "right",
|
|
82
94
|
...yAxisDefaults,
|
|
95
|
+
...narrowProps,
|
|
83
96
|
hide: secondary.hidden,
|
|
84
97
|
domain: secondary.domain,
|
|
85
98
|
tickCount: secondary.tickCount,
|
|
@@ -187,6 +200,7 @@ function AreaChartRenderer({
|
|
|
187
200
|
animate
|
|
188
201
|
}) {
|
|
189
202
|
warnOnSpecIssues(spec);
|
|
203
|
+
const narrow = useNarrowFrame();
|
|
190
204
|
const hasSecondary = spec.series.some((s) => s.axis === "secondary");
|
|
191
205
|
const valueFormat = valueFormatOf(spec, spec.stacked);
|
|
192
206
|
return /* @__PURE__ */ jsxs(
|
|
@@ -200,8 +214,8 @@ function AreaChartRenderer({
|
|
|
200
214
|
margin: { top: 8, bottom: 4 },
|
|
201
215
|
children: [
|
|
202
216
|
renderGrid(spec),
|
|
203
|
-
renderXAxis(spec.xKey, spec, formats),
|
|
204
|
-
renderYAxes(spec, formats, valueFormat, hasSecondary),
|
|
217
|
+
renderXAxis(spec.xKey, spec, formats, { narrow }),
|
|
218
|
+
renderYAxes(spec, formats, valueFormat, hasSecondary, { narrow }),
|
|
205
219
|
renderTooltip(spec, valueFormat, tooltipCrosshairDefaults),
|
|
206
220
|
renderLegend(spec, spec.series.length),
|
|
207
221
|
renderReferenceLines(spec),
|
|
@@ -244,6 +258,7 @@ function BarChartRenderer({
|
|
|
244
258
|
animate
|
|
245
259
|
}) {
|
|
246
260
|
warnOnSpecIssues(spec);
|
|
261
|
+
const narrow = useNarrowFrame();
|
|
247
262
|
const horizontal = spec.orientation === "horizontal";
|
|
248
263
|
const hasSecondary = !horizontal && spec.series.some((s) => s.axis === "secondary");
|
|
249
264
|
const valueFormat = valueFormatOf(spec, spec.stacked);
|
|
@@ -283,6 +298,7 @@ function BarChartRenderer({
|
|
|
283
298
|
{
|
|
284
299
|
type: "number",
|
|
285
300
|
...xAxisDefaults,
|
|
301
|
+
...narrow ? narrowXAxisProps : {},
|
|
286
302
|
hide: spec.yAxis?.hidden,
|
|
287
303
|
domain: spec.yAxis?.domain,
|
|
288
304
|
tickFormatter: formats.resolve(spec.yAxis?.format, valueFormat)
|
|
@@ -318,8 +334,8 @@ function BarChartRenderer({
|
|
|
318
334
|
margin: { top: 8, bottom: 4 },
|
|
319
335
|
children: [
|
|
320
336
|
renderGrid(spec),
|
|
321
|
-
renderXAxis(spec.xKey, spec, formats),
|
|
322
|
-
renderYAxes(spec, formats, valueFormat, hasSecondary),
|
|
337
|
+
renderXAxis(spec.xKey, spec, formats, { narrow }),
|
|
338
|
+
renderYAxes(spec, formats, valueFormat, hasSecondary, { narrow }),
|
|
323
339
|
renderTooltip(spec, valueFormat, tooltipCursorDefaults),
|
|
324
340
|
renderLegend(spec, spec.series.length),
|
|
325
341
|
renderReferenceLines(spec),
|
|
@@ -381,6 +397,7 @@ function ComposedChartRenderer({
|
|
|
381
397
|
animate
|
|
382
398
|
}) {
|
|
383
399
|
warnOnSpecIssues(spec);
|
|
400
|
+
const narrow = useNarrowFrame();
|
|
384
401
|
const hasSecondary = spec.series.some((s) => s.axis === "secondary");
|
|
385
402
|
const valueFormat = valueFormatOf(spec);
|
|
386
403
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -393,8 +410,8 @@ function ComposedChartRenderer({
|
|
|
393
410
|
margin: { top: 8, bottom: 4 },
|
|
394
411
|
children: [
|
|
395
412
|
renderGrid(spec),
|
|
396
|
-
renderXAxis(spec.xKey, spec, formats),
|
|
397
|
-
renderYAxes(spec, formats, valueFormat, hasSecondary),
|
|
413
|
+
renderXAxis(spec.xKey, spec, formats, { narrow }),
|
|
414
|
+
renderYAxes(spec, formats, valueFormat, hasSecondary, { narrow }),
|
|
398
415
|
renderTooltip(spec, valueFormat, tooltipCursorDefaults),
|
|
399
416
|
renderLegend(spec, spec.series.length),
|
|
400
417
|
renderReferenceLines(spec),
|
|
@@ -465,6 +482,7 @@ function LineChartRenderer({
|
|
|
465
482
|
animate
|
|
466
483
|
}) {
|
|
467
484
|
warnOnSpecIssues(spec);
|
|
485
|
+
const narrow = useNarrowFrame();
|
|
468
486
|
const hasSecondary = spec.series.some((s) => s.axis === "secondary");
|
|
469
487
|
const valueFormat = valueFormatOf(spec);
|
|
470
488
|
return /* @__PURE__ */ jsxs5(
|
|
@@ -477,8 +495,8 @@ function LineChartRenderer({
|
|
|
477
495
|
margin: { top: 8, bottom: 4 },
|
|
478
496
|
children: [
|
|
479
497
|
renderGrid(spec),
|
|
480
|
-
renderXAxis(spec.xKey, spec, formats),
|
|
481
|
-
renderYAxes(spec, formats, valueFormat, hasSecondary),
|
|
498
|
+
renderXAxis(spec.xKey, spec, formats, { narrow }),
|
|
499
|
+
renderYAxes(spec, formats, valueFormat, hasSecondary, { narrow }),
|
|
482
500
|
renderTooltip(spec, valueFormat, tooltipCrosshairDefaults),
|
|
483
501
|
renderLegend(spec, spec.series.length),
|
|
484
502
|
renderReferenceLines(spec),
|
|
@@ -667,11 +685,12 @@ function ScatterChartRenderer({
|
|
|
667
685
|
);
|
|
668
686
|
}
|
|
669
687
|
const active = activeCategoryValues(markInteraction);
|
|
688
|
+
const narrow = useNarrowFrame();
|
|
670
689
|
const valueFormat = spec.yAxis?.format ?? "number";
|
|
671
690
|
return /* @__PURE__ */ jsxs9(ScatterChart, { width, height, margin: { top: 8, bottom: 4 }, children: [
|
|
672
691
|
renderGrid(spec),
|
|
673
|
-
renderXAxis(spec.xKey, spec, formats, { numeric: true }),
|
|
674
|
-
renderYAxes(spec, formats, valueFormat, false),
|
|
692
|
+
renderXAxis(spec.xKey, spec, formats, { numeric: true, narrow }),
|
|
693
|
+
renderYAxes(spec, formats, valueFormat, false, { narrow }),
|
|
675
694
|
spec.sizeKey && /* @__PURE__ */ jsx10(ZAxis, { dataKey: spec.sizeKey, range: spec.sizeRange ?? [64, 400] }),
|
|
676
695
|
renderTooltip(spec, valueFormat, tooltipCrosshairDefaults),
|
|
677
696
|
spec.groupKey ? renderLegend(spec, groups.length) : null,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultChartRegistry
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-J2KWQJGC.js";
|
|
4
4
|
import {
|
|
5
5
|
prefersReducedMotion
|
|
6
6
|
} from "./chunk-HNJKYOPE.js";
|
|
7
7
|
import {
|
|
8
8
|
chart_frame_default
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SID6O4PY.js";
|
|
10
10
|
import {
|
|
11
11
|
createFormatters
|
|
12
12
|
} from "./chunk-7WABBHMT.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chart_default
|
|
3
|
+
} from "./chunk-NDBDHQME.js";
|
|
4
|
+
|
|
5
|
+
// src/forecast-chart.tsx
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
function ForecastChart({
|
|
8
|
+
rows,
|
|
9
|
+
xKey,
|
|
10
|
+
actualKey,
|
|
11
|
+
forecastKey,
|
|
12
|
+
lowerKey,
|
|
13
|
+
upperKey,
|
|
14
|
+
title,
|
|
15
|
+
description,
|
|
16
|
+
format,
|
|
17
|
+
actualLabel = "Actual",
|
|
18
|
+
forecastLabel = "Forecast",
|
|
19
|
+
intervalLabel = "Forecast interval",
|
|
20
|
+
...chartProps
|
|
21
|
+
}) {
|
|
22
|
+
const spec = {
|
|
23
|
+
version: 1,
|
|
24
|
+
type: "composed",
|
|
25
|
+
title,
|
|
26
|
+
description,
|
|
27
|
+
xKey,
|
|
28
|
+
xAxis: { scale: "time" },
|
|
29
|
+
yAxis: { format },
|
|
30
|
+
series: [
|
|
31
|
+
...lowerKey ? [{ field: lowerKey, label: `${intervalLabel} lower`, mark: "area", paletteSlot: 3, format }] : [],
|
|
32
|
+
...upperKey ? [{ field: upperKey, label: `${intervalLabel} upper`, mark: "area", paletteSlot: 3, format }] : [],
|
|
33
|
+
{ field: actualKey, label: actualLabel, mark: "line", paletteSlot: 1, format },
|
|
34
|
+
{ field: forecastKey, label: forecastLabel, mark: "line", paletteSlot: 2, format }
|
|
35
|
+
],
|
|
36
|
+
tooltip: true,
|
|
37
|
+
legend: true
|
|
38
|
+
};
|
|
39
|
+
return /* @__PURE__ */ jsx(chart_default, { ...chartProps, rows, spec });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
ForecastChart
|
|
44
|
+
};
|
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
useState
|
|
25
25
|
} from "react";
|
|
26
26
|
import { ResponsiveContainer } from "recharts";
|
|
27
|
-
import Alert from "@zuilib/
|
|
28
|
-
import { cn } from "@zuilib/
|
|
27
|
+
import Alert from "@zuilib/primitives/alert";
|
|
28
|
+
import { cn } from "@zuilib/primitives/lib/cn";
|
|
29
29
|
|
|
30
30
|
// src/legend-toggle.ts
|
|
31
31
|
function legendToggle(allFields, hidden, field, options) {
|
|
@@ -39,6 +39,8 @@ function legendToggle(allFields, hidden, field, options) {
|
|
|
39
39
|
|
|
40
40
|
// src/chart-frame.tsx
|
|
41
41
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
42
|
+
var NARROW_FRAME_WIDTH = 384;
|
|
43
|
+
var MIN_RESPONSIVE_HEIGHT = 200;
|
|
42
44
|
var ChartFrame = forwardRef(
|
|
43
45
|
({
|
|
44
46
|
config,
|
|
@@ -77,9 +79,17 @@ var ChartFrame = forwardRef(
|
|
|
77
79
|
},
|
|
78
80
|
[allFields, hiddenSeries, innerHidden, onHiddenSeriesChange]
|
|
79
81
|
);
|
|
82
|
+
const [measuredWidth, setMeasuredWidth] = useState(null);
|
|
83
|
+
const onResize = useCallback((nextWidth) => {
|
|
84
|
+
setMeasuredWidth(nextWidth > 0 ? Math.round(nextWidth) : null);
|
|
85
|
+
}, []);
|
|
86
|
+
const fixedSize = typeof width === "number" && typeof height === "number";
|
|
87
|
+
const frameWidth = fixedSize ? width : measuredWidth;
|
|
88
|
+
const narrow = frameWidth !== null && frameWidth < NARROW_FRAME_WIDTH;
|
|
89
|
+
const responsiveAspect = measuredWidth !== null && measuredWidth / aspect < MIN_RESPONSIVE_HEIGHT ? measuredWidth / MIN_RESPONSIVE_HEIGHT : aspect;
|
|
80
90
|
const context = useMemo(
|
|
81
|
-
() => ({ config, formats, labels, hiddenSeries: hidden, onLegendClick, categoryField, categories }),
|
|
82
|
-
[config, formats, labels, hidden, onLegendClick, categoryField, categories]
|
|
91
|
+
() => ({ config, formats, labels, hiddenSeries: hidden, onLegendClick, categoryField, categories, narrow }),
|
|
92
|
+
[config, formats, labels, hidden, onLegendClick, categoryField, categories, narrow]
|
|
83
93
|
);
|
|
84
94
|
const colorStyle = useMemo(() => {
|
|
85
95
|
const vars = {};
|
|
@@ -93,7 +103,6 @@ var ChartFrame = forwardRef(
|
|
|
93
103
|
() => Object.entries(config).filter((entry) => !!entry[1].theme && !entry[1].color),
|
|
94
104
|
[config]
|
|
95
105
|
);
|
|
96
|
-
const fixedSize = typeof width === "number" && typeof height === "number";
|
|
97
106
|
let body;
|
|
98
107
|
if (loading) {
|
|
99
108
|
body = /* @__PURE__ */ jsx(
|
|
@@ -124,7 +133,15 @@ var ChartFrame = forwardRef(
|
|
|
124
133
|
if (fixedSize && isValidElement(chart)) {
|
|
125
134
|
body = cloneElement(chart, { width, height });
|
|
126
135
|
} else {
|
|
127
|
-
body = /* @__PURE__ */ jsx(
|
|
136
|
+
body = /* @__PURE__ */ jsx(
|
|
137
|
+
ResponsiveContainer,
|
|
138
|
+
{
|
|
139
|
+
width: "100%",
|
|
140
|
+
onResize,
|
|
141
|
+
...height !== void 0 ? { height } : { aspect: responsiveAspect },
|
|
142
|
+
children: chart
|
|
143
|
+
}
|
|
144
|
+
);
|
|
128
145
|
}
|
|
129
146
|
}
|
|
130
147
|
return /* @__PURE__ */ jsxs(
|
|
@@ -136,7 +153,11 @@ var ChartFrame = forwardRef(
|
|
|
136
153
|
role: "figure",
|
|
137
154
|
"aria-label": ariaLabel,
|
|
138
155
|
className: cn(
|
|
139
|
-
|
|
156
|
+
/* `@container` (inline-size containment) is load-bearing beyond
|
|
157
|
+
container queries: the rendered svg carries a pixel width, and
|
|
158
|
+
without containment a flex/grid parent's `min-width: auto`
|
|
159
|
+
would grow to it and never let the chart shrink again. */
|
|
160
|
+
"@container min-w-0 text-foreground text-xs [&_.recharts-surface]:outline-none [&_.recharts-sector]:outline-none",
|
|
140
161
|
fixedSize ? "" : "w-full",
|
|
141
162
|
className
|
|
142
163
|
),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ChartProps } from './chart.js';
|
|
3
|
+
import { d as ChartRow, F as FormatToken } from './chart-spec-CH0tAkLG.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './chart-frame-B9fxUxGG.js';
|
|
6
|
+
import './format.js';
|
|
7
|
+
import './registry.js';
|
|
8
|
+
import './authoring.js';
|
|
9
|
+
import './cross-filter.js';
|
|
10
|
+
|
|
11
|
+
interface ForecastChartProps extends Omit<ChartProps, 'spec' | 'rows'> {
|
|
12
|
+
rows: ChartRow[];
|
|
13
|
+
xKey: string;
|
|
14
|
+
actualKey: string;
|
|
15
|
+
forecastKey: string;
|
|
16
|
+
lowerKey?: string;
|
|
17
|
+
upperKey?: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
format?: FormatToken;
|
|
21
|
+
actualLabel?: string;
|
|
22
|
+
forecastLabel?: string;
|
|
23
|
+
intervalLabel?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Opinionated actual-versus-forecast chart with optional confidence bounds. */
|
|
26
|
+
declare function ForecastChart({ rows, xKey, actualKey, forecastKey, lowerKey, upperKey, title, description, format, actualLabel, forecastLabel, intervalLabel, ...chartProps }: ForecastChartProps): react_jsx_runtime.JSX.Element;
|
|
27
|
+
|
|
28
|
+
export { ForecastChart, type ForecastChartProps, ForecastChart as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
ForecastChart
|
|
4
|
+
} from "./chunk-PPVZ3BYP.js";
|
|
5
|
+
import "./chunk-NDBDHQME.js";
|
|
6
|
+
import "./chunk-J2KWQJGC.js";
|
|
7
|
+
import "./chunk-HNJKYOPE.js";
|
|
8
|
+
import "./chunk-SID6O4PY.js";
|
|
9
|
+
import "./chunk-7WABBHMT.js";
|
|
10
|
+
import "./chunk-3VGLTPGL.js";
|
|
11
|
+
import "./chunk-FDLNPZV6.js";
|
|
12
|
+
import "./chunk-4FWNWLEJ.js";
|
|
13
|
+
import "./chunk-WVWGHGZE.js";
|
|
14
|
+
import "./chunk-UN3XPBZ5.js";
|
|
15
|
+
import "./chunk-IPOBQIYU.js";
|
|
16
|
+
import "./chunk-7WJ26NHR.js";
|
|
17
|
+
export {
|
|
18
|
+
ForecastChart,
|
|
19
|
+
ForecastChart as default
|
|
20
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Chart, ChartProps, CustomChartProps, configFromSpec, crossFilterFieldOf, Chart as default } from './chart.js';
|
|
2
|
+
export { ForecastChart, ForecastChartProps } from './forecast-chart.js';
|
|
2
3
|
export { AnyChartSpec, ChartRegistry, ChartTypeDef, CustomChartSpec, chartSpecJsonSchema, chartTypeSchema, createChartRegistry, defaultChartRegistry, defaultChartTypes, defineChartType } from './registry.js';
|
|
3
4
|
export { C as ChartConfig, a as ChartFrame, b as ChartFrameProps, c as ChartLabels, d as ChartSeriesConfig, e as defaultChartLabels, f as fillLabel } from './chart-frame-B9fxUxGG.js';
|
|
4
5
|
export { ChartTooltipPanel, ChartTooltipPanelProps } from './chart-tooltip.js';
|
|
@@ -9,5 +10,5 @@ export { ColumnFilter, applyFilters, fromColumnFilters, toColumnFilters, toHighl
|
|
|
9
10
|
export { FormatterFactory, FormatterFn, Formatters, createFormatters } from './format.js';
|
|
10
11
|
export { A as AreaChartSpec, a as AxisSpec, B as BarChartSpec, C as CartesianFrameSpec, b as CartesianSpecCommon, c as ChartMarkEvent, d as ChartRow, e as ChartSpec, f as ChartSpecCommon, g as ComposedChartSpec, F as FormatToken, h as FunnelChartSpec, L as LineChartSpec, P as PieChartSpec, R as RadarChartSpec, i as RadialBarChartSpec, j as ReferenceLineSpec, S as ScatterChartSpec, k as SeriesSpec, T as TreemapChartSpec } from './chart-spec-CH0tAkLG.js';
|
|
11
12
|
import 'react';
|
|
12
|
-
import './authoring.js';
|
|
13
13
|
import 'react/jsx-runtime';
|
|
14
|
+
import './authoring.js';
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,14 @@ import {
|
|
|
5
5
|
toColumnFilters,
|
|
6
6
|
toHighlight
|
|
7
7
|
} from "./chunk-D7K23UFP.js";
|
|
8
|
+
import {
|
|
9
|
+
ForecastChart
|
|
10
|
+
} from "./chunk-PPVZ3BYP.js";
|
|
8
11
|
import {
|
|
9
12
|
chart_default,
|
|
10
13
|
configFromSpec,
|
|
11
14
|
crossFilterFieldOf
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-NDBDHQME.js";
|
|
13
16
|
import {
|
|
14
17
|
chartSpecJsonSchema,
|
|
15
18
|
chartTypeSchema,
|
|
@@ -17,21 +20,21 @@ import {
|
|
|
17
20
|
defaultChartRegistry,
|
|
18
21
|
defaultChartTypes,
|
|
19
22
|
defineChartType
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-J2KWQJGC.js";
|
|
21
24
|
import "./chunk-HNJKYOPE.js";
|
|
22
25
|
import {
|
|
23
26
|
chart_frame_default
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-SID6O4PY.js";
|
|
25
28
|
import {
|
|
26
29
|
createFormatters
|
|
27
30
|
} from "./chunk-7WABBHMT.js";
|
|
28
31
|
import "./chunk-3VGLTPGL.js";
|
|
29
32
|
import {
|
|
30
33
|
ChartTooltipPanel
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-FDLNPZV6.js";
|
|
32
35
|
import {
|
|
33
36
|
ChartLegendList
|
|
34
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-4FWNWLEJ.js";
|
|
35
38
|
import {
|
|
36
39
|
cross_filter_default,
|
|
37
40
|
defaultCrossFilterState,
|
|
@@ -63,6 +66,7 @@ export {
|
|
|
63
66
|
ChartTooltipPanel,
|
|
64
67
|
cross_filter_default as CrossFilterProvider,
|
|
65
68
|
DIMMED_OPACITY,
|
|
69
|
+
ForecastChart,
|
|
66
70
|
applyFilters,
|
|
67
71
|
cartesianGridDefaults,
|
|
68
72
|
chartColorVar,
|
package/dist/registry.js
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
defaultChartRegistry,
|
|
7
7
|
defaultChartTypes,
|
|
8
8
|
defineChartType
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-J2KWQJGC.js";
|
|
10
10
|
import "./chunk-HNJKYOPE.js";
|
|
11
11
|
import "./chunk-3VGLTPGL.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-FDLNPZV6.js";
|
|
13
|
+
import "./chunk-4FWNWLEJ.js";
|
|
14
14
|
import "./chunk-WVWGHGZE.js";
|
|
15
15
|
import "./chunk-UN3XPBZ5.js";
|
|
16
16
|
import "./chunk-IPOBQIYU.js";
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zuilib/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "ZUI — enterprise analytics charts on Recharts: a serializable chart spec, styled primitives, and cross-filtering that links charts to data tables",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"sideEffects":
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"**/*.css"
|
|
8
|
+
],
|
|
7
9
|
"files": [
|
|
8
10
|
"dist"
|
|
9
11
|
],
|
|
@@ -16,6 +18,10 @@
|
|
|
16
18
|
"types": "./dist/chart.d.ts",
|
|
17
19
|
"import": "./dist/chart.js"
|
|
18
20
|
},
|
|
21
|
+
"./forecast-chart": {
|
|
22
|
+
"types": "./dist/forecast-chart.d.ts",
|
|
23
|
+
"import": "./dist/forecast-chart.js"
|
|
24
|
+
},
|
|
19
25
|
"./registry": {
|
|
20
26
|
"types": "./dist/registry.d.ts",
|
|
21
27
|
"import": "./dist/registry.js"
|
|
@@ -52,16 +58,16 @@
|
|
|
52
58
|
"types": "./dist/format.d.ts",
|
|
53
59
|
"import": "./dist/format.js"
|
|
54
60
|
},
|
|
61
|
+
"./tailwind.css": "./dist/tailwind.css",
|
|
55
62
|
"./package.json": "./package.json"
|
|
56
63
|
},
|
|
57
64
|
"peerDependencies": {
|
|
58
65
|
"react": "^18.0.0 || ^19.0.0",
|
|
59
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
60
|
-
"recharts": "^3.0.0",
|
|
61
|
-
"@zuilib/tokens": "^0.2.0"
|
|
66
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
62
67
|
},
|
|
63
68
|
"dependencies": {
|
|
64
|
-
"
|
|
69
|
+
"recharts": "^3.0.0",
|
|
70
|
+
"@zuilib/primitives": "^0.3.0"
|
|
65
71
|
},
|
|
66
72
|
"devDependencies": {
|
|
67
73
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -75,10 +81,8 @@
|
|
|
75
81
|
"jsdom": "^30.0.1",
|
|
76
82
|
"react": "^18.2.0",
|
|
77
83
|
"react-dom": "^18.2.0",
|
|
78
|
-
"recharts": "^3.0.0",
|
|
79
84
|
"typescript": "^6.0.2",
|
|
80
|
-
"vitest": "^4.1.11"
|
|
81
|
-
"@zuilib/tokens": "^0.2.0"
|
|
85
|
+
"vitest": "^4.1.11"
|
|
82
86
|
},
|
|
83
87
|
"keywords": [
|
|
84
88
|
"charts",
|
package/dist/chunk-6QTIAMA3.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createFormatters
|
|
3
|
-
} from "./chunk-7WABBHMT.js";
|
|
4
|
-
import {
|
|
5
|
-
defaultChartLabels
|
|
6
|
-
} from "./chunk-UN3XPBZ5.js";
|
|
7
|
-
import {
|
|
8
|
-
ChartContext
|
|
9
|
-
} from "./chunk-IPOBQIYU.js";
|
|
10
|
-
import {
|
|
11
|
-
CHART_COLOR_COUNT,
|
|
12
|
-
seriesColorProperty
|
|
13
|
-
} from "./chunk-7WJ26NHR.js";
|
|
14
|
-
|
|
15
|
-
// src/chart-frame.tsx
|
|
16
|
-
import {
|
|
17
|
-
Children,
|
|
18
|
-
cloneElement,
|
|
19
|
-
forwardRef,
|
|
20
|
-
isValidElement,
|
|
21
|
-
useCallback,
|
|
22
|
-
useId,
|
|
23
|
-
useMemo,
|
|
24
|
-
useState
|
|
25
|
-
} from "react";
|
|
26
|
-
import { ResponsiveContainer } from "recharts";
|
|
27
|
-
import Alert from "@zuilib/components/alert";
|
|
28
|
-
import { cn } from "@zuilib/components/lib/cn";
|
|
29
|
-
|
|
30
|
-
// src/legend-toggle.ts
|
|
31
|
-
function legendToggle(allFields, hidden, field, options) {
|
|
32
|
-
if (options?.additive) {
|
|
33
|
-
return hidden.includes(field) ? hidden.filter((f) => f !== field) : [...hidden, field];
|
|
34
|
-
}
|
|
35
|
-
const visible = allFields.filter((f) => !hidden.includes(f));
|
|
36
|
-
const isolated = visible.length === 1 && visible[0] === field;
|
|
37
|
-
return isolated ? [] : allFields.filter((f) => f !== field);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/chart-frame.tsx
|
|
41
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
42
|
-
var ChartFrame = forwardRef(
|
|
43
|
-
({
|
|
44
|
-
config,
|
|
45
|
-
children,
|
|
46
|
-
width,
|
|
47
|
-
height,
|
|
48
|
-
aspect = 16 / 9,
|
|
49
|
-
loading = false,
|
|
50
|
-
error,
|
|
51
|
-
emptyState,
|
|
52
|
-
empty = false,
|
|
53
|
-
ariaLabel,
|
|
54
|
-
labels: labelOverrides,
|
|
55
|
-
formats: customFormats,
|
|
56
|
-
locale,
|
|
57
|
-
hiddenSeries,
|
|
58
|
-
defaultHiddenSeries,
|
|
59
|
-
onHiddenSeriesChange,
|
|
60
|
-
categoryField,
|
|
61
|
-
categories,
|
|
62
|
-
className,
|
|
63
|
-
style,
|
|
64
|
-
...props
|
|
65
|
-
}, ref) => {
|
|
66
|
-
const chartId = useId().replace(/:/g, "");
|
|
67
|
-
const labels = useMemo(() => ({ ...defaultChartLabels, ...labelOverrides }), [labelOverrides]);
|
|
68
|
-
const formats = useMemo(() => createFormatters(locale, customFormats), [locale, customFormats]);
|
|
69
|
-
const [innerHidden, setInnerHidden] = useState(defaultHiddenSeries ?? []);
|
|
70
|
-
const hidden = hiddenSeries ?? innerHidden;
|
|
71
|
-
const allFields = useMemo(() => Object.keys(config), [config]);
|
|
72
|
-
const onLegendClick = useCallback(
|
|
73
|
-
(field, options) => {
|
|
74
|
-
const next = legendToggle(allFields, hiddenSeries ?? innerHidden, field, options);
|
|
75
|
-
if (hiddenSeries === void 0) setInnerHidden(next);
|
|
76
|
-
onHiddenSeriesChange?.(next);
|
|
77
|
-
},
|
|
78
|
-
[allFields, hiddenSeries, innerHidden, onHiddenSeriesChange]
|
|
79
|
-
);
|
|
80
|
-
const context = useMemo(
|
|
81
|
-
() => ({ config, formats, labels, hiddenSeries: hidden, onLegendClick, categoryField, categories }),
|
|
82
|
-
[config, formats, labels, hidden, onLegendClick, categoryField, categories]
|
|
83
|
-
);
|
|
84
|
-
const colorStyle = useMemo(() => {
|
|
85
|
-
const vars = {};
|
|
86
|
-
Object.entries(config).forEach(([field, series], index) => {
|
|
87
|
-
const slot = Math.min(index + 1, CHART_COLOR_COUNT);
|
|
88
|
-
vars[seriesColorProperty(field)] = series.color ?? (series.theme ? series.theme.light : `var(--chart-${slot})`);
|
|
89
|
-
});
|
|
90
|
-
return vars;
|
|
91
|
-
}, [config]);
|
|
92
|
-
const themed = useMemo(
|
|
93
|
-
() => Object.entries(config).filter((entry) => !!entry[1].theme && !entry[1].color),
|
|
94
|
-
[config]
|
|
95
|
-
);
|
|
96
|
-
const fixedSize = typeof width === "number" && typeof height === "number";
|
|
97
|
-
let body;
|
|
98
|
-
if (loading) {
|
|
99
|
-
body = /* @__PURE__ */ jsx(
|
|
100
|
-
"div",
|
|
101
|
-
{
|
|
102
|
-
"data-slot": "chart-skeleton",
|
|
103
|
-
role: "status",
|
|
104
|
-
"aria-label": labels.loading,
|
|
105
|
-
className: "bg-muted h-full min-h-32 w-full animate-pulse rounded-md motion-reduce:animate-none"
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
} else if (error !== void 0 && error !== null && error !== false) {
|
|
109
|
-
body = /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-slot": "chart-error", children: [
|
|
110
|
-
/* @__PURE__ */ jsx(Alert.Title, { children: labels.chartError }),
|
|
111
|
-
typeof error === "boolean" ? null : /* @__PURE__ */ jsx(Alert.Description, { children: error })
|
|
112
|
-
] });
|
|
113
|
-
} else if (empty) {
|
|
114
|
-
body = /* @__PURE__ */ jsx(
|
|
115
|
-
"div",
|
|
116
|
-
{
|
|
117
|
-
"data-slot": "chart-empty",
|
|
118
|
-
className: "text-muted-foreground flex h-full min-h-32 w-full items-center justify-center text-sm",
|
|
119
|
-
children: emptyState ?? labels.noData
|
|
120
|
-
}
|
|
121
|
-
);
|
|
122
|
-
} else {
|
|
123
|
-
const chart = Children.only(children);
|
|
124
|
-
if (fixedSize && isValidElement(chart)) {
|
|
125
|
-
body = cloneElement(chart, { width, height });
|
|
126
|
-
} else {
|
|
127
|
-
body = /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", ...height !== void 0 ? { height } : { aspect }, children: chart });
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return /* @__PURE__ */ jsxs(
|
|
131
|
-
"div",
|
|
132
|
-
{
|
|
133
|
-
ref,
|
|
134
|
-
"data-slot": "chart",
|
|
135
|
-
"data-chart": chartId,
|
|
136
|
-
role: "figure",
|
|
137
|
-
"aria-label": ariaLabel,
|
|
138
|
-
className: cn(
|
|
139
|
-
"text-foreground text-xs [&_.recharts-surface]:outline-none [&_.recharts-sector]:outline-none",
|
|
140
|
-
fixedSize ? "" : "w-full",
|
|
141
|
-
className
|
|
142
|
-
),
|
|
143
|
-
style: { ...colorStyle, ...style },
|
|
144
|
-
...props,
|
|
145
|
-
children: [
|
|
146
|
-
themed.length > 0 && /* @__PURE__ */ jsx(
|
|
147
|
-
"style",
|
|
148
|
-
{
|
|
149
|
-
dangerouslySetInnerHTML: {
|
|
150
|
-
__html: [
|
|
151
|
-
`[data-chart="${chartId}"]{${themed.map(([field, s]) => `${seriesColorProperty(field)}:${s.theme.light};`).join("")}}`,
|
|
152
|
-
`.dark [data-chart="${chartId}"]{${themed.map(([field, s]) => `${seriesColorProperty(field)}:${s.theme.dark};`).join("")}}`
|
|
153
|
-
].join("\n")
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
),
|
|
157
|
-
/* @__PURE__ */ jsx(ChartContext.Provider, { value: context, children: body })
|
|
158
|
-
]
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
);
|
|
163
|
-
ChartFrame.displayName = "ChartFrame";
|
|
164
|
-
var chart_frame_default = ChartFrame;
|
|
165
|
-
|
|
166
|
-
export {
|
|
167
|
-
ChartFrame,
|
|
168
|
-
chart_frame_default
|
|
169
|
-
};
|
package/dist/chunk-MJDIQVJX.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defaultChartRegistry
|
|
3
|
-
} from "./chunk-YMPZODMC.js";
|
|
4
|
-
import {
|
|
5
|
-
prefersReducedMotion
|
|
6
|
-
} from "./chunk-HNJKYOPE.js";
|
|
7
|
-
import {
|
|
8
|
-
chart_frame_default
|
|
9
|
-
} from "./chunk-6QTIAMA3.js";
|
|
10
|
-
import {
|
|
11
|
-
createFormatters
|
|
12
|
-
} from "./chunk-7WABBHMT.js";
|
|
13
|
-
import {
|
|
14
|
-
warnOnce
|
|
15
|
-
} from "./chunk-3VGLTPGL.js";
|
|
16
|
-
import {
|
|
17
|
-
useOptionalCrossFilter
|
|
18
|
-
} from "./chunk-WVWGHGZE.js";
|
|
19
|
-
import {
|
|
20
|
-
chartColorVar
|
|
21
|
-
} from "./chunk-7WJ26NHR.js";
|
|
22
|
-
|
|
23
|
-
// src/chart.tsx
|
|
24
|
-
import { useMemo, useState } from "react";
|
|
25
|
-
import { jsx } from "react/jsx-runtime";
|
|
26
|
-
function configFromSpec(spec, registry = defaultChartRegistry) {
|
|
27
|
-
const fromDef = registry.types.get(spec.type)?.configFrom?.(spec);
|
|
28
|
-
if (fromDef) return fromDef;
|
|
29
|
-
if ("series" in spec && Array.isArray(spec.series)) {
|
|
30
|
-
return Object.fromEntries(
|
|
31
|
-
spec.series.map((series) => [
|
|
32
|
-
series.field,
|
|
33
|
-
{
|
|
34
|
-
label: series.label ?? series.field,
|
|
35
|
-
color: series.paletteSlot ? chartColorVar(series.paletteSlot) : void 0,
|
|
36
|
-
format: series.format
|
|
37
|
-
}
|
|
38
|
-
])
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
function crossFilterFieldOf(spec, registry = defaultChartRegistry) {
|
|
44
|
-
const field = registry.types.get(spec.type)?.crossFilterField?.(spec);
|
|
45
|
-
if (field !== void 0) return field;
|
|
46
|
-
const xKey = spec.xKey;
|
|
47
|
-
return typeof xKey === "string" ? xKey : "";
|
|
48
|
-
}
|
|
49
|
-
function categoriesOf(spec, rows, registry) {
|
|
50
|
-
const field = categoryFieldOf(spec, registry);
|
|
51
|
-
if (!field) return void 0;
|
|
52
|
-
const values = [];
|
|
53
|
-
for (const row of rows) {
|
|
54
|
-
const value = row[field];
|
|
55
|
-
if ((typeof value === "string" || typeof value === "number") && !values.includes(value)) values.push(value);
|
|
56
|
-
}
|
|
57
|
-
return values.map((value, index) => ({ value, color: chartColorVar(index + 1) }));
|
|
58
|
-
}
|
|
59
|
-
function categoryFieldOf(spec, registry) {
|
|
60
|
-
return registry.types.get(spec.type)?.categoryField?.(spec);
|
|
61
|
-
}
|
|
62
|
-
function renderSpec(props, registry) {
|
|
63
|
-
const def = registry.types.get(props.spec.type);
|
|
64
|
-
if (!def) {
|
|
65
|
-
warnOnce(`type-${props.spec.type}`, `@zuilib/charts: unknown chart type "${props.spec.type}"`);
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
return /* @__PURE__ */ jsx(def.render, { ...props });
|
|
69
|
-
}
|
|
70
|
-
function Chart({
|
|
71
|
-
spec,
|
|
72
|
-
rows,
|
|
73
|
-
registry,
|
|
74
|
-
formats,
|
|
75
|
-
locale,
|
|
76
|
-
className,
|
|
77
|
-
width,
|
|
78
|
-
height,
|
|
79
|
-
aspect,
|
|
80
|
-
loading,
|
|
81
|
-
error,
|
|
82
|
-
emptyState,
|
|
83
|
-
ariaLabel,
|
|
84
|
-
hiddenSeries,
|
|
85
|
-
defaultHiddenSeries,
|
|
86
|
-
onHiddenSeriesChange,
|
|
87
|
-
onMarkClick,
|
|
88
|
-
labels,
|
|
89
|
-
animate
|
|
90
|
-
}) {
|
|
91
|
-
const chartRegistry = registry ?? defaultChartRegistry;
|
|
92
|
-
const crossFilter = useOptionalCrossFilter();
|
|
93
|
-
const formatters = useMemo(() => createFormatters(locale, formats), [locale, formats]);
|
|
94
|
-
const config = useMemo(() => configFromSpec(spec, chartRegistry), [spec, chartRegistry]);
|
|
95
|
-
const [innerHidden, setInnerHidden] = useState(defaultHiddenSeries ?? []);
|
|
96
|
-
const hidden = hiddenSeries ?? innerHidden;
|
|
97
|
-
const onHiddenChange = (next) => {
|
|
98
|
-
if (hiddenSeries === void 0) setInnerHidden(next);
|
|
99
|
-
onHiddenSeriesChange?.(next);
|
|
100
|
-
};
|
|
101
|
-
const markInteraction = {
|
|
102
|
-
field: crossFilterFieldOf(spec, chartRegistry),
|
|
103
|
-
crossFilter,
|
|
104
|
-
interaction: spec.interaction ?? "filter",
|
|
105
|
-
onMarkClick
|
|
106
|
-
};
|
|
107
|
-
return /* @__PURE__ */ jsx(
|
|
108
|
-
chart_frame_default,
|
|
109
|
-
{
|
|
110
|
-
config,
|
|
111
|
-
className,
|
|
112
|
-
width,
|
|
113
|
-
height,
|
|
114
|
-
aspect,
|
|
115
|
-
loading,
|
|
116
|
-
error,
|
|
117
|
-
emptyState,
|
|
118
|
-
empty: rows.length === 0,
|
|
119
|
-
ariaLabel: ariaLabel ?? spec.title,
|
|
120
|
-
labels,
|
|
121
|
-
formats,
|
|
122
|
-
locale,
|
|
123
|
-
hiddenSeries: hidden,
|
|
124
|
-
onHiddenSeriesChange: onHiddenChange,
|
|
125
|
-
categoryField: categoryFieldOf(spec, chartRegistry),
|
|
126
|
-
categories: useMemo(() => categoriesOf(spec, rows, chartRegistry), [spec, rows, chartRegistry]),
|
|
127
|
-
children: renderSpec(
|
|
128
|
-
{
|
|
129
|
-
spec,
|
|
130
|
-
rows,
|
|
131
|
-
formats: formatters,
|
|
132
|
-
hiddenSeries: hidden,
|
|
133
|
-
markInteraction,
|
|
134
|
-
animate: animate ?? !prefersReducedMotion()
|
|
135
|
-
},
|
|
136
|
-
chartRegistry
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
var chart_default = Chart;
|
|
142
|
-
|
|
143
|
-
export {
|
|
144
|
-
configFromSpec,
|
|
145
|
-
crossFilterFieldOf,
|
|
146
|
-
Chart,
|
|
147
|
-
chart_default
|
|
148
|
-
};
|