@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +683 -1
  3. package/dist/activity-rings.d.ts +2 -1
  4. package/dist/activity-rings.js +6 -3
  5. package/dist/animation.d.ts +11 -3
  6. package/dist/animation.js +18 -10
  7. package/dist/area-chart.d.ts +3 -1
  8. package/dist/area-chart.js +15 -10
  9. package/dist/area-series.d.ts +9 -1
  10. package/dist/area-series.js +35 -8
  11. package/dist/bar-chart.d.ts +10 -3
  12. package/dist/bar-chart.js +17 -4
  13. package/dist/bar-material.js +2 -3
  14. package/dist/bar-series.d.ts +11 -1
  15. package/dist/bar-series.js +65 -13
  16. package/dist/box-plot.js +2 -2
  17. package/dist/category-cells.d.ts +7 -1
  18. package/dist/category-cells.js +32 -2
  19. package/dist/chart-background-pattern.d.ts +23 -0
  20. package/dist/chart-background-pattern.js +31 -0
  21. package/dist/chart-context.d.ts +6 -2
  22. package/dist/chart-interaction.d.ts +94 -0
  23. package/dist/chart-interaction.js +224 -0
  24. package/dist/combo-chart.d.ts +8 -5
  25. package/dist/combo-chart.js +14 -12
  26. package/dist/configured-line-chart.d.ts +5 -1
  27. package/dist/configured-line-chart.js +8 -5
  28. package/dist/emphasis.d.ts +5 -2
  29. package/dist/emphasis.js +18 -4
  30. package/dist/fill-pattern.d.ts +25 -0
  31. package/dist/fill-pattern.js +23 -0
  32. package/dist/heatmap.d.ts +4 -2
  33. package/dist/heatmap.js +34 -12
  34. package/dist/histogram-chart.js +10 -3
  35. package/dist/histogram-material.js +1 -2
  36. package/dist/index.d.ts +12 -3
  37. package/dist/index.js +8 -1
  38. package/dist/legend.d.ts +2 -0
  39. package/dist/legend.js +28 -7
  40. package/dist/line-chart.d.ts +13 -3
  41. package/dist/line-chart.js +54 -34
  42. package/dist/line-dash.d.ts +8 -0
  43. package/dist/line-dash.js +18 -0
  44. package/dist/line-material.d.ts +1 -1
  45. package/dist/line-material.js +1 -1
  46. package/dist/line-series.d.ts +9 -1
  47. package/dist/line-series.js +37 -11
  48. package/dist/loading-cartesian-designs.d.ts +6 -0
  49. package/dist/loading-cartesian-designs.js +123 -0
  50. package/dist/loading-motion.d.ts +37 -0
  51. package/dist/loading-motion.js +88 -0
  52. package/dist/loading-polar-designs.d.ts +6 -0
  53. package/dist/loading-polar-designs.js +95 -0
  54. package/dist/loading-skeleton.d.ts +29 -0
  55. package/dist/loading-skeleton.js +143 -0
  56. package/dist/loading-standalone-designs.d.ts +5 -0
  57. package/dist/loading-standalone-designs.js +126 -0
  58. package/dist/percent-stack.d.ts +18 -0
  59. package/dist/percent-stack.js +33 -0
  60. package/dist/pie-chart.d.ts +7 -3
  61. package/dist/pie-chart.js +37 -6
  62. package/dist/pie-material.js +1 -1
  63. package/dist/pie-pin-identity.d.ts +4 -0
  64. package/dist/pie-pin-identity.js +10 -0
  65. package/dist/pie-series.d.ts +4 -0
  66. package/dist/pie-series.js +138 -15
  67. package/dist/pie-tooltip-pin.d.ts +4 -0
  68. package/dist/pie-tooltip-pin.js +52 -0
  69. package/dist/point-marker.d.ts +19 -0
  70. package/dist/point-marker.js +19 -0
  71. package/dist/polar-chart.d.ts +14 -5
  72. package/dist/polar-chart.js +41 -10
  73. package/dist/polar-material.js +1 -1
  74. package/dist/polar-series.js +68 -22
  75. package/dist/radar-interaction.js +10 -4
  76. package/dist/radial-category.d.ts +2 -0
  77. package/dist/reveal-clip.d.ts +16 -0
  78. package/dist/reveal-clip.js +18 -0
  79. package/dist/root.d.ts +5 -9
  80. package/dist/root.js +46 -11
  81. package/dist/sankey-chart.d.ts +5 -1
  82. package/dist/sankey-chart.js +64 -13
  83. package/dist/sankey-colors.d.ts +3 -0
  84. package/dist/sankey-finish.d.ts +1 -1
  85. package/dist/sankey-finish.js +1 -1
  86. package/dist/sankey-interaction.d.ts +8 -0
  87. package/dist/sankey-interaction.js +32 -0
  88. package/dist/sankey-legend.d.ts +2 -1
  89. package/dist/sankey-legend.js +18 -3
  90. package/dist/sankey-node-label.d.ts +22 -0
  91. package/dist/sankey-node-label.js +34 -0
  92. package/dist/scatter-chart.d.ts +5 -3
  93. package/dist/scatter-chart.js +14 -4
  94. package/dist/scatter-material.js +1 -1
  95. package/dist/scatter-series.js +5 -3
  96. package/dist/series-color.d.ts +10 -0
  97. package/dist/series-color.js +48 -0
  98. package/dist/series-interaction.d.ts +12 -0
  99. package/dist/series-interaction.js +82 -0
  100. package/dist/series-label.d.ts +7 -0
  101. package/dist/series-label.js +18 -0
  102. package/dist/series-paint.d.ts +8 -0
  103. package/dist/series-paint.js +27 -0
  104. package/dist/styles.css +151 -6
  105. package/dist/stylesheet-warning.d.ts +2 -0
  106. package/dist/stylesheet-warning.js +86 -0
  107. package/dist/surface-material.js +1 -1
  108. package/dist/tooltip-content.d.ts +8 -1
  109. package/dist/tooltip-content.js +15 -4
  110. package/dist/tooltip.d.ts +3 -1
  111. package/dist/tooltip.js +8 -5
  112. package/dist/types.d.ts +11 -2
  113. package/dist/waterfall-chart.d.ts +4 -0
  114. package/dist/waterfall-chart.js +7 -0
  115. package/package.json +2 -2
  116. package/dist/bar-paper.d.ts +0 -2
  117. package/dist/bar-paper.js +0 -6
