# 弹出框

在一个门户中通过按钮触发显示丰富的内容。

## Features

- 可以是可控或不可控的。

- 自定义侧边、对齐、偏移、碰撞处理。

- 可选择性地渲染指向箭头。

- 完全管理和自定义焦点。

- 支持模态和非模态模式。

- 自定义处理关闭和层叠行为。

## [结构](#结构)

导入所有部分并组合在一起。

```jsx
import { Popover } from "radix-ui";

export default () => (
  <Popover.Root>
    <Popover.Trigger />

    <Popover.Anchor />

    <Popover.Portal>
      <Popover.Content>
        <Popover.Close />

        <Popover.Arrow />
      </Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
```

## [API 参考](#api-参考)

### [根](#根)

包含弹出框的所有部分。

| Prop           | Type       | Default          |
| -------------- | ---------- | ---------------- |
| `defaultOpen`  | `boolean`  | No default value |
| `open`         | `boolean`  | No default value |
| `onOpenChange` | `function` | No default value |
| `modal`        | `boolean`  | `false`          |

### [触发器](#触发器)

切换弹出框的按钮。默认情况下，`Popover.Content` 将相对于触发器定位。

| Prop      | Type      | Default |
| --------- | --------- | ------- |
| `asChild` | `boolean` | `false` |

| Data attribute | Values               |
| -------------- | -------------------- |
| `[data-state]` | `"open" \| "closed"` |

### [锚](#锚)

可选元素，用于将`Popover.Content` 定位。如果未使用此部分，内容将相对于 `Popover.Trigger` 定位。

| Prop      | Type      | Default |
| --------- | --------- | ------- |
| `asChild` | `boolean` | `false` |

### [门户](#门户)

使用时，将内容部分门户到 `body`。

| Prop         | Type          | Default          |
| ------------ | ------------- | ---------------- |
| `forceMount` | `boolean`     | No default value |
| `container`  | `HTMLElement` | `document.body`  |

### [内容](#内容)

在弹出框打开时弹出的组件。

| Prop                   | Type                | Default          |
| ---------------------- | ------------------- | ---------------- |
| `asChild`              | `boolean`           | `false`          |
| `onOpenAutoFocus`      | `function`          | No default value |
| `onCloseAutoFocus`     | `function`          | No default value |
| `onEscapeKeyDown`      | `function`          | No default value |
| `onPointerDownOutside` | `function`          | No default value |
| `onFocusOutside`       | `function`          | No default value |
| `onInteractOutside`    | `function`          | No default value |
| `forceMount`           | `boolean`           | No default value |
| `side`                 | `enum`              | `"bottom"`       |
| `sideOffset`           | `number`            | `0`              |
| `align`                | `enum`              | `"center"`       |
| `alignOffset`          | `number`            | `0`              |
| `avoidCollisions`      | `boolean`           | `true`           |
| `collisionBoundary`    | `Boundary`          | `[]`             |
| `collisionPadding`     | `number \| Padding` | `0`              |
| `arrowPadding`         | `number`            | `0`              |
| `sticky`               | `enum`              | `"partial"`      |
| `hideWhenDetached`     | `boolean`           | `false`          |

| Data attribute | Values                                   |
| -------------- | ---------------------------------------- |
| `[data-state]` | `"open" \| "closed"`                     |
| `[data-side]`  | `"left" \| "right" \| "bottom" \| "top"` |
| `[data-align]` | `"start" \| "end" \| "center"`           |

| CSS Variable                               | Description                                      |
| ------------------------------------------ | ------------------------------------------------ |
| `--radix-popover-content-transform-origin` | 从内容和箭头位置/偏移量计算的 `transform-origin` |
| `--radix-popover-content-available-width`  | 触发器和边界边缘之间的剩余宽度                   |
| `--radix-popover-content-available-height` | 触发器和边界边缘之间的剩余高度                   |
| `--radix-popover-trigger-width`            | 触发器的宽度                                     |
| `--radix-popover-trigger-height`           | 触发器的高度                                     |

### [箭头](#箭头)

可选的箭头元素，与弹出框一起渲染。这可以用来帮助视觉上将锚与 `Popover.Content` 进行连接。必须在 `Popover.Content` 内部渲染。

| Prop      | Type      | Default |
| --------- | --------- | ------- |
| `asChild` | `boolean` | `false` |
| `width`   | `number`  | `10`    |
| `height`  | `number`  | `5`     |

### [关闭](#关闭)

关闭打开的弹出框的按钮。

