{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart-tailwind",
  "type": "registry:item",
  "title": "Chart (Tailwind)",
  "description": "Composable chart components for building any type of data visualization with Recharts primitives.",
  "dependencies": [
    "recharts"
  ],
  "files": [
    {
      "path": "registry/brook/tailwind/ui/chart.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentProps,\n  type ComponentType,\n  type CSSProperties,\n  createContext,\n  type ReactNode,\n  useContext,\n  useId,\n  useMemo,\n} from \"react\";\nimport { Legend, ResponsiveContainer, Tooltip } from \"recharts\";\nimport { cn } from \"@/lib/utils\";\n\nconst THEMES = { light: \"\", dark: \".dark\" } as const;\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: ReactNode;\n    icon?: ComponentType;\n  } & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });\n};\n\ntype ChartContextProps = {\n  config: ChartConfig;\n};\n\nconst ChartContext = createContext<ChartContextProps | null>(null);\n\nfunction useChart() {\n  const context = useContext(ChartContext);\n\n  if (!context) {\n    throw new Error(\"useChart must be used within a <ChartContainer />\");\n  }\n\n  return context;\n}\n\nfunction ChartContainer({\n  id,\n  className,\n  children,\n  config,\n  ...props\n}: ComponentProps<\"div\"> & {\n  config: ChartConfig;\n  children: ComponentProps<typeof ResponsiveContainer>[\"children\"];\n}) {\n  const uniqueId = useId();\n  const chartId = `chart-${id || uniqueId.replace(/:/g, \"\")}`;\n\n  return (\n    <ChartContext.Provider value={{ config }}>\n      <div\n        {...props}\n        className={cn(\"flex h-full w-full justify-center text-xs\", className)}\n        data-chart={chartId}\n        data-slot=\"chart\"\n      >\n        <ChartStyle config={config} id={chartId} />\n        <ResponsiveContainer>{children}</ResponsiveContainer>\n        <style global jsx>{`\n          [data-chart=\"${chartId}\"] .recharts-cartesian-axis-tick text {\n            fill: var(--muted-foreground);\n          }\n          [data-chart=\"${chartId}\"] .recharts-cartesian-grid line {\n            stroke: oklch(from var(--border) l c h / 0.3);\n          }\n          [data-chart=\"${chartId}\"] .recharts-curve.recharts-tooltip-cursor {\n            stroke: var(--border);\n          }\n          [data-chart=\"${chartId}\"] .recharts-polar-grid line {\n            stroke: var(--border);\n          }\n          [data-chart=\"${chartId}\"] .recharts-radial-bar-background-sector {\n            fill: var(--muted);\n          }\n          [data-chart=\"${chartId}\"] .recharts-rectangle.recharts-tooltip-cursor {\n            fill: var(--muted);\n            opacity: 0.3;\n          }\n          [data-chart=\"${chartId}\"] .recharts-reference-line line {\n            stroke: var(--border);\n          }\n          [data-chart=\"${chartId}\"] .recharts-dot[stroke=\"#fff\"] {\n            stroke: transparent;\n          }\n          [data-chart=\"${chartId}\"] .recharts-layer {\n            outline: none;\n          }\n          [data-chart=\"${chartId}\"] .recharts-sector {\n            outline: none;\n          }\n          [data-chart=\"${chartId}\"] .recharts-sector[stroke=\"#fff\"] {\n            stroke: transparent;\n          }\n          [data-chart=\"${chartId}\"] .recharts-sector {\n            stroke: transparent;\n          }\n          [data-chart=\"${chartId}\"] .recharts-surface {\n            outline: none;\n          }\n        `}</style>\n      </div>\n    </ChartContext.Provider>\n  );\n}\n\nconst ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {\n  const colorConfig = Object.entries(config).filter(([, itemConfig]) => itemConfig.theme || itemConfig.color);\n\n  if (!colorConfig.length) {\n    return null;\n  }\n\n  return (\n    <style\n      // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - content is generated from config object, not user input\n      dangerouslySetInnerHTML={{\n        __html: Object.entries(THEMES)\n          .map(\n            ([theme, prefix]) => `\n${prefix} [data-chart=${id}] {\n${colorConfig\n  .map(([key, itemConfig]) => {\n    const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;\n    return color ? `  --color-${key}: ${color};` : null;\n  })\n  .join(\"\\n\")}\n}\n`\n          )\n          .join(\"\\n\"),\n      }}\n    />\n  );\n};\n\nconst ChartTooltip = Tooltip;\n\n// Helper to get indicator style class\nfunction getIndicatorStyleClass(indicator: \"line\" | \"dot\" | \"dashed\"): string {\n  if (indicator === \"dot\") {\n    return \"h-2.5 w-2.5\";\n  }\n  if (indicator === \"line\") {\n    return \"w-1\";\n  }\n  return \"w-0 border-[1.5px] border-dashed bg-transparent\";\n}\n\nfunction TooltipItem({\n  item,\n  index,\n  formatter,\n  itemConfig,\n  indicator,\n  indicatorColor,\n  hideIndicator,\n  nestLabel,\n  tooltipLabel,\n}: {\n  item: {\n    value?: number | string;\n    name?: string;\n    dataKey?: string;\n    color?: string;\n    fill?: string;\n    payload: Record<string, unknown>;\n    type?: string;\n  };\n  index: number;\n  formatter?: (value: number | string, name: string, item: unknown, index: number, payload: unknown) => ReactNode;\n  itemConfig?: { icon?: ComponentType; label?: ReactNode };\n  indicator: \"line\" | \"dot\" | \"dashed\";\n  indicatorColor?: string;\n  hideIndicator: boolean;\n  nestLabel: boolean;\n  tooltipLabel: ReactNode;\n}) {\n  const indicatorStyleClass = getIndicatorStyleClass(indicator);\n  const nestedClass = nestLabel && indicator === \"dashed\" ? \"my-0.5\" : \"\";\n\n  // Format the value using the formatter if provided\n  const formattedValue =\n    formatter && item?.value !== undefined && item.name\n      ? formatter(item.value, item.name, item, index, item.payload)\n      : item.value?.toLocaleString();\n\n  return (\n    <>\n      {itemConfig?.icon ? (\n        <div className=\"h-2.5 w-2.5 text-muted-foreground\">\n          <itemConfig.icon />\n        </div>\n      ) : (\n        !hideIndicator && (\n          <div\n            className={cn(\n              \"shrink-0 rounded-sm border border-[var(--color-border)] bg-[var(--color-bg)]\",\n              indicatorStyleClass,\n              nestedClass\n            )}\n            style={\n              {\n                \"--color-bg\": indicatorColor || \"currentColor\",\n                \"--color-border\": indicatorColor || \"currentColor\",\n              } as CSSProperties\n            }\n          />\n        )\n      )}\n      <div\n        className={cn(\"flex min-w-0 flex-1 items-center justify-between gap-2 leading-none\", nestLabel && \"items-end\")}\n      >\n        <div className=\"grid min-w-0 flex-1 gap-1.5\">\n          {nestLabel ? tooltipLabel : null}\n          <span className=\"max-w-[150px] break-words text-muted-foreground\">{itemConfig?.label || item.name}</span>\n        </div>\n        {formattedValue && (\n          <span className=\"shrink-0 whitespace-nowrap font-medium font-mono text-foreground tabular-nums\">\n            {formattedValue}\n          </span>\n        )}\n      </div>\n    </>\n  );\n}\n\n// Helper to get the label value\nfunction getTooltipLabelValue(\n  labelKey: string | undefined,\n  label: string | number | undefined,\n  config: ChartConfig,\n  item: { payload?: Record<string, unknown> } | undefined\n): ReactNode {\n  if (labelKey && item?.payload) {\n    return item.payload[labelKey] as ReactNode;\n  }\n  if (!labelKey && typeof label === \"string\") {\n    return config[label as keyof typeof config]?.label || label;\n  }\n  return label;\n}\n\nfunction ChartTooltipContent({\n  active,\n  payload,\n  className,\n  indicator = \"dot\",\n  hideLabel = false,\n  hideIndicator = false,\n  label,\n  labelFormatter,\n  labelClassName,\n  formatter,\n  color,\n  nameKey,\n  labelKey,\n}: {\n  active?: boolean;\n  payload?: Array<{\n    value?: number | string;\n    name?: string;\n    dataKey?: string;\n    color?: string;\n    fill?: string;\n    payload: Record<string, unknown>;\n    type?: string;\n  }>;\n  label?: string | number;\n  className?: string;\n  indicator?: \"line\" | \"dot\" | \"dashed\";\n  hideLabel?: boolean;\n  hideIndicator?: boolean;\n  labelFormatter?: (value: unknown, payload: unknown[]) => ReactNode;\n  labelClassName?: string;\n  formatter?: (value: number | string, name: string, item: unknown, index: number, payload: unknown) => ReactNode;\n  color?: string;\n  nameKey?: string;\n  labelKey?: string;\n}) {\n  const { config } = useChart();\n\n  const tooltipLabel = useMemo(() => {\n    if (hideLabel || !payload?.length) {\n      return null;\n    }\n\n    const [item] = payload;\n    const value = getTooltipLabelValue(labelKey, label, config, item);\n\n    if (labelFormatter) {\n      return <div className={cn(\"font-medium\", labelClassName)}>{labelFormatter(value, payload)}</div>;\n    }\n\n    if (!value) {\n      return null;\n    }\n\n    return <div className={cn(\"font-medium\", labelClassName)}>{value}</div>;\n  }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);\n\n  if (!(active && payload?.length)) {\n    return null;\n  }\n\n  const nestLabel = payload.length === 1 && indicator !== \"dot\";\n\n  return (\n    <div\n      className={cn(\n        \"grid min-w-32 items-start gap-1.5 rounded-[var(--radius)] bg-background px-2.5 py-1.5 text-xs shadow-[0_10px_15px_-3px_rgb(0_0_0_/_0.1),_0_4px_6px_-4px_rgb(0_0_0_/_0.1)]\",\n        className\n      )}\n      style={{\n        border: \"0.5px solid oklch(from var(--border) l c h / 0.6)\",\n      }}\n    >\n      {nestLabel ? null : tooltipLabel}\n      <div className=\"grid gap-1.5\">\n        {payload\n          .filter((item) => item.type !== \"none\")\n          .map((item, index) => {\n            const key = `${nameKey || item.name || item.dataKey || \"value\"}`;\n            const itemConfig = getPayloadConfigFromPayload(config, item, key);\n\n            // Get color from config (handling both color and theme)\n            const configColor = itemConfig?.theme ? undefined : itemConfig?.color;\n\n            const indicatorColor: string | undefined =\n              color ||\n              item.color ||\n              (typeof item.fill === \"string\" ? item.fill : undefined) ||\n              (typeof item.payload?.fill === \"string\" ? (item.payload.fill as string) : undefined) ||\n              configColor ||\n              (item.dataKey ? `var(--color-${item.dataKey})` : undefined);\n\n            return (\n              <div\n                className={cn(\"flex w-full flex-wrap items-stretch gap-2\", indicator === \"dot\" && \"items-center\")}\n                key={item.dataKey}\n              >\n                <TooltipItem\n                  formatter={formatter}\n                  hideIndicator={hideIndicator}\n                  index={index}\n                  indicator={indicator}\n                  indicatorColor={indicatorColor}\n                  item={item}\n                  itemConfig={itemConfig}\n                  nestLabel={nestLabel}\n                  tooltipLabel={tooltipLabel}\n                />\n              </div>\n            );\n          })}\n      </div>\n    </div>\n  );\n}\n\nconst ChartLegend = Legend;\n\nfunction ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  verticalAlign = \"bottom\",\n  nameKey,\n}: {\n  className?: string;\n  hideIcon?: boolean;\n  payload?: Array<{\n    value?: string;\n    id?: string;\n    type?: string;\n    color?: string;\n    dataKey?: string;\n  }>;\n  verticalAlign?: \"top\" | \"bottom\";\n  nameKey?: string;\n}) {\n  const { config } = useChart();\n\n  if (!payload?.length) {\n    return null;\n  }\n\n  return (\n    <div className={cn(\"flex items-center justify-center gap-4\", verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\", className)}>\n      {payload\n        .filter((item) => item.type !== \"none\")\n        .map((item) => {\n          const key = `${nameKey || item.dataKey || \"value\"}`;\n          const itemConfig = getPayloadConfigFromPayload(config, item, key);\n          const legendColor = item.color || (item.dataKey ? `var(--color-${item.dataKey})` : undefined);\n\n          return (\n            <div className=\"flex items-center gap-1.5\" key={item.value}>\n              {itemConfig?.icon && !hideIcon ? (\n                <div className=\"h-3 w-3 text-muted-foreground\">\n                  <itemConfig.icon />\n                </div>\n              ) : (\n                <div\n                  className=\"h-2 w-2 shrink-0 rounded-sm\"\n                  style={{\n                    backgroundColor: legendColor,\n                  }}\n                />\n              )}\n              <span className=\"text-xs\">{itemConfig?.label}</span>\n            </div>\n          );\n        })}\n    </div>\n  );\n}\n\n// Helper to extract item config from a payload.\nfunction getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {\n  if (typeof payload !== \"object\" || payload === null) {\n    return;\n  }\n\n  const payloadPayload =\n    \"payload\" in payload && typeof payload.payload === \"object\" && payload.payload !== null\n      ? payload.payload\n      : undefined;\n\n  let configLabelKey: string = key;\n\n  if (key in payload && typeof payload[key as keyof typeof payload] === \"string\") {\n    configLabelKey = payload[key as keyof typeof payload] as string;\n  } else if (\n    payloadPayload &&\n    key in payloadPayload &&\n    typeof payloadPayload[key as keyof typeof payloadPayload] === \"string\"\n  ) {\n    configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;\n  }\n\n  return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];\n}\n\nexport { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle, useChart };\n",
      "type": "registry:file",
      "target": "~/components/ui/chart/chart.tsx"
    }
  ]
}