cortena-ui 1.3.0 → 1.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 +74 -3
- package/dist/a2ui/catalogue-doc.d.ts +44 -0
- package/dist/a2ui/catalogue-doc.js +169 -0
- package/dist/a2ui/catalogue-doc.js.map +1 -0
- package/dist/a2ui/catalogue.d.ts +44 -0
- package/dist/a2ui/catalogue.js +942 -0
- package/dist/a2ui/catalogue.js.map +1 -0
- package/dist/a2ui/index.d.ts +7 -0
- package/dist/a2ui/message.d.ts +232 -0
- package/dist/a2ui/message.js +365 -0
- package/dist/a2ui/message.js.map +1 -0
- package/dist/a2ui/renderer.d.ts +46 -0
- package/dist/a2ui/renderer.js +252 -0
- package/dist/a2ui/renderer.js.map +1 -0
- package/dist/a2ui/views.d.ts +80 -0
- package/dist/a2ui/views.js +596 -0
- package/dist/a2ui/views.js.map +1 -0
- package/dist/a2ui.d.ts +8 -0
- package/dist/a2ui.js +7 -0
- package/dist/chart.d.ts +9 -0
- package/dist/chart.js +9 -0
- package/dist/components/accordion.d.ts +22 -0
- package/dist/components/accordion.js +63 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert.d.ts +15 -0
- package/dist/components/alert.js +64 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.d.ts +18 -0
- package/dist/components/avatar.js +40 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +47 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/breadcrumb.d.ts +71 -0
- package/dist/components/breadcrumb.js +123 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button-link.d.ts +28 -0
- package/dist/components/button-link.js +24 -0
- package/dist/components/button-link.js.map +1 -0
- package/dist/components/button.d.ts +24 -0
- package/dist/components/button.js +65 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.d.ts +28 -0
- package/dist/components/calendar.js +123 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/card.d.ts +32 -0
- package/dist/components/card.js +57 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/chart/chart.d.ts +71 -0
- package/dist/components/chart/chart.js +167 -0
- package/dist/components/chart/chart.js.map +1 -0
- package/dist/components/chart/container.d.ts +88 -0
- package/dist/components/chart/container.js +189 -0
- package/dist/components/chart/container.js.map +1 -0
- package/dist/components/chart/data.js +128 -0
- package/dist/components/chart/data.js.map +1 -0
- package/dist/components/chart/index.d.ts +9 -0
- package/dist/components/chart/index.js +10 -0
- package/dist/components/chart/nivo-charts.js +225 -0
- package/dist/components/chart/nivo-charts.js.map +1 -0
- package/dist/components/chart/nivo-lazy.js +50 -0
- package/dist/components/chart/nivo-lazy.js.map +1 -0
- package/dist/components/chart/nivo-theme.d.ts +156 -0
- package/dist/components/chart/nivo-theme.js +161 -0
- package/dist/components/chart/nivo-theme.js.map +1 -0
- package/dist/components/chart/recharts-charts.js +276 -0
- package/dist/components/chart/recharts-charts.js.map +1 -0
- package/dist/components/chart/recharts-lazy.js +58 -0
- package/dist/components/chart/recharts-lazy.js.map +1 -0
- package/dist/components/chart/recharts-parts.d.ts +10 -0
- package/dist/components/chart/recharts-parts.js +25 -0
- package/dist/components/chart/recharts-parts.js.map +1 -0
- package/dist/components/chart/tokens.d.ts +62 -0
- package/dist/components/chart/tokens.js +192 -0
- package/dist/components/chart/tokens.js.map +1 -0
- package/dist/components/chart/types.d.ts +114 -0
- package/dist/components/chart/types.js +27 -0
- package/dist/components/chart/types.js.map +1 -0
- package/dist/components/checkbox.d.ts +20 -0
- package/dist/components/checkbox.js +24 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +17 -0
- package/dist/components/chip.js +47 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/combobox.d.ts +76 -0
- package/dist/components/combobox.js +201 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/command.d.ts +50 -0
- package/dist/components/command.js +117 -0
- package/dist/components/command.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts +65 -0
- package/dist/components/data-table/data-table.js +588 -0
- package/dist/components/data-table/data-table.js.map +1 -0
- package/dist/components/data-table/export.d.ts +23 -0
- package/dist/components/data-table/export.js +95 -0
- package/dist/components/data-table/export.js.map +1 -0
- package/dist/components/data-table/features.d.ts +66 -0
- package/dist/components/data-table/features.js +103 -0
- package/dist/components/data-table/features.js.map +1 -0
- package/dist/components/data-table/index.d.ts +8 -0
- package/dist/components/data-table/parts.d.ts +79 -0
- package/dist/components/data-table/parts.js +400 -0
- package/dist/components/data-table/parts.js.map +1 -0
- package/dist/components/data-table/system-columns.js +84 -0
- package/dist/components/data-table/system-columns.js.map +1 -0
- package/dist/components/data-table/types.d.ts +172 -0
- package/dist/components/data-table/use-data-table.d.ts +19 -0
- package/dist/components/data-table/use-data-table.js +171 -0
- package/dist/components/data-table/use-data-table.js.map +1 -0
- package/dist/components/data-table/use-server-source.js +200 -0
- package/dist/components/data-table/use-server-source.js.map +1 -0
- package/dist/components/date-field.d.ts +54 -0
- package/dist/components/date-field.js +235 -0
- package/dist/components/date-field.js.map +1 -0
- package/dist/components/date-picker.d.ts +42 -0
- package/dist/components/date-picker.js +125 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +36 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +54 -0
- package/dist/components/dropdown-menu.js +191 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/dropzone.d.ts +48 -0
- package/dist/components/dropzone.js +185 -0
- package/dist/components/dropzone.js.map +1 -0
- package/dist/components/empty-state.d.ts +22 -0
- package/dist/components/empty-state.js +46 -0
- package/dist/components/empty-state.js.map +1 -0
- package/dist/components/error-banner.d.ts +17 -0
- package/dist/components/error-banner.js +52 -0
- package/dist/components/error-banner.js.map +1 -0
- package/dist/components/field.d.ts +64 -0
- package/dist/components/field.js +133 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/form.d.ts +37 -0
- package/dist/components/form.js +103 -0
- package/dist/components/form.js.map +1 -0
- package/dist/components/input.d.ts +17 -0
- package/dist/components/input.js +26 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/kbd.d.ts +11 -0
- package/dist/components/kbd.js +20 -0
- package/dist/components/kbd.js.map +1 -0
- package/dist/components/label.d.ts +14 -0
- package/dist/components/label.js +28 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/loading-skeleton.d.ts +22 -0
- package/dist/components/loading-skeleton.js +123 -0
- package/dist/components/loading-skeleton.js.map +1 -0
- package/dist/components/markdown.d.ts +44 -0
- package/dist/components/markdown.js +231 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/page-header.d.ts +26 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/page-header.js.map +1 -0
- package/dist/components/popover.d.ts +29 -0
- package/dist/components/popover.js +74 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.d.ts +22 -0
- package/dist/components/progress.js +49 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +29 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/scroll-area.d.ts +25 -0
- package/dist/components/scroll-area.js +39 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/section-card.d.ts +29 -0
- package/dist/components/section-card.js +55 -0
- package/dist/components/section-card.js.map +1 -0
- package/dist/components/segmented.d.ts +27 -0
- package/dist/components/segmented.js +32 -0
- package/dist/components/segmented.js.map +1 -0
- package/dist/components/select.d.ts +62 -0
- package/dist/components/select.js +97 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +19 -0
- package/dist/components/separator.js +23 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +35 -0
- package/dist/components/sheet.js +131 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/skeleton.d.ts +12 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/sortable-list.d.ts +67 -0
- package/dist/components/sortable-list.js +206 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/spinner.d.ts +29 -0
- package/dist/components/spinner.js +34 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/status-dot.d.ts +26 -0
- package/dist/components/status-dot.js +47 -0
- package/dist/components/status-dot.js.map +1 -0
- package/dist/components/switch.d.ts +21 -0
- package/dist/components/switch.js +21 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/tabs.d.ts +24 -0
- package/dist/components/tabs.js +60 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +19 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toast.d.ts +47 -0
- package/dist/components/toast.js +106 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/toolbar.d.ts +19 -0
- package/dist/components/toolbar.js +45 -0
- package/dist/components/toolbar.js.map +1 -0
- package/dist/components/tooltip.d.ts +28 -0
- package/dist/components/tooltip.js +59 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/core.d.ts +44 -0
- package/dist/core.js +44 -0
- package/dist/data-table.d.ts +9 -0
- package/dist/data-table.js +7 -0
- package/dist/form.d.ts +7 -0
- package/dist/form.js +7 -0
- package/dist/hooks/use-cortena-theme.d.ts +98 -0
- package/dist/hooks/use-cortena-theme.js +145 -0
- package/dist/hooks/use-cortena-theme.js.map +1 -0
- package/dist/index.d.ts +75 -2678
- package/dist/index.js +66 -8598
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +15 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/render.d.ts +23 -0
- package/dist/lib/render.js +20 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/shakeable.js +39 -0
- package/dist/lib/shakeable.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.js +3 -0
- package/dist/sortable-list.d.ts +3 -0
- package/dist/sortable-list.js +3 -0
- package/package.json +37 -14
- package/src/a2ui/catalogue.ts +140 -103
- package/src/a2ui/views.tsx +5 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/chart/chart.tsx +72 -25
- package/src/components/chart/container.tsx +24 -39
- package/src/components/chart/index.tsx +4 -2
- package/src/components/chart/nivo-charts.tsx +41 -13
- package/src/components/chart/nivo-lazy.tsx +52 -0
- package/src/components/chart/recharts-charts.tsx +98 -3
- package/src/components/chart/recharts-lazy.tsx +72 -0
- package/src/components/chart/recharts-parts.tsx +34 -0
- package/src/components/chart/tokens.ts +1 -1
- package/src/components/chart/types.ts +14 -0
- package/src/components/chip.tsx +1 -1
- package/src/components/combobox.tsx +2 -2
- package/src/components/data-table/export.ts +18 -2
- package/src/components/data-table/features.ts +77 -45
- package/src/components/data-table/parts.tsx +15 -0
- package/src/components/data-table/use-data-table.ts +7 -2
- package/src/components/data-table/use-server-source.ts +17 -4
- package/src/components/date-field.tsx +1 -1
- package/src/components/error-banner.tsx +1 -1
- package/src/components/field.tsx +2 -2
- package/src/components/form.tsx +2 -2
- package/src/components/input.tsx +1 -1
- package/src/components/label.tsx +1 -1
- package/src/components/markdown.tsx +5 -4
- package/src/components/sheet.tsx +2 -2
- package/src/components/sortable-list.tsx +2 -2
- package/src/components/textarea.tsx +1 -1
- package/src/components/toast.tsx +1 -1
- package/src/entries/a2ui.ts +11 -0
- package/src/entries/chart.ts +10 -0
- package/src/entries/core.ts +91 -0
- package/src/entries/data-table.ts +9 -0
- package/src/entries/form.ts +24 -0
- package/src/entries/markdown.ts +9 -0
- package/src/entries/sortable-list.ts +13 -0
- package/src/index.ts +32 -98
- package/src/lib/shakeable.ts +33 -0
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ChartConfig, ChartDataFor, ChartLabels, ChartRenderer, ChartScale, ChartType } from "./types.js";
|
|
3
|
+
import { ChartContainerProps } from "./container.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
//#region src/components/chart/chart.d.ts
|
|
6
|
+
export interface ChartProps<T extends ChartType = ChartType> extends Omit<ChartContainerProps, "children" | "config" | "responsive"> {
|
|
7
|
+
/** Which chart to draw; the package picks the engine. See {@link ChartType} for the data shape per type. */
|
|
8
|
+
type: T;
|
|
9
|
+
/** The data, in the shape documented on {@link ChartType}. */
|
|
10
|
+
data: ChartDataFor<T>;
|
|
11
|
+
/** Series presentation keyed by series id; colours default to `--ds-viz-1..n` in order. */
|
|
12
|
+
config?: ChartConfig;
|
|
13
|
+
/** Nivo renderer; `canvas` for large series. Ignored by the Recharts types and by sunburst. */
|
|
14
|
+
renderer?: ChartRenderer;
|
|
15
|
+
/** Category key for row data (bar, line, area, radar); inferred as the first string field. */
|
|
16
|
+
indexBy?: string;
|
|
17
|
+
/** Series keys for row data; inferred as every numeric field. */
|
|
18
|
+
keys?: string[];
|
|
19
|
+
/** Stack bars and areas instead of grouping them. */
|
|
20
|
+
stacked?: boolean;
|
|
21
|
+
/** Continuous scale for heatmap and calendar. Default sequential. */
|
|
22
|
+
scale?: ChartScale;
|
|
23
|
+
/** Show a legend. Default: when there is more than one series. */
|
|
24
|
+
legend?: boolean;
|
|
25
|
+
/** Draw grid lines (cartesian and radar types). Default true. */
|
|
26
|
+
grid?: boolean;
|
|
27
|
+
/** Show a tooltip on hover. Default true. */
|
|
28
|
+
tooltip?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Per-arc labels for `pie` and `donut`; ignored by every other type. Default
|
|
31
|
+
* `percent`, which is what the reader almost always wants and what cortenaweb's
|
|
32
|
+
* donuts showed before this package existed. See {@link ChartLabels}.
|
|
33
|
+
*/
|
|
34
|
+
labels?: ChartLabels;
|
|
35
|
+
/** Format values in axes, tooltips and labels. */
|
|
36
|
+
valueFormatter?: (value: number) => string;
|
|
37
|
+
/** Calendar range (ISO dates); defaults to the data's extent. */
|
|
38
|
+
from?: string;
|
|
39
|
+
to?: string;
|
|
40
|
+
/** Render the loading skeleton instead of the data. */
|
|
41
|
+
loading?: boolean;
|
|
42
|
+
/** Render the error state with this message instead of the data. */
|
|
43
|
+
error?: React.ReactNode | Error | null;
|
|
44
|
+
/** Content of the empty state. Default "No data". */
|
|
45
|
+
emptyMessage?: React.ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Chart — one component for every chart type the fleet draws.
|
|
49
|
+
*
|
|
50
|
+
* `type` picks the engine: Recharts for bar, line, area, pie, donut, scatter,
|
|
51
|
+
* radar and funnel; Nivo for heatmap, calendar, treemap and sunburst, with
|
|
52
|
+
* `renderer="canvas"` for large series. Both are loaded on the first render of
|
|
53
|
+
* a chart that needs them — Recharts as one chunk, each `@nivo/*` package as
|
|
54
|
+
* its own — so importing `Chart` costs nothing until a chart is drawn, and a
|
|
55
|
+
* screen that draws a bar chart never fetches Nivo.
|
|
56
|
+
* Colours, fonts, axes, grid, tooltip, legend and the empty, loading and
|
|
57
|
+
* error states all come from cortena-design tokens; a consumer passes data
|
|
58
|
+
* and, optionally, a `config` of labels.
|
|
59
|
+
*
|
|
60
|
+
* `heatmap` takes either the Nivo series shape or a categorical
|
|
61
|
+
* {@link ChartMatrix} — `{ rows, columns, values }` — for the correlation and
|
|
62
|
+
* confusion matrices that are already in that form.
|
|
63
|
+
*
|
|
64
|
+
* `testMode` gives the drawing area a 600×300 floor so a chart still draws
|
|
65
|
+
* where the layout gives it no size; see {@link ChartContainerProps.testMode}
|
|
66
|
+
* and README.md, "Charts in tests".
|
|
67
|
+
*/
|
|
68
|
+
declare function Chart<T extends ChartType>({ type, data, config, renderer, indexBy, keys, stacked, scale, legend, grid, tooltip, labels, valueFormatter, from, to, loading, error, emptyMessage, className, height, ...props }: ChartProps<T>): React.JSX.Element;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { Chart };
|
|
71
|
+
//# sourceMappingURL=chart.d.ts.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../../lib/cn.js";
|
|
4
|
+
import { RechartsChartLazy, resetRechartsChunks } from "./recharts-lazy.js";
|
|
5
|
+
import { ChartContainer, ChartEmpty, ChartError, ChartLoading } from "./container.js";
|
|
6
|
+
import { configFor, isEmptyData, isMatrix, isSeriesArray, toRows, toSlices } from "./data.js";
|
|
7
|
+
import { NivoChartLazy, resetNivoChunks } from "./nivo-lazy.js";
|
|
8
|
+
import { chartEngine } from "./types.js";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
//#region src/components/chart/chart.tsx
|
|
12
|
+
function seriesKeys(type, data, indexBy, keys) {
|
|
13
|
+
switch (type) {
|
|
14
|
+
case "bar":
|
|
15
|
+
case "line":
|
|
16
|
+
case "area":
|
|
17
|
+
case "radar": return toRows(data, indexBy, keys).keys;
|
|
18
|
+
case "pie":
|
|
19
|
+
case "donut":
|
|
20
|
+
case "funnel": return toSlices(data).map((s) => s.id);
|
|
21
|
+
case "heatmap":
|
|
22
|
+
if (isMatrix(data)) return data.rows.map(String);
|
|
23
|
+
return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
|
|
24
|
+
case "scatter":
|
|
25
|
+
case "scatterplot": return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
|
|
26
|
+
case "treemap":
|
|
27
|
+
case "sunburst": return (data.children ?? []).map((c) => String(c.id ?? c.name ?? ""));
|
|
28
|
+
case "calendar": return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Catches a Nivo chunk that failed to load. Recovering takes two things,
|
|
33
|
+
* because `React.lazy` memoises a rejection for the life of the page: fresh
|
|
34
|
+
* lazy components ({@link resetNivoChunks}) and a boundary that leaves its
|
|
35
|
+
* failed state. `resetKey` does the second on its own when the chart changes,
|
|
36
|
+
* and the fallback gets a `retry` that does both.
|
|
37
|
+
*/
|
|
38
|
+
var ChunkBoundary = class extends React.Component {
|
|
39
|
+
state = {
|
|
40
|
+
failed: false,
|
|
41
|
+
key: this.props.resetKey
|
|
42
|
+
};
|
|
43
|
+
static getDerivedStateFromError() {
|
|
44
|
+
return { failed: true };
|
|
45
|
+
}
|
|
46
|
+
static getDerivedStateFromProps(props, state) {
|
|
47
|
+
return props.resetKey === state.key ? null : {
|
|
48
|
+
failed: false,
|
|
49
|
+
key: props.resetKey
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
retry = () => {
|
|
53
|
+
resetNivoChunks();
|
|
54
|
+
resetRechartsChunks();
|
|
55
|
+
this.setState({ failed: false });
|
|
56
|
+
};
|
|
57
|
+
render() {
|
|
58
|
+
return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** Shown when an engine chunk could not be fetched, with a way back. */
|
|
62
|
+
function chunkFallback(retry) {
|
|
63
|
+
return /* @__PURE__ */ jsxs(ChartError, { children: [
|
|
64
|
+
"Chart could not be loaded.",
|
|
65
|
+
" ",
|
|
66
|
+
/* @__PURE__ */ jsx("button", {
|
|
67
|
+
type: "button",
|
|
68
|
+
"data-slot": "chart-retry",
|
|
69
|
+
onClick: retry,
|
|
70
|
+
className: "underline underline-offset-2",
|
|
71
|
+
children: "Retry"
|
|
72
|
+
})
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Chart — one component for every chart type the fleet draws.
|
|
77
|
+
*
|
|
78
|
+
* `type` picks the engine: Recharts for bar, line, area, pie, donut, scatter,
|
|
79
|
+
* radar and funnel; Nivo for heatmap, calendar, treemap and sunburst, with
|
|
80
|
+
* `renderer="canvas"` for large series. Both are loaded on the first render of
|
|
81
|
+
* a chart that needs them — Recharts as one chunk, each `@nivo/*` package as
|
|
82
|
+
* its own — so importing `Chart` costs nothing until a chart is drawn, and a
|
|
83
|
+
* screen that draws a bar chart never fetches Nivo.
|
|
84
|
+
* Colours, fonts, axes, grid, tooltip, legend and the empty, loading and
|
|
85
|
+
* error states all come from cortena-design tokens; a consumer passes data
|
|
86
|
+
* and, optionally, a `config` of labels.
|
|
87
|
+
*
|
|
88
|
+
* `heatmap` takes either the Nivo series shape or a categorical
|
|
89
|
+
* {@link ChartMatrix} — `{ rows, columns, values }` — for the correlation and
|
|
90
|
+
* confusion matrices that are already in that form.
|
|
91
|
+
*
|
|
92
|
+
* `testMode` gives the drawing area a 600×300 floor so a chart still draws
|
|
93
|
+
* where the layout gives it no size; see {@link ChartContainerProps.testMode}
|
|
94
|
+
* and README.md, "Charts in tests".
|
|
95
|
+
*/
|
|
96
|
+
function Chart({ type, data, config, renderer = "svg", indexBy, keys, stacked, scale = "sequential", legend, grid = true, tooltip = true, labels = "percent", valueFormatter, from, to, loading, error, emptyMessage, className, height, ...props }) {
|
|
97
|
+
const engine = chartEngine(type);
|
|
98
|
+
const empty = isEmptyData(data);
|
|
99
|
+
const fullConfig = React.useMemo(() => configFor(empty ? [] : seriesKeys(type, data, indexBy, keys), config), [
|
|
100
|
+
type,
|
|
101
|
+
data,
|
|
102
|
+
indexBy,
|
|
103
|
+
keys,
|
|
104
|
+
config,
|
|
105
|
+
empty
|
|
106
|
+
]);
|
|
107
|
+
const showLegend = legend ?? Object.keys(fullConfig).length > 1;
|
|
108
|
+
const errorText = error instanceof Error ? error.message : error;
|
|
109
|
+
let body;
|
|
110
|
+
let responsive = false;
|
|
111
|
+
if (loading) body = /* @__PURE__ */ jsx(ChartLoading, {});
|
|
112
|
+
else if (errorText) body = /* @__PURE__ */ jsx(ChartError, { children: errorText });
|
|
113
|
+
else if (empty) body = /* @__PURE__ */ jsx(ChartEmpty, { children: emptyMessage });
|
|
114
|
+
else if (engine === "nivo") body = /* @__PURE__ */ jsx(ChunkBoundary, {
|
|
115
|
+
resetKey: `${type}|${renderer}`,
|
|
116
|
+
fallback: chunkFallback,
|
|
117
|
+
children: /* @__PURE__ */ jsx(React.Suspense, {
|
|
118
|
+
fallback: /* @__PURE__ */ jsx(ChartLoading, {}),
|
|
119
|
+
children: /* @__PURE__ */ jsx(NivoChartLazy, {
|
|
120
|
+
type,
|
|
121
|
+
data,
|
|
122
|
+
config: fullConfig,
|
|
123
|
+
renderer,
|
|
124
|
+
scale,
|
|
125
|
+
legend: showLegend,
|
|
126
|
+
valueFormatter,
|
|
127
|
+
from,
|
|
128
|
+
to
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
else {
|
|
133
|
+
responsive = true;
|
|
134
|
+
body = /* @__PURE__ */ jsx(ChunkBoundary, {
|
|
135
|
+
resetKey: type,
|
|
136
|
+
fallback: chunkFallback,
|
|
137
|
+
children: /* @__PURE__ */ jsx(RechartsChartLazy, {
|
|
138
|
+
type,
|
|
139
|
+
data,
|
|
140
|
+
config: fullConfig,
|
|
141
|
+
indexBy,
|
|
142
|
+
keys,
|
|
143
|
+
stacked,
|
|
144
|
+
legend: showLegend,
|
|
145
|
+
grid,
|
|
146
|
+
tooltip,
|
|
147
|
+
labels,
|
|
148
|
+
valueFormatter
|
|
149
|
+
})
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return /* @__PURE__ */ jsx(ChartContainer, {
|
|
153
|
+
"data-type": type,
|
|
154
|
+
"data-engine": engine,
|
|
155
|
+
"data-renderer": engine === "nivo" ? renderer : "svg",
|
|
156
|
+
config: fullConfig,
|
|
157
|
+
height,
|
|
158
|
+
responsive,
|
|
159
|
+
className: cn(className),
|
|
160
|
+
...props,
|
|
161
|
+
children: body
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
export { Chart };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart.js","names":[],"sources":["../../../src/components/chart/chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { ChartContainer, ChartEmpty, ChartError, ChartLoading, type ChartContainerProps } from \"./container\";\nimport { configFor, isEmptyData, isMatrix, isSeriesArray, toRows, toSlices } from \"./data\";\nimport type { NivoType } from \"./nivo-charts\";\nimport { NivoChartLazy, resetNivoChunks } from \"./nivo-lazy\";\nimport type { RechartsType } from \"./recharts-charts\";\nimport { RechartsChartLazy, resetRechartsChunks } from \"./recharts-lazy\";\nimport {\n chartEngine,\n type ChartConfig,\n type ChartDataFor,\n type ChartLabels,\n type ChartMatrix,\n type ChartRenderer,\n type ChartRow,\n type ChartScale,\n type ChartSeries,\n type ChartSlice,\n type ChartTree,\n type ChartType,\n} from \"./types\";\n\nexport interface ChartProps<T extends ChartType = ChartType>\n extends Omit<ChartContainerProps, \"children\" | \"config\" | \"responsive\"> {\n /** Which chart to draw; the package picks the engine. See {@link ChartType} for the data shape per type. */\n type: T;\n /** The data, in the shape documented on {@link ChartType}. */\n data: ChartDataFor<T>;\n /** Series presentation keyed by series id; colours default to `--ds-viz-1..n` in order. */\n config?: ChartConfig;\n /** Nivo renderer; `canvas` for large series. Ignored by the Recharts types and by sunburst. */\n renderer?: ChartRenderer;\n /** Category key for row data (bar, line, area, radar); inferred as the first string field. */\n indexBy?: string;\n /** Series keys for row data; inferred as every numeric field. */\n keys?: string[];\n /** Stack bars and areas instead of grouping them. */\n stacked?: boolean;\n /** Continuous scale for heatmap and calendar. Default sequential. */\n scale?: ChartScale;\n /** Show a legend. Default: when there is more than one series. */\n legend?: boolean;\n /** Draw grid lines (cartesian and radar types). Default true. */\n grid?: boolean;\n /** Show a tooltip on hover. Default true. */\n tooltip?: boolean;\n /**\n * Per-arc labels for `pie` and `donut`; ignored by every other type. Default\n * `percent`, which is what the reader almost always wants and what cortenaweb's\n * donuts showed before this package existed. See {@link ChartLabels}.\n */\n labels?: ChartLabels;\n /** Format values in axes, tooltips and labels. */\n valueFormatter?: (value: number) => string;\n /** Calendar range (ISO dates); defaults to the data's extent. */\n from?: string;\n to?: string;\n /** Render the loading skeleton instead of the data. */\n loading?: boolean;\n /** Render the error state with this message instead of the data. */\n error?: React.ReactNode | Error | null;\n /** Content of the empty state. Default \"No data\". */\n emptyMessage?: React.ReactNode;\n}\n\nfunction seriesKeys(type: ChartType, data: unknown, indexBy?: string, keys?: string[]): string[] {\n switch (type) {\n case \"bar\":\n case \"line\":\n case \"area\":\n case \"radar\":\n return toRows(data as ChartRow[] | ChartSeries[], indexBy, keys).keys;\n case \"pie\":\n case \"donut\":\n case \"funnel\":\n return toSlices(data as ChartSlice[]).map((s) => s.id);\n case \"heatmap\":\n if (isMatrix(data)) return data.rows.map(String);\n return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];\n case \"scatter\":\n case \"scatterplot\":\n return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];\n case \"treemap\":\n case \"sunburst\":\n return ((data as ChartTree).children ?? []).map((c) => String(c.id ?? c.name ?? \"\"));\n case \"calendar\":\n return [];\n }\n}\n\ninterface ChunkBoundaryProps {\n children: React.ReactNode;\n /** Changing it clears a previous failure and renders the children again. */\n resetKey: string;\n fallback: (retry: () => void) => React.ReactNode;\n}\n\n/**\n * Catches a Nivo chunk that failed to load. Recovering takes two things,\n * because `React.lazy` memoises a rejection for the life of the page: fresh\n * lazy components ({@link resetNivoChunks}) and a boundary that leaves its\n * failed state. `resetKey` does the second on its own when the chart changes,\n * and the fallback gets a `retry` that does both.\n */\nclass ChunkBoundary extends React.Component<ChunkBoundaryProps, { failed: boolean; key: string }> {\n state = { failed: false, key: this.props.resetKey };\n static getDerivedStateFromError() {\n return { failed: true };\n }\n static getDerivedStateFromProps(props: ChunkBoundaryProps, state: { failed: boolean; key: string }) {\n return props.resetKey === state.key ? null : { failed: false, key: props.resetKey };\n }\n retry = () => {\n resetNivoChunks();\n resetRechartsChunks();\n this.setState({ failed: false });\n };\n render() {\n return this.state.failed ? this.props.fallback(this.retry) : this.props.children;\n }\n}\n\n/** Shown when an engine chunk could not be fetched, with a way back. */\nfunction chunkFallback(retry: () => void): React.ReactNode {\n return (\n <ChartError>\n Chart could not be loaded.{\" \"}\n <button type=\"button\" data-slot=\"chart-retry\" onClick={retry} className=\"underline underline-offset-2\">\n Retry\n </button>\n </ChartError>\n );\n}\n\n/**\n * Chart — one component for every chart type the fleet draws.\n *\n * `type` picks the engine: Recharts for bar, line, area, pie, donut, scatter,\n * radar and funnel; Nivo for heatmap, calendar, treemap and sunburst, with\n * `renderer=\"canvas\"` for large series. Both are loaded on the first render of\n * a chart that needs them — Recharts as one chunk, each `@nivo/*` package as\n * its own — so importing `Chart` costs nothing until a chart is drawn, and a\n * screen that draws a bar chart never fetches Nivo.\n * Colours, fonts, axes, grid, tooltip, legend and the empty, loading and\n * error states all come from cortena-design tokens; a consumer passes data\n * and, optionally, a `config` of labels.\n *\n * `heatmap` takes either the Nivo series shape or a categorical\n * {@link ChartMatrix} — `{ rows, columns, values }` — for the correlation and\n * confusion matrices that are already in that form.\n *\n * `testMode` gives the drawing area a 600×300 floor so a chart still draws\n * where the layout gives it no size; see {@link ChartContainerProps.testMode}\n * and README.md, \"Charts in tests\".\n */\nfunction Chart<T extends ChartType>({\n type,\n data,\n config,\n renderer = \"svg\",\n indexBy,\n keys,\n stacked,\n scale = \"sequential\",\n legend,\n grid = true,\n tooltip = true,\n labels = \"percent\",\n valueFormatter,\n from,\n to,\n loading,\n error,\n emptyMessage,\n className,\n height,\n ...props\n}: ChartProps<T>) {\n const engine = chartEngine(type);\n const empty = isEmptyData(data);\n const fullConfig = React.useMemo(\n () => configFor(empty ? [] : seriesKeys(type, data, indexBy, keys), config),\n [type, data, indexBy, keys, config, empty],\n );\n const showLegend = legend ?? Object.keys(fullConfig).length > 1;\n const errorText = error instanceof Error ? error.message : error;\n\n let body: React.ReactNode;\n let responsive = false;\n if (loading) body = <ChartLoading />;\n else if (errorText) body = <ChartError>{errorText}</ChartError>;\n else if (empty) body = <ChartEmpty>{emptyMessage}</ChartEmpty>;\n else if (engine === \"nivo\") {\n body = (\n <ChunkBoundary resetKey={`${type}|${renderer}`} fallback={chunkFallback}>\n <React.Suspense fallback={<ChartLoading />}>\n <NivoChartLazy\n type={type as NivoType}\n data={data}\n config={fullConfig}\n renderer={renderer}\n scale={scale}\n legend={showLegend}\n valueFormatter={valueFormatter}\n from={from}\n to={to}\n />\n </React.Suspense>\n </ChunkBoundary>\n );\n } else {\n responsive = true;\n body = (\n <ChunkBoundary resetKey={type} fallback={chunkFallback}>\n <RechartsChartLazy\n type={type as RechartsType}\n data={data}\n config={fullConfig}\n indexBy={indexBy}\n keys={keys}\n stacked={stacked}\n legend={showLegend}\n grid={grid}\n tooltip={tooltip}\n labels={labels}\n valueFormatter={valueFormatter}\n />\n </ChunkBoundary>\n );\n }\n\n return (\n <ChartContainer\n data-type={type}\n data-engine={engine}\n data-renderer={engine === \"nivo\" ? renderer : \"svg\"}\n config={fullConfig}\n height={height}\n responsive={responsive}\n className={cn(className)}\n {...props}\n >\n {body}\n </ChartContainer>\n );\n}\n\nexport { Chart };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ChartConfig } from "./types.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { LegendPayload, TooltipContentProps } from "recharts";
|
|
5
|
+
//#region src/components/chart/container.d.ts
|
|
6
|
+
interface ChartContextValue {
|
|
7
|
+
config: ChartConfig;
|
|
8
|
+
/** The container element, so token resolution can honour a nested theme scope. */
|
|
9
|
+
element: HTMLDivElement | null;
|
|
10
|
+
}
|
|
11
|
+
/** The nearest ChartContainer's config and element; null outside one. */
|
|
12
|
+
export declare function useChart(): ChartContextValue | null;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the colour a series key paints with: `config[key].color` when set,
|
|
15
|
+
* otherwise `var(--ds-viz-N)` by the key's position in the config (or the
|
|
16
|
+
* `index` given). Recharts consumers reference it as `var(--color-<key>)`,
|
|
17
|
+
* which ChartContainer defines on its element.
|
|
18
|
+
*/
|
|
19
|
+
export declare function seriesColor(config: ChartConfig | undefined, key: string, index?: number): string;
|
|
20
|
+
export interface ChartContainerProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
21
|
+
/** Series presentation; colours default to `--ds-viz-1..n` in key order. */
|
|
22
|
+
config?: ChartConfig;
|
|
23
|
+
/** Height of the drawing area; width is always 100% of the parent. Default 300. */
|
|
24
|
+
height?: number | string;
|
|
25
|
+
/**
|
|
26
|
+
* Wrap children in Recharts' ResponsiveContainer (default). Nivo's
|
|
27
|
+
* `Responsive*` components size themselves, so the Nivo renderers pass
|
|
28
|
+
* `responsive={false}`.
|
|
29
|
+
*/
|
|
30
|
+
responsive?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Give the drawing area a floor of 600×300 so it draws where the layout
|
|
33
|
+
* gives it none.
|
|
34
|
+
*
|
|
35
|
+
* Both engines size themselves from the parent box. Under jsdom nothing has
|
|
36
|
+
* layout — every element measures 0×0 — so a page test that renders a screen
|
|
37
|
+
* containing a Chart gets an empty `<div>` and can assert nothing about it.
|
|
38
|
+
* `testMode` sets `min-width` / `min-height` on the container and hands the
|
|
39
|
+
* same floor to Recharts' ResponsiveContainer, so the chart draws at
|
|
40
|
+
* 600×300. It is a floor, not a fixed size: a parent that *does* give the
|
|
41
|
+
* chart 900px still gets 900px, so a screenshot test is unaffected.
|
|
42
|
+
*/
|
|
43
|
+
testMode?: boolean;
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* ChartContainer — the sized, token-styled surface every chart draws in.
|
|
48
|
+
*
|
|
49
|
+
* It defines `--color-<key>` for each config key (so Recharts parts can say
|
|
50
|
+
* `fill="var(--color-revenue)"`) and styles Recharts' axis, grid, tick and
|
|
51
|
+
* cursor classes from tokens with a caption-sm floor for text, so a custom
|
|
52
|
+
* chart composed inside it needs no local colours or fonts.
|
|
53
|
+
*/
|
|
54
|
+
declare function ChartContainer({ config, height, responsive, testMode, className, style, children, ...props }: ChartContainerProps): React.JSX.Element;
|
|
55
|
+
/** "No data" placeholder; rendered by Chart when the data is empty. */
|
|
56
|
+
declare function ChartEmpty({ children, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
57
|
+
/** Error placeholder; rendered by Chart for an `error` prop or a failed Nivo chunk. */
|
|
58
|
+
declare function ChartError({ className, children, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
59
|
+
/** Loading placeholder: a skeleton the size of the drawing area, shown while a Nivo chunk loads. */
|
|
60
|
+
declare function ChartLoading({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
61
|
+
export interface ChartTooltipContentProps extends Partial<Pick<TooltipContentProps, "active" | "payload" | "label">> {
|
|
62
|
+
className?: string;
|
|
63
|
+
/** Hide the series label column. */
|
|
64
|
+
hideLabel?: boolean;
|
|
65
|
+
/** Hide the colour swatch. */
|
|
66
|
+
hideIndicator?: boolean;
|
|
67
|
+
/** Format a value; defaults to `toLocaleString`. */
|
|
68
|
+
formatter?: (value: number | string, name: string) => React.ReactNode;
|
|
69
|
+
/** Format the header (the category). */
|
|
70
|
+
labelFormatter?: (label: React.ReactNode) => React.ReactNode;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* ChartTooltipContent — the popover body for Recharts' Tooltip, styled like
|
|
74
|
+
* the package Tooltip on the popover tokens. Pass as `content` to
|
|
75
|
+
* ChartTooltip; reads series labels and colours from the ChartContainer config.
|
|
76
|
+
*/
|
|
77
|
+
declare function ChartTooltipContent({ active, payload, label, className, hideLabel, hideIndicator, formatter, labelFormatter }: ChartTooltipContentProps): React.JSX.Element | null;
|
|
78
|
+
export interface ChartLegendContentProps {
|
|
79
|
+
payload?: readonly LegendPayload[];
|
|
80
|
+
verticalAlign?: "top" | "bottom" | "middle";
|
|
81
|
+
className?: string;
|
|
82
|
+
hideIcon?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/** ChartLegendContent — Recharts' legend on tokens; reads labels from the config. */
|
|
85
|
+
declare function ChartLegendContent({ payload, verticalAlign, className, hideIcon }: ChartLegendContentProps): React.JSX.Element | null;
|
|
86
|
+
//#endregion
|
|
87
|
+
export { ChartContainer, ChartEmpty, ChartError, ChartLegendContent, ChartLoading, ChartTooltipContent };
|
|
88
|
+
//# sourceMappingURL=container.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../../lib/cn.js";
|
|
4
|
+
import { Skeleton } from "../skeleton.js";
|
|
5
|
+
import { RechartsResponsive } from "./recharts-lazy.js";
|
|
6
|
+
import { vizVar } from "./tokens.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
//#region src/components/chart/container.tsx
|
|
10
|
+
const ChartContext = /* @__PURE__ */ React.createContext(null);
|
|
11
|
+
/** The nearest ChartContainer's config and element; null outside one. */
|
|
12
|
+
function useChart() {
|
|
13
|
+
return React.useContext(ChartContext);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the colour a series key paints with: `config[key].color` when set,
|
|
17
|
+
* otherwise `var(--ds-viz-N)` by the key's position in the config (or the
|
|
18
|
+
* `index` given). Recharts consumers reference it as `var(--color-<key>)`,
|
|
19
|
+
* which ChartContainer defines on its element.
|
|
20
|
+
*/
|
|
21
|
+
function seriesColor(config, key, index) {
|
|
22
|
+
const entry = config?.[key];
|
|
23
|
+
if (entry?.color) return entry.color;
|
|
24
|
+
const position = (config ? Object.keys(config) : []).indexOf(key);
|
|
25
|
+
return vizVar(position >= 0 ? position : index ?? 0);
|
|
26
|
+
}
|
|
27
|
+
/** The box `testMode` falls back to when the layout gives the chart none. */
|
|
28
|
+
const TEST_WIDTH = 600;
|
|
29
|
+
const TEST_HEIGHT = 300;
|
|
30
|
+
/**
|
|
31
|
+
* ChartContainer — the sized, token-styled surface every chart draws in.
|
|
32
|
+
*
|
|
33
|
+
* It defines `--color-<key>` for each config key (so Recharts parts can say
|
|
34
|
+
* `fill="var(--color-revenue)"`) and styles Recharts' axis, grid, tick and
|
|
35
|
+
* cursor classes from tokens with a caption-sm floor for text, so a custom
|
|
36
|
+
* chart composed inside it needs no local colours or fonts.
|
|
37
|
+
*/
|
|
38
|
+
function ChartContainer({ config = {}, height = 300, responsive = true, testMode = false, className, style, children, ...props }) {
|
|
39
|
+
const [element, setElement] = React.useState(null);
|
|
40
|
+
const vars = React.useMemo(() => {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const key of Object.keys(config)) out[`--color-${key}`] = seriesColor(config, key);
|
|
43
|
+
return out;
|
|
44
|
+
}, [config]);
|
|
45
|
+
const ctx = React.useMemo(() => ({
|
|
46
|
+
config,
|
|
47
|
+
element
|
|
48
|
+
}), [config, element]);
|
|
49
|
+
return /* @__PURE__ */ jsx(ChartContext.Provider, {
|
|
50
|
+
value: ctx,
|
|
51
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
52
|
+
ref: setElement,
|
|
53
|
+
"data-slot": "chart",
|
|
54
|
+
"data-test-mode": testMode ? "" : void 0,
|
|
55
|
+
className: cn("relative w-full min-w-0 overflow-hidden text-[length:var(--ds-text-caption-sm)] text-[color:var(--ds-foreground)]", responsive ? "flex justify-center" : "block [&>div]:h-full [&>div]:w-full", "[&_.recharts-cartesian-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-polar-angle-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-polar-radius-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-cartesian-grid-horizontal_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-cartesian-grid-vertical_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-angle_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-concentric-polygon]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-concentric-circle]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-cartesian-axis-line]:stroke-[var(--ds-border)]", "[&_.recharts-cartesian-axis-tick-line]:stroke-[var(--ds-border)]", "[&_.recharts-radial-bar-background-sector]:fill-[var(--ds-muted)]", "[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-[var(--ds-hover)]", "[&_.recharts-curve.recharts-tooltip-cursor]:stroke-[var(--ds-border-strong)]", "[&_.recharts-reference-line_line]:stroke-[var(--ds-border-strong)]", "[&_.recharts-pie-sector_.recharts-sector]:stroke-[var(--ds-card)]", "[&_.recharts-funnel-trapezoid_.recharts-trapezoid]:stroke-[var(--ds-card)]", "[&_.recharts-layer]:outline-hidden [&_.recharts-surface]:outline-hidden [&_.recharts-sector]:outline-hidden", "[&_text]:font-[family-name:var(--ds-font-body)]", className),
|
|
56
|
+
style: {
|
|
57
|
+
height,
|
|
58
|
+
...testMode ? {
|
|
59
|
+
minWidth: TEST_WIDTH,
|
|
60
|
+
minHeight: TEST_HEIGHT
|
|
61
|
+
} : null,
|
|
62
|
+
...vars,
|
|
63
|
+
...style
|
|
64
|
+
},
|
|
65
|
+
...props,
|
|
66
|
+
children: responsive ? /* @__PURE__ */ jsx(React.Suspense, {
|
|
67
|
+
fallback: /* @__PURE__ */ jsx(ChartLoading, {}),
|
|
68
|
+
children: /* @__PURE__ */ jsx(RechartsResponsive, {
|
|
69
|
+
width: "100%",
|
|
70
|
+
height: "100%",
|
|
71
|
+
minWidth: testMode ? TEST_WIDTH : void 0,
|
|
72
|
+
minHeight: testMode ? TEST_HEIGHT : void 0,
|
|
73
|
+
children
|
|
74
|
+
})
|
|
75
|
+
}) : children
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function ChartState({ className, ...props }) {
|
|
80
|
+
return /* @__PURE__ */ jsx("div", {
|
|
81
|
+
className: cn("flex h-full w-full items-center justify-center rounded-[var(--ds-radius-md)] border border-dashed border-[var(--ds-border-subtle)]", "text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-muted-foreground)]", className),
|
|
82
|
+
...props
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** "No data" placeholder; rendered by Chart when the data is empty. */
|
|
86
|
+
function ChartEmpty({ children = "No data", ...props }) {
|
|
87
|
+
return /* @__PURE__ */ jsx(ChartState, {
|
|
88
|
+
"data-slot": "chart-empty",
|
|
89
|
+
role: "status",
|
|
90
|
+
...props,
|
|
91
|
+
children
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/** Error placeholder; rendered by Chart for an `error` prop or a failed Nivo chunk. */
|
|
95
|
+
function ChartError({ className, children = "Chart could not be rendered", ...props }) {
|
|
96
|
+
return /* @__PURE__ */ jsx(ChartState, {
|
|
97
|
+
"data-slot": "chart-error",
|
|
98
|
+
role: "alert",
|
|
99
|
+
className: cn("border-[var(--ds-destructive)] text-[color:var(--ds-destructive)]", className),
|
|
100
|
+
...props,
|
|
101
|
+
children
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** Loading placeholder: a skeleton the size of the drawing area, shown while a Nivo chunk loads. */
|
|
105
|
+
function ChartLoading({ className, ...props }) {
|
|
106
|
+
return /* @__PURE__ */ jsx("div", {
|
|
107
|
+
"data-slot": "chart-loading",
|
|
108
|
+
className: cn("h-full w-full", className),
|
|
109
|
+
...props,
|
|
110
|
+
children: /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full rounded-[var(--ds-radius-md)]" })
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function formatValue(value, formatter, name = "") {
|
|
114
|
+
if (formatter && (typeof value === "number" || typeof value === "string")) return formatter(value, name);
|
|
115
|
+
if (typeof value === "number") return value.toLocaleString();
|
|
116
|
+
if (value == null) return "";
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* ChartTooltipContent — the popover body for Recharts' Tooltip, styled like
|
|
121
|
+
* the package Tooltip on the popover tokens. Pass as `content` to
|
|
122
|
+
* ChartTooltip; reads series labels and colours from the ChartContainer config.
|
|
123
|
+
*/
|
|
124
|
+
function ChartTooltipContent({ active, payload, label, className, hideLabel, hideIndicator, formatter, labelFormatter }) {
|
|
125
|
+
const chart = useChart();
|
|
126
|
+
if (!active || !payload?.length) return null;
|
|
127
|
+
const heading = labelFormatter ? labelFormatter(label) : label;
|
|
128
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
129
|
+
"data-slot": "chart-tooltip",
|
|
130
|
+
className: cn("grid min-w-32 gap-1.5 rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-popover)] px-2.5 py-1.5", "text-[length:var(--ds-text-caption)] text-[color:var(--ds-popover-foreground)] shadow-[var(--ds-shadow-md)]", className),
|
|
131
|
+
children: [!hideLabel && heading != null && heading !== "" ? /* @__PURE__ */ jsx("div", {
|
|
132
|
+
className: "font-medium",
|
|
133
|
+
children: heading
|
|
134
|
+
}) : null, /* @__PURE__ */ jsx("div", {
|
|
135
|
+
className: "grid gap-1",
|
|
136
|
+
children: payload.map((item, i) => {
|
|
137
|
+
const key = String(item.dataKey ?? item.name ?? i);
|
|
138
|
+
const entry = chart?.config[key];
|
|
139
|
+
const colour = item.color ?? item.fill ?? item.stroke ?? `var(--color-${key})`;
|
|
140
|
+
const name = entry?.label ?? item.name ?? key;
|
|
141
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
142
|
+
className: "flex items-center gap-2",
|
|
143
|
+
children: [
|
|
144
|
+
hideIndicator ? null : /* @__PURE__ */ jsx("span", {
|
|
145
|
+
"aria-hidden": true,
|
|
146
|
+
"data-slot": "chart-tooltip-indicator",
|
|
147
|
+
className: "size-2 shrink-0 rounded-[var(--ds-radius-sm)]",
|
|
148
|
+
style: { background: colour }
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ jsx("span", {
|
|
151
|
+
className: "flex-1 text-[color:var(--ds-muted-foreground)]",
|
|
152
|
+
children: name
|
|
153
|
+
}),
|
|
154
|
+
/* @__PURE__ */ jsx("span", {
|
|
155
|
+
className: "font-[family-name:var(--ds-font-mono)] font-medium tabular-nums",
|
|
156
|
+
children: formatValue(item.value, formatter, String(name))
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
}, key);
|
|
160
|
+
})
|
|
161
|
+
})]
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** ChartLegendContent — Recharts' legend on tokens; reads labels from the config. */
|
|
165
|
+
function ChartLegendContent({ payload, verticalAlign = "bottom", className, hideIcon }) {
|
|
166
|
+
const chart = useChart();
|
|
167
|
+
if (!payload?.length) return null;
|
|
168
|
+
return /* @__PURE__ */ jsx("div", {
|
|
169
|
+
"data-slot": "chart-legend",
|
|
170
|
+
className: cn("flex flex-wrap items-center justify-center gap-x-4 gap-y-1", "text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]", verticalAlign === "top" ? "pb-3" : "pt-3", className),
|
|
171
|
+
children: payload.map((item, i) => {
|
|
172
|
+
const key = String(item.dataKey ?? item.value ?? i);
|
|
173
|
+
const entry = chart?.config[key];
|
|
174
|
+
const Icon = entry?.icon;
|
|
175
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
176
|
+
className: "flex items-center gap-1.5",
|
|
177
|
+
children: [hideIcon ? null : Icon ? /* @__PURE__ */ jsx(Icon, {}) : /* @__PURE__ */ jsx("span", {
|
|
178
|
+
"aria-hidden": true,
|
|
179
|
+
className: "size-2 shrink-0 rounded-[var(--ds-radius-sm)]",
|
|
180
|
+
style: { background: item.color ?? `var(--color-${key})` }
|
|
181
|
+
}), entry?.label ?? item.value]
|
|
182
|
+
}, key);
|
|
183
|
+
})
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { ChartContainer, ChartContext, ChartEmpty, ChartError, ChartLegendContent, ChartLoading, ChartTooltipContent, seriesColor, useChart };
|
|
188
|
+
|
|
189
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.js","names":[],"sources":["../../../src/components/chart/container.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n// Types only. The two Recharts *values* this file used to reach for — `Tooltip`\n// and `Legend` — are in `recharts-parts.tsx`, so that nothing on the eager path\n// imports the engine; see the note there.\nimport type { LegendPayload, TooltipContentProps } from \"recharts\";\nimport { RechartsResponsive } from \"./recharts-lazy\";\nimport { cn } from \"@/lib/cn\";\nimport { Skeleton } from \"@/components/skeleton\";\nimport { vizVar } from \"./tokens\";\nimport type { ChartConfig } from \"./types\";\n\n/* ── context ─────────────────────────────────────────────────────────────── */\n\ninterface ChartContextValue {\n config: ChartConfig;\n /** The container element, so token resolution can honour a nested theme scope. */\n element: HTMLDivElement | null;\n}\n\nconst ChartContext = /* @__PURE__ */ React.createContext<ChartContextValue | null>(null);\n\n/** The nearest ChartContainer's config and element; null outside one. */\nexport function useChart(): ChartContextValue | null {\n return React.useContext(ChartContext);\n}\n\n/**\n * Resolve the colour a series key paints with: `config[key].color` when set,\n * otherwise `var(--ds-viz-N)` by the key's position in the config (or the\n * `index` given). Recharts consumers reference it as `var(--color-<key>)`,\n * which ChartContainer defines on its element.\n */\nexport function seriesColor(config: ChartConfig | undefined, key: string, index?: number): string {\n const entry = config?.[key];\n if (entry?.color) return entry.color;\n const keys = config ? Object.keys(config) : [];\n const position = keys.indexOf(key);\n return vizVar(position >= 0 ? position : (index ?? 0));\n}\n\n/* ── container ───────────────────────────────────────────────────────────── */\n\nexport interface ChartContainerProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** Series presentation; colours default to `--ds-viz-1..n` in key order. */\n config?: ChartConfig;\n /** Height of the drawing area; width is always 100% of the parent. Default 300. */\n height?: number | string;\n /**\n * Wrap children in Recharts' ResponsiveContainer (default). Nivo's\n * `Responsive*` components size themselves, so the Nivo renderers pass\n * `responsive={false}`.\n */\n responsive?: boolean;\n /**\n * Give the drawing area a floor of 600×300 so it draws where the layout\n * gives it none.\n *\n * Both engines size themselves from the parent box. Under jsdom nothing has\n * layout — every element measures 0×0 — so a page test that renders a screen\n * containing a Chart gets an empty `<div>` and can assert nothing about it.\n * `testMode` sets `min-width` / `min-height` on the container and hands the\n * same floor to Recharts' ResponsiveContainer, so the chart draws at\n * 600×300. It is a floor, not a fixed size: a parent that *does* give the\n * chart 900px still gets 900px, so a screenshot test is unaffected.\n */\n testMode?: boolean;\n children: React.ReactNode;\n}\n\n/** The box `testMode` falls back to when the layout gives the chart none. */\nconst TEST_WIDTH = 600;\nconst TEST_HEIGHT = 300;\n\n/**\n * ChartContainer — the sized, token-styled surface every chart draws in.\n *\n * It defines `--color-<key>` for each config key (so Recharts parts can say\n * `fill=\"var(--color-revenue)\"`) and styles Recharts' axis, grid, tick and\n * cursor classes from tokens with a caption-sm floor for text, so a custom\n * chart composed inside it needs no local colours or fonts.\n */\nfunction ChartContainer({\n config = {},\n height = 300,\n responsive = true,\n testMode = false,\n className,\n style,\n children,\n ...props\n}: ChartContainerProps) {\n const [element, setElement] = React.useState<HTMLDivElement | null>(null);\n const vars = React.useMemo(() => {\n const out: Record<string, string> = {};\n for (const key of Object.keys(config)) out[`--color-${key}`] = seriesColor(config, key);\n return out;\n }, [config]);\n const ctx = React.useMemo(() => ({ config, element }), [config, element]);\n\n return (\n <ChartContext.Provider value={ctx}>\n <div\n ref={setElement}\n data-slot=\"chart\"\n data-test-mode={testMode ? \"\" : undefined}\n className={cn(\n \"relative w-full min-w-0 overflow-hidden text-[length:var(--ds-text-caption-sm)] text-[color:var(--ds-foreground)]\",\n responsive ? \"flex justify-center\" : \"block [&>div]:h-full [&>div]:w-full\",\n \"[&_.recharts-cartesian-axis-tick-value]:fill-[var(--ds-muted-foreground)]\",\n \"[&_.recharts-polar-angle-axis-tick-value]:fill-[var(--ds-muted-foreground)]\",\n \"[&_.recharts-polar-radius-axis-tick-value]:fill-[var(--ds-muted-foreground)]\",\n \"[&_.recharts-cartesian-grid-horizontal_line]:stroke-[var(--ds-border-subtle)]\",\n \"[&_.recharts-cartesian-grid-vertical_line]:stroke-[var(--ds-border-subtle)]\",\n \"[&_.recharts-polar-grid-angle_line]:stroke-[var(--ds-border-subtle)]\",\n \"[&_.recharts-polar-grid-concentric-polygon]:stroke-[var(--ds-border-subtle)]\",\n \"[&_.recharts-polar-grid-concentric-circle]:stroke-[var(--ds-border-subtle)]\",\n \"[&_.recharts-cartesian-axis-line]:stroke-[var(--ds-border)]\",\n \"[&_.recharts-cartesian-axis-tick-line]:stroke-[var(--ds-border)]\",\n \"[&_.recharts-radial-bar-background-sector]:fill-[var(--ds-muted)]\",\n \"[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-[var(--ds-hover)]\",\n \"[&_.recharts-curve.recharts-tooltip-cursor]:stroke-[var(--ds-border-strong)]\",\n \"[&_.recharts-reference-line_line]:stroke-[var(--ds-border-strong)]\",\n \"[&_.recharts-pie-sector_.recharts-sector]:stroke-[var(--ds-card)]\",\n \"[&_.recharts-funnel-trapezoid_.recharts-trapezoid]:stroke-[var(--ds-card)]\",\n \"[&_.recharts-layer]:outline-hidden [&_.recharts-surface]:outline-hidden [&_.recharts-sector]:outline-hidden\",\n \"[&_text]:font-[family-name:var(--ds-font-body)]\",\n className,\n )}\n style={\n {\n height,\n ...(testMode ? { minWidth: TEST_WIDTH, minHeight: TEST_HEIGHT } : null),\n ...vars,\n ...style,\n } as React.CSSProperties\n }\n {...props}\n >\n {responsive ? (\n // Recharts' ResponsiveContainer arrives with the engine chunk, so\n // this suspends on the first chart drawn on the page — and so does\n // whatever it wraps, which is why the skeleton lives here rather than\n // in `Chart`: this is the innermost boundary, so it is the one that\n // catches. A `ChartContainer` used directly, to compose a one-off\n // Recharts chart, gets the same treatment and needs no boundary of\n // its own.\n <React.Suspense fallback={<ChartLoading />}>\n <RechartsResponsive\n width=\"100%\"\n height=\"100%\"\n minWidth={testMode ? TEST_WIDTH : undefined}\n minHeight={testMode ? TEST_HEIGHT : undefined}\n >\n {children as React.ReactElement}\n </RechartsResponsive>\n </React.Suspense>\n ) : (\n children\n )}\n </div>\n </ChartContext.Provider>\n );\n}\n\n/* ── states ──────────────────────────────────────────────────────────────── */\n\nfunction ChartState({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\n \"flex h-full w-full items-center justify-center rounded-[var(--ds-radius-md)] border border-dashed border-[var(--ds-border-subtle)]\",\n \"text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-muted-foreground)]\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** \"No data\" placeholder; rendered by Chart when the data is empty. */\nfunction ChartEmpty({ children = \"No data\", ...props }: React.ComponentProps<\"div\">) {\n return (\n <ChartState data-slot=\"chart-empty\" role=\"status\" {...props}>\n {children}\n </ChartState>\n );\n}\n\n/** Error placeholder; rendered by Chart for an `error` prop or a failed Nivo chunk. */\nfunction ChartError({ className, children = \"Chart could not be rendered\", ...props }: React.ComponentProps<\"div\">) {\n return (\n <ChartState\n data-slot=\"chart-error\"\n role=\"alert\"\n className={cn(\"border-[var(--ds-destructive)] text-[color:var(--ds-destructive)]\", className)}\n {...props}\n >\n {children}\n </ChartState>\n );\n}\n\n/** Loading placeholder: a skeleton the size of the drawing area, shown while a Nivo chunk loads. */\nfunction ChartLoading({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div data-slot=\"chart-loading\" className={cn(\"h-full w-full\", className)} {...props}>\n <Skeleton className=\"h-full w-full rounded-[var(--ds-radius-md)]\" />\n </div>\n );\n}\n\n/* ── tooltip ─────────────────────────────────────────────────────────────── */\n\nexport interface ChartTooltipContentProps\n extends Partial<Pick<TooltipContentProps, \"active\" | \"payload\" | \"label\">> {\n className?: string;\n /** Hide the series label column. */\n hideLabel?: boolean;\n /** Hide the colour swatch. */\n hideIndicator?: boolean;\n /** Format a value; defaults to `toLocaleString`. */\n formatter?: (value: number | string, name: string) => React.ReactNode;\n /** Format the header (the category). */\n labelFormatter?: (label: React.ReactNode) => React.ReactNode;\n}\n\nfunction formatValue(value: unknown, formatter?: ChartTooltipContentProps[\"formatter\"], name = \"\"): React.ReactNode {\n if (formatter && (typeof value === \"number\" || typeof value === \"string\")) return formatter(value, name);\n if (typeof value === \"number\") return value.toLocaleString();\n if (value == null) return \"\";\n return String(value);\n}\n\n/**\n * ChartTooltipContent — the popover body for Recharts' Tooltip, styled like\n * the package Tooltip on the popover tokens. Pass as `content` to\n * ChartTooltip; reads series labels and colours from the ChartContainer config.\n */\nfunction ChartTooltipContent({\n active,\n payload,\n label,\n className,\n hideLabel,\n hideIndicator,\n formatter,\n labelFormatter,\n}: ChartTooltipContentProps) {\n const chart = useChart();\n if (!active || !payload?.length) return null;\n const heading = labelFormatter ? labelFormatter(label) : label;\n return (\n <div\n data-slot=\"chart-tooltip\"\n className={cn(\n \"grid min-w-32 gap-1.5 rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-popover)] px-2.5 py-1.5\",\n \"text-[length:var(--ds-text-caption)] text-[color:var(--ds-popover-foreground)] shadow-[var(--ds-shadow-md)]\",\n className,\n )}\n >\n {!hideLabel && heading != null && heading !== \"\" ? (\n <div className=\"font-medium\">{heading}</div>\n ) : null}\n <div className=\"grid gap-1\">\n {payload.map((item, i) => {\n const key = String(item.dataKey ?? item.name ?? i);\n const entry = chart?.config[key];\n const colour = item.color ?? item.fill ?? item.stroke ?? `var(--color-${key})`;\n const name = entry?.label ?? item.name ?? key;\n return (\n <div key={key} className=\"flex items-center gap-2\">\n {hideIndicator ? null : (\n <span\n aria-hidden\n data-slot=\"chart-tooltip-indicator\"\n className=\"size-2 shrink-0 rounded-[var(--ds-radius-sm)]\"\n style={{ background: colour }}\n />\n )}\n <span className=\"flex-1 text-[color:var(--ds-muted-foreground)]\">{name}</span>\n <span className=\"font-[family-name:var(--ds-font-mono)] font-medium tabular-nums\">\n {formatValue(item.value, formatter, String(name))}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n/* ── legend ──────────────────────────────────────────────────────────────── */\n\nexport interface ChartLegendContentProps {\n payload?: readonly LegendPayload[];\n verticalAlign?: \"top\" | \"bottom\" | \"middle\";\n className?: string;\n hideIcon?: boolean;\n}\n\n/** ChartLegendContent — Recharts' legend on tokens; reads labels from the config. */\nfunction ChartLegendContent({ payload, verticalAlign = \"bottom\", className, hideIcon }: ChartLegendContentProps) {\n const chart = useChart();\n if (!payload?.length) return null;\n return (\n <div\n data-slot=\"chart-legend\"\n className={cn(\n \"flex flex-wrap items-center justify-center gap-x-4 gap-y-1\",\n \"text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]\",\n verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\",\n className,\n )}\n >\n {payload.map((item, i) => {\n const key = String(item.dataKey ?? item.value ?? i);\n const entry = chart?.config[key];\n const Icon = entry?.icon;\n return (\n <div key={key} className=\"flex items-center gap-1.5\">\n {hideIcon ? null : Icon ? (\n <Icon />\n ) : (\n <span\n aria-hidden\n className=\"size-2 shrink-0 rounded-[var(--ds-radius-sm)]\"\n style={{ background: item.color ?? `var(--color-${key})` }}\n />\n )}\n {entry?.label ?? item.value}\n </div>\n );\n })}\n </div>\n );\n}\n\nexport { ChartContainer, ChartContext, ChartEmpty, ChartError, ChartLoading, ChartTooltipContent, ChartLegendContent };\n"],"mappings":"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"}
|