# RadialChart

Values on an arc — a gauge, or a few totals against one scale.

- Group: Charts
- Import: `import { RadialChart } from '@misoto22/folio/charts'`
- Page: https://ui.misoto22.com/components/radial-chart/
- Related: pie-chart, bar-chart

## When to reach for it

A single value against a fixed total. Past about four bars a BarChart is the honest choice, because a radial bar’s radius is not its value.

## Anatomy

- **Figure frame** (required) — ChartFigure’s <figure>, with an empty state at zero rows. Its hidden table needs a value field, which it takes from valueKey or, failing that, from the dataKey of the composed <RadialChart.RadialBar> — so a chart that names neither still has no table.
- **Arcs** (required) — <RadialChart.RadialBar>, one per row, 14px thick with a 5px cap. variant="semi" drops the centre to 70% so a half arc sits in the middle of its own box rather than at the top of it.
- **Track** — showTrack, on by default, painting the unfilled remainder behind each arc in --chart-track. It is what makes a gauge a gauge: without it there is no visible whole for the fill to be a part of.
- **Scale** — The PolarAngleAxis the root inserts when max is set, with domain [0, max] and its ticks off. Leave max unset and the domain is taken from the data instead.
- **Legend** — <RadialChart.Legend>. An arc has no category axis, so above one bar this is the only thing naming them.

## Best practices

### Do

- Set max on anything that is a gauge. Without it the scale comes from the data, so the largest bar always fills the arc and 62% and 98% are drawn identically.
- Pass valueKey when the arc is not the only mark. It names the field the hidden table prints and the field the LEGEND reports a selection from; without either it or a composed <RadialChart.RadialBar> there is no value field, and the table is not empty but absent.
- Use variant="semi" for a single value. A half arc reads as a dial with a floor and a ceiling, where a full ring asks the reader to work out what a whole circle was worth.

### Don’t

- Do not compare bars across radii. A radial bar’s LENGTH is its value but its RADIUS is not, so an inner arc and an outer arc holding the same number are drawn different lengths — past about four bars a BarChart is the honest form.
- Do not let a reader take an arc as a share of the ring when max is unset. A full sweep then means the biggest thing here, which is a different sentence from all of it.

## Accessibility

- title is required; pass valueKey and the rows are also rendered as a visually hidden table.
- Set max or the scale comes from the data and the largest bar always fills the arc — which makes 62% and 98% look identical.
- showTrack draws the unfilled remainder, which is what makes a gauge readable at all.

## RadialChart

Values on an arc — a gauge, or a small set of totals against one scale. The caveat worth knowing before reaching for it: a radial bar's LENGTH is its value, but its RADIUS is not, so an inner bar and an outer bar of the same value are drawn different lengths. That makes it a poor comparison and a good single-value gauge; past about four bars, a `<BarChart>` is the honest choice.

### Props

- `config` (required) — `ChartConfig`. Bar names → their label and paint. Keys must match the `nameKey` values.
- `data` (required) — `TData[]`. The rows the chart draws. One entry per point, bar or category.
- `nameKey` (required) — `keyof TData & string`. The row field naming each bar.
- `title` (required) — `string`. What the chart shows, in a sentence a reader could act on. Required, and announced to a screen reader even when it is not printed.
- `showTitle` — `boolean`. Prints the title above the plot instead of hiding it from sight.
- `description` — `ReactNode`. A line under the title — the unit, the window, the caveat.
- `children` (required) — `ReactNode`. The composed parts — axes, grid, tooltip, legend, and the marks themselves.
- `className` — `string`. Merged onto the figure, last, so a call site can size or space it.
- `chartProps` — `ComponentProps<typeof RechartsRadialBarChart>`. Escape hatch onto the raw Recharts chart element.
- `variant` — `RadialVariant` default `'full'`. The arc shape — a full ring, or the half circle a gauge wants.
- `max` — `number`. What a full sweep is worth. Without it the scale comes from the data, so the largest bar always fills the arc — which is right for a comparison and wrong for a gauge. Set it (typically 100) whenever a single value has to read against a fixed total, or "62%" and "98%" will look identical.
- `innerRadius` — `number | string` default `'30%'`. Where the arc starts, from the centre.
- `outerRadius` — `number | string` default `'100%'`. Where the arc ends.
- `defaultSelectedBar` — `string | null` default `null`. The bar lit on first render, when the chart keeps its own selection.
- `selectedBar` — `string | null`. The selected bar, driven from outside. Give this and the chart follows it; leave it undefined and the chart keeps its own, starting from `defaultSelectedBar`.
- `onSelectionChange` — `(selection: { name: string; value: number } | null) => void`. Fires when the selection changes, and with null when it is cleared.
- `isLoading` — `boolean` default `false`. Swaps the marks for an animated skeleton, keeping the measured height so the page does not jump when the data lands.
- `valueKey` — `keyof TData & string`. The row field holding each bar's number, for the table view and for the selection the legend reports. Falls back to the `dataKey` of the composed `<RadialChart.RadialBar>`, so the usual call site needs neither.
- `hideDataTable` — `boolean` default `false`. Drops the hidden table view. Only correct when the page prints the data itself.
- `empty` — `ChartEmptyProps | false`. What the chart shows when it has nothing to draw. `false` keeps the empty plot, for a chart whose emptiness is itself the reading.

## Example — default

```tsx
import { RadialChart, type ChartConfig } from '@misoto22/folio/charts'

<RadialChart
  title="Storage used by tier"
  config={config}
  data={data}
  nameKey="tier"
  valueKey="used"
>
  <RadialChart.RadialBar dataKey="used" isClickable />
  <RadialChart.Tooltip />
  <RadialChart.Legend isClickable />
</RadialChart>
```

## Example — gauge

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { RadialChart, type ChartConfig, type RadialVariant } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={variant}
    onValueChange={(next) => next && setVariant(next as RadialVariant)}
    aria-label="Arc"
  >
    {VARIANTS.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <RadialChart
    title="Storage used"
    config={config}
    data={data}
    nameKey="tier"
    valueKey="used"
    variant={variant}
    max={100}
    innerRadius="65%"
  >
    <RadialChart.RadialBar dataKey="used" barSize={22} cornerRadius={11} />
    <RadialChart.Tooltip />
  </RadialChart>
</div>
```

## Example — loading

```tsx
import { RadialChart, type ChartConfig } from '@misoto22/folio/charts'

<RadialChart title="Storage used by tier" config={config} data={[]} nameKey="tier" isLoading>
  <RadialChart.RadialBar dataKey="used" />
</RadialChart>
```
