diff --git a/.gitignore b/.gitignore index 45ff68d..5c69fb0 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ next-env.d.ts .cursor/* .kilo .worktrees/ +.worktree/ diff --git a/components/ui/chart.tsx b/components/ui/chart.tsx index 7b06ddb..7c2dc84 100644 --- a/components/ui/chart.tsx +++ b/components/ui/chart.tsx @@ -1,31 +1,42 @@ -"use client"; +"use client" -import * as React from "react"; -import * as RechartsPrimitive from "recharts"; +import * as React from "react" +import * as RechartsPrimitive from "recharts" +import type { TooltipValueType } from "recharts" -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils" -export type ChartConfig = { - [k in string]: { - label?: React.ReactNode; - color?: string; - }; -}; +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +const INITIAL_DIMENSION = { width: 320, height: 200 } as const +type TooltipNameType = number | string + +export type ChartConfig = Record< + string, + { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +> type ChartContextProps = { - config: ChartConfig; -}; + config: ChartConfig +} -const ChartContext = React.createContext(null); +const ChartContext = React.createContext(null) function useChart() { - const context = React.useContext(ChartContext); + const context = React.useContext(ChartContext) if (!context) { - throw new Error("useChart must be used within a "); + throw new Error("useChart must be used within a ") } - return context; + return context } function ChartContainer({ @@ -33,13 +44,20 @@ function ChartContainer({ className, children, config, + initialDimension = INITIAL_DIMENSION, ...props }: React.ComponentProps<"div"> & { - config: ChartConfig; - children: React.ComponentProps["children"]; + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + initialDimension?: { + width: number + height: number + } }) { - const uniqueId = React.useId(); - const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`; + const uniqueId = React.useId() + const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}` return ( @@ -47,120 +65,309 @@ function ChartContainer({ data-slot="chart" data-chart={chartId} className={cn( - "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-legend-item_text]:fill-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border [&_.recharts-surface]:outline-none", - className, + "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden", + className )} {...props} > - {children} + + {children} + - ); + ) } -function ChartStyle({ id, config }: { id: string; config: ChartConfig }) { - const colorConfig = Object.entries(config).filter(([, item]) => item.color); +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 null } return (