# OtpInput

Field mã một lần hiện thành các slot bằng nhau, cho mã xác minh email và SMS. Đây là một input thật với autocomplete one-time-code, không phải một input cho mỗi chữ số, nên nền tảng điền được mã từ một tin nhắn (DECISIONS 2026-10-15). Các slot là một lớp trực quan; input mang giá trị, việc điền và mọi hành vi bàn phím. Dùng Input cho văn bản tự do.

**Phụ thuộc:** npm: 1 trực tiếp, 2 tổng; registry: 2 trực tiếp, 2 tổng.

## Cách dùng

### Khi nào dùng

- Nhập một mã xác minh ngắn từ email hoặc SMS, kiểm từng chữ số hoặc cả mã.
- Một mã mà nền tảng điền được từ tin nhắn, qua autocomplete one-time-code của input duy nhất.

### Khi nào không dùng

- Một mã định danh dài hơn mà người dùng sao chép trọn vẹn, chẳng hạn API key hoặc số thẻ. Dùng Input.
- Một số lượng mà người dùng chỉnh. Dùng NumberInput.
- Văn bản tự do. Dùng Input.

## Biến thể

### pattern

numeric chỉ nhận chữ số (inputmode numeric); alphanumeric cũng nhận chữ cái.

Giá trị: `numeric`, `alphanumeric`. Mặc định: `numeric`.

### size

Kích thước slot và khoảng cách. lg theo mặc định (44px trên cảm ứng), md trong form đặc, xl cho bước xác minh hero. Không có sm: một ô mã 28px quá nhỏ để đọc và chạm.

Giá trị: `md`, `lg`, `xl`. Mặc định: `lg`.

## Ví dụ

### Mã xác minh

Mã sáu chữ số, được xác minh khi đủ.

```tsx
<Field>
  <FieldLabel>Mã xác minh</FieldLabel>
  <FieldControl>
    <OtpInput length={6} onComplete={verifyCode} />
  </FieldControl>
</Field>
```

### Mã bị từ chối

```tsx
<OtpInput length={6} defaultValue="123456" invalid />
```

### Bốn chữ số, ưu tiên cảm ứng

```tsx
<OtpInput length={4} size="xl" />
```

## Nên và không nên

### Nên

- Dùng bên trong Field để label và lỗi được nối, và đặt invalid khi server từ chối mã.
- Xác minh trong onComplete, và giữ field enabled để người dùng sửa mã bị từ chối.
- Giữ lg mặc định, để mỗi slot là 44px hoặc lớn hơn trên cảm ứng; dùng xl cho bước xác minh hero.

### Không nên

- Đừng dựng một input cho mỗi chữ số; việc nền tảng điền từ tin nhắn xác minh cần input duy nhất.
- Đừng dùng cho mã định danh mà người dùng sao chép trọn vẹn, chẳng hạn API key; dùng Input.
- Đừng autofocus trên các trang mà người dùng phải đọc hướng dẫn trước.

## Props

| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
| --- | --- | --- | --- | --- |
| `length` | `number` |  | `6` | Số slot. Các mã phổ biến nhất là 4 và 6 chữ số. |
| `pattern` | `enum` | `numeric`, `alphanumeric` | `numeric` | numeric chỉ nhận chữ số (inputmode numeric); alphanumeric cũng nhận chữ cái. |
| `value` | `string` |  |  | Giá trị mã controlled. Ký tự ngoài pattern và phần vượt quá length bị lọc. |
| `defaultValue` | `string` |  |  | Mã ban đầu cho input uncontrolled. |
| `onValueChange` | `function` |  |  | Được gọi với mã đã lọc trên mỗi lần đổi. |
| `onComplete` | `function` |  |  | Được gọi một lần mỗi khi mã đạt đủ độ dài trong lúc người dùng gõ, để việc xác minh có thể bắt đầu. |
| `size` | `enum` | `md`, `lg`, `xl` | `lg` | Kích thước slot và khoảng cách. lg theo mặc định (44px trên cảm ứng), md trong form đặc, xl cho bước xác minh hero. Không có sm: một ô mã 28px quá nhỏ để đọc và chạm. |
| `disabled` | `boolean` |  | `false` | Làm input disabled; các slot nhận nền và chữ disabled. |
| `invalid` | `boolean` |  | `false` | Mọi slot nhận viền danger, input đặt aria-invalid. |
| `required` | `boolean` |  | `false` | Thuộc tính required chuẩn trên input. |
| `name` | `string` |  |  | Tên field của form, được form bao ngoài gửi đi. |

### Slot

| Tên | Bắt buộc | Mô tả |
| --- | --- | --- |
| `value` | không | Giá trị mã controlled hoặc uncontrolled, đã lọc theo pattern và length. |

## Cấu tạo

| Phần | Mô tả |
| --- | --- |
| `root` | Dải: một hàng flex relative, mang khoảng cách của size và vẽ vòng focus trên slot active (has-input-focus-visible). |
| `slot` | Một ô mã vuông, kích thước theo size-element để các size cảm ứng được áp dụng. Hiện ký tự, hoặc một thanh caret accent khi đó là slot kế tiếp cần điền trong lúc focus. Khi đang focus, slot active (slot kế tiếp cần điền, hoặc slot cuối khi mã đã đầy) mang data-active và vòng focus. aria-hidden. |
| `caret` | Thanh accent trong slot kế tiếp cần điền khi đang focus, thay cho caret của input trong suốt. |
| `input` | Input văn bản duy nhất phủ lên dải: chữ và caret trong suốt trên các slot, autocomplete one-time-code, inputmode numeric hoặc text. Mang value, name, disabled, required và aria-invalid. |

## Khả năng truy cập

Role: `textbox`

| Phím | Hành động |
| --- | --- |
| `0-9 / A-Z` | Điền slot trống kế tiếp. Ký tự ngoài pattern bị bỏ. |
| `Backspace` | Xóa ký tự nhập cuối cùng. |
| `Left / Right` | Di chuyển caret văn bản bên trong mã, hành vi input gốc. |
| `Paste` | Dán mã; ký tự ngoài pattern bị bỏ, phần còn lại điền từ đầu. |

- Các slot là aria-hidden; trình đọc màn hình thông báo một textbox với cả mã, không phải một vùng cho mỗi chữ số.
- Việc nền tảng điền từ SMS xác minh hoạt động vì trình duyệt thấy một input duy nhất với autocomplete one-time-code.
- Dùng bên trong Field để label và lỗi được nối. aria-invalid được đặt trên input khi invalid là true.

## Cài đặt

```bash
npx werewolf-ui add otp-input
```

Chạy `npx werewolf-ui init` một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem [Cài đặt](https://www.werewolf-ui.dev/vi/docs/installation.md) để biết yêu cầu và cách cài qua registry.
