{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pie-chart",
  "title": "Pie Chart",
  "description": "Proportional pie or donut chart for part-to-whole comparisons.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/pie-chart/pie-chart.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * A single slice of a {@link PieChart}.\n *\n * @public\n */\nexport type PieDatum = {\n  /** Optional explicit slice color. Falls back to the chart palette. */\n  color?: string;\n  /** Slice label shown in the native tooltip. */\n  label: string;\n  /** Slice weight. The chart drops zero or negative values. */\n  value: number;\n};\n\n/**\n * Props for {@link PieChart}.\n *\n * @public\n */\nexport type PieChartProps = {\n  /** Base palette color. Defaults to `currentColor` to follow the text token. */\n  color?: string;\n  /** Explicit per-slice palette. Overrides the opacity-stepped default. */\n  colors?: string[];\n  /** Slices to render. */\n  data: PieDatum[];\n  /**\n   * Inner radius as a fraction of the outer radius (`0`–`1`). `0` draws a full\n   * pie; a value above `0` draws a donut.\n   *\n   * @defaultValue 0\n   */\n  innerRadius?: number;\n  /** Square viewport size in pixels. @defaultValue 200 */\n  size?: number;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nconst DEFAULT_SIZE = 200;\nconst TAU = Math.PI * 2;\n\ntype Point = { x: number; y: number };\ntype Slice = {\n  color?: string;\n  end: number;\n  label: string;\n  start: number;\n  value: number;\n};\ntype SliceGeom = { center: Point; inner: number; outer: number };\n\nfunction buildSlices(data: PieDatum[]): Slice[] {\n  const positive = data.filter((point) => point.value > 0);\n  const total = positive.reduce((sum, point) => sum + point.value, 0);\n  if (total <= 0) return [];\n\n  return positive.reduce<{ cursor: number; slices: Slice[] }>(\n    (accumulator, point) => {\n      const start = accumulator.cursor;\n      const end = start + (point.value / total) * TAU;\n      accumulator.slices.push({\n        color: point.color,\n        end,\n        label: point.label,\n        start,\n        value: point.value,\n      });\n      accumulator.cursor = end;\n      return accumulator;\n    },\n    { cursor: 0, slices: [] },\n  ).slices;\n}\n\nfunction polar(center: Point, radius: number, angle: number): Point {\n  return {\n    x: center.x + radius * Math.sin(angle),\n    y: center.y - radius * Math.cos(angle),\n  };\n}\n\nfunction slicePath(geom: SliceGeom, slice: Slice): string {\n  const { center, inner, outer } = geom;\n  const largeArc = slice.end - slice.start > Math.PI ? 1 : 0;\n  const o0 = polar(center, outer, slice.start);\n  const o1 = polar(center, outer, slice.end);\n\n  if (inner <= 0) {\n    return `M ${center.x} ${center.y} L ${o0.x} ${o0.y} A ${outer} ${outer} 0 ${largeArc} 1 ${o1.x} ${o1.y} Z`;\n  }\n\n  const innerEnd = polar(center, inner, slice.end);\n  const innerStart = polar(center, inner, slice.start);\n  return `M ${o0.x} ${o0.y} A ${outer} ${outer} 0 ${largeArc} 1 ${o1.x} ${o1.y} L ${innerEnd.x} ${innerEnd.y} A ${inner} ${inner} 0 ${largeArc} 0 ${innerStart.x} ${innerStart.y} Z`;\n}\n\nfunction sliceOpacity(index: number, count: number): number {\n  if (count <= 1) return 1;\n  return 1 - (index / count) * 0.65;\n}\n\nfunction PieSlice({\n  fill,\n  fillOpacity,\n  fullCircle,\n  geom,\n  slice,\n}: {\n  fill: string;\n  fillOpacity: number;\n  fullCircle: boolean;\n  geom: SliceGeom;\n  slice: Slice;\n}) {\n  const title = `${slice.label}: ${slice.value.toLocaleString()}`;\n  if (fullCircle) {\n    const isDonut = geom.inner > 0;\n    return (\n      <circle\n        cx={geom.center.x}\n        cy={geom.center.y}\n        fill={fill}\n        fillOpacity={fillOpacity}\n        r={isDonut ? (geom.outer + geom.inner) / 2 : geom.outer}\n        stroke={isDonut ? fill : \"none\"}\n        strokeWidth={isDonut ? geom.outer - geom.inner : 0}\n      >\n        <title>{title}</title>\n      </circle>\n    );\n  }\n  return (\n    <path d={slicePath(geom, slice)} fill={fill} fillOpacity={fillOpacity}>\n      <title>{title}</title>\n    </path>\n  );\n}\n\n/**\n * Token-styled SVG pie / donut chart.\n *\n * Pure SVG, no chart dependency. Colors come from `currentColor` (set via a\n * text-color token such as `text-primary`) with opacity steps, so the chart\n * follows the active theme. Pass `colors` or a per-slice `color` to set an\n * explicit palette. Returns `null` without any positive value.\n *\n * @example\n * ```tsx\n * <PieChart\n *   className=\"text-primary\"\n *   innerRadius={0.6}\n *   data={[\n *     { label: \"Direct\", value: 40 },\n *     { label: \"Referral\", value: 25 },\n *     { label: \"Organic\", value: 35 },\n *   ]}\n * />\n * ```\n *\n * @public\n */\nexport const PieChart = ({\n  className,\n  color = \"currentColor\",\n  colors,\n  data,\n  innerRadius = 0,\n  ref,\n  size = DEFAULT_SIZE,\n  ...props\n}: PieChartProps & { ref?: React.Ref<HTMLDivElement> }) => {\n  const slices = buildSlices(data);\n  if (slices.length === 0) return null;\n\n  const outer = size / 2 - 1;\n  const geom: SliceGeom = {\n    center: { x: size / 2, y: size / 2 },\n    inner: Math.max(0, Math.min(innerRadius, 0.95)) * outer,\n    outer,\n  };\n  const fullCircle = slices.length === 1;\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=\"Pie chart\"\n        className=\"h-full w-full\"\n        height={size}\n        role=\"img\"\n        viewBox={`0 0 ${size} ${size}`}\n        width={size}\n      >\n        {slices.map((slice, index) => (\n          <PieSlice\n            fill={colors?.[index] ?? slice.color ?? color}\n            fillOpacity={\n              colors || slice.color ? 1 : sliceOpacity(index, slices.length)\n            }\n            fullCircle={fullCircle}\n            geom={geom}\n            key={`${slice.label}-${index}`}\n            slice={slice}\n          />\n        ))}\n      </svg>\n    </div>\n  );\n};\n\nPieChart.displayName = \"PieChart\";\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.2.1",
  "stability": "stable"
}
