Skip to content

Commit 5df2fff

Browse files
author
Roman Snapko
committed
Add interactive legend toggle functionality to BarChart component
1 parent da06fb8 commit 5df2fff

2 files changed

Lines changed: 55 additions & 11 deletions

File tree

packages/ui-components/src/ui/chart/bar-chart.tsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import { useCallback, useMemo, useState } from 'react';
23
import {
34
Bar,
45
Customized,
@@ -55,6 +56,33 @@ export function BarChart({
5556
legendClassName,
5657
className,
5758
}: BarChartProps): React.JSX.Element {
59+
const [hiddenKeys, setHiddenKeys] = useState<Set<string>>(new Set());
60+
61+
const yAxisDomain = useMemo<[number, number]>(() => {
62+
let max = 0;
63+
for (const row of data) {
64+
for (const bar of bars) {
65+
const v = Number(row[bar.dataKey] ?? 0);
66+
if (v > max) {
67+
max = v;
68+
}
69+
}
70+
}
71+
return [0, max];
72+
}, [data, bars]);
73+
74+
const toggleKey = useCallback((key: string) => {
75+
setHiddenKeys((prev) => {
76+
const next = new Set(prev);
77+
if (next.has(key)) {
78+
next.delete(key);
79+
} else {
80+
next.add(key);
81+
}
82+
return next;
83+
});
84+
}, []);
85+
5886
return (
5987
<ChartContainer config={config} className={className}>
6088
<RechartsBarChart
@@ -77,23 +105,30 @@ export function BarChart({
77105
}}
78106
/>
79107
)}
80-
{showYAxis && (
81-
<YAxis
82-
tickFormatter={yAxisTickFormatter}
83-
tickLine={false}
84-
axisLine={false}
85-
tick={{
86-
fill: 'hsl(var(--foreground))',
87-
}}
88-
/>
89-
)}
108+
<YAxis
109+
tickFormatter={yAxisTickFormatter}
110+
tickLine={false}
111+
axisLine={false}
112+
tick={{
113+
fill: 'hsl(var(--foreground))',
114+
}}
115+
domain={yAxisDomain}
116+
allowDataOverflow
117+
hide={!showYAxis}
118+
/>
90119
{showTooltip && (
91120
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
92121
)}
93122
{showLegend && (
94123
<ChartLegend
95124
verticalAlign="top"
96-
content={<ChartLegendContent className={legendClassName} />}
125+
content={
126+
<ChartLegendContent
127+
className={legendClassName}
128+
onItemClick={toggleKey}
129+
hiddenKeys={hiddenKeys}
130+
/>
131+
}
97132
/>
98133
)}
99134
{bars.map((bar) => (
@@ -103,6 +138,7 @@ export function BarChart({
103138
fill={`var(--color-${bar.dataKey})`}
104139
radius={bar.radius ?? 4}
105140
stackId={bar.stackId}
141+
hide={hiddenKeys.has(bar.dataKey)}
106142
/>
107143
))}
108144
</RechartsBarChart>

packages/ui-components/src/ui/chart/chart.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,14 @@ function ChartLegendContent({
253253
payload,
254254
verticalAlign = 'bottom',
255255
nameKey,
256+
onItemClick,
257+
hiddenKeys,
256258
}: React.ComponentProps<'div'> &
257259
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
258260
hideIcon?: boolean;
259261
nameKey?: string;
262+
onItemClick?: (dataKey: string) => void;
263+
hiddenKeys?: Set<string>;
260264
}) {
261265
const { config } = useChart();
262266

@@ -277,13 +281,17 @@ function ChartLegendContent({
277281
.map((item) => {
278282
const key = `${nameKey || item.dataKey || 'value'}`;
279283
const itemConfig = getPayloadConfigFromPayload(config, item, key);
284+
const isHidden = hiddenKeys?.has(key);
280285

281286
return (
282287
<div
283288
key={item.value}
284289
className={cn(
285290
'flex items-center gap-1.5 text-foreground [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-foreground',
291+
onItemClick && 'cursor-pointer select-none',
292+
isHidden && 'opacity-40',
286293
)}
294+
onClick={() => onItemClick?.(key)}
287295
>
288296
{itemConfig?.icon && !hideIcon ? (
289297
<itemConfig.icon />

0 commit comments

Comments
 (0)