@chakra-ui/charts 3.15.0 → 3.16.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.
@@ -40,6 +40,7 @@ function BarListTooltip(props) {
40
40
  const { payload, labelFormatter, ...rest } = props;
41
41
  const chart = React__namespace.useContext(ChartContext);
42
42
  const formatter = labelFormatter || chart.formatNumber({ style: "decimal" });
43
+ if (!payload || chart.highlightedSeries !== payload.name) return null;
43
44
  return /* @__PURE__ */ jsxRuntime.jsx(
44
45
  react.AbsoluteCenter,
45
46
  {
@@ -61,7 +62,7 @@ function BarListTooltip(props) {
61
62
  );
62
63
  }
63
64
  function BarListBar(props) {
64
- const { label, showTooltip, ...rest } = props;
65
+ const { label, tooltip, ...rest } = props;
65
66
  const chart = React__namespace.useContext(ChartContext);
66
67
  const getPercent = (value) => chart.getValuePercent("value", value, (e) => [0, e.max]);
67
68
  const series = chart.getSeries({ name: "name" });
@@ -74,16 +75,17 @@ function BarListBar(props) {
74
75
  gap: "8",
75
76
  _hover: { bg: "bg.subtle" },
76
77
  onMouseMove: () => {
77
- if (!showTooltip) return;
78
+ if (!tooltip) return;
78
79
  if (chart.highlightedSeries === item.name) return;
79
80
  chart.setHighlightedSeries(item.name);
80
81
  },
81
82
  onMouseLeave: () => {
82
- if (!showTooltip) return;
83
+ if (!tooltip) return;
83
84
  chart.setHighlightedSeries(null);
84
85
  },
85
86
  children: /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { pos: "relative", flex: "1", className: "group", children: [
86
- showTooltip && chart.highlightedSeries === item.name && /* @__PURE__ */ jsxRuntime.jsx(BarListTooltip, { payload: item }),
87
+ typeof tooltip === "function" ? tooltip({ payload: item }) : null,
88
+ typeof tooltip === "boolean" && tooltip && /* @__PURE__ */ jsxRuntime.jsx(BarListTooltip, { payload: item }),
87
89
  /* @__PURE__ */ jsxRuntime.jsx(
88
90
  react.Box,
89
91
  {
@@ -65,11 +65,11 @@ function BarSegmentLabel(props) {
65
65
  )) });
66
66
  }
67
67
  function BarSegmentBar(props) {
68
- const { showTooltip, children, ...rest } = props;
68
+ const { tooltip, children, ...rest } = props;
69
69
  const chart = React__namespace.useContext(ChartContext);
70
70
  const getPercent = (value) => chart.getValuePercent("value", value);
71
71
  return /* @__PURE__ */ jsxRuntime.jsxs(react.HStack, { pos: "relative", gap: "1", ...rest, children: [
72
- chart.data.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
72
+ chart.data.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
73
73
  react.Box,
74
74
  {
75
75
  pos: "relative",
@@ -79,13 +79,16 @@ function BarSegmentBar(props) {
79
79
  bg: item.color,
80
80
  rounded: "l1",
81
81
  onMouseMove: () => {
82
- if (!showTooltip) return;
82
+ if (!tooltip) return;
83
83
  chart.setHighlightedSeries(item.name);
84
84
  },
85
85
  style: {
86
86
  ["--bar-percent"]: `${getPercent(item.value)}%`
87
87
  },
88
- children: showTooltip && /* @__PURE__ */ jsxRuntime.jsx(BarSegmentTooltip, { payload: item })
88
+ children: [
89
+ typeof tooltip === "function" ? tooltip({ payload: item }) : null,
90
+ typeof tooltip === "boolean" && tooltip && /* @__PURE__ */ jsxRuntime.jsx(BarSegmentTooltip, { payload: item })
91
+ ]
89
92
  },
90
93
  item.name
91
94
  )),
@@ -93,7 +96,7 @@ function BarSegmentBar(props) {
93
96
  ] });
94
97
  }
95
98
  function BarSegmentReference(props) {
96
- const { value, label } = props;
99
+ const { value, label, ...rest } = props;
97
100
  const chart = React__namespace.useContext(ChartContext);
98
101
  const getPercent = (value2) => chart.getValuePercent("value", value2);
99
102
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -104,6 +107,7 @@ function BarSegmentReference(props) {
104
107
  insetStart: `var(--bar-percent)`,
105
108
  bottom: "0",
106
109
  style: { ["--bar-percent"]: `${getPercent(value)}%` },
110
+ ...rest,
107
111
  children: /* @__PURE__ */ jsxRuntime.jsxs(react.Flex, { gap: "2", h: "full", children: [
108
112
  /* @__PURE__ */ jsxRuntime.jsx(
109
113
  react.Span,
@@ -168,11 +172,9 @@ function BarSegmentLegend(props) {
168
172
  ] }, item.name)) });
169
173
  }
170
174
  function BarSegmentTooltip(props) {
171
- const { payload } = props;
175
+ const { payload, ...rest } = props;
172
176
  const chart = React__namespace.useContext(ChartContext);
173
177
  if (!payload || chart.highlightedSeries !== payload.name) return null;
174
- const item = chart.data.find((d) => d.name === payload.name);
175
- if (!item) return null;
176
178
  const formatter = chart.formatNumber({ maximumFractionDigits: 2 });
177
179
  return /* @__PURE__ */ jsxRuntime.jsxs(
178
180
  react.HStack,
@@ -188,18 +190,18 @@ function BarSegmentTooltip(props) {
188
190
  gap: "1.5",
189
191
  rounded: "l2",
190
192
  shadow: "md",
191
- ...props,
193
+ ...rest,
192
194
  children: [
193
195
  /* @__PURE__ */ jsxRuntime.jsx(
194
196
  react.ColorSwatch,
195
197
  {
196
- value: chart.color(item.color),
198
+ value: chart.color(payload.color),
197
199
  boxSize: "0.82em",
198
200
  rounded: "full"
199
201
  }
200
202
  ),
201
- /* @__PURE__ */ jsxRuntime.jsx(react.Span, { children: item.name }),
202
- /* @__PURE__ */ jsxRuntime.jsx(react.Span, { fontFamily: "mono", fontWeight: "medium", children: formatter(item.value) })
203
+ /* @__PURE__ */ jsxRuntime.jsx(react.Span, { children: payload.name }),
204
+ /* @__PURE__ */ jsxRuntime.jsx(react.Span, { fontFamily: "mono", fontWeight: "medium", children: formatter(payload.value) })
203
205
  ]
204
206
  }
205
207
  );
@@ -11,4 +11,5 @@ exports.Label = barSegment.BarSegmentLabel;
11
11
  exports.Legend = barSegment.BarSegmentLegend;
12
12
  exports.Reference = barSegment.BarSegmentReference;
13
13
  exports.Root = barSegment.BarSegmentRoot;
14
+ exports.Tooltip = barSegment.BarSegmentTooltip;
14
15
  exports.Value = barSegment.BarSegmentValue;
@@ -8,27 +8,8 @@ var React = require('react');
8
8
  var recharts = require('recharts');
9
9
  var useChart = require('../use-chart.cjs');
10
10
 
11
- function _interopNamespaceDefault(e) {
12
- var n = Object.create(null);
13
- if (e) {
14
- Object.keys(e).forEach(function (k) {
15
- if (k !== 'default') {
16
- var d = Object.getOwnPropertyDescriptor(e, k);
17
- Object.defineProperty(n, k, d.get ? d : {
18
- enumerable: true,
19
- get: function () { return e[k]; }
20
- });
21
- }
22
- });
23
- }
24
- n.default = e;
25
- return Object.freeze(n);
26
- }
27
-
28
- var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
29
-
30
- const ChartContext = React__namespace.createContext({});
31
- const useChartContext = () => React__namespace.useContext(ChartContext);
11
+ const ChartContext = React.createContext({});
12
+ const useChartContext = () => React.useContext(ChartContext);
32
13
  const baseCss = react.defineStyle({
33
14
  width: "100%",
34
15
  [`& :where(${[
@@ -50,26 +31,23 @@ const baseCss = react.defineStyle({
50
31
  overflow: "visible"
51
32
  }
52
33
  });
53
- const ChartRoot = React__namespace.forwardRef(
54
- function ChartRoot2(props, ref) {
55
- const { children, css, chart, ...rest } = props;
56
- return /* @__PURE__ */ jsxRuntime.jsx(ChartContext.Provider, { value: chart, children: /* @__PURE__ */ jsxRuntime.jsx(
57
- react.Box,
58
- {
59
- ref,
60
- aspectRatio: "landscape",
61
- textStyle: "xs",
62
- css: [baseCss, css],
63
- ...rest,
64
- children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { children })
65
- }
66
- ) });
67
- }
68
- );
69
- const ChartGradient = React__namespace.forwardRef(function ChartGradient2(props, ref) {
34
+ function ChartRoot(props) {
35
+ const { children, css, chart, ...rest } = props;
36
+ return /* @__PURE__ */ jsxRuntime.jsx(ChartContext.Provider, { value: chart, children: /* @__PURE__ */ jsxRuntime.jsx(
37
+ react.Box,
38
+ {
39
+ aspectRatio: "landscape",
40
+ textStyle: "xs",
41
+ css: [baseCss, css],
42
+ ...rest,
43
+ children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { children })
44
+ }
45
+ ) });
46
+ }
47
+ function ChartGradient(props) {
70
48
  const chart = useChartContext();
71
49
  const { id, fillOpacity, stops } = props;
72
- return /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id, x1: "0", y1: "0", x2: "0", y2: "1", ref, children: stops.map((stop, index) => /* @__PURE__ */ jsxRuntime.jsx(
50
+ return /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id, x1: "0", y1: "0", x2: "0", y2: "1", children: stops.map((stop, index) => /* @__PURE__ */ jsxRuntime.jsx(
73
51
  "stop",
74
52
  {
75
53
  offset: stop.offset,
@@ -78,7 +56,7 @@ const ChartGradient = React__namespace.forwardRef(function ChartGradient2(props,
78
56
  },
79
57
  index
80
58
  )) });
81
- });
59
+ }
82
60
  const hAlignMap = {
83
61
  left: "flex-start",
84
62
  center: "center",
@@ -92,7 +70,8 @@ function ChartLegend(props) {
92
70
  title,
93
71
  orientation,
94
72
  nameKey,
95
- spacing = "2.5"
73
+ spacing = "3",
74
+ interaction = "hover"
96
75
  } = props;
97
76
  const chart = useChartContext();
98
77
  const filteredPayload = payload?.filter(
@@ -119,18 +98,40 @@ function ChartLegend(props) {
119
98
  flexWrap: "wrap",
120
99
  children: filteredPayload.map((item, index) => {
121
100
  const config = chart.getSeries(item);
101
+ const seriesName = config?.name?.toString();
122
102
  const name = useChart.getProp(item.payload, nameKey);
123
- return /* @__PURE__ */ jsxRuntime.jsxs(react.HStack, { gap: "1", _icon: { boxSize: "3" }, children: [
124
- config?.icon || /* @__PURE__ */ jsxRuntime.jsx(
125
- react.ColorSwatch,
126
- {
127
- boxSize: "2.5",
128
- rounded: "full",
129
- value: chart.color(config?.color)
130
- }
131
- ),
132
- /* @__PURE__ */ jsxRuntime.jsx(react.Span, { color: "fg.muted", children: name || config?.label })
133
- ] }, index);
103
+ return /* @__PURE__ */ jsxRuntime.jsxs(
104
+ react.HStack,
105
+ {
106
+ gap: "1.5",
107
+ _icon: { boxSize: "3" },
108
+ style: {
109
+ opacity: chart.getSeriesOpacity(seriesName, 0.6)
110
+ },
111
+ onClick: () => {
112
+ if (interaction === "click" && seriesName) {
113
+ chart.setHighlightedSeries(
114
+ (prev) => prev === seriesName ? null : seriesName
115
+ );
116
+ }
117
+ },
118
+ onMouseEnter: () => {
119
+ if (interaction === "hover" && seriesName) {
120
+ chart.setHighlightedSeries(seriesName);
121
+ }
122
+ },
123
+ onMouseLeave: () => {
124
+ if (interaction === "hover" && seriesName) {
125
+ chart.setHighlightedSeries(null);
126
+ }
127
+ },
128
+ children: [
129
+ config?.icon || /* @__PURE__ */ jsxRuntime.jsx(react.ColorSwatch, { boxSize: "2", value: chart.color(config?.color) }),
130
+ /* @__PURE__ */ jsxRuntime.jsx(react.Span, { color: "fg.muted", children: name || config?.label })
131
+ ]
132
+ },
133
+ index
134
+ );
134
135
  })
135
136
  }
136
137
  )
@@ -156,7 +157,7 @@ function ChartTooltip(props) {
156
157
  (item) => item.color !== "none" || item.type !== "none"
157
158
  );
158
159
  const total = React.useMemo(() => chart.getPayloadTotal(payload), [payload, chart]);
159
- const tooltipLabel = React__namespace.useMemo(() => {
160
+ const tooltipLabel = React.useMemo(() => {
160
161
  const item = payload?.[0];
161
162
  const itemLabel = `${useChart.getProp(item?.payload, nameKey) || label || item?.dataKey || "value"}`;
162
163
  return labelFormatter?.(itemLabel, payload ?? []) ?? itemLabel;
@@ -233,7 +234,7 @@ function ChartTooltip(props) {
233
234
  }
234
235
  const isPolarViewBox = (viewBox) => "cx" in viewBox && "cy" in viewBox;
235
236
  function ChartRadialText(props) {
236
- const { viewBox, title, description, gap = 24 } = props;
237
+ const { viewBox, title, description, gap = 24, fontSize = "2rem" } = props;
237
238
  const chart = useChartContext();
238
239
  if (!viewBox || !isPolarViewBox(viewBox)) return null;
239
240
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -250,7 +251,7 @@ function ChartRadialText(props) {
250
251
  {
251
252
  x: viewBox.cx,
252
253
  y: viewBox.cy,
253
- style: { fontSize: "2rem", fontWeight: 600 },
254
+ style: { fontSize, fontWeight: 600 },
254
255
  children: title
255
256
  }
256
257
  ),
@@ -27,10 +27,8 @@ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
27
27
  function useChart(props) {
28
28
  const { data, series = [], sort } = props;
29
29
  const id = React__namespace.useId();
30
- const [selectedSeries, setSelectedSeries] = React__namespace.useState(
31
- null
32
- );
33
30
  const [highlightedSeries, setHighlightedSeries] = React__namespace.useState(null);
31
+ const isHighlightedSeries = (name) => highlightedSeries === name;
34
32
  const env = react.useLocaleContext();
35
33
  const sys = react.useChakraContext();
36
34
  const color = (key2) => sys.token(`colors.${key2}`, key2);
@@ -93,11 +91,19 @@ function useChart(props) {
93
91
  return sort.direction === "desc" ? bValue - aValue : aValue - bValue;
94
92
  });
95
93
  }, [data, sort]);
94
+ const getSeriesOpacity = (name, fallback = 0.2) => {
95
+ if (name && highlightedSeries)
96
+ return isHighlightedSeries(name) ? 1 : fallback;
97
+ };
98
+ const groupByImpl = (key2) => {
99
+ return groupBy(data, key2);
100
+ };
96
101
  return {
97
102
  id,
98
103
  key,
99
104
  // series
100
105
  data: sortedData,
106
+ groupBy: groupByImpl,
101
107
  series,
102
108
  getSeries,
103
109
  // token functions
@@ -108,10 +114,10 @@ function useChart(props) {
108
114
  formatNumber,
109
115
  formatDate,
110
116
  // state
111
- selectedSeries,
112
- setSelectedSeries,
113
117
  highlightedSeries,
114
118
  setHighlightedSeries,
119
+ isHighlightedSeries,
120
+ getSeriesOpacity,
115
121
  // value functions
116
122
  getTotal,
117
123
  getMin,
@@ -125,6 +131,16 @@ function getProp(item, key) {
125
131
  if (!key || !isObject(item)) return;
126
132
  return Reflect.get(item, key);
127
133
  }
134
+ function groupBy(data, key) {
135
+ const groups = /* @__PURE__ */ new Map();
136
+ for (const item of data) {
137
+ const value = item[key];
138
+ const group = groups.get(value) || [];
139
+ group.push(item);
140
+ groups.set(value, group);
141
+ }
142
+ return Array.from(groups.values());
143
+ }
128
144
 
129
145
  exports.getProp = getProp;
130
146
  exports.useChart = useChart;
@@ -19,6 +19,7 @@ function BarListTooltip(props) {
19
19
  const { payload, labelFormatter, ...rest } = props;
20
20
  const chart = React.useContext(ChartContext);
21
21
  const formatter = labelFormatter || chart.formatNumber({ style: "decimal" });
22
+ if (!payload || chart.highlightedSeries !== payload.name) return null;
22
23
  return /* @__PURE__ */ jsx(
23
24
  AbsoluteCenter,
24
25
  {
@@ -40,7 +41,7 @@ function BarListTooltip(props) {
40
41
  );
41
42
  }
42
43
  function BarListBar(props) {
43
- const { label, showTooltip, ...rest } = props;
44
+ const { label, tooltip, ...rest } = props;
44
45
  const chart = React.useContext(ChartContext);
45
46
  const getPercent = (value) => chart.getValuePercent("value", value, (e) => [0, e.max]);
46
47
  const series = chart.getSeries({ name: "name" });
@@ -53,16 +54,17 @@ function BarListBar(props) {
53
54
  gap: "8",
54
55
  _hover: { bg: "bg.subtle" },
55
56
  onMouseMove: () => {
56
- if (!showTooltip) return;
57
+ if (!tooltip) return;
57
58
  if (chart.highlightedSeries === item.name) return;
58
59
  chart.setHighlightedSeries(item.name);
59
60
  },
60
61
  onMouseLeave: () => {
61
- if (!showTooltip) return;
62
+ if (!tooltip) return;
62
63
  chart.setHighlightedSeries(null);
63
64
  },
64
65
  children: /* @__PURE__ */ jsxs(Box, { pos: "relative", flex: "1", className: "group", children: [
65
- showTooltip && chart.highlightedSeries === item.name && /* @__PURE__ */ jsx(BarListTooltip, { payload: item }),
66
+ typeof tooltip === "function" ? tooltip({ payload: item }) : null,
67
+ typeof tooltip === "boolean" && tooltip && /* @__PURE__ */ jsx(BarListTooltip, { payload: item }),
66
68
  /* @__PURE__ */ jsx(
67
69
  Box,
68
70
  {
@@ -44,11 +44,11 @@ function BarSegmentLabel(props) {
44
44
  )) });
45
45
  }
46
46
  function BarSegmentBar(props) {
47
- const { showTooltip, children, ...rest } = props;
47
+ const { tooltip, children, ...rest } = props;
48
48
  const chart = React.useContext(ChartContext);
49
49
  const getPercent = (value) => chart.getValuePercent("value", value);
50
50
  return /* @__PURE__ */ jsxs(HStack, { pos: "relative", gap: "1", ...rest, children: [
51
- chart.data.map((item) => /* @__PURE__ */ jsx(
51
+ chart.data.map((item) => /* @__PURE__ */ jsxs(
52
52
  Box,
53
53
  {
54
54
  pos: "relative",
@@ -58,13 +58,16 @@ function BarSegmentBar(props) {
58
58
  bg: item.color,
59
59
  rounded: "l1",
60
60
  onMouseMove: () => {
61
- if (!showTooltip) return;
61
+ if (!tooltip) return;
62
62
  chart.setHighlightedSeries(item.name);
63
63
  },
64
64
  style: {
65
65
  ["--bar-percent"]: `${getPercent(item.value)}%`
66
66
  },
67
- children: showTooltip && /* @__PURE__ */ jsx(BarSegmentTooltip, { payload: item })
67
+ children: [
68
+ typeof tooltip === "function" ? tooltip({ payload: item }) : null,
69
+ typeof tooltip === "boolean" && tooltip && /* @__PURE__ */ jsx(BarSegmentTooltip, { payload: item })
70
+ ]
68
71
  },
69
72
  item.name
70
73
  )),
@@ -72,7 +75,7 @@ function BarSegmentBar(props) {
72
75
  ] });
73
76
  }
74
77
  function BarSegmentReference(props) {
75
- const { value, label } = props;
78
+ const { value, label, ...rest } = props;
76
79
  const chart = React.useContext(ChartContext);
77
80
  const getPercent = (value2) => chart.getValuePercent("value", value2);
78
81
  return /* @__PURE__ */ jsx(
@@ -83,6 +86,7 @@ function BarSegmentReference(props) {
83
86
  insetStart: `var(--bar-percent)`,
84
87
  bottom: "0",
85
88
  style: { ["--bar-percent"]: `${getPercent(value)}%` },
89
+ ...rest,
86
90
  children: /* @__PURE__ */ jsxs(Flex, { gap: "2", h: "full", children: [
87
91
  /* @__PURE__ */ jsx(
88
92
  Span,
@@ -147,11 +151,9 @@ function BarSegmentLegend(props) {
147
151
  ] }, item.name)) });
148
152
  }
149
153
  function BarSegmentTooltip(props) {
150
- const { payload } = props;
154
+ const { payload, ...rest } = props;
151
155
  const chart = React.useContext(ChartContext);
152
156
  if (!payload || chart.highlightedSeries !== payload.name) return null;
153
- const item = chart.data.find((d) => d.name === payload.name);
154
- if (!item) return null;
155
157
  const formatter = chart.formatNumber({ maximumFractionDigits: 2 });
156
158
  return /* @__PURE__ */ jsxs(
157
159
  HStack,
@@ -167,18 +169,18 @@ function BarSegmentTooltip(props) {
167
169
  gap: "1.5",
168
170
  rounded: "l2",
169
171
  shadow: "md",
170
- ...props,
172
+ ...rest,
171
173
  children: [
172
174
  /* @__PURE__ */ jsx(
173
175
  ColorSwatch,
174
176
  {
175
- value: chart.color(item.color),
177
+ value: chart.color(payload.color),
176
178
  boxSize: "0.82em",
177
179
  rounded: "full"
178
180
  }
179
181
  ),
180
- /* @__PURE__ */ jsx(Span, { children: item.name }),
181
- /* @__PURE__ */ jsx(Span, { fontFamily: "mono", fontWeight: "medium", children: formatter(item.value) })
182
+ /* @__PURE__ */ jsx(Span, { children: payload.name }),
183
+ /* @__PURE__ */ jsx(Span, { fontFamily: "mono", fontWeight: "medium", children: formatter(payload.value) })
182
184
  ]
183
185
  }
184
186
  );
@@ -1,2 +1,2 @@
1
1
  "use strict";
2
- export { BarSegmentBar as Bar, BarSegmentContent as Content, BarSegmentLabel as Label, BarSegmentLegend as Legend, BarSegmentReference as Reference, BarSegmentRoot as Root, BarSegmentValue as Value } from './bar-segment.js';
2
+ export { BarSegmentBar as Bar, BarSegmentContent as Content, BarSegmentLabel as Label, BarSegmentLegend as Legend, BarSegmentReference as Reference, BarSegmentRoot as Root, BarSegmentTooltip as Tooltip, BarSegmentValue as Value } from './bar-segment.js';
@@ -2,13 +2,12 @@
2
2
  "use client";
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { defineStyle, Box, Stack, Text, Flex, HStack, ColorSwatch, Span, Separator } from '@chakra-ui/react';
5
- import * as React from 'react';
6
- import { useMemo } from 'react';
5
+ import { createContext, useMemo, useContext } from 'react';
7
6
  import { ResponsiveContainer } from 'recharts';
8
7
  import { getProp } from '../use-chart.js';
9
8
 
10
- const ChartContext = React.createContext({});
11
- const useChartContext = () => React.useContext(ChartContext);
9
+ const ChartContext = createContext({});
10
+ const useChartContext = () => useContext(ChartContext);
12
11
  const baseCss = defineStyle({
13
12
  width: "100%",
14
13
  [`& :where(${[
@@ -30,26 +29,23 @@ const baseCss = defineStyle({
30
29
  overflow: "visible"
31
30
  }
32
31
  });
33
- const ChartRoot = React.forwardRef(
34
- function ChartRoot2(props, ref) {
35
- const { children, css, chart, ...rest } = props;
36
- return /* @__PURE__ */ jsx(ChartContext.Provider, { value: chart, children: /* @__PURE__ */ jsx(
37
- Box,
38
- {
39
- ref,
40
- aspectRatio: "landscape",
41
- textStyle: "xs",
42
- css: [baseCss, css],
43
- ...rest,
44
- children: /* @__PURE__ */ jsx(ResponsiveContainer, { children })
45
- }
46
- ) });
47
- }
48
- );
49
- const ChartGradient = React.forwardRef(function ChartGradient2(props, ref) {
32
+ function ChartRoot(props) {
33
+ const { children, css, chart, ...rest } = props;
34
+ return /* @__PURE__ */ jsx(ChartContext.Provider, { value: chart, children: /* @__PURE__ */ jsx(
35
+ Box,
36
+ {
37
+ aspectRatio: "landscape",
38
+ textStyle: "xs",
39
+ css: [baseCss, css],
40
+ ...rest,
41
+ children: /* @__PURE__ */ jsx(ResponsiveContainer, { children })
42
+ }
43
+ ) });
44
+ }
45
+ function ChartGradient(props) {
50
46
  const chart = useChartContext();
51
47
  const { id, fillOpacity, stops } = props;
52
- return /* @__PURE__ */ jsx("linearGradient", { id, x1: "0", y1: "0", x2: "0", y2: "1", ref, children: stops.map((stop, index) => /* @__PURE__ */ jsx(
48
+ return /* @__PURE__ */ jsx("linearGradient", { id, x1: "0", y1: "0", x2: "0", y2: "1", children: stops.map((stop, index) => /* @__PURE__ */ jsx(
53
49
  "stop",
54
50
  {
55
51
  offset: stop.offset,
@@ -58,7 +54,7 @@ const ChartGradient = React.forwardRef(function ChartGradient2(props, ref) {
58
54
  },
59
55
  index
60
56
  )) });
61
- });
57
+ }
62
58
  const hAlignMap = {
63
59
  left: "flex-start",
64
60
  center: "center",
@@ -72,7 +68,8 @@ function ChartLegend(props) {
72
68
  title,
73
69
  orientation,
74
70
  nameKey,
75
- spacing = "2.5"
71
+ spacing = "3",
72
+ interaction = "hover"
76
73
  } = props;
77
74
  const chart = useChartContext();
78
75
  const filteredPayload = payload?.filter(
@@ -99,18 +96,40 @@ function ChartLegend(props) {
99
96
  flexWrap: "wrap",
100
97
  children: filteredPayload.map((item, index) => {
101
98
  const config = chart.getSeries(item);
99
+ const seriesName = config?.name?.toString();
102
100
  const name = getProp(item.payload, nameKey);
103
- return /* @__PURE__ */ jsxs(HStack, { gap: "1", _icon: { boxSize: "3" }, children: [
104
- config?.icon || /* @__PURE__ */ jsx(
105
- ColorSwatch,
106
- {
107
- boxSize: "2.5",
108
- rounded: "full",
109
- value: chart.color(config?.color)
110
- }
111
- ),
112
- /* @__PURE__ */ jsx(Span, { color: "fg.muted", children: name || config?.label })
113
- ] }, index);
101
+ return /* @__PURE__ */ jsxs(
102
+ HStack,
103
+ {
104
+ gap: "1.5",
105
+ _icon: { boxSize: "3" },
106
+ style: {
107
+ opacity: chart.getSeriesOpacity(seriesName, 0.6)
108
+ },
109
+ onClick: () => {
110
+ if (interaction === "click" && seriesName) {
111
+ chart.setHighlightedSeries(
112
+ (prev) => prev === seriesName ? null : seriesName
113
+ );
114
+ }
115
+ },
116
+ onMouseEnter: () => {
117
+ if (interaction === "hover" && seriesName) {
118
+ chart.setHighlightedSeries(seriesName);
119
+ }
120
+ },
121
+ onMouseLeave: () => {
122
+ if (interaction === "hover" && seriesName) {
123
+ chart.setHighlightedSeries(null);
124
+ }
125
+ },
126
+ children: [
127
+ config?.icon || /* @__PURE__ */ jsx(ColorSwatch, { boxSize: "2", value: chart.color(config?.color) }),
128
+ /* @__PURE__ */ jsx(Span, { color: "fg.muted", children: name || config?.label })
129
+ ]
130
+ },
131
+ index
132
+ );
114
133
  })
115
134
  }
116
135
  )
@@ -136,7 +155,7 @@ function ChartTooltip(props) {
136
155
  (item) => item.color !== "none" || item.type !== "none"
137
156
  );
138
157
  const total = useMemo(() => chart.getPayloadTotal(payload), [payload, chart]);
139
- const tooltipLabel = React.useMemo(() => {
158
+ const tooltipLabel = useMemo(() => {
140
159
  const item = payload?.[0];
141
160
  const itemLabel = `${getProp(item?.payload, nameKey) || label || item?.dataKey || "value"}`;
142
161
  return labelFormatter?.(itemLabel, payload ?? []) ?? itemLabel;
@@ -213,7 +232,7 @@ function ChartTooltip(props) {
213
232
  }
214
233
  const isPolarViewBox = (viewBox) => "cx" in viewBox && "cy" in viewBox;
215
234
  function ChartRadialText(props) {
216
- const { viewBox, title, description, gap = 24 } = props;
235
+ const { viewBox, title, description, gap = 24, fontSize = "2rem" } = props;
217
236
  const chart = useChartContext();
218
237
  if (!viewBox || !isPolarViewBox(viewBox)) return null;
219
238
  return /* @__PURE__ */ jsxs(
@@ -230,7 +249,7 @@ function ChartRadialText(props) {
230
249
  {
231
250
  x: viewBox.cx,
232
251
  y: viewBox.cy,
233
- style: { fontSize: "2rem", fontWeight: 600 },
252
+ style: { fontSize, fontWeight: 600 },
234
253
  children: title
235
254
  }
236
255
  ),
@@ -6,10 +6,8 @@ import * as React from 'react';
6
6
  function useChart(props) {
7
7
  const { data, series = [], sort } = props;
8
8
  const id = React.useId();
9
- const [selectedSeries, setSelectedSeries] = React.useState(
10
- null
11
- );
12
9
  const [highlightedSeries, setHighlightedSeries] = React.useState(null);
10
+ const isHighlightedSeries = (name) => highlightedSeries === name;
13
11
  const env = useLocaleContext();
14
12
  const sys = useChakraContext();
15
13
  const color = (key2) => sys.token(`colors.${key2}`, key2);
@@ -72,11 +70,19 @@ function useChart(props) {
72
70
  return sort.direction === "desc" ? bValue - aValue : aValue - bValue;
73
71
  });
74
72
  }, [data, sort]);
73
+ const getSeriesOpacity = (name, fallback = 0.2) => {
74
+ if (name && highlightedSeries)
75
+ return isHighlightedSeries(name) ? 1 : fallback;
76
+ };
77
+ const groupByImpl = (key2) => {
78
+ return groupBy(data, key2);
79
+ };
75
80
  return {
76
81
  id,
77
82
  key,
78
83
  // series
79
84
  data: sortedData,
85
+ groupBy: groupByImpl,
80
86
  series,
81
87
  getSeries,
82
88
  // token functions
@@ -87,10 +93,10 @@ function useChart(props) {
87
93
  formatNumber,
88
94
  formatDate,
89
95
  // state
90
- selectedSeries,
91
- setSelectedSeries,
92
96
  highlightedSeries,
93
97
  setHighlightedSeries,
98
+ isHighlightedSeries,
99
+ getSeriesOpacity,
94
100
  // value functions
95
101
  getTotal,
96
102
  getMin,
@@ -104,5 +110,15 @@ function getProp(item, key) {
104
110
  if (!key || !isObject(item)) return;
105
111
  return Reflect.get(item, key);
106
112
  }
113
+ function groupBy(data, key) {
114
+ const groups = /* @__PURE__ */ new Map();
115
+ for (const item of data) {
116
+ const value = item[key];
117
+ const group = groups.get(value) || [];
118
+ group.push(item);
119
+ groups.set(value, group);
120
+ }
121
+ return Array.from(groups.values());
122
+ }
107
123
 
108
124
  export { getProp, useChart };
@@ -19,9 +19,9 @@ export interface BarListTooltipProps extends AbsoluteCenterProps {
19
19
  payload: BarListData;
20
20
  labelFormatter?: (value: number) => React.ReactNode;
21
21
  }
22
- export declare function BarListTooltip(props: BarListTooltipProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function BarListTooltip(props: BarListTooltipProps): import("react/jsx-runtime").JSX.Element | null;
23
23
  export interface BarListBarProps extends StackProps {
24
- showTooltip?: boolean;
24
+ tooltip?: boolean | ((props: BarListTooltipProps) => React.ReactNode);
25
25
  label?: (props: {
26
26
  payload: BarListData;
27
27
  index: number;
@@ -1,4 +1,4 @@
1
- import type { StackProps, Tokens } from "@chakra-ui/react";
1
+ import type { BoxProps, StackProps, Tokens } from "@chakra-ui/react";
2
2
  import * as React from "react";
3
3
  import type { UseChartReturn } from "../use-chart";
4
4
  export interface BarSegmentData {
@@ -18,10 +18,10 @@ export interface BarSegmentLabelProps extends StackProps {
18
18
  }
19
19
  export declare function BarSegmentLabel(props: BarSegmentLabelProps): import("react/jsx-runtime").JSX.Element;
20
20
  export interface BarSegmentBarProps extends StackProps {
21
- showTooltip?: boolean;
21
+ tooltip?: boolean | ((props: BarSegmentTooltipProps) => React.ReactNode);
22
22
  }
23
23
  export declare function BarSegmentBar(props: BarSegmentBarProps): import("react/jsx-runtime").JSX.Element;
24
- export interface BarSegmentReferenceProps {
24
+ export interface BarSegmentReferenceProps extends BoxProps {
25
25
  value: number;
26
26
  label?: React.ReactNode;
27
27
  }
@@ -35,7 +35,7 @@ export interface BarSegmentLegendProps extends StackProps {
35
35
  valueFormatter?: (value: number) => string;
36
36
  }
37
37
  export declare function BarSegmentLegend(props: BarSegmentLegendProps): import("react/jsx-runtime").JSX.Element;
38
- export interface BarSegmentTooltipProps {
38
+ export interface BarSegmentTooltipProps extends StackProps {
39
39
  payload: BarSegmentData;
40
40
  }
41
41
  export declare function BarSegmentTooltip(props: BarSegmentTooltipProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,2 +1,2 @@
1
- export { BarSegmentBar as Bar, BarSegmentContent as Content, BarSegmentLabel as Label, BarSegmentRoot as Root, BarSegmentValue as Value, BarSegmentLegend as Legend, BarSegmentReference as Reference, } from "./bar-segment";
1
+ export { BarSegmentBar as Bar, BarSegmentContent as Content, BarSegmentLabel as Label, BarSegmentRoot as Root, BarSegmentValue as Value, BarSegmentLegend as Legend, BarSegmentReference as Reference, BarSegmentTooltip as Tooltip, } from "./bar-segment";
2
2
  export type { BarSegmentData as Data, BarSegmentLegendProps as LegendProps, BarSegmentRootProps as RootProps, BarSegmentValueProps as ValueProps, BarSegmentReferenceProps as ReferenceProps, BarSegmentBarProps as BarProps, BarSegmentLabelProps as LabelProps, BarSegmentTooltipProps as TooltipProps, } from "./bar-segment";
@@ -1,14 +1,13 @@
1
1
  import type { BoxProps } from "@chakra-ui/react";
2
- import * as React from "react";
3
2
  import type { LegendProps, TooltipProps } from "recharts";
4
3
  import type { Payload } from "recharts/types/component/DefaultTooltipContent";
5
4
  import type { ViewBox } from "recharts/types/util/types";
6
5
  import { type ChartColor, type UseChartReturn } from "../use-chart";
7
- export interface ChartRootProps extends BoxProps {
6
+ export interface ChartRootProps<T> extends BoxProps {
8
7
  children: React.ReactElement;
9
- chart: UseChartReturn<unknown>;
8
+ chart: UseChartReturn<T>;
10
9
  }
11
- export declare const ChartRoot: React.ForwardRefExoticComponent<ChartRootProps & React.RefAttributes<HTMLDivElement>>;
10
+ export declare function ChartRoot<T>(props: ChartRootProps<T>): import("react/jsx-runtime").JSX.Element;
12
11
  export interface ChartGradientProps {
13
12
  id: string;
14
13
  fillOpacity?: number;
@@ -18,10 +17,11 @@ export interface ChartGradientProps {
18
17
  opacity?: number;
19
18
  }[];
20
19
  }
21
- export declare const ChartGradient: React.ForwardRefExoticComponent<ChartGradientProps & React.RefAttributes<SVGLinearGradientElement>>;
20
+ export declare function ChartGradient(props: ChartGradientProps): import("react/jsx-runtime").JSX.Element;
22
21
  export interface ChartLegendProps extends LegendProps {
23
22
  title?: React.ReactNode;
24
23
  nameKey?: string;
24
+ interaction?: "hover" | "click";
25
25
  }
26
26
  export declare function ChartLegend(props: ChartLegendProps): import("react/jsx-runtime").JSX.Element | null;
27
27
  export interface ChartTooltipProps extends TooltipProps<string, string> {
@@ -40,5 +40,6 @@ export interface ChartRadialTextProps {
40
40
  title: React.ReactNode;
41
41
  description: React.ReactNode;
42
42
  gap?: number;
43
+ fontSize?: string;
43
44
  }
44
45
  export declare function ChartRadialText(props: ChartRadialTextProps): import("react/jsx-runtime").JSX.Element | null;
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  export type ChartColor = Tokens["colors"] | React.CSSProperties["color"];
4
4
  export type ChartSize = Tokens["sizes"] | (string & {});
5
5
  export type ChartSpacing = Tokens["spacing"] | (string & {});
6
- type ItemDataKey<T> = T extends Array<infer U> ? keyof U : keyof T;
6
+ type ItemDataKey<T> = keyof T;
7
7
  interface SeriesItem<T> {
8
8
  name?: ItemDataKey<T>;
9
9
  color?: ChartColor;
@@ -18,7 +18,7 @@ export interface UseChartProps<T> {
18
18
  data: T[];
19
19
  series?: SeriesItem<T>[];
20
20
  sort?: {
21
- by: keyof T;
21
+ by: ItemDataKey<T>;
22
22
  direction: "asc" | "desc";
23
23
  };
24
24
  }
@@ -26,10 +26,11 @@ type ValueDomain = [number, number] | ((props: {
26
26
  min: number;
27
27
  max: number;
28
28
  }) => [number, number]);
29
- export declare function useChart<T>(props: UseChartProps<T>): {
29
+ export declare function useChart<T = any>(props: UseChartProps<T>): {
30
30
  id: string;
31
31
  key: <K extends ItemDataKey<T>>(prop: K | undefined) => K;
32
32
  data: T[];
33
+ groupBy: (key: ItemDataKey<T>) => T[][];
33
34
  series: SeriesItem<T>[];
34
35
  getSeries: (item: unknown) => SeriesItem<T> | undefined;
35
36
  color: (key: ChartColor | undefined) => any;
@@ -37,10 +38,10 @@ export declare function useChart<T>(props: UseChartProps<T>): {
37
38
  spacing: (key: ChartSpacing | undefined) => any;
38
39
  formatNumber: (options?: Intl.NumberFormatOptions) => (value: number) => string;
39
40
  formatDate: (options?: Intl.DateTimeFormatOptions) => (value: string) => string;
40
- selectedSeries: string | null;
41
- setSelectedSeries: React.Dispatch<React.SetStateAction<string | null>>;
42
41
  highlightedSeries: string | null;
43
42
  setHighlightedSeries: React.Dispatch<React.SetStateAction<string | null>>;
43
+ isHighlightedSeries: (name: string | undefined) => boolean;
44
+ getSeriesOpacity: (name: string | undefined, fallback?: number) => number | undefined;
44
45
  getTotal: (key: keyof T) => number;
45
46
  getMin: (key: keyof T) => number;
46
47
  getMax: (key: keyof T) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chakra-ui/charts",
3
- "version": "3.15.0",
3
+ "version": "3.16.0",
4
4
  "description": "Data visualization components for Chakra UI",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/index.cjs",
@@ -43,10 +43,10 @@
43
43
  "./package.json": "./package.json"
44
44
  },
45
45
  "devDependencies": {
46
- "recharts": "2.13.3",
47
- "react": "19.0.0",
48
- "react-dom": "19.0.0",
49
- "@chakra-ui/react": "3.15.0"
46
+ "recharts": "2.15.2",
47
+ "react": "19.1.0",
48
+ "react-dom": "19.1.0",
49
+ "@chakra-ui/react": "3.16.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@chakra-ui/react": ">=3",