@kind-ui/charts 0.1.1 → 0.3.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 +683 -1
- 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-material.js +2 -3
- 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 +4 -2
- package/dist/heatmap.js +34 -12
- package/dist/histogram-chart.js +10 -3
- package/dist/histogram-material.js +1 -2
- 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-material.d.ts +1 -1
- package/dist/line-material.js +1 -1
- package/dist/line-series.d.ts +9 -1
- package/dist/line-series.js +37 -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-material.js +1 -1
- 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 +138 -15
- 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-material.js +1 -1
- 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-finish.d.ts +1 -1
- package/dist/sankey-finish.js +1 -1
- 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-material.js +1 -1
- 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 -6
- package/dist/stylesheet-warning.d.ts +2 -0
- package/dist/stylesheet-warning.js +86 -0
- package/dist/surface-material.js +1 -1
- 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/bar-paper.d.ts +0 -2
- package/dist/bar-paper.js +0 -6
package/dist/sankey-legend.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useOptionalChartInteraction } from "./chart-interaction.js";
|
|
3
4
|
/** Node identity legend; solid links inherit source color, gradient links use both endpoints. */
|
|
4
|
-
export function SankeyLegend({ config, children, ...props }) {
|
|
5
|
-
|
|
5
|
+
export function SankeyLegend({ config, interactionBinding, children, ...props }) {
|
|
6
|
+
const interaction = useOptionalChartInteraction();
|
|
7
|
+
if (interactionBinding &&
|
|
8
|
+
(!interaction || interaction.kind !== "node" || interaction.mode !== "focus"))
|
|
9
|
+
throw new Error("SankeyLegend Root interaction binding requires node focus");
|
|
10
|
+
return (
|
|
11
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: Delegates clicks from native buttons, which already implement Enter and Space.
|
|
12
|
+
_jsx("ul", { "aria-label": "Chart legend", ...props, "data-kind-ui": "chart-legend", onClick: (event) => {
|
|
13
|
+
props.onClick?.(event);
|
|
14
|
+
const button = event.target.closest("button[data-node-key]");
|
|
15
|
+
if (interactionBinding &&
|
|
16
|
+
button?.dataset.nodeKey !== undefined &&
|
|
17
|
+
event.currentTarget.contains(button))
|
|
18
|
+
interaction?.activate({ kind: "node", key: button.dataset.nodeKey }, "legend", event);
|
|
19
|
+
}, children: Object.entries(config).map(([key, item]) => {
|
|
6
20
|
const marker = (_jsx("span", { "aria-hidden": "true", "data-kind-ui": "chart-indicator", style: { "--kind-ui-chart-indicator-color": item.color } }));
|
|
7
|
-
|
|
21
|
+
const content = children ? (children({ key, label: item.label, color: item.color, marker })) : (_jsxs(_Fragment, { children: [marker, item.label] }));
|
|
22
|
+
return (_jsx("li", { "data-kind-ui": "chart-legend-item", "data-node": key, children: interactionBinding && interaction?.eligible.includes(key) ? (_jsx("button", { type: "button", "aria-pressed": interaction.selected === key, "data-node-key": key, children: content })) : (_jsx("span", { children: content })) }, key));
|
|
8
23
|
}) }));
|
|
9
24
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef } from "react";
|
|
2
|
+
import type { SankeyNodeConfig } from "./sankey-colors.js";
|
|
3
|
+
import { type SankeyFlowData } from "./sankey-data.js";
|
|
4
|
+
import type { SankeyNodeProps } from "./sankey-marks.js";
|
|
5
|
+
export type SankeyNodeLabelProps = Omit<ComponentPropsWithRef<"text">, "position"> & {
|
|
6
|
+
/** Geometry from the native node callback; identity comes from payload.id, never index. */
|
|
7
|
+
node: Pick<SankeyNodeProps, "x" | "y" | "width" | "height" | "payload">;
|
|
8
|
+
data: SankeyFlowData;
|
|
9
|
+
/** Explicit metadata; icons are looked up by payload.id. Labels still use data names. */
|
|
10
|
+
nodeConfig?: SankeyNodeConfig;
|
|
11
|
+
/** Square SVG viewport size and gap in chart units; inside icons share the node clip. */
|
|
12
|
+
iconSize?: number;
|
|
13
|
+
iconGap?: number;
|
|
14
|
+
position?: "inside" | "outside";
|
|
15
|
+
/** Outside defaults to left for sinks and right otherwise. Reserve chart margins for text. */
|
|
16
|
+
side?: "left" | "right";
|
|
17
|
+
showValues?: boolean;
|
|
18
|
+
valueFormatter?: (value: number) => string;
|
|
19
|
+
offset?: number;
|
|
20
|
+
};
|
|
21
|
+
/** Explicit sibling of SankeyNode; preserves native renderer and consumer text ownership. */
|
|
22
|
+
export declare function SankeyNodeLabel({ node, data, nodeConfig, iconSize, iconGap, position, side, showValues, valueFormatter, offset, children, ...textProps }: SankeyNodeLabelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { prepareSankeyData } from "./sankey-data.js";
|
|
5
|
+
/** Explicit sibling of SankeyNode; preserves native renderer and consumer text ownership. */
|
|
6
|
+
export function SankeyNodeLabel({ node, data, nodeConfig, iconSize = 16, iconGap = 4, position = "outside", side, showValues = false, valueFormatter = String, offset = 8, children, ...textProps }) {
|
|
7
|
+
const clipId = `sankey-label-${useId().replace(/:/g, "")}`;
|
|
8
|
+
const validated = prepareSankeyData(data);
|
|
9
|
+
const index = validated.nodes.findIndex((item) => item.id === node.payload.id);
|
|
10
|
+
const item = validated.nodes[index];
|
|
11
|
+
if (!item)
|
|
12
|
+
throw new Error(`SankeyNodeLabel requires node id "${node.payload.id}" in data`);
|
|
13
|
+
if (![node.x, node.y, node.width, node.height, offset, iconSize, iconGap].every(Number.isFinite) ||
|
|
14
|
+
node.width < 0 ||
|
|
15
|
+
node.height < 0 ||
|
|
16
|
+
offset < 0 ||
|
|
17
|
+
iconSize < 0 ||
|
|
18
|
+
iconGap < 0)
|
|
19
|
+
throw new Error("SankeyNodeLabel requires finite geometry and nonnegative dimensions/offset/icon size/gap");
|
|
20
|
+
const incoming = validated.links.reduce((sum, link) => sum + (link.target === index ? link.value : 0), 0);
|
|
21
|
+
const outgoing = validated.links.reduce((sum, link) => sum + (link.source === index ? link.value : 0), 0);
|
|
22
|
+
const value = Math.max(incoming, outgoing);
|
|
23
|
+
const label = showValues ? `${item.name}: ${valueFormatter(value)}` : item.name;
|
|
24
|
+
const left = (side ?? (validated.links.some((link) => link.source === index) ? "right" : "left")) === "left";
|
|
25
|
+
const inside = position === "inside";
|
|
26
|
+
const icon = nodeConfig && Object.hasOwn(nodeConfig, item.id) ? nodeConfig[item.id]?.icon : undefined;
|
|
27
|
+
const hasIcon = icon !== undefined && icon !== null && typeof icon !== "boolean" && iconSize > 0;
|
|
28
|
+
const iconSpace = hasIcon ? iconSize + iconGap : 0;
|
|
29
|
+
const edge = left ? node.x - offset : node.x + node.width + offset;
|
|
30
|
+
// Inside uses a centered icon above centered text; outside puts the icon nearest the node.
|
|
31
|
+
const iconX = inside ? node.x + (node.width - iconSize) / 2 : left ? edge - iconSize : edge;
|
|
32
|
+
const centerY = node.y + node.height / 2;
|
|
33
|
+
return (_jsxs("g", { "data-kind-ui": "sankey-node-label", "data-node-id": item.id, children: [_jsx("title", { children: label }), inside && (_jsx("defs", { children: _jsx("clipPath", { id: clipId, children: _jsx("rect", { x: node.x, y: node.y, width: node.width, height: node.height }) }) })), _jsxs("g", { clipPath: inside ? `url(#${clipId})` : undefined, children: [hasIcon && (_jsx("svg", { "data-kind-ui": "sankey-node-icon", x: iconX, y: inside ? centerY - iconSize - iconGap / 2 : centerY - iconSize / 2, width: iconSize, height: iconSize, viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", pointerEvents: "none", overflow: "hidden", children: icon })), _jsx("text", { x: inside ? node.x + node.width / 2 : left ? edge - iconSpace : edge + iconSpace, y: inside && hasIcon ? centerY + iconGap / 2 + 6 : centerY, textAnchor: inside ? "middle" : left ? "end" : "start", dominantBaseline: "middle", pointerEvents: "none", fontSize: 12, ...textProps, children: children === undefined ? label : children })] })] }));
|
|
34
|
+
}
|
package/dist/scatter-chart.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type MotionValue } from "motion/react";
|
|
2
2
|
import { type ComponentProps } from "react";
|
|
3
3
|
import { ScatterChart as EngineScatterChart } from "recharts";
|
|
4
|
-
import { type
|
|
5
|
-
export type ScatterAnimation =
|
|
4
|
+
import { type BaseAnimation } from "./animation.js";
|
|
5
|
+
export type ScatterAnimation = BaseAnimation;
|
|
6
6
|
export type ScatterChartProps = ComponentProps<typeof EngineScatterChart> & {
|
|
7
7
|
animate?: boolean | ScatterAnimation | undefined;
|
|
8
|
+
loading?: boolean | undefined;
|
|
9
|
+
loadingLabel?: string | undefined;
|
|
8
10
|
};
|
|
9
11
|
export declare const ScatterMotion: import("react").Context<{
|
|
10
12
|
reveal: boolean;
|
|
@@ -13,4 +15,4 @@ export declare const ScatterMotion: import("react").Context<{
|
|
|
13
15
|
progress: MotionValue<number> | null;
|
|
14
16
|
}>;
|
|
15
17
|
/** Native numeric geometry and item selection, with optional entrance fade and shared tooltip Motion. */
|
|
16
|
-
export declare function ScatterChart({ animate, children, ...props }: ScatterChartProps): import("react").JSX.Element;
|
|
18
|
+
export declare function ScatterChart({ animate, loading, loadingLabel, children, ...props }: ScatterChartProps): import("react").JSX.Element;
|
package/dist/scatter-chart.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createContext, useCallback, useLayoutEffect, useRef, useState, useSyncE
|
|
|
5
5
|
import { ScatterChart as EngineScatterChart } from "recharts";
|
|
6
6
|
import { MotionContext } from "./animation.js";
|
|
7
7
|
import { LineChartFrame } from "./line-chart.js";
|
|
8
|
+
import { CartesianLoadingDesign } from "./loading-cartesian-designs.js";
|
|
8
9
|
export const ScatterMotion = createContext({
|
|
9
10
|
reveal: false,
|
|
10
11
|
options: {},
|
|
@@ -21,11 +22,20 @@ const snapshot = () => window.matchMedia(query).matches;
|
|
|
21
22
|
const serverSnapshot = () => true;
|
|
22
23
|
const defaultHover = { type: "spring", stiffness: 210, damping: 28, mass: 0.8 };
|
|
23
24
|
/** Native numeric geometry and item selection, with optional entrance fade and shared tooltip Motion. */
|
|
24
|
-
export function ScatterChart({ animate = false, children, ...props }) {
|
|
25
|
+
export function ScatterChart({ animate = false, loading, loadingLabel, children, ...props }) {
|
|
25
26
|
const reduced = useSyncExternalStore(subscribe, snapshot, serverSnapshot);
|
|
26
27
|
const [interacted, setInteracted] = useState(false);
|
|
27
|
-
const enabled = animate !== false && !reduced;
|
|
28
|
-
const interact = useCallback(() =>
|
|
28
|
+
const enabled = animate !== false && !reduced && !loading;
|
|
29
|
+
const interact = useCallback(() => {
|
|
30
|
+
if (!loading)
|
|
31
|
+
setInteracted(true);
|
|
32
|
+
}, [loading]);
|
|
33
|
+
useLayoutEffect(() => {
|
|
34
|
+
if (loading) {
|
|
35
|
+
setInteracted(false);
|
|
36
|
+
started.current = false;
|
|
37
|
+
}
|
|
38
|
+
}, [loading]);
|
|
29
39
|
const finish = useCallback(() => {
|
|
30
40
|
if (enabled)
|
|
31
41
|
setInteracted(true);
|
|
@@ -60,5 +70,5 @@ export function ScatterChart({ animate = false, children, ...props }) {
|
|
|
60
70
|
});
|
|
61
71
|
return () => controls.stop();
|
|
62
72
|
}, [reveal, duration, easing, progress, interact]);
|
|
63
|
-
return (_jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(ScatterMotion, { value: { reveal, options, finish, progress }, children: _jsx("div", { style: { display: "contents" }, onFocusCapture: interact, onPointerDownCapture: interact, onPointerMoveCapture: interact, onKeyDownCapture: interact, children: _jsx(LineChartFrame, { chartProps: props, engine: EngineScatterChart, motionEnabled: enabled, interrupt: finish, children: children }) }) }) }));
|
|
73
|
+
return (_jsx(MotionContext, { value: { enabled, transition: options.hoverTransition ?? defaultHover }, children: _jsx(ScatterMotion, { value: { reveal, options, finish, progress }, children: _jsx("div", { style: { display: "contents" }, onFocusCapture: interact, onPointerDownCapture: interact, onPointerMoveCapture: interact, onKeyDownCapture: interact, children: _jsx(LineChartFrame, { loading: loading, loadingLabel: loadingLabel, loadingSkeleton: "scatter", loadingDesign: (seed) => _jsx(CartesianLoadingDesign, { family: "scatter", seed: seed }), loadingAnimation: options, chartProps: props, engine: EngineScatterChart, motionEnabled: enabled, interrupt: finish, children: children }) }) }) }));
|
|
64
74
|
}
|
package/dist/scatter-material.js
CHANGED
|
@@ -35,5 +35,5 @@ export function ScatterMaterialSymbol({ material, ...props }) {
|
|
|
35
35
|
const soft = Math.min(2, span * 0.1);
|
|
36
36
|
const halo = Math.min(1.8, span * 0.065);
|
|
37
37
|
const extent = span * 3 + strokeExtent;
|
|
38
|
-
return (_jsxs(_Fragment, { children: [_jsxs("defs", { "data-kind-ui": "scatter-material", "data-material": material, pointerEvents: "none", children: [material === "glow" && (_jsxs(_Fragment, { children: [_jsx("clipPath", { id: `${id}-geometry`, clipPathUnits: "userSpaceOnUse", children: _jsx(Symbols, { cx: props.cx, cy: props.cy, size: props.size ?? 64, ...(props.type !== undefined ? { type: props.type } : {}), ...(props.sizeType !== undefined ? { sizeType: props.sizeType } : {}), fill: "#000", stroke: "#000", strokeWidth: props.strokeWidth ?? 0 }) }), _jsx("filter", { id: `${id}-guard`, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", children: _jsx("feMorphology", { in: "SourceGraphic", operator: "dilate", radius: guard }) }), _jsxs("mask", { id: `${id}-outside`, maskUnits: "userSpaceOnUse", x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, children: [_jsx("rect", { x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, fill: "#fff" }), _jsx("g", { filter: `url(#${id}-guard)`, children: _jsx("rect", { x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, fill: "#000", clipPath: `url(#${id}-geometry)` }) })] }), _jsxs("filter", { id: `${id}-halo`, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "painted", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), _jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: halo, result: "halo" }), _jsx("feFlood", { floodOpacity: "var(--kind-ui-scatter-glow-opacity, 0.65)", result: "strength" }), _jsx("feComposite", { in: "halo", in2: "strength", operator: "in", result: "softHalo" }), _jsx("feComposite", { in: "softHalo", in2: "painted", operator: "out" })] })] })), _jsxs("filter", { id: id, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "footprint", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), material === "
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsxs("defs", { "data-kind-ui": "scatter-material", "data-material": material, pointerEvents: "none", children: [material === "glow" && (_jsxs(_Fragment, { children: [_jsx("clipPath", { id: `${id}-geometry`, clipPathUnits: "userSpaceOnUse", children: _jsx(Symbols, { cx: props.cx, cy: props.cy, size: props.size ?? 64, ...(props.type !== undefined ? { type: props.type } : {}), ...(props.sizeType !== undefined ? { sizeType: props.sizeType } : {}), fill: "#000", stroke: "#000", strokeWidth: props.strokeWidth ?? 0 }) }), _jsx("filter", { id: `${id}-guard`, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", children: _jsx("feMorphology", { in: "SourceGraphic", operator: "dilate", radius: guard }) }), _jsxs("mask", { id: `${id}-outside`, maskUnits: "userSpaceOnUse", x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, children: [_jsx("rect", { x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, fill: "#fff" }), _jsx("g", { filter: `url(#${id}-guard)`, children: _jsx("rect", { x: (props.cx ?? 0) - extent, y: (props.cy ?? 0) - extent, width: extent * 2, height: extent * 2, fill: "#000", clipPath: `url(#${id}-geometry)` }) })] }), _jsxs("filter", { id: `${id}-halo`, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "painted", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), _jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: halo, result: "halo" }), _jsx("feFlood", { floodOpacity: "var(--kind-ui-scatter-glow-opacity, 0.65)", result: "strength" }), _jsx("feComposite", { in: "halo", in2: "strength", operator: "in", result: "softHalo" }), _jsx("feComposite", { in: "softHalo", in2: "painted", operator: "out" })] })] })), _jsxs("filter", { id: id, x: -1, y: -1, width: 3, height: 3, filterUnits: "objectBoundingBox", primitiveUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "footprint", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), material === "clay" ? (_jsxs(_Fragment, { children: [_jsx("feOffset", { in: "footprint", dx: relief, dy: relief, result: "lower" }), _jsx("feComposite", { in: "footprint", in2: "lower", operator: "out", result: "top" }), _jsx("feGaussianBlur", { in: "top", stdDeviation: soft, result: "softTop" }), _jsx("feFlood", { floodColor: "var(--kind-ui-scatter-clay-light, #fff)", floodOpacity: 0.62 }), _jsx("feComposite", { in2: "softTop", operator: "in", result: "light" }), _jsx("feOffset", { in: "footprint", dx: -relief, dy: -relief, result: "upper" }), _jsx("feComposite", { in: "footprint", in2: "upper", operator: "out", result: "bottom" }), _jsx("feGaussianBlur", { in: "bottom", stdDeviation: soft, result: "softBottom" }), _jsx("feFlood", { floodColor: "var(--kind-ui-scatter-clay-shade, #17212b)", floodOpacity: 0.38 }), _jsx("feComposite", { in2: "softBottom", operator: "in", result: "shade" }), _jsx("feComposite", { in: "shade", in2: "SourceGraphic", operator: "atop", result: "shaded" }), _jsx("feComposite", { in: "light", in2: "shaded", operator: "atop", result: "body" }), _jsx("feTurbulence", { type: "fractalNoise", baseFrequency: 0.7, numOctaves: 1, seed: 17, result: "grain" }), _jsx("feFlood", { floodColor: "#fff", floodOpacity: 0.035 }), _jsx("feComposite", { in2: "grain", operator: "in", result: "texture" }), _jsx("feComposite", { in: "texture", in2: "body", operator: "atop" })] })) : (_jsxs(_Fragment, { children: [_jsx("feMorphology", { in: "footprint", operator: "erode", radius: rim, result: "inside" }), _jsx("feComposite", { in: "footprint", in2: "inside", operator: "out", result: "edge" }), _jsx("feFlood", { floodColor: "var(--kind-ui-scatter-glow-light, #fff)", floodOpacity: 0.55 }), _jsx("feComposite", { in2: "edge", operator: "in", result: "light" }), _jsx("feComposite", { in: "light", in2: "SourceGraphic", operator: "atop", result: "body" }), _jsx("feFlood", { floodColor: "var(--kind-ui-scatter-glow-light, #fff)", floodOpacity: 0.12, result: "bloom" }), _jsx("feComposite", { in: "bloom", in2: "body", operator: "atop", result: "luminousBody" })] }))] })] }), material === "glow" ? (_jsxs(_Fragment, { children: [_jsx("use", { "data-kind-ui": "scatter-light", href: `#${id}-body`, filter: `url(#${id}-halo)`, mask: `url(#${id}-outside)`, pointerEvents: "none", "aria-hidden": "true", focusable: "false", tabIndex: -1 }), _jsx("g", { id: `${id}-body`, children: _jsx(Symbols, { ...props, filter: `url(#${id})` }) })] })) : (_jsx(Symbols, { ...props, filter: `url(#${id})` }))] }));
|
|
39
39
|
}
|
package/dist/scatter-series.js
CHANGED
|
@@ -7,6 +7,7 @@ import { useChart } from "./chart-context.js";
|
|
|
7
7
|
import { useLineInteraction } from "./line-chart.js";
|
|
8
8
|
import { ScatterMotion } from "./scatter-chart.js";
|
|
9
9
|
import { ScatterMaterialSymbol } from "./scatter-material.js";
|
|
10
|
+
import { SeriesInteractionLayer, useSeriesInteraction } from "./series-interaction.js";
|
|
10
11
|
// Delay by native screen X, rather than sorting or changing quantitative data.
|
|
11
12
|
function EntranceSymbol({ material, type, ...props }) {
|
|
12
13
|
const { progress } = use(ScatterMotion);
|
|
@@ -20,7 +21,7 @@ function EntranceSymbol({ material, type, ...props }) {
|
|
|
20
21
|
}
|
|
21
22
|
/** Native Scatter owns marks, Cells, labels, handlers, axis mapping and point payloads. */
|
|
22
23
|
export function ScatterSeries({ seriesKey, hide, fill, material = "plain", ...props }) {
|
|
23
|
-
const { config, visibleSeries } = useChart();
|
|
24
|
+
const { config, paints, visibleSeries } = useChart();
|
|
24
25
|
const { registerSeries, invalidate } = useLineInteraction();
|
|
25
26
|
const shapes = useMemo(() => {
|
|
26
27
|
function finish(option) {
|
|
@@ -69,6 +70,7 @@ export function ScatterSeries({ seriesKey, hide, fill, material = "plain", ...pr
|
|
|
69
70
|
}, [id, key, registerSeries]);
|
|
70
71
|
if (key === undefined && visibleSeries !== undefined)
|
|
71
72
|
throw new Error("ScatterSeries requires seriesKey for controlled non-string dataKey");
|
|
72
|
-
const
|
|
73
|
-
|
|
73
|
+
const interaction = useSeriesInteraction(key, hidden, hide === true, props.data, props.onClick);
|
|
74
|
+
const color = fill ?? (key && Object.hasOwn(config, key) ? paints[key] : undefined);
|
|
75
|
+
return (_jsx(ZIndexLayer, { zIndex: props.zIndex ?? DefaultZIndexes.scatter, children: _jsx(SeriesInteractionLayer, { seriesKey: key, hidden: hidden, children: _jsx("g", { "data-kind-ui": "scatter-fade", children: _jsx(Scatter, { ...props, ...(interaction.onClick !== undefined ? { onClick: interaction.onClick } : {}), ...(shapes.shape !== undefined ? { shape: shapes.shape } : {}), ...(shapes.activeShape !== undefined ? { activeShape: shapes.activeShape } : {}), id: id, hide: hidden, ...(color !== undefined ? { fill: color } : {}), zIndex: 0, isAnimationActive: false }) }) }) }));
|
|
74
76
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SeriesColor } from "./types.js";
|
|
2
|
+
export type ColorStops = {
|
|
3
|
+
offsets: number[];
|
|
4
|
+
colors: string[];
|
|
5
|
+
};
|
|
6
|
+
/** Preserve each theme's evenly spaced stops, even with unequal stop counts. */
|
|
7
|
+
export declare function resolveSeriesColor(color: SeriesColor, key: string): ColorStops;
|
|
8
|
+
export declare function colorResourceId(id: string, key: string): string;
|
|
9
|
+
/** Disjoint from legacy --color-<key>, whose valid keys include numeric suffixes. */
|
|
10
|
+
export declare function colorStopToken(key: string, index: number | "gradient"): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function stops(value, path) {
|
|
2
|
+
const values = typeof value === "string" ? [value] : value;
|
|
3
|
+
if (!Array.isArray(values) || values.length === 0)
|
|
4
|
+
throw new Error(`Series color ${path} requires a nonempty string or nonempty array of color strings`);
|
|
5
|
+
for (let index = 0; index < values.length; index++) {
|
|
6
|
+
if (typeof values[index] !== "string" || values[index].trim() === "")
|
|
7
|
+
throw new Error(`Series color ${path}${Array.isArray(value) ? `[${index}]` : ""} requires a nonempty color string`);
|
|
8
|
+
}
|
|
9
|
+
return values;
|
|
10
|
+
}
|
|
11
|
+
function at(colors, offset) {
|
|
12
|
+
if (colors.length === 1)
|
|
13
|
+
return colors[0];
|
|
14
|
+
const position = offset * (colors.length - 1);
|
|
15
|
+
const index = Math.floor(position);
|
|
16
|
+
const fraction = position - index;
|
|
17
|
+
if (fraction < 1e-10)
|
|
18
|
+
return colors[index];
|
|
19
|
+
return `color-mix(in srgb, ${colors[index]} ${(1 - fraction) * 100}%, ${colors[index + 1]} ${fraction * 100}%)`;
|
|
20
|
+
}
|
|
21
|
+
/** Preserve each theme's evenly spaced stops, even with unequal stop counts. */
|
|
22
|
+
export function resolveSeriesColor(color, key) {
|
|
23
|
+
const path = `config[${JSON.stringify(key)}].color`;
|
|
24
|
+
const themed = color !== null && typeof color === "object" && !Array.isArray(color);
|
|
25
|
+
if (themed &&
|
|
26
|
+
(!Object.hasOwn(color, "light") ||
|
|
27
|
+
!Object.hasOwn(color, "dark") ||
|
|
28
|
+
Object.keys(color).some((key) => key !== "light" && key !== "dark")))
|
|
29
|
+
throw new Error(`Series color ${path} requires both light and dark definitions and no other fields`);
|
|
30
|
+
const variants = color;
|
|
31
|
+
const light = stops(themed ? variants.light : color, themed ? `${path}.light` : path);
|
|
32
|
+
const dark = stops(themed ? variants.dark : color, themed ? `${path}.dark` : path);
|
|
33
|
+
const offsets = [
|
|
34
|
+
...new Set([light, dark].flatMap((values) => values.length === 1 ? [0] : values.map((_, index) => index / (values.length - 1)))),
|
|
35
|
+
].sort((a, b) => a - b);
|
|
36
|
+
return {
|
|
37
|
+
offsets,
|
|
38
|
+
colors: offsets.map((offset) => themed ? `light-dark(${at(light, offset)}, ${at(dark, offset)})` : at(light, offset)),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function colorResourceId(id, key) {
|
|
42
|
+
return `kind-ui-color-${Array.from(`${id}:${key}`, (char) => char.codePointAt(0)?.toString(16)).join("-")}`;
|
|
43
|
+
}
|
|
44
|
+
/** Disjoint from legacy --color-<key>, whose valid keys include numeric suffixes. */
|
|
45
|
+
export function colorStopToken(key, index) {
|
|
46
|
+
const encoded = Array.from(key, (char) => char.codePointAt(0)?.toString(16)).join("-");
|
|
47
|
+
return `--kind-ui-series-${encoded}-${index}`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type KeyboardEventHandler, type ReactNode } from "react";
|
|
2
|
+
/** Native handlers retain their original data/index/event tuple and run first. */
|
|
3
|
+
export declare function useSeriesInteraction<Args extends unknown[]>(key: string | undefined, hidden: boolean, nativeHidden: boolean | undefined, seriesData: readonly unknown[] | undefined, handler: ((...args: Args) => void) | undefined): {
|
|
4
|
+
onClick: ((...args: Args) => void) | undefined;
|
|
5
|
+
};
|
|
6
|
+
/** One keyboard target per logical series, within its native ZIndex portal. */
|
|
7
|
+
export declare function SeriesInteractionLayer({ seriesKey, hidden, children, onKeyDown, }: {
|
|
8
|
+
seriesKey: string | undefined;
|
|
9
|
+
hidden: boolean;
|
|
10
|
+
onKeyDown?: KeyboardEventHandler<SVGElement> | undefined;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useLayoutEffect, useRef, } from "react";
|
|
4
|
+
import { useChart } from "./chart-context.js";
|
|
5
|
+
import { useChartInteraction, useInteractionAvailability, useInteractionRegistration, } from "./chart-interaction.js";
|
|
6
|
+
import { useEmphasis } from "./emphasis.js";
|
|
7
|
+
import { useChartKeyboard, useLineInteraction } from "./line-chart.js";
|
|
8
|
+
/** Native handlers retain their original data/index/event tuple and run first. */
|
|
9
|
+
export function useSeriesInteraction(key, hidden, nativeHidden = false, seriesData, handler) {
|
|
10
|
+
const interaction = useChartInteraction();
|
|
11
|
+
const { data } = useLineInteraction();
|
|
12
|
+
const { visibleSeries } = useChart();
|
|
13
|
+
const rootHidden = key !== undefined && visibleSeries !== undefined && !visibleSeries.includes(key);
|
|
14
|
+
// Some consumers mirror Root visibility into native hide. Their hidden IDs stay restorable.
|
|
15
|
+
useInteractionAvailability(interaction.kind === "series" ? key : undefined, nativeHidden && !rootHidden);
|
|
16
|
+
useInteractionRegistration(interaction.kind === "series" && key !== undefined && (seriesData ?? data)?.length ? [key] : []);
|
|
17
|
+
const interactive = key !== undefined &&
|
|
18
|
+
!hidden &&
|
|
19
|
+
interaction.kind === "series" &&
|
|
20
|
+
interaction.interactive &&
|
|
21
|
+
interaction.markActivation &&
|
|
22
|
+
interaction.eligible.includes(key);
|
|
23
|
+
const latest = useRef({ handler, interaction, interactive, key });
|
|
24
|
+
useLayoutEffect(() => {
|
|
25
|
+
latest.current = { handler, interaction, interactive, key };
|
|
26
|
+
});
|
|
27
|
+
const onClick = useCallback((...args) => {
|
|
28
|
+
latest.current.handler?.(...args);
|
|
29
|
+
const current = latest.current;
|
|
30
|
+
const event = args[args.length - 1];
|
|
31
|
+
if (current.interactive && current.key !== undefined && event?.button === 0)
|
|
32
|
+
current.interaction.activate({ kind: "series", key: current.key }, "mark", event);
|
|
33
|
+
}, []);
|
|
34
|
+
// Native animation IDs include handler identity even when native animation is disabled.
|
|
35
|
+
return { onClick: interactive ? onClick : handler };
|
|
36
|
+
}
|
|
37
|
+
/** One keyboard target per logical series, within its native ZIndex portal. */
|
|
38
|
+
export function SeriesInteractionLayer({ seriesKey, hidden, children, onKeyDown, }) {
|
|
39
|
+
const interaction = useChartInteraction();
|
|
40
|
+
const { config } = useChart();
|
|
41
|
+
const enabled = seriesKey !== undefined && !hidden && interaction.kind === "series" && interaction.configured;
|
|
42
|
+
const emphasis = useEmphasis({ kind: "series", key: seriesKey ?? "", scope: "series", seriesKey }, enabled);
|
|
43
|
+
const keyboard = useChartKeyboard();
|
|
44
|
+
const interactive = enabled &&
|
|
45
|
+
interaction.interactive &&
|
|
46
|
+
interaction.markActivation &&
|
|
47
|
+
interaction.eligible.includes(seriesKey);
|
|
48
|
+
// Native axis inspection includes all series at this category, so keep their paint readable.
|
|
49
|
+
const nativeInspection = keyboard &&
|
|
50
|
+
typeof document !== "undefined" &&
|
|
51
|
+
document.activeElement?.getAttribute("role") === "application";
|
|
52
|
+
const factor = nativeInspection && emphasis.active === null ? 1 : emphasis.factor;
|
|
53
|
+
return (
|
|
54
|
+
// biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useAriaPropsSupportedByRole: Role, pressed state and handlers are enabled together for opted-in SVG controls.
|
|
55
|
+
_jsx("g", { "data-kind-ui": "series-interaction", "data-series": seriesKey, role: interactive ? "button" : undefined, tabIndex: interactive ? 0 : undefined, "aria-label": interactive
|
|
56
|
+
? `${interaction.mode === "focus" ? "Highlight" : "Toggle"} ${config[seriesKey]?.label ?? seriesKey}`
|
|
57
|
+
: undefined, "aria-pressed": interactive
|
|
58
|
+
? interaction.mode === "focus"
|
|
59
|
+
? interaction.selected === seriesKey
|
|
60
|
+
: (interaction.visible?.includes(seriesKey) ?? true)
|
|
61
|
+
: undefined, onPointerEnter: (event) => {
|
|
62
|
+
if (event.pointerType !== "touch")
|
|
63
|
+
emphasis.enter("pointer");
|
|
64
|
+
}, onPointerMove: (event) => {
|
|
65
|
+
if (event.pointerType !== "touch")
|
|
66
|
+
emphasis.enter("pointer");
|
|
67
|
+
}, onPointerLeave: () => emphasis.leave("pointer"), onPointerCancel: () => emphasis.leave("pointer"), onFocus: (event) => {
|
|
68
|
+
if (event.target.matches(":focus-visible"))
|
|
69
|
+
emphasis.enter("keyboard");
|
|
70
|
+
}, onBlur: () => emphasis.leave("keyboard"), onKeyDown: (event) => {
|
|
71
|
+
if (event.target === event.currentTarget)
|
|
72
|
+
onKeyDown?.(event);
|
|
73
|
+
if (interactive &&
|
|
74
|
+
event.target === event.currentTarget &&
|
|
75
|
+
!event.defaultPrevented &&
|
|
76
|
+
(event.key === "Enter" || event.key === " ")) {
|
|
77
|
+
if (!event.repeat)
|
|
78
|
+
interaction.activate({ kind: "series", key: seriesKey }, "mark", event);
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
}
|
|
81
|
+
}, children: _jsx("g", { "data-kind-ui": "series-interaction-paint", style: { opacity: factor }, children: children }) }));
|
|
82
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SeriesConfig } from "./types.js";
|
|
2
|
+
/** Presentation only: never rewrite metadata identity, paint or consumer data. */
|
|
3
|
+
export declare function resolveSeriesLabel(key: string, label?: string): string;
|
|
4
|
+
export type ResolvedSeriesConfig = Readonly<Record<string, SeriesConfig[string] & {
|
|
5
|
+
label: string;
|
|
6
|
+
}>>;
|
|
7
|
+
export declare function resolveSeriesConfig(config: SeriesConfig): ResolvedSeriesConfig;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Presentation only: never rewrite metadata identity, paint or consumer data. */
|
|
2
|
+
export function resolveSeriesLabel(key, label) {
|
|
3
|
+
if (label !== undefined)
|
|
4
|
+
return label;
|
|
5
|
+
const words = key
|
|
6
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1 $2")
|
|
7
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
8
|
+
.replace(/[_-]+/g, " ")
|
|
9
|
+
.trim()
|
|
10
|
+
.toLowerCase();
|
|
11
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
12
|
+
}
|
|
13
|
+
export function resolveSeriesConfig(config) {
|
|
14
|
+
return Object.fromEntries(Object.entries(config).map(([key, item]) => [
|
|
15
|
+
key,
|
|
16
|
+
{ ...item, label: resolveSeriesLabel(key, item.label) },
|
|
17
|
+
]));
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** Each native SVG owns its viewport-relative resources; metadata stays Root-owned. */
|
|
3
|
+
export declare function SeriesPaintBoundary({ children }: {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
|
|
6
|
+
export declare function SeriesColorDefinitions({ viewport }: {
|
|
7
|
+
viewport?: boolean;
|
|
8
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { use, useId, useMemo } from "react";
|
|
4
|
+
import { ChartContext } from "./chart-context.js";
|
|
5
|
+
import { colorResourceId, colorStopToken } from "./series-color.js";
|
|
6
|
+
/** Each native SVG owns its viewport-relative resources; metadata stays Root-owned. */
|
|
7
|
+
export function SeriesPaintBoundary({ children }) {
|
|
8
|
+
const chart = use(ChartContext);
|
|
9
|
+
const colorId = useId();
|
|
10
|
+
const colorStops = chart?.colorStops;
|
|
11
|
+
const paints = useMemo(() => Object.fromEntries(Object.entries(colorStops ?? {}).map(([key, stops]) => [
|
|
12
|
+
key,
|
|
13
|
+
stops.colors.length > 1
|
|
14
|
+
? `url(#${colorResourceId(colorId, key)})`
|
|
15
|
+
: `var(--color-${key})`,
|
|
16
|
+
])), [colorStops, colorId]);
|
|
17
|
+
const value = useMemo(() => (chart ? { ...chart, colorId, paints } : null), [chart, colorId, paints]);
|
|
18
|
+
return value ? _jsx(ChartContext, { value: value, children: children }) : children;
|
|
19
|
+
}
|
|
20
|
+
export function SeriesColorDefinitions({ viewport = false }) {
|
|
21
|
+
const chart = use(ChartContext);
|
|
22
|
+
if (!chart)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsx("defs", { "data-kind-ui": "series-color-definitions", children: Object.entries(chart.colorStops)
|
|
25
|
+
.filter(([, stops]) => stops.colors.length > 1)
|
|
26
|
+
.map(([key, stops]) => (_jsx("linearGradient", { id: colorResourceId(chart.colorId, key), x1: "0", y1: "0", x2: viewport ? "100%" : "1", y2: "0", gradientUnits: viewport ? "userSpaceOnUse" : "objectBoundingBox", children: stops.offsets.map((offset, index) => (_jsx("stop", { offset: offset, stopColor: `var(${colorStopToken(key, index)})` }, offset))) }, key))) }));
|
|
27
|
+
}
|
package/dist/styles.css
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
:where([data-kind-ui="chart"]) {
|
|
41
|
+
--kind-ui-styles-loaded: 1;
|
|
41
42
|
width: 100%;
|
|
42
43
|
min-width: 0;
|
|
43
44
|
}
|
|
@@ -86,7 +87,10 @@
|
|
|
86
87
|
flex-shrink: 0;
|
|
87
88
|
width: 8px;
|
|
88
89
|
height: 8px;
|
|
89
|
-
background: var(
|
|
90
|
+
background: var(
|
|
91
|
+
--kind-ui-chart-indicator-background,
|
|
92
|
+
var(--kind-ui-chart-indicator-color, currentColor)
|
|
93
|
+
);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
:where(
|
|
@@ -414,11 +418,6 @@
|
|
|
414
418
|
/* Heatmap materials are background paint, confined to an 8% perimeter.
|
|
415
419
|
The quantitative center and native geometry are untouched. */
|
|
416
420
|
@layer kind-ui {
|
|
417
|
-
[data-kind-ui="heatmap-grid"] td[data-material="paper"] {
|
|
418
|
-
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22grain%22%20width%3D%226%22%20height%3D%227%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cpath%20d%3D%22M1%201h1M4%204h1M2%206h1%22%20stroke%3D%22%23705c48%22%20stroke-opacity%3D%22.32%22%20stroke-width%3D%22.6%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Cpath%20d%3D%22M0%200H100V100H0Z%20M8%208V92H92V8Z%22%20fill%3D%22%23eadac0%22%20fill-opacity%3D%22.7%22%20fill-rule%3D%22evenodd%22%2F%3E%3Cpath%20d%3D%22M0%200H100V100H0Z%20M8%208V92H92V8Z%22%20fill%3D%22url%28%23grain%29%22%20fill-rule%3D%22evenodd%22%2F%3E%3Cpath%20d%3D%22M3%205L26%203L50%205L75%203L97%205L95%2030L97%2055L95%2078L97%2096L72%2095L48%2097L25%2095L3%2097L5%2074L3%2049L5%2024Z%22%20fill%3D%22none%22%20stroke%3D%22%235c4938%22%20stroke-opacity%3D%22.65%22%20stroke-width%3D%22.8%22%2F%3E%3Cpath%20d%3D%22M5%203L30%205L58%203L83%205L95%203M3%207L5%2035L3%2062L5%2090%22%20fill%3D%22none%22%20stroke%3D%22%23fffaf0%22%20stroke-opacity%3D%22.75%22%20stroke-width%3D%22.7%22%2F%3E%3C%2Fsvg%3E");
|
|
419
|
-
background-size: 100% 100%;
|
|
420
|
-
background-repeat: no-repeat;
|
|
421
|
-
}
|
|
422
421
|
[data-kind-ui="heatmap-grid"] td[data-material="clay"] {
|
|
423
422
|
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22top%22%20x2%3D%220%22%20y2%3D%221%22%3E%3Cstop%20stop-color%3D%22%23fff%22%20stop-opacity%3D%22.7%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22bottom%22%20x2%3D%220%22%20y2%3D%221%22%3E%3Cstop%20stop-color%3D%22%23172033%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23172033%22%20stop-opacity%3D%22.4%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22left%22%3E%3Cstop%20stop-color%3D%22%23fff%22%20stop-opacity%3D%22.5%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22right%22%3E%3Cstop%20stop-color%3D%22%23172033%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23172033%22%20stop-opacity%3D%22.3%22%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cpath%20d%3D%22M0%200H100V8H0Z%22%20fill%3D%22url%28%23top%29%22%2F%3E%3Cpath%20d%3D%22M0%2092H100V100H0Z%22%20fill%3D%22url%28%23bottom%29%22%2F%3E%3Cpath%20d%3D%22M0%208H8V92H0Z%22%20fill%3D%22url%28%23left%29%22%2F%3E%3Cpath%20d%3D%22M92%208H100V92H92Z%22%20fill%3D%22url%28%23right%29%22%2F%3E%3C%2Fsvg%3E");
|
|
424
423
|
background-size: 100% 100%;
|
|
@@ -441,3 +440,149 @@
|
|
|
441
440
|
}
|
|
442
441
|
}
|
|
443
442
|
}
|
|
443
|
+
|
|
444
|
+
@layer kind-ui {
|
|
445
|
+
:where([data-kind-ui="chart-indicator"][data-fill-pattern]) {
|
|
446
|
+
width: 16px;
|
|
447
|
+
height: 16px;
|
|
448
|
+
background: none;
|
|
449
|
+
}
|
|
450
|
+
:where([data-kind-ui="chart-legend-button"][aria-pressed="false"] [data-fill-pattern]) {
|
|
451
|
+
opacity: 0.5;
|
|
452
|
+
background: none;
|
|
453
|
+
}
|
|
454
|
+
@media (forced-colors: active) {
|
|
455
|
+
:where([data-kind-ui="fill-pattern"]) {
|
|
456
|
+
forced-color-adjust: none;
|
|
457
|
+
}
|
|
458
|
+
:where([data-kind-ui="fill-pattern"] > rect:first-child) {
|
|
459
|
+
fill: Canvas;
|
|
460
|
+
}
|
|
461
|
+
:where(
|
|
462
|
+
[data-kind-ui="fill-pattern"] > rect:not(:first-child),
|
|
463
|
+
[data-kind-ui="fill-pattern"] > circle
|
|
464
|
+
) {
|
|
465
|
+
fill: CanvasText;
|
|
466
|
+
}
|
|
467
|
+
:where([data-kind-ui="fill-pattern"] > path) {
|
|
468
|
+
stroke: CanvasText;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
[data-kind-ui="heatmap-scroll"][data-loading] {
|
|
473
|
+
position: relative;
|
|
474
|
+
}
|
|
475
|
+
[data-kind-ui="heatmap-scroll"][data-loading] > table {
|
|
476
|
+
visibility: hidden;
|
|
477
|
+
}
|
|
478
|
+
[data-kind-ui="heatmap-scroll"] > [data-kind-ui="chart-loading-skeleton"],
|
|
479
|
+
[data-kind-ui="sankey"] > [data-kind-ui="chart-loading-skeleton"] {
|
|
480
|
+
position: absolute;
|
|
481
|
+
inset: 0;
|
|
482
|
+
}
|
|
483
|
+
[data-kind-ui="sankey-content"][data-loading] > * {
|
|
484
|
+
visibility: hidden;
|
|
485
|
+
}
|
|
486
|
+
:where(.kind-ui-loading-chart-pending) .recharts-tooltip-wrapper {
|
|
487
|
+
display: none;
|
|
488
|
+
}
|
|
489
|
+
:where(.kind-ui-loading-chart-pending > *) {
|
|
490
|
+
visibility: hidden;
|
|
491
|
+
}
|
|
492
|
+
:where(.kind-ui-loading-chart-pending .recharts-layer) {
|
|
493
|
+
visibility: hidden;
|
|
494
|
+
}
|
|
495
|
+
:where([data-kind-ui="chart-loading-skeleton"]) {
|
|
496
|
+
visibility: visible;
|
|
497
|
+
color: var(--kind-ui-chart-loading-color, #8b969b);
|
|
498
|
+
pointer-events: none;
|
|
499
|
+
}
|
|
500
|
+
:where([data-kind-ui="chart-loading-skeleton"]) {
|
|
501
|
+
animation: kind-ui-loading-pulse var(--kind-ui-loading-cycle, 1770ms) linear infinite;
|
|
502
|
+
}
|
|
503
|
+
:where([data-kind-ui="chart-loading-skeleton"] svg) {
|
|
504
|
+
overflow: visible;
|
|
505
|
+
}
|
|
506
|
+
:where([data-kind-ui="chart-loading-status"]) {
|
|
507
|
+
position: absolute;
|
|
508
|
+
width: 1px;
|
|
509
|
+
height: 1px;
|
|
510
|
+
padding: 0;
|
|
511
|
+
margin: -1px;
|
|
512
|
+
overflow: hidden;
|
|
513
|
+
clip-path: inset(50%);
|
|
514
|
+
white-space: nowrap;
|
|
515
|
+
border: 0;
|
|
516
|
+
}
|
|
517
|
+
@keyframes kind-ui-loading-pulse {
|
|
518
|
+
0%,
|
|
519
|
+
12%,
|
|
520
|
+
94%,
|
|
521
|
+
100% {
|
|
522
|
+
opacity: 0;
|
|
523
|
+
}
|
|
524
|
+
14%,
|
|
525
|
+
90% {
|
|
526
|
+
opacity: 1;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
:where([data-kind-ui="chart-loading-skeleton"] [data-loading-motion="grow"]) {
|
|
530
|
+
mask: var(--kind-ui-loading-vertical);
|
|
531
|
+
}
|
|
532
|
+
:where(
|
|
533
|
+
[data-family="bar"],
|
|
534
|
+
[data-family="waterfall"],
|
|
535
|
+
[data-family="histogram"],
|
|
536
|
+
[data-family="box-plot"]
|
|
537
|
+
)
|
|
538
|
+
[data-loading-motion="grow"] {
|
|
539
|
+
mask: none;
|
|
540
|
+
}
|
|
541
|
+
:where([data-kind-ui="chart-loading-skeleton"] [data-loading-motion="reveal"]) {
|
|
542
|
+
mask: var(--kind-ui-loading-horizontal);
|
|
543
|
+
}
|
|
544
|
+
:where([data-kind-ui="chart-loading-skeleton"] [data-loading-motion="area-reveal"]) {
|
|
545
|
+
mask: var(--kind-ui-loading-area);
|
|
546
|
+
}
|
|
547
|
+
:where([data-family="combo"][data-loading-layout="vertical"] [data-loading-motion="grow"]) {
|
|
548
|
+
transform: matrix(0, 0.375, -2.6666667, 0, 640, 0);
|
|
549
|
+
}
|
|
550
|
+
@media (prefers-reduced-motion: reduce) {
|
|
551
|
+
:where([data-kind-ui="chart-loading-skeleton"]) {
|
|
552
|
+
animation: none;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Only the default stroke moves; dots and chart entrance clips are independent. */
|
|
558
|
+
@layer kind-ui {
|
|
559
|
+
:where(.kind-ui-line-dash .recharts-line-curve) {
|
|
560
|
+
animation: kind-ui-line-dash var(--kind-ui-dash-duration) linear infinite;
|
|
561
|
+
animation-direction: var(--kind-ui-dash-direction);
|
|
562
|
+
}
|
|
563
|
+
:where([data-motion="off"] .kind-ui-line-dash .recharts-line-curve),
|
|
564
|
+
:where(.kind-ui-loading-chart-pending .kind-ui-line-dash .recharts-line-curve) {
|
|
565
|
+
animation: none;
|
|
566
|
+
}
|
|
567
|
+
@media (prefers-reduced-motion: reduce) {
|
|
568
|
+
:where(.kind-ui-line-dash .recharts-line-curve) {
|
|
569
|
+
animation: none;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
@keyframes kind-ui-line-dash {
|
|
573
|
+
from {
|
|
574
|
+
stroke-dashoffset: var(--kind-ui-dash-offset);
|
|
575
|
+
}
|
|
576
|
+
to {
|
|
577
|
+
stroke-dashoffset: calc(var(--kind-ui-dash-offset) - var(--kind-ui-dash-cycle));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
@layer kind-ui {
|
|
583
|
+
[data-kind-ui="chart-background-pattern"],
|
|
584
|
+
[data-kind-ui="chart-background-pattern"] * {
|
|
585
|
+
/* biome-ignore lint/complexity/noImportantStyles: Decorative custom tiles must not intercept chart pointers. */
|
|
586
|
+
pointer-events: none !important;
|
|
587
|
+
}
|
|
588
|
+
}
|