# Rating

Chọn hoặc hiện một điểm trên thang sao ngắn (đánh giá, phản hồi), theo bước nguyên hoặc nửa. Khi là input thì đó là radio group của các giá trị được phép; khi là display (readOnly) thì đó là một image với giá trị chữ.

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

## Cách dùng

### Khi nào dùng

- Thu một điểm ngắn, như đánh giá sản phẩm, theo bước sao nguyên hoặc nửa.
- Hiện một điểm read-only với giá trị accessible cho công nghệ hỗ trợ.

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

- Thu hơn mười lựa chọn. Dùng Slider hoặc NumberInput.
- Hỏi câu có hoặc không. Dùng checkbox hoặc Switch.

## Biến thể

### size

Cỡ sao: 16px ở sm và md, 20px ở lg. Mỗi sao chiếm một ô vuông size-target-min, kể cả trên cảm ứng.

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

## Ví dụ

### Nhập

```tsx
<Field id="rating">
  <FieldLabel id="rating-label">Đánh giá của bạn</FieldLabel>
  <FieldControl>
    <Rating aria-labelledby="rating-label" defaultValue={0} />
  </FieldControl>
</Field>
```

### Hiển thị

```tsx
<Rating value={4} readOnly />
```

### Sao nửa

```tsx
<Rating aria-label="Đánh giá của bạn" defaultValue={3.5} allowHalf />
```

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

### Nên

- Hiện số cạnh rating readOnly (4.6) và số lượt đánh giá.
- Dùng Field cho label và lỗi.

### Không nên

- Đừng dùng cho hơn 10 mức; dùng Slider hoặc NumberInput.
- Đừng dùng cho lựa chọn có hoặc không.

## Props

| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
| --- | --- | --- | --- | --- |
| `value` | `number` |  |  | Giá trị đã chọn khi controlled: 0 là chưa chọn, rồi bước nguyên, hoặc bước nửa khi có allowHalf. |
| `defaultValue` | `number` |  | `0` | Giá trị ban đầu khi uncontrolled. |
| `onValueChange` | `function` |  |  | Được gọi với giá trị mới. |
| `max` | `number` |  | `5` | Số sao. |
| `size` | `enum` | `sm`, `md`, `lg` | `md` | Cỡ sao: 16px ở sm và md, 20px ở lg. Mỗi sao chiếm một ô vuông size-target-min, kể cả trên cảm ứng. |
| `readOnly` | `boolean` |  | `false` | Chỉ để hiện: role img, không focus, không hover. |
| `disabled` | `boolean` |  | `false` | Diện mạo disabled, không tương tác. |
| `name` | `string` |  |  | Tên trường trong form. |
| `required` | `boolean` |  | `false` | Phải chọn ít nhất 1. |
| `itemLabel` | `(value: number, max: number) => string` |  |  | Tên accessible của mỗi giá trị, số được format theo locale. Mặc định EN `{value} of {max} stars` (3.5 of 5 stars); VI `{value} trên {max} sao` (3,5 trên 5 sao). |
| `valueText` | `(value: number, max: number) => string` |  |  | Tên accessible khi readOnly, số được format theo locale. Mặc định EN `Rated {value} of {max} stars`; VI `Đánh giá {value} trên {max} sao`; với 0 thì EN `No rating yet`, VI `Chưa có đánh giá`. |
| `allowHalf` | `boolean` |  | `false` | Cho phép giá trị nửa (0.5, 1.5, ...). Phím mũi tên và con trỏ khi đó đi theo bước 0.5. |
| `locale` | `string` |  |  | Locale BCP 47 cho các số trong label. Mặc định theo ngôn ngữ của document. |

## Cấu tạo

| Phần | Mô tả |
| --- | --- |
| `root` | Radio group (hoặc img khi readOnly), một hàng sao. |
| `item` | Slot hình của một sao. Ở chế độ bước nửa, slot chứa hai phương án radio. |
| `glyph` | Icon sao: outline khi tắt, Filled khi bật, và với giá trị nửa thì một icon có nửa đầu được tô (fill bị cắt), từ bên phải trong RTL. Phần tô dùng cùng màu với outline (currentColor). |
| `hitArea` | Mỗi sao chiếm một ô vuông size-target-min. Ở chế độ bước nửa, hai phương án radio của sao chia ô thành hai vùng con trỏ kề nhau, mỗi nửa một vùng. |

## Khả năng truy cập

Role: `radiogroup`

| Phím | Hành động |
| --- | --- |
| `Tab` | Đưa focus tới giá trị checked, hoặc giá trị thấp nhất khi trống. |
| `ArrowRight / ArrowUp` | Giá trị kế: cộng 1, hoặc cộng 0.5 khi có allowHalf (trái và phải đảo trong RTL). |
| `ArrowLeft / ArrowDown` | Giá trị trước: trừ 1, hoặc trừ 0.5 khi có allowHalf. |
| `Home / End` | Giá trị thấp nhất hoặc cao nhất. |

- radiogroup tương tác phải có aria-label hoặc aria-labelledby.
- Mỗi giá trị được phép là một radio do itemLabel đặt tên, với số format theo locale: 3.5 of 5 stars (EN), 3,5 trên 5 sao (VI).
- Sao nửa: mỗi sao một icon sao. Với giá trị nửa thì nửa đầu tô bằng color-warning và nửa sau giữ outline; trong RTL phần tô bắt đầu từ bên phải. Không bao giờ ghép hai icon nửa.
- Chọn lại giá trị hiện tại thì giữ nguyên. Không đưa về 0 khi nhấn lần nữa; ứng dụng tự có hành động xóa nếu cần.
- readOnly render role img với aria-label từ valueText thay cho radio group, và không có gì focus được.
- Root đặt data-read-only khi readOnly; các binding read-only đọc attribute đó (stateAttributes), không bao giờ dùng CSS :read-only, vì selector đó khớp mọi phần tử không sửa được.
- Hover xem trước giá trị đến chỗ con trỏ (đến nửa dưới con trỏ khi có allowHalf) mà không đổi giá trị.
- Màu không phải tín hiệu duy nhất: hình đã tô khác với outline.

## Cài đặt

```bash
npx werewolf-ui add rating
```

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.
