# Masked input

A text field that shapes what is typed into a pattern: a card number in fours, a code like "AB-1234", or a whole number grouped as the locale groups it. The app gets the raw value (the characters only) and the shown one.

With a `mask`, the literals are typed for the person and the caret steps over them; a paste is read through the mask, keeping the characters that fit. Under the field the whole pattern is drawn with what is typed filled in, as a guide (hidden from screen readers), and the field is described by the pattern as an example, "Format: 1234 5678 9012". Digits typed on Arabic-Indic, Persian or full-width keyboards are read as digits.

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

## Install

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

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

## Example

```tsx
"use client"

import * as React from "react"
import { Field } from "@/components/candor/ui/field"
import { MaskedInput } from "@/components/candor/ui/masked-input"

// The app keeps the raw value; the field shows it in its shape. A paste is read through the mask, and the pattern is
// read with the field as an example.
export default function Example() {
  const [card, setCard] = React.useState("")
  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <Field label="Card number">
        <MaskedInput mask="#### #### #### ####" autoComplete="cc-number" value={card} onValueChange={(raw) => setCard(raw)} />
      </Field>
      <Field label="Order reference">
        <MaskedInput mask="AA-######" />
      </Field>
      <Field label="Seats" hint="Grouped as your language groups numbers.">
        <MaskedInput group="digits" defaultValue="12500" />
      </Field>
    </div>
  )
}
```

## <MaskedInput>

Props:

- `mask?: string | undefined`: The value's shape: `#` a digit, `A` a letter, anything else written for the person ("#### #### ####", "AA-####"). The literals are typed for them, the caret steps over them, and a paste is read through the mask.
- `group?: "digits" | undefined`: In place of a mask: group a whole number's digits as the locale does, "1,234,567", "12,34,567" in Hindi.
- `value?: string | undefined`: The raw value (the mask's characters only, no literals), when the app controls it.
- `defaultValue?: string | undefined`: The raw value at first, when the field keeps its own.
- `onValueChange?: ((raw: string, shown: string) => void) | undefined`: Called with the raw value and the value as shown, as the person types or pastes.

Also takes the props of `Omit<React.ComponentProps<typeof Input>, "value" | "defaultValue" | "onChange" | "type">`.

## Strings (`maskedInput`)

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

- `format`: Format: {example}

## 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`.
