{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"ui-chart","type":"registry:component","title":"Chart","description":"Chart container and helpers for Recharts visualizations.","version":"1.0.0","status":"ga","files":[{"path":"src/components/ui/chart.tsx","type":"registry:component","content":"'use client'\n\nimport * as React from 'react'\n\nimport * as RechartsPrimitive from 'recharts'\n\nimport { cn } from '@/lib/utils'\n\n// Format: { THEME_NAME: CSS_SELECTOR }\nconst THEMES = { light: '', dark: '.dark' } as const\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: React.ReactNode\n    icon?: React.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 = 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\nfunction ChartContainer({\n  id,\n  className,\n  children,\n  config,\n  ...props\n}: React.ComponentProps<'div'> & {\n  config: ChartConfig\n  children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children']\n}) {\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(\n          \"[&_.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-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 flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden\",\n          className\n        )}\n        {...props}\n      >\n        <ChartStyle id={chartId} config={config} />\n        <RechartsPrimitive.ResponsiveContainer>{children}</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(([, config]) => config.theme || config.color)\n\n  if (!colorConfig.length) {\n    return null\n  }\n\n  return (\n    <style\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\n    return color ? `  --color-${key}: ${color};` : null\n  })\n  .join('\\n')}\n}\n`\n          )\n          .join('\\n')\n      }}\n    />\n  )\n}\n\nconst ChartTooltip = RechartsPrimitive.Tooltip\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}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &\n  React.ComponentProps<'div'> & {\n    hideLabel?: boolean\n    hideIndicator?: boolean\n    indicator?: 'line' | 'dot' | 'dashed'\n    nameKey?: string\n    labelKey?: string\n  }) {\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\n    const value =\n      !labelKey && typeof label === 'string' ? config[label as keyof typeof config]?.label || label : itemConfig?.label\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        'border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',\n        className\n      )}\n    >\n      {!nestLabel ? tooltipLabel : null}\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            const indicatorColor = color || item.payload.fill || item.color\n\n            return (\n              <div\n                key={item.dataKey}\n                className={cn(\n                  '[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',\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 rounded-[2px] border-(--color-border) bg-(--color-bg)', {\n                            'h-2.5 w-2.5': indicator === 'dot',\n                            'w-1': indicator === 'line',\n                            'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',\n                            'my-0.5': nestLabel && indicator === 'dashed'\n                          })}\n                          style={\n                            {\n                              '--color-bg': indicatorColor,\n                              '--color-border': indicatorColor\n                            } as React.CSSProperties\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'>{itemConfig?.label || item.name}</span>\n                      </div>\n                      {item.value && (\n                        <span className='text-foreground font-mono font-medium tabular-nums'>\n                          {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\nfunction ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  verticalAlign = 'bottom',\n  nameKey\n}: React.ComponentProps<'div'> &\n  Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {\n    hideIcon?: boolean\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\n          return (\n            <div\n              key={item.value}\n              className={cn('[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3')}\n            >\n              {itemConfig?.icon && !hideIcon ? (\n                <itemConfig.icon />\n              ) : (\n                <div\n                  className='h-2 w-2 shrink-0 rounded-[2px]'\n                  style={{\n                    backgroundColor: item.color\n                  }}\n                />\n              )}\n              {itemConfig?.label}\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 undefined\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 }\n"}]}