package/dist/emphasis.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, } from "react";
4
+ import { useChartInteraction } from "./chart-interaction.js";
4
5
  const Context = createContext(null);
5
6
  const Actions = createContext(null);
6
7
  const identity = (target) => JSON.stringify([target.kind, target.scope, target.key]);
@@ -59,6 +60,9 @@ export function EmphasisProvider({ enabled, children, }) {
59
60
  const actions = useMemo(() => ({ enabled, register, set, clear, reset, clearScope }), [enabled, register, set, clear, reset, clearScope]);
60
61
  return (_jsx(Actions, { value: actions, children: _jsx(Context, { value: active, children: children }) }));
61
62
  }
63
+ export function useOptionalEmphasisActions() {
64
+ return use(Actions);
65
+ }
62
66
  export function useEmphasisActions() {
63
67
  const value = use(Actions);
64
68
  if (!value)
@@ -71,7 +75,8 @@ function useEmphasisState() {
71
75
  return { ...actions, active };
72
76
  }
73
77
  /** Explicit contract for custom and portaled marks; apply factor to an extra paint wrapper. */
74
- export function useEmphasis(target, enabled = true) {
78
+ export function useEmphasis(target, enabled = true, persistent = true) {
79
+ const interaction = useChartInteraction();
75
80
  const state = useEmphasisState();
76
81
  const owner = useId();
77
82
  const { kind, key, scope, seriesKey } = target;
@@ -84,7 +89,16 @@ export function useEmphasis(target, enabled = true) {
84
89
  const related = active === null ||
85
90
  (active.kind === "series" ? active.key === seriesKey : identity(active) === identity(target));
86
91
  const applicable = active?.kind === "series" ? seriesKey !== undefined : active?.scope === scope;
87
- const dimmed = enabled && state.enabled && applicable && !related;
92
+ const persistentKey = interaction.kind === "series"
93
+ ? seriesKey
94
+ : target.kind === "sector" || target.kind === "category"
95
+ ? key
96
+ : undefined;
97
+ const persistentDimmed = persistent &&
98
+ persistentKey !== undefined &&
99
+ interaction.selected !== null &&
100
+ persistentKey !== interaction.selected;
101
+ const dimmed = enabled && (active !== null ? state.enabled && applicable && !related : persistentDimmed);
88
102
  const enter = useCallback((channel) => {
89
103
  if (enabled && state.enabled) {
90
104
  if (channel === "keyboard")
@@ -96,8 +110,8 @@ export function useEmphasis(target, enabled = true) {
96
110
  return { active, dimmed: Boolean(dimmed), factor: dimmed ? 0.28 : 1, enter, leave };
97
111
  }
98
112
  /** Multiplicative paint layer; native geometry, consumer opacity and hit testing stay intact. */
99
- export function EmphasisMark({ target, enabled = true, keyboardActive = false, children, ...props }) {
100
- const emphasis = useEmphasis(target, enabled);
113
+ export function EmphasisMark({ target, enabled = true, persistent = true, keyboardActive = false, children, ...props }) {
114
+ const emphasis = useEmphasis(target, enabled, persistent);
101
115
  // Only depend on identity and the engine's inspection edge, not the changing paint state.
102
116
  useLayoutEffect(() => {
103
117
  if (keyboardActive)
@@ -0,0 +1,25 @@
1
+ import { type ComponentPropsWithRef } from "react";
2
+ /** Static paint encoding, independent of material and chart geometry. */
3
+ export type FillPattern = {
4
+ kind: "hatch" | "stripe" | "duotone" | "dots" | "lines";
5
+ /** Second ink; defaults to CanvasText and follows the host's color scheme. */
6
+ color?: string;
7
+ /** Positive tile size in SVG user units (default 8). */
8
+ size?: number;
9
+ /** Positive stroke/stripe width or dot diameter, <= size (default 1; stripe 2). */
10
+ width?: number;
11
+ /** Rotation in degrees (default 45 for hatch, 0 otherwise). */
12
+ angle?: number;
13
+ };
14
+ export declare function patternResourceId(id: string): string;
15
+ export declare function FillPatternDefinition({ id, pattern, baseColor, }: {
16
+ id: string;
17
+ pattern: FillPattern;
18
+ baseColor: string;
19
+ }): import("react").JSX.Element;
20
+ export type FillPatternSwatchProps = Omit<ComponentPropsWithRef<"svg">, "children" | "color"> & {
21
+ pattern: FillPattern;
22
+ color: string;
23
+ };
24
+ /** Decorative marker for explicit series/consumer legends; native svg props remain available. */
25
+ export declare function FillPatternSwatch({ pattern, color, ...props }: FillPatternSwatchProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ export function patternResourceId(id) {
5
+ return `kind-ui-pattern-${Array.from(id, (char) => char.codePointAt(0)?.toString(16)).join("-")}`;
6
+ }
7
+ export function FillPatternDefinition({ id, pattern, baseColor, }) {
8
+ const { kind, size = 8, width = kind === "stripe" ? 2 : 1, angle = kind === "hatch" ? 45 : 0, } = pattern;
9
+ if (!["hatch", "stripe", "duotone", "dots", "lines"].includes(kind) ||
10
+ !Number.isFinite(size) ||
11
+ size <= 0 ||
12
+ !Number.isFinite(width) ||
13
+ width <= 0 ||
14
+ width > size ||
15
+ !Number.isFinite(angle))
16
+ throw new Error("FillPattern requires a known kind, positive size/width (width <= size), and finite angle");
17
+ return (_jsxs("pattern", { id: id, "data-kind-ui": "fill-pattern", "data-pattern": kind, width: size, height: size, patternUnits: "userSpaceOnUse", patternTransform: `rotate(${angle})`, children: [_jsx("rect", { width: size, height: size, fill: baseColor }), kind === "dots" ? (_jsx("circle", { cx: size / 2, cy: size / 2, r: width / 2, fill: pattern.color ?? "CanvasText" })) : kind === "hatch" || kind === "lines" ? (_jsx("path", { d: `M0 0V${size} M${size} 0V${size}`, stroke: pattern.color ?? "CanvasText", strokeWidth: width })) : (_jsx("rect", { width: kind === "duotone" ? size / 2 : width, height: size, fill: pattern.color ?? "CanvasText" }))] }));
18
+ }
19
+ /** Decorative marker for explicit series/consumer legends; native svg props remain available. */
20
+ export function FillPatternSwatch({ pattern, color, ...props }) {
21
+ const id = patternResourceId(useId());
22
+ return (_jsxs("svg", { "aria-hidden": "true", focusable: "false", viewBox: "0 0 16 16", ...props, "data-kind-ui": "chart-indicator", "data-fill-pattern": "", children: [_jsx("defs", { children: _jsx(FillPatternDefinition, { id: id, pattern: pattern, baseColor: color }) }), _jsx("rect", { width: 16, height: 16, fill: `url(#${id})` })] }));
23
+ }
package/dist/heatmap.d.ts CHANGED
@@ -7,16 +7,18 @@ export type HeatmapChartProps = ComponentPropsWithRef<"div"> & HeatmapModelOptio
7
7
  missingLabel?: string;
8
8
  /** Diagonal cell entrance; quantitative colors and table geometry are unchanged. */
9
9
  animate?: boolean;
10
+ loading?: boolean | undefined;
11
+ loadingLabel?: string | undefined;
10
12
  };
11
13
  /** Native table layout owns categorical geometry. No numeric scatter approximation or SVG overlay. */
12
- export declare function HeatmapChart({ rows, columns, data, duplicates, scale, formatValue, missingLabel, animate, children, onPointerLeave, ...props }: HeatmapChartProps): import("react").JSX.Element;
14
+ export declare function HeatmapChart({ rows, columns, data, duplicates, scale, formatValue, missingLabel, animate, loading, loadingLabel, children, onPointerLeave, ...props }: HeatmapChartProps): import("react").JSX.Element;
13
15
  export type HeatmapCellContentProps = {
14
16
  cell: HeatmapCell;
15
17
  fill: string;
16
18
  formattedValue: string;
17
19
  };
18
20
  export declare function HeatmapCellContent({ fill, formattedValue }: HeatmapCellContentProps): import("react").JSX.Element;
19
- export type HeatmapMaterial = "plain" | "paper" | "clay" | "glow";
21
+ export type HeatmapMaterial = "plain" | "clay" | "glow";
20
22
  export type HeatmapGridProps = Omit<ComponentPropsWithRef<"table">, "children"> & {
21
23
  /** Required accessible name, also rendered as a native caption. */
22
24
  caption: string;
package/dist/heatmap.js CHANGED
@@ -4,6 +4,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  import { animateMini, animate as animateValue } from "motion/react";
5
5
  import { createContext, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from "react";
6
6
  import { createHeatmapModel, } from "./heatmap-model.js";
7
+ import { LoadingSkeletonSurface, LoadingStatus, useLoadingSeed } from "./loading-skeleton.js";
8
+ import { StandaloneLoadingDesign } from "./loading-standalone-designs.js";
7
9
  import { TooltipNumber } from "./tooltip-number.js";
8
10
  const HeatmapContext = createContext(null);
9
11
  function useHeatmap() {
@@ -35,16 +37,19 @@ const samePointer = (a, b) => a !== null &&
35
37
  a.clientY === b.clientY;
36
38
  const labelOf = (cell, context) => `${cell.row}, ${cell.column}: ${cell.value === null ? context.missingLabel : context.formatValue(cell.value)}`;
37
39
  /** Native table layout owns categorical geometry. No numeric scatter approximation or SVG overlay. */
38
- export function HeatmapChart({ rows, columns, data, duplicates, scale, formatValue = number, missingLabel = "Missing", animate = false, children, onPointerLeave, ...props }) {
40
+ export function HeatmapChart({ rows, columns, data, duplicates, scale, formatValue = number, missingLabel = "Missing", animate = false, loading, loadingLabel, children, onPointerLeave, ...props }) {
41
+ const loadingSeed = useLoadingSeed(loading);
39
42
  const model = useMemo(() => createHeatmapModel({ rows, columns, data, ...(duplicates ? { duplicates } : {}) }), [rows, columns, data, duplicates]);
40
43
  const [active, updateActive] = useState(null);
41
44
  const pointer = useRef(null);
42
45
  const dismissed = useRef(false);
43
46
  const setActive = useCallback((key) => {
47
+ if (loading)
48
+ return;
44
49
  if (key)
45
50
  dismissed.current = false;
46
51
  updateActive(key);
47
- }, []);
52
+ }, [loading]);
48
53
  const dismiss = useCallback(() => {
49
54
  dismissed.current = true;
50
55
  updateActive(null);
@@ -60,8 +65,20 @@ export function HeatmapChart({ rows, columns, data, duplicates, scale, formatVal
60
65
  const [tooltipMounted, setTooltipMounted] = useState(false);
61
66
  const reduced = useSyncExternalStore(subscribeReduced, reducedSnapshot, reducedServerSnapshot);
62
67
  const [finished, setFinished] = useState(false);
63
- const interrupt = useCallback(() => setFinished(true), []);
64
- const enabled = animate && !reduced;
68
+ const previousLoading = useRef(loading);
69
+ const completing = previousLoading.current === true && loading !== true;
70
+ const interrupt = useCallback(() => {
71
+ if (!loading)
72
+ setFinished(true);
73
+ }, [loading]);
74
+ const enabled = animate && !reduced && !loading;
75
+ useLayoutEffect(() => {
76
+ if (loading) {
77
+ setFinished(false);
78
+ updateActive(null);
79
+ pointer.current = null;
80
+ }
81
+ }, [loading]);
65
82
  const entrance = enabled && !finished;
66
83
  const root = useRef(null);
67
84
  useEffect(() => {
@@ -113,11 +130,14 @@ export function HeatmapChart({ rows, columns, data, duplicates, scale, formatVal
113
130
  const previousEnabled = useRef(enabled);
114
131
  useLayoutEffect(() => {
115
132
  const inputs = [model, scale, children];
116
- if (inputs.some((value, index) => value !== previous.current[index]) ||
117
- (previousEnabled.current && !enabled))
133
+ if (!loading &&
134
+ !completing &&
135
+ (inputs.some((value, index) => value !== previous.current[index]) ||
136
+ (previousEnabled.current && !enabled)))
118
137
  interrupt();
119
138
  previous.current = inputs;
120
139
  previousEnabled.current = enabled;
140
+ previousLoading.current = loading;
121
141
  });
122
142
  useLayoutEffect(() => {
123
143
  if (!entrance)
@@ -136,19 +156,21 @@ export function HeatmapChart({ rows, columns, data, duplicates, scale, formatVal
136
156
  return () => document.removeEventListener("keydown", keydown);
137
157
  }, [active, tooltipMounted, dismiss]);
138
158
  return (_jsx(HeatmapContext, { value: {
159
+ loading,
160
+ loadingSeed,
139
161
  model,
140
162
  entrance,
141
163
  scale,
142
164
  formatValue,
143
165
  missingLabel,
144
- active,
166
+ active: loading ? null : active,
145
167
  setActive,
146
168
  dismiss,
147
169
  inspectPointer,
148
170
  tooltipId,
149
171
  tooltipMounted,
150
172
  setTooltipMounted,
151
- }, children: _jsx("div", { ...props, ref: ref, "data-kind-ui": "heatmap", onFocusCapture: (event) => {
173
+ }, children: _jsxs("div", { ...props, ref: ref, "data-kind-ui": "heatmap", "aria-busy": props["aria-busy"], onFocusCapture: (event) => {
152
174
  props.onFocusCapture?.(event);
153
175
  interrupt();
154
176
  }, onPointerMoveCapture: (event) => {
@@ -169,7 +191,7 @@ export function HeatmapChart({ rows, columns, data, duplicates, scale, formatVal
169
191
  ? model.cells.flat().find((item) => keyOf(item) === focused?.dataset.cellKey)
170
192
  : undefined;
171
193
  setActive(cell ? [cell.row, cell.column] : null);
172
- }, children: _jsx("div", { "data-kind-ui": "heatmap-entrance", children: children }) }) }));
194
+ }, children: [_jsx("div", { "data-kind-ui": "heatmap-entrance", children: children }), _jsx(LoadingStatus, { loading: loading, label: loadingLabel })] }) }));
173
195
  }
174
196
  // A native cell keeps its styles, ref and handlers. Motion temporarily multiplies its opacity.
175
197
  function EntranceCell({ diagonal, ...props }) {
@@ -293,7 +315,7 @@ export function HeatmapGrid({ caption, material = "plain", layout, Cell = Heatma
293
315
  }
294
316
  }
295
317
  }
296
- return (_jsxs("div", { "data-kind-ui": "heatmap-scroll", children: [_jsxs("table", { ...props, ref: (node) => {
318
+ return (_jsxs("div", { "data-kind-ui": "heatmap-scroll", "data-loading": context.loading || undefined, children: [_jsxs("table", { ...props, ref: (node) => {
297
319
  table.current = node;
298
320
  if (typeof props.ref === "function")
299
321
  return props.ref(node);
@@ -301,7 +323,7 @@ export function HeatmapGrid({ caption, material = "plain", layout, Cell = Heatma
301
323
  props.ref.current = node;
302
324
  },
303
325
  // biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: WAI-ARIA APG data grid uses a native table with roving cell focus.
304
- role: "grid", "aria-label": caption, "data-kind-ui": "heatmap-grid", "data-cell-sizing": layout?.cellSize === undefined ? undefined : "fixed", "data-row-labels": layout?.rowLabels, "data-column-labels": layout?.columnLabels, style: {
326
+ role: "grid", "aria-label": caption, "aria-busy": context.loading, "aria-hidden": context.loading || undefined, inert: context.loading || undefined, "data-kind-ui": "heatmap-grid", "data-cell-sizing": layout?.cellSize === undefined ? undefined : "fixed", "data-row-labels": layout?.rowLabels, "data-column-labels": layout?.columnLabels, style: {
305
327
  "--heatmap-columns": model.columns.length,
306
328
  ...(layout?.cellSize === undefined
307
329
  ? {}
@@ -333,7 +355,7 @@ export function HeatmapGrid({ caption, material = "plain", layout, Cell = Heatma
333
355
  context.inspectPointer(event, [cell.row, cell.column]);
334
356
  } },
335
357
  _jsx(Cell, { cell: cell, fill: fill, formattedValue: formattedValue })));
336
- })] }, row))) })] }), model.rows.length === 0 || model.columns.length === 0 ? (_jsx("p", { "data-kind-ui": "heatmap-empty", children: "No cells in the selected domains." })) : null] }));
358
+ })] }, row))) })] }), context.loading && (_jsx(LoadingSkeletonSurface, { family: "heatmap", design: (seed) => _jsx(StandaloneLoadingDesign, { family: "heatmap", seed: seed }), width: "100%", height: "100%", seed: context.loadingSeed })), !context.loading && (model.rows.length === 0 || model.columns.length === 0) ? (_jsx("p", { "data-kind-ui": "heatmap-empty", children: "No cells in the selected domains." })) : null] }));
337
359
  }
338
360
  /** In-flow tooltip stays within narrow containers and remains available for hover, focus and touch. */
339
361
  export function HeatmapTooltip({ Content, valueAnimation, ...props }) {
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { createContext, useMemo } from "react";
4
4
  import { XAxis, YAxis } from "recharts";
5
- import { BarChart } from "./bar-chart.js";
5
+ import { BarChartImplementation } from "./bar-chart.js";
6
6
  import { validateHistogram } from "./histogram-data.js";
7
7
  export const HistogramContext = createContext(null);
8
8
  /** Bounded vertical histogram; quantitative axes are owned here, native children compose. */
@@ -23,5 +23,12 @@ export function HistogramChart({ bins, measure, xAxisProps, yAxisProps, children
23
23
  const domain = bins.length ? [bins[0]?.lower ?? 0, bins[bins.length - 1]?.upper ?? 1] : [0, 1];
24
24
  if (!Number.isFinite((domain[1] ?? 1) - (domain[0] ?? 0)))
25
25
  throw new Error("Histogram domain must have a finite span");
26
- return (_jsx(HistogramContext, { value: measure, children: _jsxs(BarChart, { ...props, data: data, layout: "horizontal", barCategoryGap: 0, barGap: 0, children: [_jsx(XAxis, { ...xAxisProps, xAxisId: 0, dataKey: "midpoint", type: "number", scale: "linear", domain: domain, allowDataOverflow: true, reversed: false }), _jsx(YAxis, { ...yAxisProps, yAxisId: 0, type: "number", scale: "linear", domain: [0, "dataMax"], reversed: false }), children] }) }));
26
+ return (_jsx(HistogramContext, { value: measure, children: _jsx(BarChartImplementation, { family: "histogram", chartProps: {
27
+ ...props,
28
+ data,
29
+ layout: "horizontal",
30
+ barCategoryGap: 0,
31
+ barGap: 0,
32
+ children: (_jsxs(_Fragment, { children: [_jsx(XAxis, { ...xAxisProps, xAxisId: 0, dataKey: "midpoint", type: "number", scale: "linear", domain: domain, allowDataOverflow: true, reversed: false }), _jsx(YAxis, { ...yAxisProps, yAxisId: 0, type: "number", scale: "linear", domain: [0, "dataMax"], reversed: false }), children] })),
33
+ } }) }));
27
34
  }
@@ -1,8 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { BarClay } from "./bar-clay.js";
4
- import { BarPaper } from "./bar-paper.js";
5
4
  /** Reuse bar surface paint, with no cast shade across quantitative interval edges. */
6
5
  export function HistogramMaterialFilter({ material, id, bounds, padding, }) {
7
- return (_jsx("filter", { id: id, filterUnits: "userSpaceOnUse", primitiveUnits: "userSpaceOnUse", x: bounds.x - padding, y: bounds.y - padding, width: bounds.width + padding * 2, height: bounds.height + padding * 2, colorInterpolationFilters: "sRGB", children: material === "paper" ? (_jsx(BarPaper, {})) : material === "clay" ? (_jsxs(_Fragment, { children: [_jsx(BarClay, { horizontal: false }), _jsx("feComposite", { in2: "footprint", operator: "in" })] })) : (_jsxs(_Fragment, { children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "footprint", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), _jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: 2.5, result: "halo" }), _jsx("feFlood", { floodOpacity: "var(--kind-ui-bar-glow-opacity, 0.6)", result: "strength" }), _jsx("feComposite", { in: "halo", in2: "strength", operator: "in", result: "softHalo" }), _jsx("feComposite", { in: "softHalo", in2: "footprint", operator: "out", result: "unpaintedHalo" }), _jsx("feFlood", { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, floodColor: "#fff", result: "binBounds" }), _jsx("feComposite", { in: "unpaintedHalo", in2: "binBounds", operator: "out", result: "exteriorHalo" }), _jsx("feMorphology", { in: "footprint", operator: "erode", radius: 1.5, result: "inside" }), _jsx("feComposite", { in: "footprint", in2: "inside", operator: "out", result: "edge" }), _jsx("feFlood", { floodColor: "var(--kind-ui-bar-glow-light, #fff)", floodOpacity: 0.75 }), _jsx("feComposite", { in2: "edge", operator: "in", result: "light" }), _jsx("feComposite", { in: "light", in2: "SourceGraphic", operator: "atop", result: "body" }), _jsx("feGaussianBlur", { in: "light", stdDeviation: 1.5, result: "innerLight" }), _jsx("feComposite", { in: "innerLight", in2: "body", operator: "atop", result: "litBody" }), _jsxs("feMerge", { children: [_jsx("feMergeNode", { in: "exteriorHalo" }), _jsx("feMergeNode", { in: "litBody" })] })] })) }));
6
+ return (_jsx("filter", { id: id, filterUnits: "userSpaceOnUse", primitiveUnits: "userSpaceOnUse", x: bounds.x - padding, y: bounds.y - padding, width: bounds.width + padding * 2, height: bounds.height + padding * 2, colorInterpolationFilters: "sRGB", children: material === "clay" ? (_jsxs(_Fragment, { children: [_jsx(BarClay, { horizontal: false }), _jsx("feComposite", { in2: "footprint", operator: "in" })] })) : (_jsxs(_Fragment, { children: [_jsx("feComponentTransfer", { in: "SourceAlpha", result: "footprint", children: _jsx("feFuncA", { type: "linear", slope: 100000 }) }), _jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: 2.5, result: "halo" }), _jsx("feFlood", { floodOpacity: "var(--kind-ui-bar-glow-opacity, 0.6)", result: "strength" }), _jsx("feComposite", { in: "halo", in2: "strength", operator: "in", result: "softHalo" }), _jsx("feComposite", { in: "softHalo", in2: "footprint", operator: "out", result: "unpaintedHalo" }), _jsx("feFlood", { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, floodColor: "#fff", result: "binBounds" }), _jsx("feComposite", { in: "unpaintedHalo", in2: "binBounds", operator: "out", result: "exteriorHalo" }), _jsx("feMorphology", { in: "footprint", operator: "erode", radius: 1.5, result: "inside" }), _jsx("feComposite", { in: "footprint", in2: "inside", operator: "out", result: "edge" }), _jsx("feFlood", { floodColor: "var(--kind-ui-bar-glow-light, #fff)", floodOpacity: 0.75 }), _jsx("feComposite", { in2: "edge", operator: "in", result: "light" }), _jsx("feComposite", { in: "light", in2: "SourceGraphic", operator: "atop", result: "body" }), _jsx("feGaussianBlur", { in: "light", stdDeviation: 1.5, result: "innerLight" }), _jsx("feComposite", { in: "innerLight", in2: "body", operator: "atop", result: "litBody" }), _jsxs("feMerge", { children: [_jsx("feMergeNode", { in: "exteriorHalo" }), _jsx("feMergeNode", { in: "litBody" })] })] })) }));
8
7
  }
