Components › Forms & input › Text field › Masked input

Masked input

canon

Structured value typed straight into a visible mask (IČO, DIČ, PSČ, rodné číslo) with inline check-digit validation; input holds the typed text, a muted ghost draws the rest of the template.

Merged to origin/main 28 September 2026 (acrm MR !4082, "fix(web): IČO, DIČ, PSČ a rodné číslo jako maska (MaskedInput v @acrm/ui)"), under the owner's binding rule of 14 Sep 2026 (".claude/rules/design-system.md" §"Vstupy a mobil"): a structured value is a mask, never free text — only valid characters, separators added automatically, the template stays visible, the error is reported while typing. Unlike "masked-date-input", this component IS exported from @acrm/ui (source=package) and ships four masks as pure functions in `field-masks.ts`: IČO (8 digits, mod-11 check digit), DIČ (digits-first = Czech, "CZ" is added automatically; letters-first = the typed country code, CZ is never forced; mod-11 check only for CZ), PSČ ("### ##", Czech/Slovak only — a foreign address falls back to a plain Input in the host), rodné číslo ("######/####" or 9 digits before 1953, date part validated per segment, +50 on the month for women, 10-digit form checked mod 11 with the pre-1985 exception). Two host shapes, no new look: `variant="field"` (default) wraps `.acrm-inputwrap` (`.acrm-mask`/`.acrm-mask-ghost` inside, `.acrm-maskwrap.is-error` colours the typed text `--critical-ink` in addition to the wrapper's usual red border+ring) — e.g. IČO/DIČ in the "Nová firma" drawer (`FirmyPage.tsx`); `variant="formless"` reuses the exact `.fl-mask`/`.fl-mask-in`/`.fl-mask-ghost`/`.fl-mask.bad` classes `masked-date-input` already established in `dialog-formless.css` — e.g. rodné číslo in the "Nová osoba" dialog (`NewContactDialog.tsx`). Save gate: the host reads the value out only when `mask.complete(text)` is true (`ico: mask.complete(f.ico) ? f.ico : null`); an incomplete or invalid field is simply not saved, it never blocks the rest of the form (AD 28 Sep 2026) — same rule as `masked-date-input`'s ISO gate. An old invalid stored value (recorded before the mask existed) is loaded through `applyMask()` and shown as-is, editable, not rejected. `describedBy` links the error text via `aria-describedby` when the caller passes an id — the formless caller (`NewContactDialog`, rodné číslo) wires it; the field-variant callers in `FirmyPage` (IČO, DIČ, PSČ) do not, leaving the same label/error linking gap `text-field/input.a11y.md` documents for `<Field>`. Sibling pattern `MaskedPhoneInput` (phone, dial codes) moved into `packages/ui` 28 September 2026 (acrm MR !4085) alongside the new `PhoneInput` form field and `DialCodePicker`; it now has its own registry card, `phone-input`.

Used in 2 files · 2 areas · 2× — components/org, components/person

Import

import { MaskedInput } from '@acrm/ui'

Props

MaskedInput packages/ui/src/components/MaskedInput.tsx

NameTypeDefaultDescription
mask *FieldMask——
value *string—Text pole v masce („800101/1235").
onValue *(text: string) => void——
variant'field' | 'formless''field'—
ariaLabelstring——
invalidboolean——
describedBystring—Id textu chyby pod polem — čtečka ho přečte spolu s aria-invalid.
classNamestring—Šířková varianta obalu pole (acrm-inputwrap--code …).
placeholderstring—Popisek prázdného pole (výchozí = šablona podtržítky).
onBlur() => void——
autoFocusboolean——

CSS classes

.acrm-maskwrap.acrm-maskwrap.is-error.acrm-mask.acrm-mask-ghost.fl-mask.fl-mask-in.fl-mask-ghost.fl-mask.bad

Files