{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart",
  "type": "registry:item",
  "title": "Chart",
  "description": "Composable chart components for building any type of data visualization with Recharts primitives.",
  "dependencies": [
    "recharts"
  ],
  "files": [
    {
      "path": "registry/brook/ui/chart/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\";\n\nimport styles from \"./chart.module.css\";\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 {...props} className={`${styles.chartContainer} ${className || \"\"}`} data-chart={chartId} data-slot=\"chart\">\n        <ChartStyle config={config} id={chartId} />\n        <ResponsiveContainer>{children}</ResponsiveContainer>\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 styles.indicatorDot;\n  }\n  if (indicator === \"line\") {\n    return styles.indicatorLine;\n  }\n  return styles.indicatorDashed;\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\" ? styles.indicatorNested : \"\";\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={styles.tooltipIcon}>\n          <itemConfig.icon />\n        </div>\n      ) : (\n        !hideIndicator && (\n          <div\n            className={`${styles.tooltipIndicator} ${indicatorStyleClass} ${nestedClass}`}\n            style={\n              {\n                \"--color-bg\": indicatorColor || \"currentColor\",\n                \"--color-border\": indicatorColor || \"currentColor\",\n              } as CSSProperties\n            }\n          />\n        )\n      )}\n      <div className={`${styles.tooltipItemContent} ${nestLabel ? styles.tooltipItemContentNested : \"\"}`}>\n        <div className={styles.tooltipItemLabel}>\n          {nestLabel ? tooltipLabel : null}\n          <span className={styles.tooltipItemName}>{itemConfig?.label || item.name}</span>\n        </div>\n        {formattedValue && <span className={styles.tooltipItemValue}>{formattedValue}</span>}\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={`${styles.tooltipLabel} ${labelClassName || \"\"}`}>{labelFormatter(value, payload)}</div>;\n    }\n\n    if (!value) {\n      return null;\n    }\n\n    return <div className={`${styles.tooltipLabel} ${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 className={`${styles.tooltipContent} ${className || \"\"}`}>\n      {nestLabel ? null : tooltipLabel}\n      <div className={styles.tooltipItems}>\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={`${styles.tooltipItem} ${indicator === \"dot\" ? styles.tooltipItemDot : \"\"}`}\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\n      className={`${styles.legendContent} ${\n        verticalAlign === \"top\" ? styles.legendTop : styles.legendBottom\n      } ${className || \"\"}`}\n    >\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={styles.legendItem} key={item.value}>\n              {itemConfig?.icon && !hideIcon ? (\n                <div className={styles.legendIcon}>\n                  <itemConfig.icon />\n                </div>\n              ) : (\n                <div\n                  className={styles.legendIndicator}\n                  style={{\n                    backgroundColor: legendColor,\n                  }}\n                />\n              )}\n              <span className={styles.legendLabel}>{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"
    },
    {
      "path": "registry/brook/ui/chart/chart.module.css",
      "content": ".chartContainer {\n  display: flex;\n  width: 100%;\n  height: 100%;\n  justify-content: center;\n  font-size: 12px;\n}\n\n.chartContainer :global(.recharts-cartesian-axis-tick text) {\n  fill: var(--muted-foreground);\n}\n\n.chartContainer :global(.recharts-cartesian-grid line) {\n  stroke: oklch(from var(--border) l c h / 0.3);\n}\n\n.chartContainer :global(.recharts-curve.recharts-tooltip-cursor) {\n  stroke: var(--border);\n}\n\n.chartContainer :global(.recharts-polar-grid line) {\n  stroke: var(--border);\n}\n\n.chartContainer :global(.recharts-radial-bar-background-sector) {\n  fill: var(--muted);\n}\n\n.chartContainer :global(.recharts-rectangle.recharts-tooltip-cursor) {\n  fill: var(--muted);\n  opacity: 0.3;\n}\n\n.chartContainer :global(.recharts-reference-line line) {\n  stroke: var(--border);\n}\n\n.chartContainer :global(.recharts-dot[stroke=\"#fff\"]) {\n  stroke: transparent;\n}\n\n.chartContainer :global(.recharts-layer) {\n  outline: none;\n}\n\n.chartContainer :global(.recharts-sector) {\n  outline: none;\n}\n\n.chartContainer :global(.recharts-sector[stroke=\"#fff\"]) {\n  stroke: transparent;\n}\n\n.chartContainer :global(.recharts-surface) {\n  outline: none;\n}\n\n.tooltipContent {\n  display: grid;\n  align-items: start;\n  gap: 6px;\n  min-width: 8rem;\n  border-radius: var(--radius);\n  border: 0.5px solid oklch(from var(--border) l c h / 0.6);\n  background-color: var(--background);\n  padding: 6px 10px;\n  font-size: 12px;\n  box-shadow:\n    0 10px 15px -3px rgb(0 0 0 / 0.1),\n    0 4px 6px -4px rgb(0 0 0 / 0.1);\n}\n\n.tooltipLabel {\n  font-weight: 500;\n}\n\n.tooltipItems {\n  display: grid;\n  gap: 6px;\n}\n\n.tooltipItem {\n  display: flex;\n  width: 100%;\n  flex-wrap: wrap;\n  align-items: stretch;\n  gap: 8px;\n}\n\n.tooltipItemDot {\n  align-items: center;\n}\n\n.tooltipIcon {\n  color: var(--muted-foreground);\n  height: 10px;\n  width: 10px;\n}\n\n.tooltipIndicator {\n  flex-shrink: 0;\n  border-radius: 2px;\n  background-color: var(--color-bg);\n}\n\n.indicatorDot {\n  height: 10px;\n  width: 10px;\n}\n\n.indicatorLine {\n  width: 4px;\n}\n\n.indicatorDashed {\n  width: 0;\n  border-width: 1.5px;\n  border-style: dashed;\n  background-color: transparent;\n}\n\n.indicatorNested {\n  margin-top: 2px;\n  margin-bottom: 2px;\n}\n\n.tooltipItemContent {\n  display: flex;\n  flex: 1;\n  justify-content: space-between;\n  align-items: center;\n  line-height: 1;\n  gap: 8px;\n  min-width: 0;\n}\n\n.tooltipItemContentNested {\n  align-items: flex-end;\n}\n\n.tooltipItemLabel {\n  display: grid;\n  gap: 6px;\n  min-width: 0;\n  flex: 1;\n}\n\n.tooltipItemName {\n  color: var(--muted-foreground);\n  word-wrap: break-word;\n  overflow-wrap: break-word;\n  max-width: 150px;\n}\n\n.tooltipItemValue {\n  color: var(--foreground);\n  font-family:\n    \"SF Mono\", Monaco, \"Cascadia Code\", \"Roboto Mono\", Consolas, \"Courier New\",\n    monospace;\n  font-weight: 500;\n  font-variant-numeric: tabular-nums;\n  flex-shrink: 0;\n  white-space: nowrap;\n}\n\n.legendContent {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n}\n\n.legendTop {\n  padding-bottom: 12px;\n}\n\n.legendBottom {\n  padding-top: 12px;\n}\n\n.legendItem {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.legendIcon {\n  color: var(--muted-foreground);\n  height: 12px;\n  width: 12px;\n}\n\n.legendIndicator {\n  height: 8px;\n  width: 8px;\n  flex-shrink: 0;\n  border-radius: 2px;\n}\n\n.legendLabel {\n  font-size: 12px;\n}\n",
      "type": "registry:file",
      "target": "~/components/ui/chart/chart.module.css"
    }
  ]
}