{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart",
  "title": "Chart",
  "description": "Recharts charts with themed colors, a tooltip and legend that read labels from one config, and keyboard navigation with a visible focus ring.",
  "dependencies": [
    "cn",
    "recharts@^3.10.1"
  ],
  "registryDependencies": [
    "https://hextaui.com/r/theme.json"
  ],
  "files": [
    {
      "path": "components/ui/chart.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"cn\"\nimport * as RechartsPrimitive from \"recharts\"\nimport type {\n  DefaultLegendContentProps,\n  TooltipContentProps,\n  TooltipProps,\n  TooltipValueType,\n} from \"recharts\"\n\nconst THEMES = { light: \"\", dark: \".dark\" } as const\n\nconst INITIAL_DIMENSION = { width: 320, height: 200 } as const\n\ntype TooltipNameType = number | string\n\ntype ChartConfig = Record<\n  string,\n  {\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 <ChartContainer>.\")\n  }\n\n  return context\n}\n\nfunction toCssIdent(value: string) {\n  return value.replace(/[^\\w-]/g, \"-\")\n}\n\nfunction toCssValue(value: string) {\n  return value.replace(/[;{}<>]/g, \"\").trim()\n}\n\nfunction ChartContainer({\n  id,\n  className,\n  children,\n  config,\n  initialDimension = INITIAL_DIMENSION,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  config: ChartConfig\n  children: React.ComponentProps<\n    typeof RechartsPrimitive.ResponsiveContainer\n  >[\"children\"]\n  initialDimension?: {\n    width: number\n    height: number\n  }\n}) {\n  const uniqueId = React.useId()\n  const chartId = `chart-${toCssIdent(id ?? uniqueId)}`\n  const context = React.useMemo(() => ({ config }), [config])\n\n  return (\n    <ChartContext.Provider value={context}>\n      <div\n        role=\"figure\"\n        data-slot=\"chart\"\n        data-chart={chartId}\n        className={cn(\n          \"flex aspect-video min-w-0 justify-center text-xs tabular-nums forced-color-adjust-none [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-angle-axis-tick_text]:fill-muted-foreground [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-foreground/5 [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:rounded-md [&_.recharts-surface]:outline-hidden [&_.recharts-surface]:transition-shadow [&_.recharts-surface]:duration-150 [&_.recharts-surface]:[direction:ltr] [&_.recharts-surface_*]:outline-hidden [&_.recharts-surface:focus-visible]:ring-3 [&_.recharts-surface:focus-visible]:ring-focus-ring [&_.recharts-tooltip-wrapper[data-chart-snap]]:transition-none! [&_.recharts-wrapper]:touch-pan-y\",\n          className\n        )}\n        {...props}\n      >\n        <ChartStyle id={chartId} config={config} />\n        <RechartsPrimitive.ResponsiveContainer\n          initialDimension={initialDimension}\n        >\n          {children}\n        </RechartsPrimitive.ResponsiveContainer>\n      </div>\n    </ChartContext.Provider>\n  )\n}\n\nfunction ChartStyle({ id, config }: { id: string; config: ChartConfig }) {\n  const colorConfig = Object.entries(config).filter(\n    ([, itemConfig]) => itemConfig.theme ?? itemConfig.color\n  )\n\n  if (!colorConfig.length) {\n    return null\n  }\n\n  const css = Object.entries(THEMES)\n    .map(([theme, prefix]) => {\n      const declarations = colorConfig\n        .map(([key, itemConfig]) => {\n          const color =\n            itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??\n            itemConfig.color\n          const value = color ? toCssValue(color) : \"\"\n          return value ? `  --color-${toCssIdent(key)}: ${value};` : null\n        })\n        .filter(Boolean)\n        .join(\"\\n\")\n      return `${prefix} [data-chart=\"${toCssIdent(id)}\"] {\\n${declarations}\\n}`\n    })\n    .join(\"\\n\")\n\n  return <style dangerouslySetInnerHTML={{ __html: css }} />\n}\n\nfunction ChartTooltip({\n  animationDuration = 200,\n  animationEasing = \"ease-out\",\n  ...props\n}: TooltipProps<TooltipValueType, TooltipNameType>) {\n  return (\n    <RechartsPrimitive.Tooltip\n      animationDuration={animationDuration}\n      animationEasing={animationEasing}\n      {...props}\n    />\n  )\n}\n\nfunction useNumberFormat(locale: Intl.LocalesArgument) {\n  return React.useMemo(() => {\n    try {\n      return new Intl.NumberFormat(locale)\n    } catch {\n      return new Intl.NumberFormat(\"en-US\")\n    }\n  }, [locale])\n}\n\nfunction formatValue(value: unknown, format: Intl.NumberFormat): string {\n  if (typeof value === \"number\") {\n    return Number.isFinite(value) ? format.format(value) : String(value)\n  }\n  if (Array.isArray(value)) {\n    return value.map((item) => formatValue(item, format)).join(\" – \")\n  }\n  return value == null ? \"\" : String(value)\n}\n\nfunction snapTooltipWrapper(node: HTMLDivElement | null) {\n  const wrapper = node?.parentElement\n  if (\n    !wrapper?.classList.contains(\"recharts-tooltip-wrapper\") ||\n    typeof MutationObserver === \"undefined\"\n  ) {\n    return undefined\n  }\n\n  let hidden = wrapper.style.visibility === \"hidden\"\n  let from: string | null = null\n  let frame = 0\n  let timeout: ReturnType<typeof setTimeout> | undefined\n\n  const release = () => {\n    from = null\n    clearTimeout(timeout)\n    cancelAnimationFrame(frame)\n    frame = requestAnimationFrame(() => {\n      frame = requestAnimationFrame(() => {\n        wrapper.removeAttribute(\"data-chart-snap\")\n      })\n    })\n  }\n\n  const snap = () => {\n    cancelAnimationFrame(frame)\n    clearTimeout(timeout)\n    wrapper.setAttribute(\"data-chart-snap\", \"\")\n    from = wrapper.style.transform\n    timeout = setTimeout(release, 100)\n  }\n\n  if (!hidden) {\n    snap()\n  }\n\n  const observer = new MutationObserver(() => {\n    const nowHidden = wrapper.style.visibility === \"hidden\"\n    if (hidden && !nowHidden) {\n      snap()\n    } else if (from !== null && wrapper.style.transform !== from) {\n      release()\n    }\n    hidden = nowHidden\n  })\n  observer.observe(wrapper, { attributes: true, attributeFilter: [\"style\"] })\n\n  return () => {\n    observer.disconnect()\n    cancelAnimationFrame(frame)\n    clearTimeout(timeout)\n    wrapper.removeAttribute(\"data-chart-snap\")\n  }\n}\n\ntype ChartTooltipPayload = TooltipContentProps<\n  TooltipValueType,\n  TooltipNameType\n>[\"payload\"]\n\ntype ChartTooltipContentProps = {\n  active?: boolean\n  payload?: ChartTooltipPayload\n  label?: React.ReactNode\n  labelFormatter?: TooltipProps<\n    TooltipValueType,\n    TooltipNameType\n  >[\"labelFormatter\"]\n  formatter?: TooltipProps<TooltipValueType, TooltipNameType>[\"formatter\"]\n  valueFormatter?: (\n    value: TooltipValueType | undefined,\n    name: TooltipNameType | undefined,\n    item: ChartTooltipPayload[number]\n  ) => React.ReactNode\n  className?: string\n  labelClassName?: string\n  color?: string\n  indicator?: \"line\" | \"dot\" | \"dashed\"\n  hideLabel?: boolean\n  hideIndicator?: boolean\n  nameKey?: string\n  labelKey?: string\n  locale?: Intl.LocalesArgument\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  valueFormatter,\n  color,\n  nameKey,\n  labelKey,\n  locale = \"en-US\",\n}: ChartTooltipContentProps) {\n  const { config } = useChart()\n  const format = useNumberFormat(locale)\n\n  if (!active || !payload?.length) {\n    return null\n  }\n\n  const items = payload.filter((item) => item.type !== \"none\")\n\n  const tooltipLabel = (() => {\n    if (hideLabel) {\n      return null\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\" || typeof label === \"number\")\n        ? ((Object.hasOwn(config, label) ? config[label].label : undefined) ??\n          label)\n        : itemConfig?.label\n\n    if (labelFormatter) {\n      const formatted = labelFormatter(value, payload)\n      return formatted == null || formatted === \"\" ? null : (\n        <div\n          data-slot=\"chart-tooltip-label\"\n          className={cn(\"font-medium text-pretty\", labelClassName)}\n        >\n          {formatted}\n        </div>\n      )\n    }\n\n    if (value == null || value === \"\") {\n      return null\n    }\n\n    return (\n      <div\n        data-slot=\"chart-tooltip-label\"\n        className={cn(\"font-medium text-pretty\", labelClassName)}\n      >\n        {value}\n      </div>\n    )\n  })()\n\n  const nestLabel = items.length === 1 && indicator !== \"dot\"\n\n  return (\n    <div\n      ref={snapTooltipWrapper}\n      data-slot=\"chart-tooltip\"\n      className={cn(\n        \"grid max-w-[min(18rem,calc(100vw-2rem))] min-w-32 animate-in items-start gap-1.5 rounded-lg bg-popover px-2.5 py-1.5 text-xs wrap-anywhere text-popover-foreground shadow-md ring-(length:--hairline) ring-foreground/10 ease-out-quint animation-duration-150 fade-in-0 motion-safe:zoom-in-95 forced-colors:border\",\n        className\n      )}\n    >\n      {!nestLabel ? tooltipLabel : null}\n      <div className=\"grid gap-1.5\">\n        {items.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          const value = valueFormatter\n            ? valueFormatter(item.value, item.name, item)\n            : formatValue(item.value, format)\n\n          return (\n            <div\n              key={`${item.graphicalItemId ?? \"\"}-${String(item.dataKey ?? index)}-${index}`}\n              data-slot=\"chart-tooltip-item\"\n              className={cn(\n                \"flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5 [&>svg]:shrink-0 [&>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, payload)\n              ) : (\n                <>\n                  {itemConfig?.icon ? (\n                    <itemConfig.icon />\n                  ) : (\n                    !hideIndicator && (\n                      <div\n                        aria-hidden\n                        data-slot=\"chart-tooltip-indicator\"\n                        data-indicator={indicator}\n                        style={\n                          {\n                            \"--chart-indicator\": indicatorColor,\n                          } as React.CSSProperties\n                        }\n                        className={cn(\n                          \"shrink-0 rounded-[calc(var(--radius-sm)*0.4)] border-(--chart-indicator) bg-(--chart-indicator)\",\n                          indicator === \"dot\" && \"size-2.5\",\n                          indicator === \"line\" && \"w-1\",\n                          indicator === \"dashed\" &&\n                            \"w-0 border-[1.5px] border-dashed bg-transparent\",\n                          nestLabel && indicator === \"dashed\" && \"my-0.5\"\n                        )}\n                      />\n                    )\n                  )}\n                  <div\n                    className={cn(\n                      \"flex min-w-0 flex-1 justify-between gap-4 leading-none\",\n                      nestLabel ? \"items-end\" : \"items-center\"\n                    )}\n                  >\n                    <div className=\"grid min-w-0 gap-1.5\">\n                      {nestLabel ? tooltipLabel : null}\n                      <span className=\"text-muted-foreground\">\n                        {itemConfig?.label ?? item.name}\n                      </span>\n                    </div>\n                    {value != null && value !== \"\" && value !== false && (\n                      <span\n                        dir=\"ltr\"\n                        className=\"shrink-0 font-medium text-foreground tabular-nums\"\n                      >\n                        {value}\n                      </span>\n                    )}\n                  </div>\n                </>\n              )}\n            </div>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n\nfunction ChartLegend({\n  itemSorter = null,\n  ...props\n}: React.ComponentProps<typeof RechartsPrimitive.Legend>) {\n  return <RechartsPrimitive.Legend itemSorter={itemSorter} {...props} />\n}\n\nfunction ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  verticalAlign = \"bottom\",\n  nameKey,\n}: Pick<DefaultLegendContentProps, \"payload\" | \"verticalAlign\"> & {\n  className?: string\n  hideIcon?: boolean\n  nameKey?: string\n}) {\n  const { config } = useChart()\n\n  const items = payload?.filter((item) => item.type !== \"none\") ?? []\n\n  if (!items.length) {\n    return null\n  }\n\n  return (\n    <div\n      data-slot=\"chart-legend\"\n      className={cn(\n        \"flex flex-wrap items-center justify-center gap-x-4 gap-y-1.5\",\n        verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\",\n        className\n      )}\n    >\n      {items.map((item, index) => {\n        const key = `${nameKey ?? item.dataKey ?? \"value\"}`\n        const itemConfig = getPayloadConfigFromPayload(config, item, key)\n\n        return (\n          <div\n            key={`${String(item.dataKey ?? item.value ?? \"\")}-${index}`}\n            data-slot=\"chart-legend-item\"\n            className=\"flex min-w-0 items-center gap-1.5 [&>svg]:size-3 [&>svg]:shrink-0 [&>svg]:text-muted-foreground\"\n          >\n            {itemConfig?.icon && !hideIcon ? (\n              <itemConfig.icon />\n            ) : (\n              <div\n                aria-hidden\n                data-slot=\"chart-legend-indicator\"\n                style={\n                  { \"--chart-indicator\": item.color } as React.CSSProperties\n                }\n                className=\"size-2 shrink-0 rounded-[calc(var(--radius-sm)*0.4)] bg-(--chart-indicator)\"\n              />\n            )}\n            <span className=\"min-w-0 wrap-anywhere\">\n              {itemConfig?.label ?? item.value}\n            </span>\n          </div>\n        )\n      })}\n    </div>\n  )\n}\n\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 Object.hasOwn(config, configLabelKey)\n    ? config[configLabelKey]\n    : Object.hasOwn(config, key)\n      ? config[key]\n      : undefined\n}\n\nexport {\n  ChartContainer,\n  ChartLegend,\n  ChartLegendContent,\n  ChartStyle,\n  ChartTooltip,\n  ChartTooltipContent,\n  useChart,\n}\nexport type { ChartConfig, ChartTooltipContentProps }\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "light": {
      "chart-1": "oklch(0.575 0.163 255.5)",
      "chart-2": "oklch(0.621 0.128 163.1)",
      "chart-3": "oklch(0.67 0.143 73.2)",
      "chart-4": "oklch(0.433 0.167 283.6)",
      "chart-5": "oklch(0.622 0.171 0.8)"
    },
    "dark": {
      "chart-1": "oklch(0.622 0.161 255.1)",
      "chart-2": "oklch(0.621 0.128 163.1)",
      "chart-3": "oklch(0.67 0.143 73.2)",
      "chart-4": "oklch(0.67 0.145 286.8)",
      "chart-5": "oklch(0.622 0.171 0.8)"
    }
  },
  "categories": [
    "data-display"
  ],
  "type": "registry:ui"
}