package/dist/index.d.ts CHANGED
@@ -4,29 +4,36 @@ export { type LineAnimation, LineSeries, type LineSeriesProps, Tooltip, type Too
4
4
  export { type AreaAnimation, AreaChart, type AreaChartProps } from "./area-chart.js";
5
5
  export type { AreaMaterial } from "./area-material.js";
6
6
  export { AreaSeries, type AreaSeriesProps } from "./area-series.js";
7
- export { type BarAnimation, BarChart, BarChart as WaterfallChart, type BarChartProps, type BarChartProps as WaterfallChartProps, } from "./bar-chart.js";
7
+ export { type BarAnimation, BarChart, type BarChartProps, } from "./bar-chart.js";
8
8
  export type { BarMaterial } from "./bar-material.js";
9
- export { BarSeries, type BarSeriesProps } from "./bar-series.js";
9
+ export { type BarProjection, BarSeries, type BarSeriesProps } from "./bar-series.js";
10
10
  export type { BoxPlotMaterial } from "./box-material.js";
11
11
  export { BoxPlotChart, type BoxPlotChartProps, BoxPlotMark, type BoxPlotMarkProps, BoxPlotSeries, type BoxPlotSeriesProps, type BoxPlotShapeProps, type BoxPlotSummary, boxPlotExtent, validateBoxPlotSummary, } from "./box-plot.js";
12
+ export { ChartBackgroundPattern, type ChartBackgroundPatternDefinition, type ChartBackgroundPatternProps, type ChartBackgroundPatternRenderProps, defineChartBackgroundPattern, } from "./chart-background-pattern.js";
13
+ export { type ChartIdentity, type ChartInteractionConfig, type ChartInteractionEvent, type ChartInteractionRequest, useChartInteraction, } from "./chart-interaction.js";
12
14
  export { type ComboAnimation, ComboChart, type ComboChartProps } from "./combo-chart.js";
13
15
  export { type ConfiguredLineChartProps, type ConfiguredLineSeries, LineChart, type LineChartProps, } from "./configured-line-chart.js";
14
16
  export { EmphasisMark, type EmphasisMarkProps, type EmphasisTarget, useEmphasis, } from "./emphasis.js";
17
+ export { type FillPattern, FillPatternSwatch, type FillPatternSwatchProps, } from "./fill-pattern.js";
15
18
  export { HeatmapCellContent, type HeatmapCellContentProps, HeatmapChart, type HeatmapChartProps, HeatmapDataTable, type HeatmapDataTableProps, HeatmapGrid, type HeatmapGridProps, HeatmapLegend, type HeatmapLegendProps, type HeatmapMaterial, HeatmapTooltip, type HeatmapTooltipProps, } from "./heatmap.js";
16
19
  export { createHeatmapModel, createHeatmapScale, type HeatmapCell, type HeatmapDatum, type HeatmapDuplicatePolicy, type HeatmapModel, type HeatmapModelOptions, type HeatmapScale, type HeatmapScaleOptions, } from "./heatmap-model.js";
17
20
  export { HistogramChart, type HistogramChartProps } from "./histogram-chart.js";
18
21
  export { binHistogram, type HistogramBin, type HistogramBinningResult, type HistogramMeasure, } from "./histogram-data.js";
19
22
  export { HistogramSeries, type HistogramSeriesProps, type HistogramShapeProps, } from "./histogram-series.js";
20
23
  export { Legend, type LegendProps } from "./legend.js";
24
+ export type { LineDashAnimation } from "./line-dash.js";
21
25
  export type { LineMaterial } from "./line-material.js";
26
+ export { createPercentStack, formatPercent, type NormalizedValue, type PercentStackOptions, } from "./percent-stack.js";
22
27
  export { type PieAnimation, PieChart, type PieChartProps } from "./pie-chart.js";
23
28
  export type { PieMaterial } from "./pie-material.js";
24
29
  export { PieSeries, type PieSeriesProps } from "./pie-series.js";
30
+ export { PointMarker, type PointMarkerProps, type PointStyle } from "./point-marker.js";
25
31
  export { type RadarAnimation, RadarChart, type RadarChartProps, type RadialBarAnimation, RadialBarChart, type RadialBarChartProps, } from "./polar-chart.js";
26
32
  export type { PolarMaterial } from "./polar-material.js";
27
33
  export { RadarSeries, type RadarSeriesProps, RadialBarSeries, type RadialBarSeriesProps, } from "./polar-series.js";
28
34
  export type { RadarSelectionProps } from "./radar-interaction.js";
29
35
  export { RadialBarLabel, type RadialBarLabelProps } from "./radial-bar-label.js";
36
+ export type { RevealDirection } from "./reveal-clip.js";
30
37
  export { Root, type RootProps } from "./root.js";
31
38
  export { type SankeyAnimation, SankeyChart, type SankeyChartProps } from "./sankey-chart.js";
32
39
  export type { SankeyNodeConfig } from "./sankey-colors.js";
@@ -34,12 +41,14 @@ export { prepareSankeyData, type SankeyFlowData, type SankeyFlowLink, type Sanke
34
41
  export type { SankeyFinish } from "./sankey-finish.js";
35
42
  export { SankeyLegend, type SankeyLegendProps } from "./sankey-legend.js";
36
43
  export { SankeyLink, type SankeyLinkProps, type SankeyMaterial, SankeyNode, type SankeyNodeProps, } from "./sankey-marks.js";
44
+ export { SankeyNodeLabel, type SankeyNodeLabelProps } from "./sankey-node-label.js";
37
45
  export { SankeyTable, type SankeyTableProps } from "./sankey-table.js";
38
46
  export { type ScatterAnimation, ScatterChart, type ScatterChartProps } from "./scatter-chart.js";
39
47
  export type { ScatterMaterial } from "./scatter-material.js";
40
48
  export { ScatterSeries, type ScatterSeriesProps } from "./scatter-series.js";
41
49
  export { type ScatterSizeDimension, ScatterTooltip, ScatterTooltipContent, type ScatterTooltipContentProps, type ScatterTooltipProps, } from "./scatter-tooltip.js";
42
50
  export { TooltipContent, type TooltipContentProps } from "./tooltip-content.js";
43
- export type { SeriesConfig } from "./types.js";
51
+ export type { SeriesColor, SeriesConfig } from "./types.js";
52
+ export { WaterfallChart, type WaterfallChartProps } from "./waterfall-chart.js";
44
53
  export { computeWaterfallData, type WaterfallDatum, type WaterfallEntry, } from "./waterfall-data.js";
45
54
  export { WaterfallConnectors, type WaterfallConnectorsProps, WaterfallSeries, type WaterfallSeriesProps, } from "./waterfall-series.js";
package/dist/index.js CHANGED
@@ -5,20 +5,25 @@ export { ActivityRings, } from "./activity-rings.js";
5
5
  export { LineSeries, Tooltip, } from "./animation.js";
6
6
  export { AreaChart } from "./area-chart.js";
7
7
  export { AreaSeries } from "./area-series.js";
8
- export { BarChart, BarChart as WaterfallChart, } from "./bar-chart.js";
8
+ export { BarChart, } from "./bar-chart.js";
9
9
  export { BarSeries } from "./bar-series.js";
10
10
  export { BoxPlotChart, BoxPlotMark, BoxPlotSeries, boxPlotExtent, validateBoxPlotSummary, } from "./box-plot.js";
11
+ export { ChartBackgroundPattern, defineChartBackgroundPattern, } from "./chart-background-pattern.js";
12
+ export { useChartInteraction, } from "./chart-interaction.js";
11
13
  export { ComboChart } from "./combo-chart.js";
12
14
  export { LineChart, } from "./configured-line-chart.js";
13
15
  export { EmphasisMark, useEmphasis, } from "./emphasis.js";
16
+ export { FillPatternSwatch, } from "./fill-pattern.js";
14
17
  export { HeatmapCellContent, HeatmapChart, HeatmapDataTable, HeatmapGrid, HeatmapLegend, HeatmapTooltip, } from "./heatmap.js";
15
18
  export { createHeatmapModel, createHeatmapScale, } from "./heatmap-model.js";
16
19
  export { HistogramChart } from "./histogram-chart.js";
17
20
  export { binHistogram, } from "./histogram-data.js";
18
21
  export { HistogramSeries, } from "./histogram-series.js";
19
22
  export { Legend } from "./legend.js";
23
+ export { createPercentStack, formatPercent, } from "./percent-stack.js";
20
24
  export { PieChart } from "./pie-chart.js";
21
25
  export { PieSeries } from "./pie-series.js";
26
+ export { PointMarker } from "./point-marker.js";
22
27
  export { RadarChart, RadialBarChart, } from "./polar-chart.js";
23
28
  export { RadarSeries, RadialBarSeries, } from "./polar-series.js";
24
29
  export { RadialBarLabel } from "./radial-bar-label.js";
@@ -27,10 +32,12 @@ export { SankeyChart } from "./sankey-chart.js";
27
32
  export { prepareSankeyData, } from "./sankey-data.js";
28
33
  export { SankeyLegend } from "./sankey-legend.js";
29
34
  export { SankeyLink, SankeyNode, } from "./sankey-marks.js";
35
+ export { SankeyNodeLabel } from "./sankey-node-label.js";
30
36
  export { SankeyTable } from "./sankey-table.js";
31
37
  export { ScatterChart } from "./scatter-chart.js";
32
38
  export { ScatterSeries } from "./scatter-series.js";
33
39
  export { ScatterTooltip, ScatterTooltipContent, } from "./scatter-tooltip.js";
34
40
  export { TooltipContent } from "./tooltip-content.js";
41
+ export { WaterfallChart } from "./waterfall-chart.js";
35
42
  export { computeWaterfallData, } from "./waterfall-data.js";
36
43
  export { WaterfallConnectors, WaterfallSeries, } from "./waterfall-series.js";
package/dist/legend.d.ts CHANGED
@@ -9,6 +9,8 @@ export type LegendProps = Omit<ComponentPropsWithRef<"ul">, "children"> & {
9
9
  key: string;
10
10
  label: string;
11
11
  visible: boolean;
12
+ selected: boolean;
13
+ mode: "visibility" | "focus";
12
14
  marker: ReactNode;
13
15
  }) => ReactNode;
14
16
  };
