{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "extends": "none",
  "name": "chart",
  "title": "chart",
  "description": "chart",
  "dependencies": [
    "react-aria-components",
    "recharts",
    "cn"
  ],
  "registryDependencies": [
    "https://intentui.com/r/use-mobile",
    "https://intentui.com/r/primitive"
  ],
  "files": [
    {
      "path": "src/components/ui/chart.tsx",
      "content": "\"use client\"\n\nimport { createContext, type ReactElement, use, useCallback, useId, useMemo, useState } from \"react\"\nimport { ToggleButton } from \"react-aria-components/ToggleButton\"\nimport {\n  ToggleButtonGroup,\n  type ToggleButtonGroupProps,\n} from \"react-aria-components/ToggleButtonGroup\"\nimport type {\n  CartesianGridProps as CartesianGridPrimitiveProps,\n  CartesianGridProps,\n  LegendPayload,\n  LegendProps,\n  TooltipProps,\n  XAxisProps as XAxisPropsPrimitive,\n  YAxisProps as YAxisPrimitiveProps,\n} from \"recharts\"\nimport {\n  CartesianGrid as CartesianGridPrimitive,\n  Legend as LegendPrimitive,\n  ResponsiveContainer,\n  Tooltip as TooltipPrimitive,\n  XAxis as XAxisPrimitive,\n  YAxis as YAxisPrimitive,\n} from \"recharts\"\nimport type { ContentType as LegendContentType } from \"recharts/types/component/DefaultLegendContent\"\nimport type {\n  NameType,\n  Props as TooltipContentProps,\n  ValueType,\n} from \"recharts/types/component/DefaultTooltipContent\"\nimport type { ContentType as TooltipContentType } from \"recharts/types/component/Tooltip\"\nimport { twJoin, cn } from \"cn\"\nimport { useIsMobile } from \"@/hooks/use-mobile\"\nimport { cx } from \"@/lib/primitive\"\n\n// #region Chart Types\nexport type ChartType = \"default\" | \"stacked\" | \"percent\"\nexport type ChartLayout = \"horizontal\" | \"vertical\" | \"radial\"\nexport type IntervalType = \"preserveStartEnd\" | \"equidistantPreserveStart\"\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: React.ReactNode\n    icon?: React.ComponentType\n  } & (\n    | { color?: ChartColorKeys | (string & {}); theme?: never }\n    | { color?: never; theme: Record<keyof typeof THEMES, string> }\n  )\n}\n\nexport const CHART_COLORS = {\n  \"chart-1\": \"var(--chart-1)\",\n  \"chart-2\": \"var(--chart-2)\",\n  \"chart-3\": \"var(--chart-3)\",\n  \"chart-4\": \"var(--chart-4)\",\n  \"chart-5\": \"var(--chart-5)\",\n} as const\n\nexport type ChartColorKeys = keyof typeof CHART_COLORS | (string & {})\n\nexport const DEFAULT_COLORS = [\"chart-1\", \"chart-2\", \"chart-3\", \"chart-4\", \"chart-5\"] as const\n\nexport type ChartContextProps = {\n  config: ChartConfig\n  data?: Record<string, any>[]\n  layout: ChartLayout\n  dataKey?: string\n  selectedLegend: string | null\n  onLegendSelect: (legendItem: string | null) => void\n}\n\nconst ChartContext = createContext<ChartContextProps | null>(null)\n\nexport function useChart() {\n  const context = use(ChartContext)\n\n  if (!context) {\n    throw new Error(\"useChart must be used within a <Chart />\")\n  }\n\n  return context\n}\n\nexport function valueToPercent(value: number) {\n  return `${(value * 100).toFixed(0)}%`\n}\n\nexport function constructCategoryColors(\n  categories: string[],\n  colors: readonly ChartColorKeys[]\n): Map<string, ChartColorKeys> {\n  const categoryColors = new Map<string, ChartColorKeys>()\n\n  categories.forEach((category, index) => {\n    const color = colors[index % colors.length]\n    if (color !== undefined) {\n      categoryColors.set(category, color)\n    }\n  })\n\n  return categoryColors\n}\n\nexport function getColorValue(color?: string): string {\n  if (!color) {\n    return \"var(--chart-1)\"\n  }\n\n  return CHART_COLORS[color as \"chart-1\"] ?? color\n}\n\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 interface BaseChartProps extends React.HTMLAttributes<HTMLDivElement> {\n  containerHeight?: number\n  config: ChartConfig\n  data: Record<string, any>[]\n  dataKey: string\n  colors?: readonly (ChartColorKeys | (string & {}))[]\n  type?: ChartType\n  intervalType?: IntervalType\n  layout?: ChartLayout\n  valueFormatter?: (value: number) => string\n\n  tooltip?: TooltipContentType<ValueType, NameType> | boolean\n  tooltipProps?: Omit<TooltipProps, \"content\"> & {\n    hideLabel?: boolean\n    labelSeparator?: boolean\n    hideIndicator?: boolean\n    indicator?: \"line\" | \"dot\" | \"dashed\"\n    nameKey?: string\n    labelKey?: string\n  }\n\n  cartesianGridProps?: CartesianGridProps\n\n  legend?: LegendContentType | boolean\n  legendProps?: Omit<React.ComponentProps<typeof LegendPrimitive>, \"content\" | \"ref\">\n\n  xAxisProps?: XAxisPropsPrimitive\n  yAxisProps?: YAxisPrimitiveProps\n\n  // XAxis\n  displayEdgeLabelsOnly?: boolean\n\n  hideGridLines?: boolean\n  hideXAxis?: boolean\n  hideYAxis?: boolean\n}\n\nexport function Chart({\n  id,\n  className,\n  children,\n  config,\n  data,\n  dataKey,\n  ref,\n  layout = \"horizontal\",\n  containerHeight,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\"> &\n  Pick<ChartContextProps, \"data\" | \"dataKey\"> & {\n    config: ChartConfig\n    containerHeight?: number\n    layout?: ChartLayout\n    children: ReactElement | ((props: ChartContextProps) => ReactElement)\n  }) {\n  const isMobile = useIsMobile()\n  const uniqueId = useId()\n  const chartId = useMemo(() => `chart-${id || uniqueId.replace(/:/g, \"\")}`, [id, uniqueId])\n\n  const [selectedLegend, setSelectedLegend] = useState<string | null>(null)\n\n  const onLegendSelect = useCallback((legendItem: string | null) => {\n    setSelectedLegend(legendItem)\n  }, [])\n\n  const _data = data ?? []\n  const _dataKey = dataKey ?? \"value\"\n\n  const value = useMemo(\n    () => ({\n      config,\n      selectedLegend,\n      onLegendSelect,\n      data: _data,\n      dataKey: _dataKey,\n      layout,\n    }),\n    [config, selectedLegend, onLegendSelect, _data, _dataKey, layout]\n  )\n\n  return (\n    <ChartContext value={value}>\n      <div\n        data-chart={chartId}\n        ref={ref}\n        className={cn(\n          \"z-20 flex w-full justify-center text-xs\",\n          \"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/80 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-layer]:outline-hidden [&_.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 [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden [&_.recharts-surface_.recharts-text.recharts-cartesian-axis-tick-value]:*:fill-muted-foreground\",\n          \"[&_.recharts-dot[fill='#fff']]:fill-(--line-color)\",\n          \"[&_.recharts-active-dot>.recharts-dot]:stroke-foreground/10\",\n\n          \"[&_.recharts-surface_g]:focus:outline-hidden\",\n\n          className\n        )}\n        {...props}\n      >\n        <ChartStyle id={chartId} config={config} />\n        <ResponsiveContainer height={containerHeight ?? (isMobile ? 200 : 370)}>\n          {typeof children === \"function\" ? children(value) : children}\n        </ResponsiveContainer>\n      </div>\n    </ChartContext>\n  )\n}\n\nconst THEMES = { light: \"\", dark: \".dark\" } as const\nfunction 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    return color ? `  --color-${key}: ${color};` : null\n  })\n  .join(\"\\n\")}\n}\n`\n          )\n          .join(\"\\n\"),\n      }}\n    />\n  )\n}\n\nexport type ChartTooltipProps<TValue extends ValueType, TName extends NameType> = TooltipProps<\n  TValue,\n  TName\n>\n\nconst tooltipWrapperStyle = { outline: \"none\" } as const\n\nconst cursorStyleRadial = {\n  stroke: \"var(--muted)\",\n  strokeWidth: 0.1,\n  fill: \"var(--muted)\",\n  fillOpacity: 0.5,\n} as const\n\nconst cursorStyleDefault = {\n  stroke: \"var(--muted)\",\n  strokeWidth: 1,\n  fill: \"var(--muted)\",\n  fillOpacity: 0.5,\n} as const\n\nexport function ChartTooltip(props: TooltipProps) {\n  const { layout } = useChart()\n  const cursorStyle = layout === \"radial\" ? cursorStyleRadial : cursorStyleDefault\n\n  return <TooltipPrimitive wrapperStyle={tooltipWrapperStyle} cursor={cursorStyle} {...props} />\n}\n\nexport type ChartLegendProps = Omit<React.ComponentProps<typeof LegendPrimitive>, \"ref\">\n\nexport function ChartLegend(props: ChartLegendProps) {\n  return <LegendPrimitive align=\"center\" verticalAlign=\"bottom\" {...props} />\n}\n\nexport interface XAxisProps extends Omit<XAxisPropsPrimitive, \"ref\"> {\n  displayEdgeLabelsOnly?: boolean\n  intervalType?: IntervalType\n}\n\nconst tickHorizontal = {\n  transform: \"translate(0, 6)\",\n} as const\n\nexport function XAxis({\n  displayEdgeLabelsOnly,\n  className,\n  intervalType = \"preserveStartEnd\",\n  minTickGap = 5,\n  ...props\n}: XAxisProps) {\n  const { dataKey, data, layout } = useChart()\n\n  const ticks =\n    displayEdgeLabelsOnly && data?.length && dataKey\n      ? [data[0]?.[dataKey], data[data.length - 1]?.[dataKey]]\n      : undefined\n\n  const tick = layout === \"horizontal\" ? tickHorizontal : undefined\n  return (\n    <XAxisPrimitive\n      className={cn(\"text-muted-foreground text-xs **:[text]:fill-muted-foreground\", className)}\n      interval={displayEdgeLabelsOnly ? \"preserveStartEnd\" : intervalType}\n      tick={tick}\n      ticks={ticks}\n      tickLine={false}\n      axisLine={false}\n      minTickGap={minTickGap}\n      dataKey={layout === \"horizontal\" ? dataKey : undefined}\n      {...props}\n    />\n  )\n}\n\nconst yAxisTickHorizontal = {\n  transform: \"translate(-3, 0)\",\n} as const\n\nconst yAxisTickVertical = {\n  transform: \"translate(0, 0)\",\n} as const\n\nexport function YAxis({\n  className,\n  width,\n  domain = [\"auto\", \"auto\"],\n  type,\n  ...props\n}: Omit<YAxisPrimitiveProps, \"ref\">) {\n  const { layout, dataKey } = useChart()\n\n  const tick = layout === \"horizontal\" ? yAxisTickHorizontal : yAxisTickVertical\n\n  return (\n    <YAxisPrimitive\n      className={cn(\"text-muted-foreground text-xs **:[text]:fill-muted-foreground\", className)}\n      width={width ?? (layout === \"horizontal\" ? 48 : 80)}\n      domain={domain}\n      tick={tick}\n      dataKey={layout === \"horizontal\" ? undefined : dataKey}\n      type={type ?? (layout === \"horizontal\" ? \"number\" : \"category\")}\n      interval={layout === \"horizontal\" ? undefined : \"equidistantPreserveStart\"}\n      axisLine={false}\n      tickLine={false}\n      {...props}\n    />\n  )\n}\n\nexport function CartesianGrid({ className, ...props }: CartesianGridPrimitiveProps) {\n  const { layout } = useChart()\n  return (\n    <CartesianGridPrimitive\n      className={cn(\"stroke-1 stroke-muted\", className)}\n      horizontal={layout !== \"vertical\"}\n      vertical={layout === \"vertical\"}\n      {...props}\n    />\n  )\n}\n\nexport function ChartTooltipContent<TValue extends ValueType, TName extends NameType>({\n  payload,\n  className,\n  indicator = \"dot\",\n  hideLabel = false,\n  hideIndicator = false,\n  label,\n  labelSeparator = true,\n  labelFormatter,\n  labelClassName,\n  formatter,\n  color,\n  nameKey,\n  labelKey,\n  ref,\n}: TooltipContentProps<TValue, TName> &\n  React.ComponentProps<\"div\"> & {\n    hideLabel?: boolean\n    labelSeparator?: boolean\n    hideIndicator?: boolean\n    indicator?: \"line\" | \"dot\" | \"dashed\"\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\n    if (!item) {\n      return null\n    }\n\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 <div className={labelClassName}>{labelFormatter(value, payload)}</div>\n    }\n\n    if (!value) {\n      return null\n    }\n\n    return <div className={labelClassName}>{value}</div>\n  }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey])\n\n  if (!payload?.length) {\n    return null\n  }\n\n  const nestLabel = payload.length === 1 && indicator !== \"dot\"\n\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        \"grid min-w-48 items-start rounded-lg bg-overlay/70 p-3 py-2 text-overlay-foreground text-xs ring ring-current/10 backdrop-blur-lg\",\n        className\n      )}\n    >\n      {!hideLabel && (\n        <>\n          {!nestLabel ? <span className=\"font-medium\">{tooltipLabel}</span> : null}\n          {labelSeparator && (\n            <span aria-hidden className=\"mt-2 mb-3 block h-px w-full bg-background/10\" />\n          )}\n        </>\n      )}\n      <div className=\"grid gap-3\">\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.fill || item.color\n\n          return (\n            <div\n              key={key}\n              className={cn(\n                \"flex w-full flex-wrap items-stretch gap-2 *:[svg]:text-muted-foreground\",\n                indicator === \"dot\" && \"items-center *:[svg]:size-2.5\",\n                indicator === \"line\" && \"*:[svg]:h-full *:[svg]:w-2.5\"\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(\n                          \"shrink-0 rounded-full border-(--color-border) bg-(--color-bg)\",\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                        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\">\n                        {itemConfig?.label || item.name}\n                      </span>\n                    </div>\n\n                    {item.value && (\n                      <span className=\"font-medium font-mono text-foreground tabular-nums\">\n                        {item.value.toString()}\n                      </span>\n                    )}\n                  </div>\n                </>\n              )}\n            </div>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n\nexport type ChartLegendContentProps = ToggleButtonGroupProps &\n  Pick<LegendProps, \"align\" | \"verticalAlign\"> & {\n    payload?: ReadonlyArray<LegendPayload>\n    hideIcon?: boolean\n    nameKey?: string\n    ref?: React.Ref<any>\n  }\n\nexport function ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  align = \"right\",\n  verticalAlign = \"bottom\",\n  nameKey,\n  ref,\n}: ChartLegendContentProps) {\n  const { config, selectedLegend, onLegendSelect } = useChart()\n\n  if (!payload?.length) {\n    return null\n  }\n\n  return (\n    <ToggleButtonGroup\n      ref={ref}\n      className={cx(\n        twJoin(\n          \"flex flex-wrap items-center gap-x-1\",\n          verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\",\n          align === \"right\" ? \"justify-end\" : align === \"left\" ? \"justify-start\" : \"justify-center\"\n        ),\n        className\n      )}\n      selectedKeys={selectedLegend ? [selectedLegend] : []}\n      onSelectionChange={(v) => {\n        const key = [...v][0]?.toString() ?? null\n        onLegendSelect(key)\n      }}\n      selectionMode=\"single\"\n    >\n      {payload.map((item: LegendPayload) => {\n        const key = `${nameKey || item.dataKey || \"value\"}`\n        const itemConfig = getPayloadConfigFromPayload(config, item, key)\n\n        return (\n          <ToggleButton\n            key={key}\n            id={key}\n            className={cn(\n              \"flex items-center gap-2 rounded-sm px-2 py-1 text-muted-foreground *:[svg]:-mx-0.5 *:[svg]:size-2.5 *:[svg]:shrink-0 *:[svg]:text-muted-foreground\",\n              \"selected:bg-secondary/70 selected:text-secondary-foreground\",\n              \"hover:bg-secondary/70 hover:text-secondary-foreground\"\n            )}\n            aria-label=\"Legend Item\"\n          >\n            {itemConfig?.icon && !hideIcon ? (\n              <itemConfig.icon />\n            ) : (\n              <div\n                className=\"size-2.5 rounded-full\"\n                style={{\n                  backgroundColor: item.color,\n                }}\n              />\n            )}\n            {itemConfig?.label}\n          </ToggleButton>\n        )\n      })}\n    </ToggleButtonGroup>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "theme": {
      "color-overlay": "var(--overlay)",
      "color-overlay-foreground": "var(--overlay-foreground)"
    },
    "light": {
      "overlay": "oklch(1 0 0)",
      "overlay-foreground": "oklch(0.141 0.005 285.823)"
    },
    "dark": {
      "overlay": "oklch(0.210 0.006 285.885)",
      "overlay-foreground": "oklch(0.985 0 0)"
    }
  },
  "type": "registry:ui"
}