# Progress ring

Circular completion for a measured value, beside `Progress` and `ContextMeter`'s ring: how far through a known total something is ("38 of 50 records checked"). It is a progressbar with its value, its maximum and the words for both. A missing value (or one, or a total, that isn't a finite number, or a total of zero or less) draws the track only and says "Unknown", never an empty ring that reads as none done. There's no spinning state: work of unknown length isn't a ring (use `StatusDot`), because a ring must track a measure. The ring fills only as far as the work has gone: its ends are square, and its share is rounded down. Past the total the ring is full and its words keep the value as measured.

- Registry name: `progress-ring` (`@candor/progress-ring`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/progress-ring

## Install

```bash
npx candor-ui add progress-ring
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/progress-ring`.
It brings in: data, locale, scale, tokens, utils.

## Example

```tsx
"use client"

import { ProgressRing } from "@/components/candor/progress-ring"

export default function Example() {
  return (
    <ul className="flex max-w-xs flex-col gap-3 text-sm">
      <li className="flex items-center justify-between gap-4">
        Records checked
        <ProgressRing value={38} max={50} label="Records checked" showValue />
      </li>
      <li className="flex items-center justify-between gap-4">
        Pages read
        <ProgressRing value={null} max={120} label="Pages read" showValue />
      </li>
      <li className="flex items-center justify-between gap-4">
        Migration
        <ProgressRing value={12} max={12} label="Tables migrated" size="lg" showValue />
      </li>
    </ul>
  )
}
```

## <ProgressRing>

Props:

- `value: number | null`: How much is done, measured; `null` when it isn't known. A value that isn't a finite number is unknown too.
- `max?: number | undefined` (default `100`): The total: a full ring. Defaults to 100. A total of zero or less, or one that isn't a finite number, makes the ring unknown.
- `label: string`: What is in progress, the progressbar's name: "Records checked".
- `size?: "sm" | "md" | "lg" | undefined` (default `"md"`): 16, 24 or 40 pixels across. Defaults to md (24).
- `showValue?: boolean | undefined` (default `false`): Writes the share done beside the ring ("76%"), rounded down; past the total, the value and the total ("120 of 100").
- `format?: ValueFormat | undefined` (default `"number"`): How the value and the total are written in the words read out: number, percent (0–1), currency or duration. Defaults to number.
- `className?: string | undefined`: Classes for the root, merged with Candor's.

## Strings (`progressRing`)

The fixed words, English by default. Override any of them under `progressRing` in `<LocaleProvider strings={{ progressRing: { … } }}>`; the table is exported as `progressRingStrings`.

- `valueText`: {value} of {max}
- `unknown`: Unknown

## Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

### ValueFormat

Import: `import type { ValueFormat } from "@/lib/candor/data"` (item `data`, https://candor.themethodic.com/docs/data.md)

```ts
"number" | "percent" | "currency" | "duration"
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