package/dist/legend.js CHANGED
@@ -2,17 +2,38 @@
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Symbols } from "recharts";
4
4
  import { useChart } from "./chart-context.js";
5
+ import { useChartInteraction } from "./chart-interaction.js";
5
6
  import { useEmphasis } from "./emphasis.js";
7
+ import { FillPatternSwatch } from "./fill-pattern.js";
8
+ import { colorStopToken } from "./series-color.js";
6
9
  /** Displays configured series; becomes interactive only when a controlled change callback exists. */
7
10
  export function Legend({ hideIcon = false, emphasis = "none", children, ...props }) {
8
- const { config, visibleSeries, onVisibleSeriesChange } = useChart();
9
- return (_jsx("ul", { "aria-label": "Chart legend", ...props, "data-kind-ui": "chart-legend", children: Object.entries(config).map(([key, item]) => {
11
+ const { config, paints, visibleSeries } = useChart();
12
+ const interaction = useChartInteraction();
13
+ return (
14
+ // biome-ignore lint/a11y/useKeyWithClickEvents: Delegates clicks from native buttons, which already implement Enter and Space.
15
+ _jsx("ul", { "aria-label": "Chart legend", ...props, "data-kind-ui": "chart-legend", onClick: (event) => {
16
+ props.onClick?.(event);
17
+ const button = event.target.closest("button[data-legend-key]");
18
+ if (button?.dataset.legendKey !== undefined && event.currentTarget.contains(button))
19
+ interaction.activate({ kind: interaction.kind, key: button.dataset.legendKey }, "legend", event);
20
+ }, children: Object.entries(config).map(([key, item]) => {
10
21
  const visible = visibleSeries?.includes(key) ?? true;
11
- const marker = item.icon && !hideIcon ? (_jsx("span", { "aria-hidden": "true", "data-kind-ui": "chart-icon", children: _jsx(item.icon, {}) })) : item.legendShape && !hideIcon ? (_jsx("svg", { "aria-hidden": "true", focusable: "false", "data-kind-ui": "chart-indicator", "data-legend-shape": item.legendShape, viewBox: "-8 -8 16 16", style: { "--kind-ui-chart-indicator-color": `var(--color-${key})` }, children: _jsx(Symbols, { type: item.legendShape, cx: 0, cy: 0, size: 64 }) })) : (_jsx("span", { "aria-hidden": "true", "data-kind-ui": "chart-indicator", style: { "--kind-ui-chart-indicator-color": `var(--color-${key})` } }));
12
- const content = children ? (children({ key, label: item.label, visible, marker })) : (_jsxs(_Fragment, { children: [marker, item.label] }));
13
- return (_jsx(LegendItem, { seriesKey: key, enabled: emphasis === "series" && visible, interactive: Boolean(onVisibleSeriesChange && visibleSeries), children: onVisibleSeriesChange && visibleSeries ? (_jsx("button", { type: "button", "aria-pressed": visible, "data-kind-ui": "chart-legend-button", onClick: () => onVisibleSeriesChange(visible
14
- ? visibleSeries.filter((value) => value !== key)
15
- : [...visibleSeries, key]), children: content })) : (_jsxs("span", { children: [content, visible ? null : " (hidden)"] })) }, key));
22
+ const marker = item.icon && !hideIcon ? (_jsx("span", { "aria-hidden": "true", "data-kind-ui": "chart-icon", children: _jsx(item.icon, {}) })) : item.legendShape && !hideIcon ? (_jsx("svg", { "aria-hidden": "true", focusable: "false", "data-kind-ui": "chart-indicator", "data-legend-shape": item.legendShape, viewBox: "-8 -8 16 16", style: {
23
+ "--kind-ui-chart-indicator-color": paints[key],
24
+ }, children: _jsx(Symbols, { type: item.legendShape, cx: 0, cy: 0, size: 64 }) })) : item.pattern && !hideIcon ? (_jsx(FillPatternSwatch, { pattern: item.pattern, color: `var(--color-${key})` })) : (_jsx("span", { "aria-hidden": "true", "data-kind-ui": "chart-indicator", style: {
25
+ "--kind-ui-chart-indicator-color": `var(--color-${key})`,
26
+ "--kind-ui-chart-indicator-background": `var(${colorStopToken(key, "gradient")})`,
27
+ } }));
28
+ const content = children ? (children({
29
+ key,
30
+ label: item.label,
31
+ visible,
32
+ marker,
33
+ selected: interaction.selected === key,
34
+ mode: interaction.mode,
35
+ })) : (_jsxs(_Fragment, { children: [marker, item.label] }));
36
+ return (_jsx(LegendItem, { seriesKey: key, enabled: emphasis === "series" && visible, interactive: interaction.interactive && interaction.eligible.includes(key), children: interaction.interactive && interaction.eligible.includes(key) ? (_jsx("button", { type: "button", "aria-pressed": interaction.mode === "focus" ? interaction.selected === key : visible, "data-kind-ui": "chart-legend-button", "data-legend-key": key, children: content })) : (_jsxs("span", { children: [content, visible ? null : " (hidden)"] })) }, key));
16
37
  }) }));
17
38
  }
18
39
  function LegendItem({ seriesKey, enabled, interactive, children, }) {
@@ -1,6 +1,11 @@
1
1
  import { type Attributes, type ComponentProps, type ComponentType } from "react";
2
2
  import { LineChart as EngineLineChart, type RadarChart as EngineRadarChart } from "recharts";
3
- export type LineChartProps = ComponentProps<typeof EngineLineChart>;
3
+ import type { LoadingAnimation } from "./loading-motion.js";
4
+ import { type LoadingDesign, type LoadingFamily } from "./loading-skeleton.js";
5
+ export type LineChartProps = ComponentProps<typeof EngineLineChart> & {
6
+ loading?: boolean | undefined;
7
+ loadingLabel?: string | undefined;
8
+ };
4
9
  type Point = {
5
10
  x: number;
6
11
  y: number;
@@ -27,7 +32,7 @@ declare function createKeyboardModality(): {
27
32
  export declare function useChartKeyboard(): boolean;
28
33
  /** Internal boundary shared by the static and optional Motion entry points. */
29
34
  type NativeChartProps = LineChartProps | ComponentProps<typeof EngineRadarChart>;
30
- export declare function LineChartFrame<Props extends NativeChartProps & Attributes = LineChartProps>({ engine: EngineChart, interrupt, clip, motionEnabled, categoryEmphasis, chartProps: props, children, }: {
35
+ export declare function LineChartFrame<Props extends NativeChartProps & Attributes = LineChartProps>({ engine: EngineChart, interrupt, clip, motionEnabled, categoryEmphasis, loading, loadingLabel, loadingSkeleton, loadingAnimation, loadingDesign, chartProps: props, children, }: {
31
36
  chartProps: Props;
32
37
  engine: ComponentType<Props>;
33
38
  children?: Props["children"];
@@ -35,7 +40,12 @@ export declare function LineChartFrame<Props extends NativeChartProps & Attribut
35
40
  clip?: string;
36
41
  motionEnabled?: boolean;
37
42
  categoryEmphasis?: boolean;
43
+ loading?: boolean | undefined;
44
+ loadingLabel?: string | undefined;
45
+ loadingSkeleton?: LoadingFamily;
46
+ loadingAnimation?: LoadingAnimation | undefined;
47
+ loadingDesign?: LoadingDesign | undefined;
38
48
  }): import("react").JSX.Element;
39
49
  /** Recharts owns geometry and keyboard selection; Kind shares pointer/keyboard modality. */
40
- export declare function LineChart(props: LineChartProps): import("react").JSX.Element;
50
+ export declare function LineChart({ loading, loadingLabel, ...props }: LineChartProps): import("react").JSX.Element;
41
51
  export {};