@kind-ui/charts 0.1.0 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +657 -865
- package/dist/activity-rings.d.ts +2 -1
- package/dist/activity-rings.js +6 -3
- package/dist/animation.d.ts +11 -3
- package/dist/animation.js +18 -10
- package/dist/area-chart.d.ts +3 -1
- package/dist/area-chart.js +15 -10
- package/dist/area-series.d.ts +9 -1
- package/dist/area-series.js +35 -8
- package/dist/bar-chart.d.ts +10 -3
- package/dist/bar-chart.js +17 -4
- package/dist/bar-series.d.ts +11 -1
- package/dist/bar-series.js +65 -13
- package/dist/box-plot.js +2 -2
- package/dist/category-cells.d.ts +7 -1
- package/dist/category-cells.js +32 -2
- package/dist/chart-background-pattern.d.ts +23 -0
- package/dist/chart-background-pattern.js +31 -0
- package/dist/chart-context.d.ts +6 -2
- package/dist/chart-interaction.d.ts +94 -0
- package/dist/chart-interaction.js +224 -0
- package/dist/combo-chart.d.ts +8 -5
- package/dist/combo-chart.js +14 -12
- package/dist/configured-line-chart.d.ts +5 -1
- package/dist/configured-line-chart.js +8 -5
- package/dist/emphasis.d.ts +5 -2
- package/dist/emphasis.js +18 -4
- package/dist/fill-pattern.d.ts +25 -0
- package/dist/fill-pattern.js +23 -0
- package/dist/heatmap.d.ts +3 -1
- package/dist/heatmap.js +34 -12
- package/dist/histogram-chart.js +10 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +8 -1
- package/dist/legend.d.ts +2 -0
- package/dist/legend.js +28 -7
- package/dist/line-chart.d.ts +13 -3
- package/dist/line-chart.js +54 -34
- package/dist/line-dash.d.ts +8 -0
- package/dist/line-dash.js +18 -0
- package/dist/line-series.d.ts +9 -1
- package/dist/line-series.js +38 -11
- package/dist/loading-cartesian-designs.d.ts +6 -0
- package/dist/loading-cartesian-designs.js +123 -0
- package/dist/loading-motion.d.ts +37 -0
- package/dist/loading-motion.js +88 -0
- package/dist/loading-polar-designs.d.ts +6 -0
- package/dist/loading-polar-designs.js +95 -0
- package/dist/loading-skeleton.d.ts +29 -0
- package/dist/loading-skeleton.js +143 -0
- package/dist/loading-standalone-designs.d.ts +5 -0
- package/dist/loading-standalone-designs.js +126 -0
- package/dist/percent-stack.d.ts +18 -0
- package/dist/percent-stack.js +33 -0
- package/dist/pie-chart.d.ts +7 -3
- package/dist/pie-chart.js +37 -6
- package/dist/pie-pin-identity.d.ts +4 -0
- package/dist/pie-pin-identity.js +10 -0
- package/dist/pie-series.d.ts +4 -0
- package/dist/pie-series.js +137 -14
- package/dist/pie-tooltip-pin.d.ts +4 -0
- package/dist/pie-tooltip-pin.js +52 -0
- package/dist/point-marker.d.ts +19 -0
- package/dist/point-marker.js +19 -0
- package/dist/polar-chart.d.ts +14 -5
- package/dist/polar-chart.js +41 -10
- package/dist/polar-series.js +68 -22
- package/dist/radar-interaction.js +10 -4
- package/dist/radial-category.d.ts +2 -0
- package/dist/reveal-clip.d.ts +16 -0
- package/dist/reveal-clip.js +18 -0
- package/dist/root.d.ts +5 -9
- package/dist/root.js +46 -11
- package/dist/sankey-chart.d.ts +5 -1
- package/dist/sankey-chart.js +64 -13
- package/dist/sankey-colors.d.ts +3 -0
- package/dist/sankey-interaction.d.ts +8 -0
- package/dist/sankey-interaction.js +32 -0
- package/dist/sankey-legend.d.ts +2 -1
- package/dist/sankey-legend.js +18 -3
- package/dist/sankey-node-label.d.ts +22 -0
- package/dist/sankey-node-label.js +34 -0
- package/dist/scatter-chart.d.ts +5 -3
- package/dist/scatter-chart.js +14 -4
- package/dist/scatter-series.js +5 -3
- package/dist/series-color.d.ts +10 -0
- package/dist/series-color.js +48 -0
- package/dist/series-interaction.d.ts +12 -0
- package/dist/series-interaction.js +82 -0
- package/dist/series-label.d.ts +7 -0
- package/dist/series-label.js +18 -0
- package/dist/series-paint.d.ts +8 -0
- package/dist/series-paint.js +27 -0
- package/dist/styles.css +151 -1
- package/dist/stylesheet-warning.d.ts +2 -0
- package/dist/stylesheet-warning.js +86 -0
- package/dist/tooltip-content.d.ts +8 -1
- package/dist/tooltip-content.js +15 -4
- package/dist/tooltip.d.ts +3 -1
- package/dist/tooltip.js +8 -5
- package/dist/types.d.ts +11 -2
- package/dist/waterfall-chart.d.ts +4 -0
- package/dist/waterfall-chart.js +7 -0
- package/package.json +2 -2
package/dist/polar-chart.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { type ComponentProps } from "react";
|
|
2
2
|
import { RadarChart as EngineRadarChart, RadialBarChart as EngineRadialBarChart } from "recharts";
|
|
3
|
-
import { type
|
|
3
|
+
import { type BaseAnimation } from "./animation.js";
|
|
4
4
|
import type { CategoryKey } from "./category-cells.js";
|
|
5
5
|
import { type RadarSelectionProps } from "./radar-interaction.js";
|
|
6
|
-
export type RadarAnimation =
|
|
7
|
-
export type RadialBarAnimation =
|
|
6
|
+
export type RadarAnimation = BaseAnimation;
|
|
7
|
+
export type RadialBarAnimation = BaseAnimation;
|
|
8
8
|
export type RadarChartProps<DataPoint = unknown> = ComponentProps<typeof EngineRadarChart<DataPoint>> & RadarSelectionProps & {
|
|
9
9
|
animate?: boolean | RadarAnimation | undefined;
|
|
10
|
+
loading?: boolean | undefined;
|
|
11
|
+
loadingLabel?: string | undefined;
|
|
10
12
|
};
|
|
11
13
|
export type RadialBarChartProps<DataPoint = unknown> = ComponentProps<typeof EngineRadialBarChart<DataPoint>> & {
|
|
12
14
|
/** Opt-in category colors from Root.config, resolved from original chart rows. */
|
|
13
15
|
categoryKey?: CategoryKey<DataPoint> | undefined;
|
|
16
|
+
interactionBinding?: "root";
|
|
14
17
|
animate?: boolean | RadialBarAnimation | undefined;
|
|
18
|
+
loading?: boolean | undefined;
|
|
19
|
+
loadingLabel?: string | undefined;
|
|
15
20
|
/** Entrance direction only; native chart and axis angles stay consumer-owned. */
|
|
16
21
|
animationDirection?: "clockwise" | "anticlockwise" | undefined;
|
|
17
22
|
};
|
|
@@ -29,5 +34,9 @@ export declare const RadialMotion: import("react").Context<{
|
|
|
29
34
|
direction: "clockwise" | "anticlockwise";
|
|
30
35
|
}>;
|
|
31
36
|
/** Native polar composition with shared metadata, interaction and optional Motion. */
|
|
32
|
-
export declare function RadarChart<DataPoint = unknown>({ animate, selection, selectedSeries, onSelectedSeriesChange, children, ...props }: RadarChartProps<DataPoint>): import("react").JSX.Element;
|
|
33
|
-
export declare function RadialBarChart<DataPoint = unknown>(
|
|
37
|
+
export declare function RadarChart<DataPoint = unknown>({ animate, loading, loadingLabel, selection, selectedSeries, onSelectedSeriesChange, children, ...props }: RadarChartProps<DataPoint>): import("react").JSX.Element;
|
|
38
|
+
export declare function RadialBarChart<DataPoint = unknown>(props: RadialBarChartProps<DataPoint>): import("react").JSX.Element;
|
|
39
|
+
/** Private recipe integration; the public chart keeps its native prop contract. */
|
|
40
|
+
export declare function RadialBarChartFrame<DataPoint = unknown>({ skeletonFamily, animate, loading, loadingLabel, animationDirection, categoryKey, interactionBinding, children, ...props }: RadialBarChartProps<DataPoint> & {
|
|
41
|
+
skeletonFamily?: "radial-bar" | "activity-rings";
|
|
42
|
+
}): import("react").JSX.Element;
|
package/dist/polar-chart.js
CHANGED
|
@@ -4,7 +4,10 @@ import { animate as animateValue } from "motion/react";
|
|
|
4
4
|
import { createContext, useCallback, useLayoutEffect, useRef, useState, useSyncExternalStore, } from "react";
|
|
5
5
|
import { RadarChart as EngineRadarChart, RadialBarChart as EngineRadialBarChart } from "recharts";
|
|
6
6
|
import { MotionContext } from "./animation.js";
|
|
7
|
+
import { filterCategoryRows } from "./category-cells.js";
|
|
8
|
+
import { useChartInteraction } from "./chart-interaction.js";
|
|
7
9
|
import { LineChartFrame, useLineInteraction } from "./line-chart.js";
|
|
10
|
+
import { PolarLoadingDesign } from "./loading-polar-designs.js";
|
|
8
11
|
import { RadarSelectionProvider } from "./radar-interaction.js";
|
|
9
12
|
import { RadialCategory } from "./radial-category.js";
|
|
10
13
|
export const PolarMotion = createContext({
|
|
@@ -26,11 +29,18 @@ function subscribe(change) {
|
|
|
26
29
|
const snapshot = () => window.matchMedia(query).matches;
|
|
27
30
|
const serverSnapshot = () => true;
|
|
28
31
|
const defaultHover = { type: "spring", stiffness: 210, damping: 28, mass: 0.8 };
|
|
29
|
-
function usePolarMotion(animate) {
|
|
32
|
+
function usePolarMotion(animate, loading) {
|
|
30
33
|
const reduced = useSyncExternalStore(subscribe, snapshot, serverSnapshot);
|
|
31
34
|
const [finished, setFinished] = useState(false);
|
|
32
|
-
const interrupt = useCallback(() =>
|
|
33
|
-
|
|
35
|
+
const interrupt = useCallback(() => {
|
|
36
|
+
if (!loading)
|
|
37
|
+
setFinished(true);
|
|
38
|
+
}, [loading]);
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
if (loading)
|
|
41
|
+
setFinished(false);
|
|
42
|
+
}, [loading]);
|
|
43
|
+
const enabled = animate !== false && !reduced && !loading;
|
|
34
44
|
const options = typeof animate === "object" ? animate : {};
|
|
35
45
|
return { enabled, options, reveal: enabled && !finished, interrupt };
|
|
36
46
|
}
|
|
@@ -62,12 +72,16 @@ function PolarLifecycle({ layout, cx, cy, innerRadius, outerRadius, startAngle,
|
|
|
62
72
|
return null;
|
|
63
73
|
}
|
|
64
74
|
/** Native polar composition with shared metadata, interaction and optional Motion. */
|
|
65
|
-
export function RadarChart({ animate = false, selection, selectedSeries, onSelectedSeriesChange, children, ...props }) {
|
|
66
|
-
const { enabled, options, reveal, interrupt } = usePolarMotion(animate);
|
|
75
|
+
export function RadarChart({ animate = false, loading, loadingLabel, selection, selectedSeries, onSelectedSeriesChange, children, ...props }) {
|
|
76
|
+
const { enabled, options, reveal, interrupt } = usePolarMotion(animate, loading);
|
|
67
77
|
const [progress, setProgress] = useState(1);
|
|
68
78
|
const duration = options.revealDurationMs ?? 1000;
|
|
69
79
|
const easing = options.revealEasing ?? "easeOut";
|
|
70
80
|
const started = useRef(false);
|
|
81
|
+
useLayoutEffect(() => {
|
|
82
|
+
if (loading)
|
|
83
|
+
started.current = false;
|
|
84
|
+
}, [loading]);
|
|
71
85
|
const previous = useRef([duration, easing]);
|
|
72
86
|
const previousEnabled = useRef(enabled);
|
|
73
87
|
useLayoutEffect(() => {
|
|
@@ -93,15 +107,28 @@ export function RadarChart({ animate = false, selection, selectedSeries, onSelec
|
|
|
93
107
|
});
|
|
94
108
|
return () => controls.stop();
|
|
95
109
|
}, [reveal, duration, easing, interrupt]);
|
|
96
|
-
return (_jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(RadarMotion, { value: { reveal, progress }, children: _jsx(PolarMotion, { value: { reveal, options }, children: _jsx(RadarSelectionProvider, { selection: selection, selectedSeries: selectedSeries, onSelectedSeriesChange: onSelectedSeriesChange, children: _jsxs(LineChartFrame, { engine: (EngineRadarChart), chartProps: props, motionEnabled: enabled, interrupt: interrupt, children: [_jsx(PolarLifecycle, { ...props, children: children }), children] }) }) }) }) }));
|
|
110
|
+
return (_jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(RadarMotion, { value: { reveal, progress }, children: _jsx(PolarMotion, { value: { reveal, options }, children: _jsx(RadarSelectionProvider, { selection: selection, selectedSeries: selectedSeries, onSelectedSeriesChange: onSelectedSeriesChange, children: _jsxs(LineChartFrame, { engine: (EngineRadarChart), loading: loading, loadingLabel: loadingLabel, loadingSkeleton: "radar", loadingDesign: (seed) => _jsx(PolarLoadingDesign, { family: "radar", seed: seed }), loadingAnimation: options, chartProps: props, motionEnabled: enabled, interrupt: interrupt, children: [_jsx(PolarLifecycle, { ...props, children: children }), children] }) }) }) }) }));
|
|
97
111
|
}
|
|
98
|
-
export function RadialBarChart(
|
|
112
|
+
export function RadialBarChart(props) {
|
|
113
|
+
return _jsx(RadialBarChartFrame, { ...props });
|
|
114
|
+
}
|
|
115
|
+
/** Private recipe integration; the public chart keeps its native prop contract. */
|
|
116
|
+
export function RadialBarChartFrame({ skeletonFamily = "radial-bar", animate = false, loading, loadingLabel, animationDirection = "clockwise", categoryKey, interactionBinding, children, ...props }) {
|
|
117
|
+
const interaction = useChartInteraction();
|
|
118
|
+
if (interactionBinding &&
|
|
119
|
+
(interaction.kind !== "category" || categoryKey === undefined || props.data === undefined))
|
|
120
|
+
throw new Error("RadialBarChart Root interaction binding requires category-kind Root, categoryKey and explicit data");
|
|
121
|
+
const filtered = interactionBinding && categoryKey !== undefined && props.data
|
|
122
|
+
? filterCategoryRows(props.data, categoryKey, interaction.visible, null)
|
|
123
|
+
: undefined;
|
|
99
124
|
if (categoryKey !== undefined && props.data === undefined)
|
|
100
125
|
throw new Error("RadialBarChart categoryKey requires explicit chart data");
|
|
101
126
|
const categories = categoryKey === undefined
|
|
102
127
|
? null
|
|
103
128
|
: {
|
|
104
|
-
data: props.data ?? [],
|
|
129
|
+
data: filtered?.data ?? props.data ?? [],
|
|
130
|
+
originalData: props.data ?? [],
|
|
131
|
+
bound: interactionBinding === "root",
|
|
105
132
|
key: (row) => {
|
|
106
133
|
if (typeof categoryKey === "function")
|
|
107
134
|
return categoryKey(row);
|
|
@@ -110,11 +137,15 @@ export function RadialBarChart({ animate = false, animationDirection = "clockwis
|
|
|
110
137
|
: "";
|
|
111
138
|
},
|
|
112
139
|
};
|
|
113
|
-
const { enabled, options, reveal, interrupt } = usePolarMotion(animate);
|
|
140
|
+
const { enabled, options, reveal, interrupt } = usePolarMotion(animate, loading);
|
|
114
141
|
const [progress, setProgress] = useState(1);
|
|
115
142
|
const duration = options.revealDurationMs ?? 1000;
|
|
116
143
|
const easing = options.revealEasing ?? "easeOut";
|
|
117
144
|
const started = useRef(false);
|
|
145
|
+
useLayoutEffect(() => {
|
|
146
|
+
if (loading)
|
|
147
|
+
started.current = false;
|
|
148
|
+
}, [loading]);
|
|
118
149
|
const previous = useRef([duration, easing, animationDirection]);
|
|
119
150
|
const previousEnabled = useRef(enabled);
|
|
120
151
|
useLayoutEffect(() => {
|
|
@@ -141,5 +172,5 @@ export function RadialBarChart({ animate = false, animationDirection = "clockwis
|
|
|
141
172
|
});
|
|
142
173
|
return () => controls.stop();
|
|
143
174
|
}, [reveal, duration, easing, interrupt]);
|
|
144
|
-
return (_jsx(RadialCategory, { value: categories, children: _jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(RadialMotion, { value: { reveal, progress, direction: animationDirection }, children: _jsx(PolarMotion, { value: { reveal, options }, children: _jsxs(LineChartFrame, { engine: (EngineRadialBarChart), chartProps: props, motionEnabled: enabled, interrupt: interrupt, children: [_jsx(PolarLifecycle, { ...props, children: children }), children] }) }) }) }) }));
|
|
175
|
+
return (_jsx(RadialCategory, { value: categories, children: _jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(RadialMotion, { value: { reveal, progress, direction: animationDirection }, children: _jsx(PolarMotion, { value: { reveal, options }, children: _jsxs(LineChartFrame, { engine: (EngineRadialBarChart), loading: loading, loadingLabel: loadingLabel, loadingSkeleton: skeletonFamily, loadingDesign: (seed) => _jsx(PolarLoadingDesign, { family: skeletonFamily, seed: seed }), loadingAnimation: { ...options, direction: animationDirection }, chartProps: { ...props, ...(filtered ? { data: filtered.data } : {}) }, motionEnabled: enabled, interrupt: interrupt, children: [_jsx(PolarLifecycle, { ...props, children: children }), children] }) }) }) }) }));
|
|
145
176
|
}
|
package/dist/polar-series.js
CHANGED
|
@@ -5,25 +5,32 @@ import { animate, motion, useMotionValue } from "motion/react";
|
|
|
5
5
|
import { memo, use, useId, useLayoutEffect, useRef, useState } from "react";
|
|
6
6
|
import { DefaultZIndexes, Polygon, Radar, RadialBar, Sector, ZIndexLayer } from "recharts";
|
|
7
7
|
import { ActiveMarker } from "./animation.js";
|
|
8
|
-
import { categoryCells } from "./category-cells.js";
|
|
8
|
+
import { categoryCells, filterCategoryRows } from "./category-cells.js";
|
|
9
9
|
import { useChart } from "./chart-context.js";
|
|
10
|
+
import { useChartInteraction, useInteractionFocus } from "./chart-interaction.js";
|
|
11
|
+
import { EmphasisMark } from "./emphasis.js";
|
|
10
12
|
import { useLineInteraction } from "./line-chart.js";
|
|
11
13
|
import { PolarMotion, RadarMotion, RadialMotion } from "./polar-chart.js";
|
|
12
14
|
import { PolarMaterialFilter } from "./polar-material.js";
|
|
13
15
|
import { RadarSelectionLayer, useRadarSelectionDot } from "./radar-interaction.js";
|
|
14
16
|
import { RadialCategory } from "./radial-category.js";
|
|
17
|
+
import { SeriesInteractionLayer, useSeriesInteraction } from "./series-interaction.js";
|
|
15
18
|
// Native Radar uses a props-identity animation key even with animation disabled.
|
|
16
19
|
// Avoid replacing its polygon for unrelated frame state during a pointer press.
|
|
17
20
|
const StableRadar = memo(Radar);
|
|
18
21
|
const radarActiveDot = _jsx(ActiveMarker, {});
|
|
19
22
|
function usePolarSeries(kind, props, geometry) {
|
|
20
|
-
const { config, visibleSeries } = useChart();
|
|
23
|
+
const { config, paints, visibleSeries } = useChart();
|
|
24
|
+
const interactionOwner = useChartInteraction();
|
|
21
25
|
const { registerSeries, invalidate } = useLineInteraction();
|
|
22
26
|
const { reveal, options } = use(PolarMotion);
|
|
23
27
|
const generatedId = useId();
|
|
24
28
|
const id = props.id || generatedId;
|
|
25
29
|
const key = props.seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined);
|
|
26
|
-
const hide = props.hide === true ||
|
|
30
|
+
const hide = props.hide === true ||
|
|
31
|
+
(interactionOwner.kind === "series" &&
|
|
32
|
+
visibleSeries !== undefined &&
|
|
33
|
+
!visibleSeries.includes(key ?? ""));
|
|
27
34
|
const previous = useRef([...geometry, hide]);
|
|
28
35
|
useLayoutEffect(() => {
|
|
29
36
|
const inputs = [...geometry, hide];
|
|
@@ -56,7 +63,7 @@ function usePolarSeries(kind, props, geometry) {
|
|
|
56
63
|
}, [reveal, hide, opacity, duration, easing]);
|
|
57
64
|
if (key === undefined && visibleSeries !== undefined)
|
|
58
65
|
throw new Error(`${kind} requires seriesKey for controlled non-string dataKey`);
|
|
59
|
-
const color = key !== undefined && Object.hasOwn(config, key) ?
|
|
66
|
+
const color = key !== undefined && Object.hasOwn(config, key) ? paints[key] : undefined;
|
|
60
67
|
return { id, hide, color, opacity };
|
|
61
68
|
}
|
|
62
69
|
// Recharts injects its original computed points and callback props into this renderer.
|
|
@@ -90,24 +97,50 @@ export function RadarSeries({ seriesKey, hide, stroke, fill, className, material
|
|
|
90
97
|
props.baseLinePoints,
|
|
91
98
|
props.isRange,
|
|
92
99
|
]);
|
|
100
|
+
const interaction = useSeriesInteraction(seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), series.hide, hide === true, undefined, props.onClick);
|
|
93
101
|
const selectionDot = useRadarSelectionDot(props.dot, seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined));
|
|
94
102
|
const filterId = `kind-ui-polar-${useId().replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
95
103
|
const materialized = material !== "plain" &&
|
|
96
104
|
props.shape === undefined &&
|
|
97
105
|
props.filter === undefined &&
|
|
98
106
|
props.style?.filter === undefined;
|
|
99
|
-
return (_jsxs(ZIndexLayer, { zIndex: props.zIndex ?? DefaultZIndexes.area, children: [materialized && (_jsxs(_Fragment, { children: [_jsx(PolarMaterialFilter, { material: material, id: filterId }), _jsx("style", { children: `.${filterId} .recharts-radar-polygon > .recharts-polygon { filter: url(#${filterId}); }` })] })), _jsx(RadarSelectionLayer, { seriesKey: seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), hidden: series.hide, children: _jsx(motion.g, { "data-kind-ui": "radar-reveal", initial: false, children: _jsx(StableRadar, { activeDot: radarActiveDot, ...props, ...(props.shape === undefined ? { shape: renderRadarEntrance } : {}), ...(selectionDot !== undefined ? { dot: selectionDot } : {}), id: series.id, hide: series.hide, isAnimationActive: false, zIndex: 0, ...(stroke !== undefined
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
return (_jsxs(ZIndexLayer, { zIndex: props.zIndex ?? DefaultZIndexes.area, children: [materialized && (_jsxs(_Fragment, { children: [_jsx(PolarMaterialFilter, { material: material, id: filterId }), _jsx("style", { children: `.${filterId} .recharts-radar-polygon > .recharts-polygon { filter: url(#${filterId}); }` })] })), _jsx(SeriesInteractionLayer, { seriesKey: seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), hidden: series.hide, children: _jsx(RadarSelectionLayer, { seriesKey: seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), hidden: series.hide, children: _jsx(motion.g, { "data-kind-ui": "radar-reveal", initial: false, children: _jsx(StableRadar, { activeDot: radarActiveDot, ...props, ...(interaction.onClick !== undefined ? { onClick: interaction.onClick } : {}), ...(props.shape === undefined ? { shape: renderRadarEntrance } : {}), ...(selectionDot !== undefined ? { dot: selectionDot } : {}), id: series.id, hide: series.hide, isAnimationActive: false, zIndex: 0, ...(stroke !== undefined
|
|
108
|
+
? { stroke }
|
|
109
|
+
: series.color !== undefined
|
|
110
|
+
? { stroke: series.color }
|
|
111
|
+
: {}), ...(fill !== undefined
|
|
112
|
+
? { fill }
|
|
113
|
+
: series.color !== undefined
|
|
114
|
+
? { fill: series.color }
|
|
115
|
+
: {}), className: ["kind-ui-radar-series", filterId, className].filter(Boolean).join(" ") }) }) }) })] }));
|
|
108
116
|
}
|
|
109
117
|
// Recharts computes every arc; the entrance masks its paint without changing its path.
|
|
110
118
|
function RadialEntranceSector(props) {
|
|
119
|
+
const categories = use(RadialCategory);
|
|
120
|
+
const interaction = useChartInteraction();
|
|
121
|
+
const key = categories?.bound ? categories.key(props.payload) : undefined;
|
|
122
|
+
const content = useRadialSector(props);
|
|
123
|
+
const interactive = key !== undefined &&
|
|
124
|
+
interaction.interactive &&
|
|
125
|
+
interaction.markActivation &&
|
|
126
|
+
interaction.eligible.includes(key);
|
|
127
|
+
const focusRef = useInteractionFocus(key, interactive);
|
|
128
|
+
return key === undefined ? (content) : (_jsx(EmphasisMark, { ref: focusRef, "data-interaction-focus-key": interactive ? key : undefined, target: { kind: "category", key, scope: "radial", seriesKey: key }, role: interactive ? "button" : undefined, tabIndex: interactive ? 0 : undefined, "aria-label": interactive ? `${interaction.mode === "focus" ? "Highlight" : "Toggle"} ${key}` : undefined, "aria-pressed": interactive
|
|
129
|
+
? interaction.mode === "focus"
|
|
130
|
+
? interaction.selected === key
|
|
131
|
+
: true
|
|
132
|
+
: undefined, onKeyDown: (event) => {
|
|
133
|
+
if (interactive &&
|
|
134
|
+
event.target === event.currentTarget &&
|
|
135
|
+
!event.defaultPrevented &&
|
|
136
|
+
(event.key === "Enter" || event.key === " ")) {
|
|
137
|
+
if (!event.repeat)
|
|
138
|
+
interaction.activate({ kind: "category", key }, "mark", event);
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
}
|
|
141
|
+
}, children: content }));
|
|
142
|
+
}
|
|
143
|
+
function useRadialSector(props) {
|
|
111
144
|
const { reveal, progress, direction } = use(RadialMotion);
|
|
112
145
|
const id = `kind-ui-radial-entrance-${useId().replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
113
146
|
const group = useRef(null);
|
|
@@ -139,9 +172,13 @@ function RadialEntranceSector(props) {
|
|
|
139
172
|
/** Series identity is independent from native category payload/Cell identity. */
|
|
140
173
|
export function RadialBarSeries({ seriesKey, hide, fill, className, material = "plain", ...props }) {
|
|
141
174
|
const categories = use(RadialCategory);
|
|
142
|
-
const { config } = useChart();
|
|
175
|
+
const { config, paints } = useChart();
|
|
176
|
+
const owner = useChartInteraction();
|
|
177
|
+
const filtered = categories?.bound
|
|
178
|
+
? filterCategoryRows(categories.originalData, categories.key, owner.visible, props.children)
|
|
179
|
+
: undefined;
|
|
143
180
|
const children = categories
|
|
144
|
-
? categoryCells(categories.data, categories.key, config, props.children, fill)
|
|
181
|
+
? categoryCells(categories.data, categories.key, config, paints, filtered?.children ?? props.children, fill)
|
|
145
182
|
: props.children;
|
|
146
183
|
const series = usePolarSeries("RadialBarSeries", { ...props, seriesKey, hide }, [
|
|
147
184
|
props.dataKey,
|
|
@@ -153,17 +190,26 @@ export function RadialBarSeries({ seriesKey, hide, fill, className, material = "
|
|
|
153
190
|
props.maxBarSize,
|
|
154
191
|
props.minPointSize,
|
|
155
192
|
]);
|
|
193
|
+
const interaction = useSeriesInteraction(seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), series.hide, hide === true, undefined, props.onClick);
|
|
156
194
|
const filterId = `kind-ui-polar-${useId().replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
157
195
|
const materialized = material !== "plain" &&
|
|
158
196
|
(props.shape === undefined || typeof props.shape === "boolean") &&
|
|
159
197
|
(props.activeShape === undefined || typeof props.activeShape === "boolean") &&
|
|
160
198
|
props.filter === undefined &&
|
|
161
199
|
props.style?.filter === undefined;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
const onClick = categories?.bound
|
|
201
|
+
? (...args) => {
|
|
202
|
+
props.onClick?.(...args);
|
|
203
|
+
owner.activate({ kind: "category", key: categories.key(args[0].payload) }, "mark", args[2]);
|
|
204
|
+
}
|
|
205
|
+
: interaction.onClick;
|
|
206
|
+
return (_jsxs(ZIndexLayer, { zIndex: props.zIndex ?? DefaultZIndexes.bar, children: [materialized && _jsx(PolarMaterialFilter, { material: material, id: filterId }), _jsx(SeriesInteractionLayer, { seriesKey: seriesKey ?? (typeof props.dataKey === "string" ? props.dataKey : undefined), hidden: series.hide, children: _jsx(motion.g, { "data-kind-ui": "radial-bar-reveal", initial: false, children: _jsx(RadialBar, { ...props, ...(onClick !== undefined ? { onClick } : {}), ...(props.shape === undefined ? { shape: RadialEntranceSector } : {}), ...(categories?.bound && props.shape === undefined && props.activeShape === undefined
|
|
207
|
+
? { activeShape: RadialEntranceSector }
|
|
208
|
+
: {}), ...(props.background === true
|
|
209
|
+
? { background: { fill: "var(--kind-ui-radial-track, #f1f1f1)" } }
|
|
210
|
+
: {}), ...(materialized ? { filter: `url(#${filterId})` } : {}), id: series.id, hide: series.hide, isAnimationActive: false, zIndex: 0, ...(fill !== undefined
|
|
211
|
+
? { fill }
|
|
212
|
+
: series.color !== undefined
|
|
213
|
+
? { fill: series.color }
|
|
214
|
+
: {}), className: ["kind-ui-radial-bar-series", className].filter(Boolean).join(" "), children: children }) }) })] }));
|
|
169
215
|
}
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { cloneElement, createContext, isValidElement, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useState, } from "react";
|
|
4
4
|
import { Dot } from "recharts";
|
|
5
5
|
import { useChart } from "./chart-context.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useChartInteraction } from "./chart-interaction.js";
|
|
7
7
|
import { useLineInteraction } from "./line-chart.js";
|
|
8
8
|
const Context = createContext(null);
|
|
9
9
|
const Mode = createContext(false);
|
|
@@ -11,8 +11,11 @@ const Mode = createContext(false);
|
|
|
11
11
|
export function RadarSelectionProvider({ selection = "none", selectedSeries, onSelectedSeriesChange, children, }) {
|
|
12
12
|
if (selectedSeries !== undefined && onSelectedSeriesChange === undefined)
|
|
13
13
|
throw new Error("RadarChart requires onSelectedSeriesChange for controlled selectedSeries");
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const interaction = useChartInteraction();
|
|
15
|
+
if (interaction.mode === "focus" &&
|
|
16
|
+
(selection !== "none" || selectedSeries !== undefined || onSelectedSeriesChange !== undefined))
|
|
17
|
+
throw new Error("RadarChart selection controls conflict with Root focus; choose one selection owner");
|
|
18
|
+
const enabled = selection === "series";
|
|
16
19
|
const [internal, setInternal] = useState(null);
|
|
17
20
|
const [series, setSeries] = useState(() => new Map());
|
|
18
21
|
const register = useCallback((owner, key, visible) => {
|
|
@@ -51,7 +54,10 @@ export function RadarSelectionProvider({ selection = "none", selectedSeries, onS
|
|
|
51
54
|
const value = useMemo(() => ({ enabled, selected, register, toggle }), [enabled, selected, register, toggle]);
|
|
52
55
|
return (_jsx(Mode, { value: enabled, children: _jsx(Context, { value: value, children: _jsx("div", { style: { display: "contents" }, onKeyDown: (event) => {
|
|
53
56
|
if (event.key === "Escape" && !event.defaultPrevented && enabled && requested !== null)
|
|
54
|
-
|
|
57
|
+
queueMicrotask(() => {
|
|
58
|
+
if (!event.defaultPrevented)
|
|
59
|
+
change(null);
|
|
60
|
+
});
|
|
55
61
|
}, children: children }) }) }));
|
|
56
62
|
}
|
|
57
63
|
/** An extra paint/control layer preserves native shape, data, refs and event handlers. */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Transition } from "motion/react";
|
|
2
|
+
export type RevealDirection = "left-to-right" | "right-to-left" | "center-out" | "edges-in";
|
|
3
|
+
type Options = {
|
|
4
|
+
revealDirection?: RevealDirection;
|
|
5
|
+
revealDurationMs?: number;
|
|
6
|
+
revealEasing?: Transition["ease"];
|
|
7
|
+
};
|
|
8
|
+
/** Entrance paint only: percentage rectangles never transform native series geometry. */
|
|
9
|
+
export declare function RevealClip({ id, family, combo, options, finish, }: {
|
|
10
|
+
id: string;
|
|
11
|
+
family?: "line" | "area";
|
|
12
|
+
combo?: boolean;
|
|
13
|
+
options: Options;
|
|
14
|
+
finish?: () => void;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { motion } from "motion/react";
|
|
4
|
+
/** Entrance paint only: percentage rectangles never transform native series geometry. */
|
|
5
|
+
export function RevealClip({ id, family, combo = false, options, finish, }) {
|
|
6
|
+
const direction = options.revealDirection ?? "left-to-right";
|
|
7
|
+
const split = direction === "edges-in";
|
|
8
|
+
const initialX = direction === "right-to-left" ? "100%" : direction === "center-out" ? "50%" : "0%";
|
|
9
|
+
const transition = {
|
|
10
|
+
duration: Math.max(0, options.revealDurationMs ?? 1000) / 1000,
|
|
11
|
+
ease: options.revealEasing ?? [0.25, 0.1, 0.25, 1],
|
|
12
|
+
};
|
|
13
|
+
return (_jsx("defs", { children: _jsxs("clipPath", { id: id, clipPathUnits: "userSpaceOnUse", "data-reveal-direction": direction, children: [_jsx(motion.rect, { ...(combo
|
|
14
|
+
? { "data-combo-reveal": family }
|
|
15
|
+
: family === "area"
|
|
16
|
+
? { "data-area-reveal": "" }
|
|
17
|
+
: {}), y: 0, height: "100%", initial: { attrX: initialX, width: "0%" }, animate: { attrX: "0%", width: split ? "50%" : "100%" }, ...(finish ? { onAnimationComplete: finish } : {}), transition: transition }), split && (_jsx(motion.rect, { y: 0, height: "100%", initial: { attrX: "100%", width: "0%" }, animate: { attrX: "50%", width: "50%" }, transition: transition }))] }) }));
|
|
18
|
+
}
|
package/dist/root.d.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentPropsWithRef } from "react";
|
|
2
|
+
import { type ChartInteractionConfig, type ChartVisibilityProps } from "./chart-interaction.js";
|
|
2
3
|
import type { SeriesConfig } from "./types.js";
|
|
3
4
|
export type RootProps = ComponentPropsWithRef<"div"> & {
|
|
4
5
|
config: SeriesConfig;
|
|
5
6
|
emphasis?: "auto" | "none";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
onVisibleSeriesChange?: never;
|
|
9
|
-
} | {
|
|
10
|
-
visibleSeries: readonly string[];
|
|
11
|
-
onVisibleSeriesChange?: (next: string[]) => void;
|
|
12
|
-
});
|
|
7
|
+
interaction?: ChartInteractionConfig;
|
|
8
|
+
} & ChartVisibilityProps;
|
|
13
9
|
/** Scopes presentation metadata and CSS colors; the consumer owns chart geometry and state. */
|
|
14
|
-
export declare function Root({ config, emphasis, visibleSeries, onVisibleSeriesChange, style, children, ...props }: RootProps): import("react").JSX.Element;
|
|
10
|
+
export declare function Root({ config, emphasis, interaction, defaultVisibleSeries, visibleSeries, onVisibleSeriesChange, style, children, ...props }: RootProps): import("react").JSX.Element;
|
package/dist/root.js
CHANGED
|
@@ -1,29 +1,64 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
3
4
|
import { ChartContext } from "./chart-context.js";
|
|
5
|
+
import { ChartInteractionProvider, ChartInteractionStatus, useChartInteraction, } from "./chart-interaction.js";
|
|
4
6
|
import { EmphasisProvider, useEmphasisActions } from "./emphasis.js";
|
|
7
|
+
import { colorResourceId, colorStopToken, resolveSeriesColor, } from "./series-color.js";
|
|
8
|
+
import { resolveSeriesConfig } from "./series-label.js";
|
|
9
|
+
import { SeriesColorDefinitions } from "./series-paint.js";
|
|
10
|
+
import { StylesheetWarning } from "./stylesheet-warning.js";
|
|
5
11
|
/** Scopes presentation metadata and CSS colors; the consumer owns chart geometry and state. */
|
|
6
|
-
export function Root({ config, emphasis = "auto", visibleSeries, onVisibleSeriesChange, style, children, ...props }) {
|
|
7
|
-
if (onVisibleSeriesChange &&
|
|
8
|
-
throw new Error("Root requires visibleSeries when onVisibleSeriesChange is provided");
|
|
12
|
+
export function Root({ config, emphasis = "auto", interaction, defaultVisibleSeries, visibleSeries, onVisibleSeriesChange, style, children, ...props }) {
|
|
13
|
+
if (onVisibleSeriesChange && visibleSeries === undefined && defaultVisibleSeries === undefined)
|
|
14
|
+
throw new Error("Root requires visibleSeries or defaultVisibleSeries when onVisibleSeriesChange is provided");
|
|
15
|
+
if (visibleSeries !== undefined && defaultVisibleSeries !== undefined)
|
|
16
|
+
throw new Error("Root visibility cannot be both controlled and defaulted");
|
|
17
|
+
const id = useId();
|
|
9
18
|
const colors = {};
|
|
19
|
+
const paints = {};
|
|
20
|
+
const colorStops = {};
|
|
10
21
|
for (const [key, item] of Object.entries(config)) {
|
|
11
22
|
if (!/^[a-zA-Z][\w-]*$/.test(key))
|
|
12
23
|
throw new Error(`Chart series key "${key}" must start with a letter and contain only letters, numbers, underscores or hyphens`);
|
|
13
|
-
|
|
24
|
+
const resolved = resolveSeriesColor(item.color, key);
|
|
25
|
+
colors[`--color-${key}`] = resolved.colors[0];
|
|
26
|
+
for (const [index, color] of resolved.colors.entries())
|
|
27
|
+
colors[colorStopToken(key, index)] = color;
|
|
28
|
+
colorStops[key] = resolved;
|
|
29
|
+
const gradientId = colorResourceId(id, key);
|
|
30
|
+
const gradient = resolved.colors.length > 1;
|
|
31
|
+
paints[key] = gradient ? `url(#${gradientId})` : `var(--color-${key})`;
|
|
32
|
+
colors[colorStopToken(key, "gradient")] = gradient
|
|
33
|
+
? `linear-gradient(to right in srgb, ${resolved.offsets.map((offset, index) => `var(${colorStopToken(key, index)}) ${offset * 100}%`).join(", ")})`
|
|
34
|
+
: `var(--color-${key})`;
|
|
14
35
|
}
|
|
15
36
|
const value = {
|
|
16
|
-
config,
|
|
37
|
+
config: resolveSeriesConfig(config),
|
|
38
|
+
paints,
|
|
39
|
+
colorId: id,
|
|
40
|
+
colorStops,
|
|
17
41
|
...(visibleSeries ? { visibleSeries } : {}),
|
|
18
42
|
...(onVisibleSeriesChange ? { onVisibleSeriesChange } : {}),
|
|
19
43
|
};
|
|
20
|
-
return (_jsx(
|
|
44
|
+
return (_jsx(ChartInteractionProvider, { config: value.config, ...(interaction ? { interaction } : {}), ...(visibleSeries !== undefined
|
|
45
|
+
? { visibleSeries, ...(onVisibleSeriesChange ? { onVisibleSeriesChange } : {}) }
|
|
46
|
+
: defaultVisibleSeries !== undefined
|
|
47
|
+
? { defaultVisibleSeries, ...(onVisibleSeriesChange ? { onVisibleSeriesChange } : {}) }
|
|
48
|
+
: {}), children: (visible, change) => (_jsx(ChartContext, { value: {
|
|
49
|
+
...value,
|
|
50
|
+
...(visible !== undefined ? { visibleSeries: visible } : {}),
|
|
51
|
+
...(change ? { onVisibleSeriesChange: change } : {}),
|
|
52
|
+
}, children: _jsx(EmphasisProvider, { enabled: emphasis === "auto", children: _jsxs(RootFrame, { ...props, style: { ...colors, ...style }, children: [process.env.NODE_ENV === "development" && _jsx(StylesheetWarning, {}), Object.values(colorStops).some((stops) => stops.colors.length > 1) && (_jsx("svg", { "aria-hidden": "true", focusable: "false", width: 0, height: 0, style: { position: "absolute" }, "data-kind-ui": "color-resources", children: _jsx(SeriesColorDefinitions, {}) })), children] }) }) })) }));
|
|
21
53
|
}
|
|
22
54
|
function RootFrame({ children, ...props }) {
|
|
23
55
|
const { reset } = useEmphasisActions();
|
|
24
|
-
|
|
25
|
-
|
|
56
|
+
const interaction = useChartInteraction();
|
|
57
|
+
return (
|
|
58
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: Delegates Escape from descendants after consumer handlers; this frame is not a control.
|
|
59
|
+
_jsxs("div", { ...props, "data-kind-ui": "chart", onKeyDown: (event) => {
|
|
60
|
+
props.onKeyDown?.(event);
|
|
61
|
+
if (event.key === "Escape" && interaction.reset(event))
|
|
26
62
|
reset();
|
|
27
|
-
|
|
28
|
-
}, children: children }));
|
|
63
|
+
}, children: [children, _jsx(ChartInteractionStatus, {})] }));
|
|
29
64
|
}
|
package/dist/sankey-chart.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type SankeyChartProps = Omit<NativeProps, "data" | "node" | "link" | "onC
|
|
|
14
14
|
data: SankeyFlowData;
|
|
15
15
|
/** Explicit node-ID metadata; enables matching default Kind node/source-link paint. */
|
|
16
16
|
nodeConfig?: SankeyNodeConfig | undefined;
|
|
17
|
+
/** Explicit node-focus binding; Root must use kind node and mode focus. */
|
|
18
|
+
interactionBinding?: "root";
|
|
17
19
|
node?: Exclude<NativeProps["node"], (props: NativeNodeProps) => ReactNode> | ((props: SankeyNodeProps) => ReactNode);
|
|
18
20
|
link?: Exclude<NativeProps["link"], (props: NativeLinkProps) => ReactNode> | ((props: SankeyLinkProps) => ReturnType<NonNullable<Extract<NativeProps["link"], (props: NativeLinkProps) => ReactNode>>>);
|
|
19
21
|
onClick?: IdentityEvent;
|
|
@@ -21,6 +23,8 @@ export type SankeyChartProps = Omit<NativeProps, "data" | "node" | "link" | "onC
|
|
|
21
23
|
onMouseLeave?: IdentityEvent;
|
|
22
24
|
/** Kind SankeyLink paint flows along unchanged paths; custom renderers retain ownership. */
|
|
23
25
|
animate?: boolean | SankeyAnimation;
|
|
26
|
+
loading?: boolean | undefined;
|
|
27
|
+
loadingLabel?: string | undefined;
|
|
24
28
|
empty?: ReactNode;
|
|
25
29
|
};
|
|
26
30
|
export declare const SankeyMotion: import("react").Context<{
|
|
@@ -29,5 +33,5 @@ export declare const SankeyMotion: import("react").Context<{
|
|
|
29
33
|
width: number;
|
|
30
34
|
}>;
|
|
31
35
|
/** Recharts owns layout, renderers, labels and events. Zero flows remain in the data alternative. */
|
|
32
|
-
export declare function SankeyChart({ data, nodeConfig, animate, empty, ...props }: SankeyChartProps): import("react").JSX.Element;
|
|
36
|
+
export declare function SankeyChart({ data, nodeConfig, interactionBinding, animate, loading, loadingLabel, empty, ...props }: SankeyChartProps): import("react").JSX.Element;
|
|
33
37
|
export {};
|