PieChart

PieChart draws proportions as a colored ring with a value list. The parent supplies data, labels, and value formatting. The chart groups small items into Others. Open Others to see its complete breakdown.

import PieChart from "@haloid/azucar/ui/pie-chart";
import type { PieChartProps } from "@haloid/azucar/ui/pie-chart/types";

Data

Pass rows with three accessors. keyAccessor returns a unique, stable key. labelAccessor returns text. valueAccessor returns a number. The chart sorts positive, finite values from largest to smallest. It ignores zero, negative, and non-finite values. valueFormatter formats every displayed value, including totals. The default shows numbers with at most one decimal place.

This example groups three small items. Click Others in the ring or list to open their breakdown.

<Card className="mx-auto max-w-100">
<CardHeader title="Distribution" />
<CardContent>
  <PieChart
    data={[
      { id: "a", label: "Item A", value: 48 },
      { id: "b", label: "Item B", value: 28 },
      { id: "c", label: "Item C", value: 16 },
      { id: "d", label: "Item D", value: 4 },
      { id: "e", label: "Item E", value: 3 },
      { id: "f", label: "Item F", value: 1 },
    ]}
    keyAccessor={(item) => item.id}
    labelAccessor={(item) => item.label}
    valueAccessor={(item) => item.value}
    ariaLabel="Distribution across six items"
  />
</CardContent>
</Card>

Sizing

The chart fills its parent's width. The ring is 176px wide. The list sits below it in narrow cards. At container widths of 512px or more, the ring and list sit beside each other. The list grows to show every item. The parent owns page scrolling.

Grouping

groupBelow is a fraction of the total. The default, 0.05, groups items below 5%. maxSlices limits the overview to six slices by default, including Others. Extra items join Others, starting with the smallest. The largest item always stays visible. Others requires at least two items. A lone small item stays visible.

Change the slice limit or grouping threshold to see the group change.

maxSlices
groupBelow
<PieChart maxSlices={4}
data={[
  { id: "a", label: "Item A", value: 30 },
  { id: "b", label: "Item B", value: 24 },
  { id: "c", label: "Item C", value: 18 },
  { id: "d", label: "Item D", value: 12 },
  { id: "e", label: "Item E", value: 9 },
  { id: "f", label: "Item F", value: 7 },
]}
keyAccessor={(item) => item.id}
labelAccessor={(item) => item.label}
valueAccessor={(item) => item.value}
ariaLabel="Distribution with a configurable slice limit"
/>

Use groupBelow={0} and a sufficient maxSlices to show every item directly. The minimum slice limit is two. Non-finite settings use their defaults.

Others

Open Others to fill the ring with its items. This view shows every grouped item without grouping again. Values keep their original units. Percentages show each item's share of Others. The center also reports Others' share of the total. Select All items, or press Escape inside the chart, to return. Focus returns to the Others button. If new data changes the group's membership, the chart returns to the overview.

Interaction

Hover a slice or list row to highlight it. The center shows its label, value, and percentage. Each list row is a native button. Tab focuses rows. Enter, Space, and touch open Others or inspect an item. ariaLabel names the chart. Each button announces its value and percentage scope. View changes respect reduced-motion preferences.

Empty data

An empty array shows No data yet. The same message appears when no positive, finite values remain. Use emptyLabel to replace it.

Styling

The chart uses Azucar's blue, green, orange, and red tokens to distinguish items. Colors identify categories. Slices and legend markers share a color. Each item keeps its color when Others opens. The palette repeats after eight items. A neutral striped fill distinguishes Others. Hover connects each slice to its row.

This example shows all eight colors with grouping disabled.

<PieChart
data={[
  { id: "a", label: "Item A", value: 24 },
  { id: "b", label: "Item B", value: 20 },
  { id: "c", label: "Item C", value: 16 },
  { id: "d", label: "Item D", value: 12 },
  { id: "e", label: "Item E", value: 10 },
  { id: "f", label: "Item F", value: 8 },
  { id: "g", label: "Item G", value: 6 },
  { id: "h", label: "Item H", value: 4 },
]}
keyAccessor={(item) => item.id}
labelAccessor={(item) => item.label}
valueAccessor={(item) => item.value}
groupBelow={0}
maxSlices={8}
ariaLabel="Distribution showing all eight chart colors"
/>

Labels

totalLabel defaults to Total. othersLabel defaults to Others. className merges onto the root.

Use in north

Billing supplies individual tap-box readings and formats values in kW. Inactive tap boxes contribute zero. The chart groups these readings itself. Changes to the demo tap-box state update the chart.