# Popover

Panel nhỏ neo vào button mở nó, cho nội dung người dùng thao tác mà không rời trang: một form ngắn, bộ lọc, một link chia sẻ. Panel nằm trên bề mặt raised, có border và shadow medium, và đóng khi Escape, khi nhấn ra ngoài hoặc khi nhấn trigger.

**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

- Vài control cạnh button mở chúng: một link chia sẻ, bộ lọc, một thiết lập nhanh.
- Nội dung người dùng thao tác mà không rời trang, đóng khi Escape hoặc nhấn ra ngoài.

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

- Một danh sách hành động. Dùng DropdownMenu.
- Một gợi ý khi hover hoặc focus, không có control. Dùng Tooltip.
- Một form dài, hoặc input người dùng không được mất khi nhấn ra ngoài. Dùng Dialog.

## Biến thể

### side

Đặt trên PopoverContent. Cạnh của trigger mà panel mở về. Lật khi không đủ chỗ.

Giá trị: `top`, `right`, `bottom`, `left`. Mặc định: `bottom`.

### align

Đặt trên PopoverContent. Cách căn với trigger dọc theo cạnh đó. Dùng start hoặc end khi trigger nằm gần một mép.

Giá trị: `start`, `center`, `end`. Mặc định: `center`.

## Ví dụ

### Link chia sẻ

```tsx
<Popover>
  <PopoverTrigger asChild>
    <Button variant="secondary">Chia sẻ</Button>
  </PopoverTrigger>
  <PopoverContent aria-labelledby="share-heading" className="flex flex-col gap-md">
    <p id="share-heading" className="text-sm font-medium">Chia sẻ dự án này</p>
    <Field>
      <FieldLabel>Link</FieldLabel>
      <FieldControl>
        <Input readOnly defaultValue="https://example.com/p/42" />
      </FieldControl>
    </Field>
    <PopoverClose asChild>
      <Button>Xong</Button>
    </PopoverClose>
  </PopoverContent>
</Popover>
```

### Sát một mép

align end giữ panel trên màn hình khi trigger nằm ở bên phải của một toolbar.

```tsx
<Popover>
  <PopoverTrigger asChild>
    <Button variant="secondary" iconOnly aria-label="Bộ lọc">
      <IconList size={16} />
    </Button>
  </PopoverTrigger>
  <PopoverContent align="end" aria-label="Bộ lọc" className="flex flex-col gap-md">
    <Checkbox label="Chỉ dự án của tôi" />
    <Checkbox label="Hiện đã lưu trữ" />
  </PopoverContent>
</Popover>
```

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

### Nên

- Giữ ngắn: vài control, một link để sao chép, một bộ lọc.
- Cho panel một heading, hoặc một aria-label khi không có heading.
- Áp thay đổi ngay, hoặc đóng bằng một button PopoverClose chẳng hạn Done.

### Không nên

- Đừng đặt một danh sách hành động vào panel; dùng DropdownMenu.
- Đừng dùng cho form dài hoặc thứ người dùng không được mất khi nhấn ra ngoài; dùng Dialog.
- Đừng mở khi hover; dùng Tooltip cho một gợi ý.

## Props

| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
| --- | --- | --- | --- | --- |
| `open` | `boolean` |  |  | Đặt trên Popover. Có đang mở hay không, khi bạn điều khiển. Ghép với onOpenChange. |
| `defaultOpen` | `boolean` |  | `false` | Đặt trên Popover. Mở ở lần render đầu, khi bạn không điều khiển. |
| `onOpenChange` | `function` |  |  | Đặt trên Popover. Được gọi với true hoặc false khi mở hoặc đóng (trigger, Escape, nhấn ra ngoài, PopoverClose). |
| `modal` | `boolean` |  | `false` | Đặt trên Popover. true giữ focus bên trong và ẩn phần còn lại của trang khỏi công nghệ hỗ trợ cho đến khi đóng. Để false cho panel mà người dùng làm việc ngay cạnh. |
| `side` | `enum` | `top`, `right`, `bottom`, `left` | `bottom` | Đặt trên PopoverContent. Cạnh của trigger mà panel mở về. Lật khi không đủ chỗ. |
| `align` | `enum` | `start`, `center`, `end` | `center` | Đặt trên PopoverContent. Cách căn với trigger dọc theo cạnh đó. Dùng start hoặc end khi trigger nằm gần một mép. |
| `sideOffset` | `number` |  | `4` | Đặt trên PopoverContent. Khoảng cách giữa trigger và panel, tính bằng px. 4 là spacing-xs. |

### Slot

| Tên | Bắt buộc | Mô tả |
| --- | --- | --- |
| `trigger` | có | PopoverTrigger với asChild bọc một Button. |
| `content` | có | PopoverContent với một heading hoặc một aria-label, và các control. |

## Cấu tạo

| Phần | Mô tả |
| --- | --- |
| `root` | Popover. Giữ trạng thái mở; bản thân không render gì. |
| `trigger` | PopoverTrigger: button mở panel, thường là một Button với asChild. |
| `content` | PopoverContent: panel. Nền raised, border, radius-container, shadow-med, px-lg py-md, rộng 288px và không bao giờ rộng hơn màn hình trừ spacing-sm mỗi bên. |
| `anchor` | PopoverAnchor (tùy chọn): một phần tử khác để định vị panel theo. |
| `close` | PopoverClose (tùy chọn): một button bên trong để đóng, chẳng hạn Done. |

## Khả năng truy cập

Role: `dialog`

| Phím | Hành động |
| --- | --- |
| `Space, Enter` | Trên trigger: mở hoặc đóng popover. Focus chuyển tới phần tử focus được đầu tiên bên trong, hoặc tới panel. |
| `Tab` | Đi qua các control bên trong. Với modal, focus ở lại bên trong; nếu không, focus có thể rời, và popover vẫn mở. |
| `Escape` | Đóng popover và trả focus về trigger. |

- Trigger nhận aria-expanded và aria-controls, và panel là một dialog. Đặt tên bằng aria-label hoặc aria-labelledby khi không có heading.
- Nhấn ra ngoài thì đóng. Đặt những gì người dùng không được mất (input chưa lưu) vào Dialog.
- Không dùng cho danh sách hành động: đó là DropdownMenu, có ngữ nghĩa menu và các phím mũi tên.
- Không dùng cho gợi ý khi hover: đó là Tooltip, không chứa control.

## Cài đặt

```bash
npx werewolf-ui add popover
```

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.
