{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart-radial",
  "title": "Chart Radial",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "Radial bars: one ring is a gauge, several are concentric progress rings. Full or half circle, a track, text in the middle, in a frame with a table, patterns and states.",
  "dependencies": [
    "recharts@^3"
  ],
  "registryDependencies": [
    "https://ui.ballmac.com/r/chart.json",
    "https://ui.ballmac.com/r/chart-frame.json",
    "https://ui.ballmac.com/r/i18n.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/chart-radial.tsx",
      "content": "// Ballmac UI: Chart Radial. https://ui.ballmac.com/components/chart-radial\n\"use client\"\n\nimport * as React from \"react\"\nimport { Cell, PolarAngleAxis, RadialBar, RadialBarChart } from \"recharts\"\n\nimport { ChartCenterLabel, ChartTooltip, ChartTooltipContent } from \"@/components/ballmac/chart\"\nimport { ChartFrame, type ChartFrameProps, type ChartRow, type ChartSeries } from \"@/components/ballmac/chart-frame\"\nimport { useLocale, useMessages } from \"@/lib/ballmac/i18n\"\n\ntype ChartRadialProps = Omit<ChartFrameProps, \"children\" | \"series\" | \"xKey\" | \"summaryMode\" | \"skeleton\" | \"table\" | \"data\"> & {\n  /** One row per ring: its name under `nameKey`, its number under `valueKey`, and optionally a `label` and a `color`. One row is a gauge. */\n  data: ChartRow[]\n  nameKey?: string\n  valueKey?: string\n  /** What the full ring stands for. Defaults to 100, or the largest value when that is more. */\n  max?: number\n  /** What the numbers count, for the table header: \"minutes\". */\n  valueLabel?: string\n  /** Draw only the upper half, like a gauge. */\n  half?: boolean\n  /** Text in the middle: `true` writes the first ring's share, or give `{ value, label }`. */\n  centerLabel?: boolean | { value: string; label: string }\n  /** Draw the empty part of each ring. */\n  track?: boolean\n  tooltip?: boolean\n}\n\n/**\n * Radial bars in a ChartFrame: one ring is a gauge, several are concentric progress rings. Full or half circle, a track behind each ring, text\n * in the middle, a legend that switches rings off, a table of value and share of the maximum, patterns, and loading, empty and error states.\n */\nfunction ChartRadial({\n  data,\n  nameKey = \"name\",\n  valueKey = \"value\",\n  max,\n  valueLabel,\n  half = false,\n  centerLabel = true,\n  track = true,\n  tooltip = true,\n  categoryLabel,\n  categoryFormatter,\n  valueFormatter,\n  summary,\n  ...frame\n}: ChartRadialProps) {\n  const msg = useMessages()\n  const locale = useLocale()\n  const nf = React.useMemo(() => new Intl.NumberFormat(locale), [locale])\n  const pct = React.useMemo(() => new Intl.NumberFormat(locale, { style: \"percent\", maximumFractionDigits: 0 }), [locale])\n  const number = (v: unknown) => (typeof v === \"number\" && Number.isFinite(v) ? v : 0)\n  const write = (v: number) => (valueFormatter ? valueFormatter(v, valueKey) : nf.format(v))\n  const top = Math.max(0, ...data.map((r) => number(r[valueKey])))\n  const full = max ?? (top > 100 ? top : 100)\n\n  const items: ChartSeries[] = data.map((row) => {\n    const name = String(row[nameKey])\n    return { key: name, label: typeof row.label === \"string\" ? row.label : categoryFormatter ? categoryFormatter(row[nameKey]) : name, color: typeof row.color === \"string\" ? row.color : undefined }\n  })\n  const labelOf = (name: string) => items.find((i) => i.key === name)?.label ?? name\n  const written =\n    summary ??\n    data.map((row) => msg(\"chart-radial.summary\", \"{name}: {value} of {max} ({share}).\", { name: labelOf(String(row[nameKey])), value: write(number(row[valueKey])), max: write(full), share: pct.format(number(row[valueKey]) / full) })).join(\" \")\n\n  return (\n    <ChartFrame\n      data={data}\n      xKey={nameKey}\n      series={items}\n      summary={written}\n      aspect={half ? \"wide\" : \"square\"}\n      skeleton=\"donut\"\n      legend={data.length > 1 ? \"toggle\" : \"none\"}\n      categoryLabel={categoryLabel}\n      valueFormatter={valueFormatter}\n      categoryFormatter={categoryFormatter}\n      table={{\n        columns: [\n          { key: nameKey, label: categoryLabel ?? msg(\"chart-radial.category\", \"Category\"), format: (v) => labelOf(String(v)) },\n          { key: valueKey, label: valueLabel ? valueLabel.charAt(0).toUpperCase() + valueLabel.slice(1) : msg(\"chart-radial.value\", \"Value\"), numeric: true, format: (v) => write(number(v)) },\n          { key: \"__share\", label: msg(\"chart-radial.share\", \"Of {max}\", { max: write(full) }), numeric: true, format: (_v, row) => pct.format(number(row[valueKey]) / full) },\n        ],\n        rows: data,\n      }}\n      {...frame}\n    >\n      {(chart) => {\n        const shown = data.filter((row) => !chart.isHidden(String(row[nameKey])))\n        const first = shown[0]\n        // Rings run the way the page reads: clockwise left to right, counterclockwise right to left.\n        const [startAngle, endAngle] = half ? (chart.rtl ? [0, 180] : [180, 0]) : chart.rtl ? [90, 450] : [90, -270]\n        const center = centerLabel === true ? (first ? { value: pct.format(number(first[valueKey]) / full), label: labelOf(String(first[nameKey])) } : null) : centerLabel || null\n        return (\n          <RadialBarChart data={shown} cy={half ? \"88%\" : \"50%\"} innerRadius={half ? \"105%\" : \"58%\"} outerRadius={half ? \"165%\" : \"92%\"} startAngle={startAngle} endAngle={endAngle} barSize={shown.length > 1 ? undefined : half ? 36 : 26} accessibilityLayer>\n            <defs>{chart.defs}</defs>\n            <PolarAngleAxis type=\"number\" domain={[0, full]} angleAxisId={0} tick={false} />\n            {tooltip ? <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel nameKey={nameKey} />} /> : null}\n            <RadialBar dataKey={valueKey} angleAxisId={0} cornerRadius={10} background={track ? { fill: \"var(--muted)\" } : false} isAnimationActive={chart.animate} animationDuration={700}>\n              {shown.map((row) => {\n                const key = String(row[nameKey])\n                return <Cell key={key} fill={chart.fill(key)} stroke={chart.patterns ? chart.color(key) : undefined} strokeWidth={chart.patterns ? 1.5 : undefined} />\n              })}\n            </RadialBar>\n            {center ? <ChartCenterLabel value={center.value} label={center.label} cy={half ? 0.88 : 0.5} /> : null}\n          </RadialBarChart>\n        )\n      }}\n    </ChartFrame>\n  )\n}\n\nexport { ChartRadial, type ChartRadialProps }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/chart-radial.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "chart",
      "radial",
      "gauge",
      "ring",
      "progress",
      "goal",
      "recharts",
      "graph"
    ],
    "version": "1.0.0",
    "updated": "2026-10-18",
    "ai": {
      "summary": "<ChartRadial title description data={[{ name, label?, value, color? }]} max={100} />. One row is a gauge, several rows are concentric rings. half for a semicircle, centerLabel (true writes the first ring's share, or { value, label }), track, valueLabel, legend, patterns, state.",
      "whenToUse": [
        "Progress toward a goal",
        "A single metric against its maximum",
        "Activity or goal rings"
      ],
      "whenNotToUse": [
        "Parts of a whole (use chart-pie)",
        "Comparing many categories (use chart-bar)"
      ],
      "composesWith": [
        "chart-frame",
        "stat-card",
        "progress-ring"
      ],
      "a11y": [
        {
          "keys": "Screen readers",
          "action": "The summary reads every ring as 'value of max (share)'"
        },
        {
          "keys": "Tab, Enter / Space",
          "action": "Legend buttons switch series off and on; the Chart / Table switch shows the numbers as a table"
        },
        {
          "keys": "Screen readers",
          "action": "The figure has a name and a summary written from the data"
        }
      ],
      "customization": [
        "half and track",
        "max (the full ring)",
        "per-ring colors"
      ]
    },
    "examples": [
      "chart-radial-demo",
      "chart-radial-rings",
      "chart-radial-half",
      "chart-radial-patterns"
    ],
    "url": "https://ui.ballmac.com/components/chart-radial"
  },
  "categories": [
    "data-display"
  ],
  "type": "registry:ui"
}