"use client"; import * as React from "react"; import * as RechartsPrimitive from "recharts"; import type { DefaultLegendContentProps, TooltipContentProps } from "recharts"; import { cn } from "@/lib/utils"; const THEMES = { light: "", dark: ".dark" } as const; export type ChartConfig = { [k in string]: { label?: React.ReactNode; icon?: React.ComponentType; } & ( | { color?: string; theme?: never } | { color?: never; theme: Record } ); }; export type ChartLegendState = { hiddenKeys: Set; isSeriesHidden: (key: string) => boolean; toggleSeries: (key: string) => void; }; type ChartContextProps = { config: ChartConfig; } & ChartLegendState; const ChartContext = React.createContext(null); function useChart() { const context = React.useContext(ChartContext); if (!context) { throw new Error("useChart must be used within a "); } return context; } function ChartContainer({ id, className, children, config, legend, ...props }: Omit, "children"> & { config: ChartConfig; children: | React.ComponentProps< typeof RechartsPrimitive.ResponsiveContainer >["children"] | ((state: ChartLegendState) => React.ReactNode); legend?: React.ReactNode | ((state: ChartLegendState) => React.ReactNode); }) { const uniqueId = React.useId(); const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`; const [hiddenKeys, setHiddenKeys] = React.useState>( () => new Set(), ); const toggleSeries = React.useCallback((key: string) => { setHiddenKeys((previous) => { const next = new Set(previous); if (next.has(key)) { next.delete(key); } else { next.add(key); } return next; }); }, []); const isSeriesHidden = React.useCallback( (key: string) => hiddenKeys.has(key), [hiddenKeys], ); const legendState = React.useMemo( () => ({ hiddenKeys, isSeriesHidden, toggleSeries }), [hiddenKeys, isSeriesHidden, toggleSeries], ); const resolvedChildren = typeof children === "function" ? children(legendState) : children; const resolvedLegend = typeof legend === "function" ? legend(legendState) : legend; return ( <>
{resolvedChildren}
{resolvedLegend}
); } const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const colorConfig = Object.entries(config).filter( ([, config]) => config.theme || config.color, ); if (!colorConfig.length) { return null; } return (