{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "radar-chart",
  "title": "Radar Chart",
  "description": "Spider chart comparing several metrics on shared axes.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/radar-chart/radar-chart.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * A single axis of a {@link RadarChart}.\n *\n * @public\n */\nexport type RadarDatum = {\n  /** Axis label drawn around the perimeter. */\n  label: string;\n  /** Value plotted along the axis (`0`–`max`). */\n  value: number;\n};\n\n/**\n * Props for {@link RadarChart}.\n *\n * @public\n */\nexport type RadarChartProps = {\n  /** Stroke and fill color. Defaults to `currentColor` to follow the text token. */\n  color?: string;\n  /** One entry per axis. The chart needs at least three to form a polygon. */\n  data: RadarDatum[];\n  /** Number of concentric grid rings. @defaultValue 4 */\n  levels?: number;\n  /** Upper bound of the scale. Defaults to the largest value in `data`. */\n  max?: number;\n  /** Square viewport size in pixels. @defaultValue 240 */\n  size?: number;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nconst DEFAULT_SIZE = 240;\nconst DEFAULT_LEVELS = 4;\nconst LABEL_MARGIN = 26;\nconst TAU = Math.PI * 2;\n\ntype Point = { x: number; y: number };\ntype Spoke = { center: Point; count: number; index: number; radius: number };\n\nfunction vertex(spoke: Spoke): Point {\n  const angle = -Math.PI / 2 + (spoke.index / spoke.count) * TAU;\n  return {\n    x: spoke.center.x + spoke.radius * Math.cos(angle),\n    y: spoke.center.y + spoke.radius * Math.sin(angle),\n  };\n}\n\nfunction toPolygon(points: Point[]): string {\n  return points\n    .map((point) => `${point.x.toFixed(2)},${point.y.toFixed(2)}`)\n    .join(\" \");\n}\n\nfunction anchorFor(cosine: number): \"end\" | \"middle\" | \"start\" {\n  if (cosine > 0.2) return \"start\";\n  if (cosine < -0.2) return \"end\";\n  return \"middle\";\n}\n\nfunction RadarGrid({\n  center,\n  count,\n  levels,\n  radius,\n}: {\n  center: Point;\n  count: number;\n  levels: number;\n  radius: number;\n}) {\n  return (\n    <>\n      {Array.from({ length: levels }, (_unused, level) => {\n        const ringRadius = ((level + 1) / levels) * radius;\n        const ring = Array.from({ length: count }, (_inner, index) =>\n          vertex({ center, count, index, radius: ringRadius }),\n        );\n        return (\n          <polygon\n            className=\"stroke-border\"\n            fill=\"none\"\n            key={`ring-${level}`}\n            points={toPolygon(ring)}\n            strokeWidth={1}\n          />\n        );\n      })}\n    </>\n  );\n}\n\nfunction RadarAxes({\n  center,\n  data,\n  radius,\n}: {\n  center: Point;\n  data: RadarDatum[];\n  radius: number;\n}) {\n  const count = data.length;\n  return (\n    <>\n      {data.map((point, index) => {\n        const tip = vertex({ center, count, index, radius });\n        const label = vertex({\n          center,\n          count,\n          index,\n          radius: radius + LABEL_MARGIN / 2,\n        });\n        const angle = -Math.PI / 2 + (index / count) * TAU;\n        return (\n          <React.Fragment key={`axis-${point.label}-${index}`}>\n            <line\n              className=\"stroke-border\"\n              strokeWidth={1}\n              x1={center.x}\n              x2={tip.x}\n              y1={center.y}\n              y2={tip.y}\n            />\n            <text\n              className=\"fill-muted-foreground text-[10px]\"\n              dominantBaseline=\"middle\"\n              textAnchor={anchorFor(Math.cos(angle))}\n              x={label.x}\n              y={label.y}\n            >\n              {point.label}\n            </text>\n          </React.Fragment>\n        );\n      })}\n    </>\n  );\n}\n\nfunction RadarShape({\n  color,\n  data,\n  points,\n}: {\n  color: string;\n  data: RadarDatum[];\n  points: Point[];\n}) {\n  return (\n    <>\n      <polygon\n        fill={color}\n        fillOpacity={0.2}\n        points={toPolygon(points)}\n        stroke={color}\n        strokeLinejoin=\"round\"\n        strokeWidth={2}\n      />\n      {points.map((point, index) => (\n        <circle\n          cx={point.x}\n          cy={point.y}\n          fill={color}\n          key={`dot-${index}`}\n          r={3}\n        >\n          <title>{`${data[index]?.label ?? \"\"}: ${(data[index]?.value ?? 0).toLocaleString()}`}</title>\n        </circle>\n      ))}\n    </>\n  );\n}\n\n/**\n * Token-styled SVG radar (spider) chart.\n *\n * Pure SVG, no chart dependency. Grid rings and axes use the `border` and\n * `muted-foreground` tokens; the data polygon uses `currentColor`, so the chart\n * follows the active theme. Returns `null` for fewer than three axes or a scale\n * at or below zero.\n *\n * @example\n * ```tsx\n * <RadarChart\n *   className=\"text-primary\"\n *   max={100}\n *   data={[\n *     { label: \"Speed\", value: 80 },\n *     { label: \"Power\", value: 60 },\n *     { label: \"Range\", value: 90 },\n *     { label: \"Agility\", value: 70 },\n *     { label: \"Defense\", value: 50 },\n *   ]}\n * />\n * ```\n *\n * @public\n */\nexport const RadarChart = ({\n  className,\n  color = \"currentColor\",\n  data,\n  levels = DEFAULT_LEVELS,\n  max,\n  ref,\n  size = DEFAULT_SIZE,\n  ...props\n}: RadarChartProps & { ref?: React.Ref<HTMLDivElement> }) => {\n  const count = data.length;\n  const scaleMax = max ?? Math.max(...data.map((point) => point.value), 0);\n  if (count < 3 || scaleMax <= 0) return null;\n\n  const center: Point = { x: size / 2, y: size / 2 };\n  const radius = size / 2 - LABEL_MARGIN;\n  const points = data.map((point, index) =>\n    vertex({\n      center,\n      count,\n      index,\n      radius: (point.value / scaleMax) * radius,\n    }),\n  );\n\n  return (\n    <div\n      className={cn(\n        \"rounded-2xl border border-border bg-background/40 p-3\",\n        className,\n      )}\n      ref={ref}\n      {...props}\n    >\n      <svg\n        aria-label=\"Radar chart\"\n        className=\"h-full w-full\"\n        height={size}\n        role=\"img\"\n        viewBox={`0 0 ${size} ${size}`}\n        width={size}\n      >\n        <RadarGrid\n          center={center}\n          count={count}\n          levels={Math.max(1, levels)}\n          radius={radius}\n        />\n        <RadarAxes center={center} data={data} radius={radius} />\n        <RadarShape color={color} data={data} points={points} />\n      </svg>\n    </div>\n  );\n};\n\nRadarChart.displayName = \"RadarChart\";\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.2.1",
  "stability": "stable"
}