| Prop      | Type      | Default |
| --------- | --------- | ------- |
| `asChild` | `boolean` | `false` |

## [示例](#示例)

### [限制内容大小](#限制内容大小)

您可能想要限制内容的宽度以匹配触发器的宽度。您还可能想要限制其高度以不超过视口。

我们提供了多个 CSS 自定义属性，如 `--radix-popover-trigger-width` 和 `--radix-popover-content-available-height` 来支持此功能。使用它们来限制内容的尺寸。

```jsx
// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent" sideOffset={5}>
        …
      </Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
```

```css
/* styles.css */

.PopoverContent {
  width: var(--radix-popover-trigger-width);

  max-height: var(--radix-popover-content-available-height);
}
```

### [基于原点的动画](#基于原点的动画)

我们提供了一个 CSS 自定义属性 `--radix-popover-content-transform-origin`。使用它可以根据 `side`、`sideOffset`、`align`、`alignOffset` 以及任何碰撞动画内容。

```jsx
// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent">…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
```

```css
/* styles.css */

.PopoverContent {
  transform-origin: var(--radix-popover-content-transform-origin);

  animation: scaleIn 0.5s ease-out;
}

@keyframes scaleIn {
  from {
    opacity: 0;

    transform: scale(0);
  }

  to {
    opacity: 1;

    transform: scale(1);
  }
}
```

### [基于碰撞的动画](#基于碰撞的动画)

我们提供了 `data-side` 和 `data-align` 属性。它们的值会在运行时更改以反映碰撞。使用它们可以创建与碰撞和方向相关的动画。

```jsx
// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent">…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
```

```css
/* styles.css */

.PopoverContent {
  animation-duration: 0.6s;

  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

.PopoverContent[data-side="top"] {
  animation-name: slideUp;
}

.PopoverContent[data-side="bottom"] {
  animation-name: slideDown;
}

@keyframes slideDown {
  from {
    opacity: 0;

    transform: translateY(-10px);
  }

  to {
    opacity: 1;

    transform: translateY(0);
  }
}

@keyframes slideUp {
  from {
    opacity: 0;

    transform: translateY(10px);
  }

  to {
    opacity: 1;

    transform: translateY(0);
  }
}
```

### [使用自定义锚](#使用自定义锚)

如果您不想使用触发器作为锚，可以将内容锚定到另一个元素。

```jsx
// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Anchor asChild>
      <div className="Row">
        行作为锚 <Popover.Trigger>触发器</Popover.Trigger>
      </div>
    </Popover.Anchor>

    <Popover.Portal>
      <Popover.Content>…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
```

```css
/* styles.css */

.Row {
  background-color: gainsboro;

  padding: 20px;
}
```

## [可访问性](#可访问性)

遵循 [对话框 WAI-ARIA 设计模式](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal)。

### [键盘交互](#键盘交互)

| Key           | Description                                |
| ------------- | ------------------------------------------ |
| `Space`       | 打开/关闭弹出框。                          |
| `Enter`       | 打开/关闭弹出框。                          |
| `Tab`         | 将焦点移动到下一个可聚焦元素               |
| `Shift + Tab` | 将焦点移动到上一个可聚焦元素               |
| `Esc`         | 关闭弹出框并将焦点移到 `Popover.Trigger`。 |

## [自定义 API](#自定义-api)

通过将原始部分抽象为您自己的组件来创建自己的 API。

#### 抽象箭头并设置默认配置

此示例将 `Popover.Arrow` 部分抽象出来，并设置默认的 `sideOffset` 配置。

#### 使用

```jsx
import { Popover, PopoverTrigger, PopoverContent } from "./your-popover";

export default () => (
  <Popover>
    <PopoverTrigger>弹出框触发器</PopoverTrigger>

    <PopoverContent>弹出框内容</PopoverContent>
  </Popover>
);
```

#### 实现

```jsx
// your-popover.jsx

import * as React from "react";

import { Popover as PopoverPrimitive } from "radix-ui";

export const Popover = PopoverPrimitive.Root;

export const PopoverTrigger = PopoverPrimitive.Trigger;

export const PopoverContent = React.forwardRef(({ children, ...props }, forwardedRef) => (
  <PopoverPrimitive.Portal>
    <PopoverPrimitive.Content sideOffset={5} {...props} ref={forwardedRef}>
      {children}

      <PopoverPrimitive.Arrow />
    </PopoverPrimitive.Content>
  </PopoverPrimitive.Portal>
));
```

<!--$-->

<!--/$-->
