import * as React from "react"; import * as RechartsPrimitive from "recharts"; import { cn } from "@/lib/utils"; // Format: { THEME_NAME: CSS_SELECTOR } 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 } ); }; type ChartContextProps = { config: ChartConfig; }; 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, ...props }: React.ComponentProps<"div"> & { config: ChartConfig; children: React.ComponentProps< typeof RechartsPrimitive.ResponsiveContainer >["children"]; }) { const uniqueId = React.useId(); const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`; const containerRef = React.useRef(null); const [containerSize, setContainerSize] = React.useState<{ width: number; height: number; } | null>(null); React.useEffect(() => { const element = containerRef.current; if (!element) return; const updateSizeState = () => { const { width, height } = element.getBoundingClientRect(); const nextSize = width > 0 && height > 0 ? { width: Math.round(width), height: Math.round(height), } : null; setContainerSize((currentSize) => { if (!nextSize) { // Keep the last valid size once mounted to avoid unmount/remount thrash. return currentSize; } if ( currentSize && currentSize.width === nextSize.width && currentSize.height === nextSize.height ) { return currentSize; } return nextSize; }); }; updateSizeState(); if (typeof ResizeObserver === "undefined") { const recheckSize = () => { if (document.visibilityState === "visible") { updateSizeState(); } }; window.addEventListener("resize", recheckSize); window.addEventListener("orientationchange", recheckSize); document.addEventListener("visibilitychange", recheckSize); return () => { window.removeEventListener("resize", recheckSize); window.removeEventListener("orientationchange", recheckSize); document.removeEventListener("visibilitychange", recheckSize); }; } const observer = new ResizeObserver(() => { updateSizeState(); }); observer.observe(element); return () => observer.disconnect(); }, []); return (
{containerSize ? ( {children} ) : null}
); } 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 (