"use client" import * as React from "react" import * as Recharts from "recharts" import { cn } from "../../lib/utils.js" type ChartTheme = "light" | "dark" /** Config keys are also the CSS variable suffix in `var(--color-KEY)`. */ export type ChartConfig = Record } )> const ChartContext = React.createContext(null) function useChartConfig() { const config = React.useContext(ChartContext) if (!config) throw new Error("Chart content must be inside a ChartContainer.") return config } const colorFunctions = new Set([ "var", "rgb", "rgba", "hsl", "hsla", "hwb", "lab", "lch", "oklab", "oklch", "color", "color-mix", "light-dark", "calc", "min", "max", "clamp", ]) // CSS config can come from data. Never let it end a declaration or a style tag. function safeColor(color: string | undefined) { if (!color || !/^[a-zA-Z0-9#.,%()+\-_/ ]+$/.test(color) || color.includes("/*")) { return undefined } for (const match of color.matchAll(/([a-zA-Z-]+)\s*\(/g)) { if (!colorFunctions.has(match[1].toLowerCase())) return undefined } return color } function cssString(value: string) { return Array.from(value, (character) => { return /[a-zA-Z0-9_-]/.test(character) ? character : `\\${character.codePointAt(0)!.toString(16)} ` }).join("") } function configVariables(config: ChartConfig, theme: ChartTheme) { return Object.entries(config).flatMap(([key, series]) => { const color = safeColor(series.theme?.[theme] ?? series.color) return /^[a-zA-Z0-9_-]+$/.test(key) && color ? [` --color-${key}: ${color};`] : [] }).join("\n") } function ChartStyle({ id, config }: { id: string; config: ChartConfig }) { const light = configVariables(config, "light") const dark = configVariables(config, "dark") if (!light && !dark) return null const selector = `[data-chart="${cssString(id)}"]` const css = `${selector} {\n${light}\n}\n:where(.dark, [data-theme="dark"]) ${selector}, ${selector}:where(.dark, [data-theme="dark"]) {\n${dark}\n}` return