feat: add interactivity to DashboardPieChart and ChartLegendContent components
This commit is contained in:
parent
fd6793af50
commit
e8c46ec18b
@ -278,9 +278,13 @@ function ChartLegendContent({
|
||||
payload,
|
||||
verticalAlign = "bottom",
|
||||
nameKey,
|
||||
onItemClick,
|
||||
activeName,
|
||||
}: React.ComponentProps<"div"> & {
|
||||
hideIcon?: boolean
|
||||
nameKey?: string
|
||||
onItemClick?: (name: string) => void
|
||||
activeName?: string
|
||||
} & RechartsPrimitive.DefaultLegendContentProps) {
|
||||
const { config } = useChart()
|
||||
|
||||
@ -301,13 +305,20 @@ function ChartLegendContent({
|
||||
.map((item, index) => {
|
||||
const key = `${nameKey ?? item.dataKey ?? "value"}`
|
||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
||||
const label = typeof itemConfig?.label === 'string' ? itemConfig.label : ''
|
||||
const isInactive = activeName !== undefined && activeName !== label
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
|
||||
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
|
||||
onItemClick && "cursor-pointer select-none hover:opacity-80",
|
||||
isInactive && "opacity-40"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (label) onItemClick?.(label)
|
||||
}}
|
||||
>
|
||||
{itemConfig?.icon && !hideIcon ? (
|
||||
<itemConfig.icon />
|
||||
|
||||
@ -267,6 +267,8 @@ type DashboardPieChartProps = {
|
||||
function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartProps) {
|
||||
const hasData = data.length > 0 && data.some((d) => d.count > 0);
|
||||
|
||||
const [activeName, setActiveName] = useState<string | undefined>(undefined);
|
||||
|
||||
const pieColors = useMemo(() => generateRandomColors(5), []);
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
@ -287,6 +289,17 @@ function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartP
|
||||
}));
|
||||
}, [data, pieColors]);
|
||||
|
||||
const handlePieClick = (_: unknown, index: number) => {
|
||||
const name = chartData[index]?.name;
|
||||
if (name) {
|
||||
setActiveName((prev) => (prev === name ? undefined : name));
|
||||
}
|
||||
};
|
||||
|
||||
const handleLegendClick = (name: string) => {
|
||||
setActiveName((prev) => (prev === name ? undefined : name));
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="items-center pb-0">
|
||||
@ -311,9 +324,24 @@ function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartP
|
||||
data={chartData}
|
||||
dataKey={dataKey}
|
||||
nameKey={nameKey}
|
||||
/>
|
||||
onClick={handlePieClick}
|
||||
>
|
||||
{chartData.map((item, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
opacity={activeName === undefined || activeName === item.name ? 1 : 0.3}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
<ChartLegend
|
||||
content={<ChartLegendContent nameKey={nameKey} />}
|
||||
content={
|
||||
<ChartLegendContent
|
||||
nameKey={nameKey}
|
||||
onItemClick={handleLegendClick}
|
||||
activeName={activeName}
|
||||
/>
|
||||
}
|
||||
className="-translate-y-2 flex-wrap gap-2 *:basis-1/4 *:justify-center"
|
||||
/>
|
||||
</PieChart>
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
UserCheck,
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Pie, PieChart } from 'recharts';
|
||||
import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
type TodayAttendance = {
|
||||
@ -463,6 +463,8 @@ type DashboardPieChartProps = {
|
||||
function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartProps) {
|
||||
const hasData = data.length > 0 && data.some((d) => d.count > 0);
|
||||
|
||||
const [activeName, setActiveName] = useState<string | undefined>(undefined);
|
||||
|
||||
const pieColors = useMemo(() => generateRandomColors(5), []);
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
@ -483,6 +485,17 @@ function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartP
|
||||
}));
|
||||
}, [data, pieColors]);
|
||||
|
||||
const handlePieClick = (_: unknown, index: number) => {
|
||||
const name = chartData[index]?.name;
|
||||
if (name) {
|
||||
setActiveName((prev) => (prev === name ? undefined : name));
|
||||
}
|
||||
};
|
||||
|
||||
const handleLegendClick = (name: string) => {
|
||||
setActiveName((prev) => (prev === name ? undefined : name));
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="items-center pb-0">
|
||||
@ -507,9 +520,24 @@ function DashboardPieChart({ title, data, dataKey, nameKey }: DashboardPieChartP
|
||||
data={chartData}
|
||||
dataKey={dataKey}
|
||||
nameKey={nameKey}
|
||||
/>
|
||||
onClick={handlePieClick}
|
||||
>
|
||||
{chartData.map((item, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
opacity={activeName === undefined || activeName === item.name ? 1 : 0.3}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
<ChartLegend
|
||||
content={<ChartLegendContent nameKey={nameKey} />}
|
||||
content={
|
||||
<ChartLegendContent
|
||||
nameKey={nameKey}
|
||||
onItemClick={handleLegendClick}
|
||||
activeName={activeName}
|
||||
/>
|
||||
}
|
||||
className="-translate-y-2 flex-wrap gap-2 *:basis-1/4 *:justify-center"
|
||||
/>
|
||||
</PieChart>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user