{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart",
  "title": "Chart",
  "description": "Beautiful charts with neubrutalism styling built on Recharts",
  "dependencies": [
    "recharts"
  ],
  "registryDependencies": [
    "@boldkit/utils"
  ],
  "files": [
    {
      "path": "registry/default/ui/chart.tsx",
      "content": "import * as React from 'react'\nimport * as RechartsPrimitive from 'recharts'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn, sanitizeCssValue } from '@/lib/utils'\n\n// Format: { THEME_NAME: CSS_SELECTOR }\nconst THEMES = { light: '', dark: '.dark' } as const\n\n// Neubrutalism color palettes for charts\nexport const CHART_PALETTES = {\n  bold: [\n    'hsl(var(--primary))',\n    'hsl(var(--secondary))',\n    'hsl(var(--accent))',\n    'hsl(var(--success))',\n    'hsl(var(--warning))',\n    'hsl(var(--info))',\n  ],\n  vibrant: [\n    'hsl(0 84% 60%)',      // Coral red\n    'hsl(174 62% 50%)',    // Teal\n    'hsl(49 100% 60%)',    // Yellow\n    'hsl(280 65% 60%)',    // Purple\n    'hsl(145 63% 49%)',    // Green\n    'hsl(212 100% 60%)',   // Blue\n  ],\n  pastel: [\n    'hsl(0 84% 75%)',      // Light coral\n    'hsl(174 62% 70%)',    // Light teal\n    'hsl(49 100% 75%)',    // Light yellow\n    'hsl(280 65% 75%)',    // Light purple\n    'hsl(145 63% 70%)',    // Light green\n    'hsl(212 100% 75%)',   // Light blue\n  ],\n  monochrome: [\n    'hsl(var(--foreground))',\n    'hsl(var(--foreground) / 0.8)',\n    'hsl(var(--foreground) / 0.6)',\n    'hsl(var(--foreground) / 0.4)',\n    'hsl(var(--foreground) / 0.2)',\n    'hsl(var(--foreground) / 0.1)',\n  ],\n} as const\n\nexport type ChartPalette = keyof typeof CHART_PALETTES\n\n// Helper to get colors from a palette\nexport function getChartColor(palette: ChartPalette, index: number): string {\n  const colors = CHART_PALETTES[palette]\n  return colors[index % colors.length]\n}\n\n// Generate ChartConfig from palette\nexport function createChartConfig(\n  keys: string[],\n  labels: string[],\n  palette: ChartPalette = 'bold'\n): ChartConfig {\n  const config: ChartConfig = {}\n  keys.forEach((key, index) => {\n    config[key] = {\n      label: labels[index] || key,\n      color: getChartColor(palette, index),\n    }\n  })\n  return config\n}\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: React.ReactNode\n    icon?: React.ComponentType\n  } & (\n    | { color?: string; theme?: never }\n    | { color?: never; theme: Record<keyof typeof THEMES, string> }\n  )\n}\n\ntype ChartContextProps = {\n  config: ChartConfig\n}\n\nconst ChartContext = React.createContext<ChartContextProps | null>(null)\n\nfunction useChart() {\n  const context = React.useContext(ChartContext)\n\n  if (!context) {\n    throw new Error('useChart must be used within a <ChartContainer />')\n  }\n\n  return context\n}\n\nconst chartContainerVariants = cva(\n  'flex aspect-video justify-center overflow-hidden text-xs [&_.recharts-cartesian-axis-tick_text]:fill-foreground [&_.recharts-cartesian-grid_line[stroke=\"#ccc\"]]:stroke-muted-foreground/30 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-muted-foreground [&_.recharts-polar-grid_[stroke=\"#ccc\"]]:stroke-foreground [&_.recharts-reference-line_[stroke=\"#ccc\"]]:stroke-foreground [&_.recharts-dot[stroke=\"#fff\"]]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke=\"#fff\"]]:stroke-foreground [&_.recharts-surface]:outline-hidden [&_.recharts-layer_path]:[fill-opacity:1] [&_.recharts-layer_path]:[stroke-width:3] [&_.recharts-layer_path]:[stroke:hsl(var(--foreground))]',\n  {\n    variants: {\n      variant: {\n        default: 'border-3 border-foreground bg-background p-4 shadow-[4px_4px_0px_hsl(var(--shadow-color))]',\n        elevated: 'border-3 border-foreground bg-background p-4 shadow-[6px_6px_0px_hsl(var(--shadow-color))] hover:shadow-[8px_8px_0px_hsl(var(--shadow-color))] hover:translate-x-[-2px] hover:translate-y-[-2px] transition',\n        flat: 'border-3 border-foreground bg-background p-4',\n        filled: 'border-3 border-foreground bg-muted/30 p-4 shadow-[4px_4px_0px_hsl(var(--shadow-color))]',\n        minimal: 'bg-background p-4',\n        accent: 'border-3 border-foreground bg-accent/10 p-4 shadow-[4px_4px_0px_hsl(var(--accent))]',\n        primary: 'border-3 border-foreground bg-primary/10 p-4 shadow-[4px_4px_0px_hsl(var(--primary))]',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n    },\n  }\n)\n\nexport interface ChartContainerProps\n  extends React.ComponentProps<'div'>,\n    VariantProps<typeof chartContainerVariants> {\n  config: ChartConfig\n  children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children']\n}\n\nfunction ChartContainer({\n  id,\n  className,\n  children,\n  config,\n  variant,\n  ...props\n}: ChartContainerProps) {\n  const uniqueId = React.useId()\n  const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`\n\n  return (\n    <ChartContext.Provider value={{ config }}>\n      <div\n        data-slot=\"chart\"\n        data-chart={chartId}\n        className={cn(chartContainerVariants({ variant }), className)}\n        {...props}\n      >\n        <ChartStyle id={chartId} config={config} />\n        <RechartsPrimitive.ResponsiveContainer>\n          {children}\n        </RechartsPrimitive.ResponsiveContainer>\n      </div>\n    </ChartContext.Provider>\n  )\n}\n\nconst ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {\n  const colorConfig = Object.entries(config).filter(\n    ([, configItem]) => configItem.theme || configItem.color\n  )\n\n  if (!colorConfig.length) {\n    return null\n  }\n\n  const safeId = id.replace(/[^a-zA-Z0-9_-]/g, '')\n\n  return (\n    <style\n      dangerouslySetInnerHTML={{\n        __html: Object.entries(THEMES)\n          .map(\n            ([theme, prefix]) => `\n${prefix} [data-chart=${safeId}] {\n${colorConfig\n  .map(([key, itemConfig]) => {\n    const color =\n      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||\n      itemConfig.color\n    return color ? `  --color-${sanitizeCssValue(key)}: ${sanitizeCssValue(color)};` : null\n  })\n  .join('\\n')}\n}\n`\n          )\n          .join('\\n'),\n      }}\n    />\n  )\n}\n\nconst ChartTooltip = RechartsPrimitive.Tooltip\n\ninterface ChartTooltipContentProps extends React.ComponentProps<'div'> {\n  active?: boolean\n  payload?: Array<{\n    name?: string\n    value?: number\n    dataKey?: string\n    color?: string\n    payload?: Record<string, unknown>\n    fill?: string\n  }>\n  label?: string\n  hideLabel?: boolean\n  hideIndicator?: boolean\n  indicator?: 'line' | 'dot' | 'dashed'\n  nameKey?: string\n  labelKey?: string\n  labelFormatter?: (label: unknown, payload: unknown[]) => React.ReactNode\n  formatter?: (\n    value: unknown,\n    name: unknown,\n    item: unknown,\n    index: number,\n    payload: unknown\n  ) => React.ReactNode\n  labelClassName?: string\n  color?: string\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}: ChartTooltipContentProps) {\n  const { config } = useChart()\n\n  const tooltipLabel = React.useMemo(() => {\n    if (hideLabel || !payload?.length) {\n      return null\n    }\n\n    const [item] = payload\n    const key = `${labelKey || item?.dataKey || item?.name || 'value'}`\n    const itemConfig = getPayloadConfigFromPayload(config, item, key)\n    const value =\n      !labelKey && typeof label === 'string'\n        ? config[label as keyof typeof config]?.label || label\n        : itemConfig?.label\n\n    if (labelFormatter) {\n      return (\n        <div className={cn('font-bold uppercase tracking-wide', labelClassName)}>\n          {labelFormatter(value, payload)}\n        </div>\n      )\n    }\n\n    if (!value) {\n      return null\n    }\n\n    return <div className={cn('font-bold', 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-[8rem] items-start gap-1.5 border-3 border-foreground bg-background px-2.5 py-1.5 text-xs shadow-[4px_4px_0px_hsl(var(--shadow-color))]',\n        className\n      )}\n    >\n      {!nestLabel ? tooltipLabel : null}\n      <div className=\"grid gap-1.5\">\n        {payload.map((item, index) => {\n          const key = `${nameKey || item.name || item.dataKey || 'value'}`\n          const itemConfig = getPayloadConfigFromPayload(config, item, key)\n          const indicatorColor = color || (item.payload as Record<string, string>)?.fill || item.color\n\n          return (\n            <div\n              key={item.dataKey || index}\n              className={cn(\n                'flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground',\n                indicator === 'dot' && 'items-center'\n              )}\n            >\n              {formatter && item?.value !== undefined && item.name ? (\n                formatter(item.value, item.name, item, index, item.payload)\n              ) : (\n                <>\n                  {itemConfig?.icon ? (\n                    <itemConfig.icon />\n                  ) : (\n                    !hideIndicator && (\n                      <div\n                        className={cn('shrink-0 border-2 border-foreground', {\n                          'h-2.5 w-2.5': indicator === 'dot',\n                          'w-1': indicator === 'line',\n                          'w-0 border-[1.5px] border-dashed bg-transparent':\n                            indicator === 'dashed',\n                          'my-0.5': nestLabel && indicator === 'dashed',\n                        })}\n                        style={{\n                          backgroundColor: indicatorColor,\n                        }}\n                      />\n                    )\n                  )}\n                  <div\n                    className={cn(\n                      'flex flex-1 justify-between leading-none',\n                      nestLabel ? 'items-end' : 'items-center'\n                    )}\n                  >\n                    <div className=\"grid gap-1.5\">\n                      {nestLabel ? tooltipLabel : null}\n                      <span className=\"text-muted-foreground\">\n                        {itemConfig?.label || item.name}\n                      </span>\n                    </div>\n                    {item.value !== undefined && Number.isFinite(Number(item.value)) && (\n                      <span className=\"font-mono font-bold tabular-nums text-foreground\">\n                        {Number(item.value).toLocaleString()}\n                      </span>\n                    )}\n                  </div>\n                </>\n              )}\n            </div>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n\nconst ChartLegend = RechartsPrimitive.Legend\n\ninterface ChartLegendContentProps extends React.ComponentProps<'div'> {\n  payload?: Array<{\n    value?: string\n    dataKey?: string\n    color?: string\n  }>\n  verticalAlign?: 'top' | 'bottom'\n  hideIcon?: boolean\n  nameKey?: string\n}\n\nfunction ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  verticalAlign = 'bottom',\n  nameKey,\n}: ChartLegendContentProps) {\n  const { config } = useChart()\n\n  if (!payload?.length) {\n    return null\n  }\n\n  return (\n    <div\n      className={cn(\n        'flex items-center justify-center gap-4 font-bold uppercase tracking-wide',\n        verticalAlign === 'top' ? 'pb-3' : 'pt-3',\n        className\n      )}\n    >\n      {payload.map((item) => {\n        const key = `${nameKey || item.dataKey || 'value'}`\n        const itemConfig = getPayloadConfigFromPayload(config, item, key)\n\n        return (\n          <div\n            key={item.value}\n            className={cn(\n              'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-foreground'\n            )}\n          >\n            {itemConfig?.icon && !hideIcon ? (\n              <itemConfig.icon />\n            ) : (\n              <div\n                className=\"h-3 w-3 shrink-0 border-2 border-foreground\"\n                style={{\n                  backgroundColor: item.color,\n                }}\n              />\n            )}\n            <span className=\"text-sm\">{itemConfig?.label}</span>\n          </div>\n        )\n      })}\n    </div>\n  )\n}\n\n// Helper to extract item config from a payload.\nfunction getPayloadConfigFromPayload(\n  config: ChartConfig,\n  payload: unknown,\n  key: string\n) {\n  if (typeof payload !== 'object' || payload === null) {\n    return undefined\n  }\n\n  const payloadPayload =\n    'payload' in payload &&\n    typeof payload.payload === 'object' &&\n    payload.payload !== null\n      ? payload.payload\n      : undefined\n\n  let configLabelKey: string = key\n\n  if (\n    key in payload &&\n    typeof payload[key as keyof typeof payload] === 'string'\n  ) {\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[\n      key as keyof typeof payloadPayload\n    ] as string\n  }\n\n  return configLabelKey in config\n    ? config[configLabelKey]\n    : config[key as keyof typeof config]\n}\n\n// ──────────────────────────────────────────────────────────────────\n// ChartEmpty — fallback display when a chart receives no data.\n// Originally lived in src/components/ui/chart/empty.tsx, merged here so\n// that synced chart-X files can do `import { ChartEmpty } from './chart'`\n// without needing the split-file structure on the consumer side.\n// ──────────────────────────────────────────────────────────────────\n\nexport interface ChartEmptyProps extends React.HTMLAttributes<HTMLDivElement> {\n  message?: React.ReactNode\n}\n\nconst ChartEmpty = React.forwardRef<HTMLDivElement, ChartEmptyProps>(\n  ({ message = 'No data', className, ...props }, ref) => {\n    return (\n      <div\n        ref={ref}\n        role=\"status\"\n        aria-live=\"polite\"\n        className={cn(\n          'flex min-h-[120px] w-full items-center justify-center border-3 border-dashed border-foreground/40 bg-muted/20 p-6 text-xs font-bold uppercase tracking-wide text-muted-foreground',\n          className\n        )}\n        {...props}\n      >\n        {message}\n      </div>\n    )\n  }\n)\nChartEmpty.displayName = 'ChartEmpty'\n\nexport {\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n  ChartLegend,\n  ChartLegendContent,\n  ChartStyle,\n  ChartEmpty,\n  chartContainerVariants,\n}\n",
      "type": "registry:ui",
      "target": "components/ui/chart.tsx"
    }
  ],
  "type": "registry:ui"
}