# Number input

A field for one number, with buttons to step it down and up. Arrow Up and Arrow Down step it from the keyboard, a screen reader hears a spin button with its value, and the phone shows a number pad. Empty is `null`, never 0: nothing is filled in for the person.

The number is read and shown in LocaleProvider's locale, not the browser's: "1,5" is one and a half in German, and digits in Arabic-Indic or Persian script are read too. What the person types stays as typed until they leave the field, unless the value changes from outside to a different number, or to `null` from a number. Text that isn't a number is already `null`, so only its form's reset clears it (an uncontrolled field then goes back to `defaultValue`). With `name`, a form gets the number in plain digits ("1.5"), whatever the locale.

The step buttons are for a pointer and stay out of the tab order (the arrow keys do the same from the field). At `min` or `max` the button that would pass it is locked and says why (in a tooltip: needs a `TooltipProvider`), and an arrow key pressed at the limit says the same to a screen reader. Values typed outside the range are kept as typed, for your validation to name; the buttons and arrow keys stop at the range. From empty, the first step goes to 0, or to the nearest end of the range when 0 is outside it.

Use `Field` for the label, hint and error, and `unit` for what the number counts ("minutes", "USD"); the unit is read with the field. A phone's number pad has no minus sign, so with a `min` below 0 (or none) the field asks for the full keyboard.

- Registry name: `number-input` (`@candor/number-input`)
- 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/number-input

## Install

```bash
npx candor-ui add number-input
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/number-input`.
It brings in: button, input, locale, locked, tokens, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { Field } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"
import { TooltipProvider } from "@/components/ui/tooltip"

// Empty is null, not 0. The buttons stop at the range and say why; a typed value outside it is left for your check to name.
export default function Example() {
  const [minutes, setMinutes] = React.useState<number | null>(15)
  const error = minutes !== null && (minutes < 1 || minutes > 120) ? "Enter a time limit from 1 to 120 minutes" : undefined
  return (
    <TooltipProvider>
      <Field label="Time limit per run" hint="The agent stops and reports what it finished." error={error} className="w-full max-w-xs">
        <NumberInput value={minutes} onValueChange={setMinutes} min={1} max={120} step={5} unit="minutes" />
      </Field>
    </TooltipProvider>
  )
}
```

## <NumberInput>

Props:

- `value?: number | null | undefined`: The number, when you control it. `null` is empty.
- `defaultValue?: number | undefined`: The number at first, when you don't control it. Left out, the field is empty.
- `onValueChange?: ((value: number | null) => void) | undefined`: Called with the number as the person types or steps, or `null` when the field is emptied or what is typed isn't a number.
- `min?: number | undefined`: The lowest the buttons and arrow keys go. 0 or more also brings up the phone's number pad.
- `max?: number | undefined`: The highest the buttons and arrow keys go.
- `step?: number | undefined` (default `1`): How much a button press or arrow key changes the number. Defaults to 1.
- `unit?: string | undefined`: What the number counts, printed after it and read with the field: "minutes", "USD".
- `attend?: boolean | undefined`: Rings the field once, as `Input`'s `attend` does, after a failed submit.
- `name?: string | undefined`: The form field's name. The form gets the number in plain digits ("1.5"), or "" when empty.
- `"aria-describedby"?: string | undefined`: Ids of anything else that describes the field; the unit's id is added after them.

Also takes the props of `Omit<React.ComponentProps<"input">, "value" | "defaultValue" | "type" | "onChange" | "min" | "max" | "step" | "inputMode">`.

## Strings (`numberInput`)

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

- `decrease`: Decrease
- `increase`: Increase
- `atMin`: Already at the lowest, {min}.
- `atMax`: Already at the highest, {max}.

## 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` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
