# 提示

一个临时显示的简洁消息。

## Features

- 自动关闭。

- 在鼠标悬停、聚焦和窗口失焦时暂停关闭。

- 支持热键跳转到提示视口。

- 支持通过滑动手势关闭。

- 暴露滑动手势动画的 CSS 变量。

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

## [解剖](#解剖)

导入组件。

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

export default () => (
  <Toast.Provider>
    <Toast.Root>
      <Toast.Title />

      <Toast.Description />

      <Toast.Action />

      <Toast.Close />
    </Toast.Root>

    <Toast.Viewport />
  </Toast.Provider>
);
```

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

### [Provider](#provider)

包裹你的提示和提示视口的提供者。通常包裹整个应用程序。

| Prop             | Type     | Default          |
| ---------------- | -------- | ---------------- |
| `duration`       | `number` | `5000`           |
| `label*`         | `string` | `"Notification"` |
| `swipeDirection` | `enum`   | `"right"`        |
| `swipeThreshold` | `number` | `50`             |

### [Viewport](#viewport)

提示出现的固定区域。用户可以通过按下热键跳转到视口。确保热键对于键盘用户的可发现性是你的责任。

| Prop      | Type       | Default                      |
| --------- | ---------- | ---------------------------- |
| `asChild` | `boolean`  | `false`                      |
| `hotkey`  | `string[]` | `["F8"]`                     |
| `label`   | `string`   | `"Notifications ({hotkey})"` |

### [Root](#root)

自动关闭的提示。应避免被保持开启以[获取用户响应](/primitives/docs/components/toast#action)。

| Prop              | Type       | Default          |
| ----------------- | ---------- | ---------------- |
| `asChild`         | `boolean`  | `false`          |
| `type`            | `enum`     | `"foreground"`   |
| `duration`        | `number`   | No default value |
| `defaultOpen`     | `boolean`  | `true`           |
| `open`            | `boolean`  | No default value |
| `onOpenChange`    | `function` | No default value |
| `onEscapeKeyDown` | `function` | No default value |
| `onPause`         | `function` | No default value |
| `onResume`        | `function` | No default value |
| `onSwipeStart`    | `function` | No default value |
| `onSwipeMove`     | `function` | No default value |
| `onSwipeEnd`      | `function` | No default value |
| `onSwipeCancel`   | `function` | No default value |
| `forceMount`      | `boolean`  | No default value |

| Data attribute           | Values                                   |
| ------------------------ | ---------------------------------------- |
| `[data-state]`           | `"open" \| "closed"`                     |
| `[data-swipe]`           | `"start" \| "move" \| "cancel" \| "end"` |
| `[data-swipe-direction]` | `"up" \| "down" \| "left" \| "right"`    |

| CSS Variable                 | Description                  |
| ---------------------------- | ---------------------------- |
| `--radix-toast-swipe-move-x` | 水平滑动时提示的偏移位置     |
| `--radix-toast-swipe-move-y` | 垂直滑动时提示的偏移位置     |
| `--radix-toast-swipe-end-x`  | 水平滑动后提示的偏移结束位置 |
| `--radix-toast-swipe-end-y`  | 垂直滑动后提示的偏移结束位置 |

### [Title](#title)

提示的可选标题。

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

### [Description](#description)

提示消息。

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

### [Action](#action)

一个安全可以忽略的操作，以确保用户不需要在[时间限制](https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-required-behaviors.html)的情况下完成任务。

当获取用户响应是必要的时，应该将一个 [`AlertDialog`](/primitives/docs/components/alert-dialog) 风格化为提示并插入到视口中。

| Prop       | Type      | Default          |
| ---------- | --------- | ---------------- |
| `asChild`  | `boolean` | `false`          |
| `altText*` | `string`  | No default value |

### [Close](#close)

一个允许用户在其持续时间未结束之前关闭提示的按钮。

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

## [示例](#示例)

### [自定义热键](#自定义热键)

使用热键重写默认热键，使用来自 [keycode.info](https://keycode.info/) 的每个键的 `event.code` 值。

```jsx
<Toast.Provider>
  {/* ... */}

  <Toast.Viewport hotkey={["altKey", "KeyT"]} />
</Toast.Provider>
```

### [自定义持续时间](#自定义持续时间)

自定义提示的持续时间以覆盖提供者的值。

```jsx
<Toast.Root duration={3000}>
  <Toast.Description>已保存!</Toast.Description>
</Toast.Root>
```

### [重复提示](#重复提示)

当每次用户点击按钮时提示必须出现时，使用状态渲染多个相同提示的实例（见下文）。或者，你可以抽象部分以创建自己的[命令式 API](/primitives/docs/components/toast#imperative-api)。

```jsx
export default () => {
  const [savedCount, setSavedCount] = React.useState(0);

  return (
    <div>
      <form onSubmit={() => setSavedCount((count) => count + 1)}>
        {/* ... */}

        <button>保存</button>
      </form>

      {Array.from({ length: savedCount }).map((_, index) => (
        <Toast.Root key={index}>
          <Toast.Description>已保存!</Toast.Description>
        </Toast.Root>
      ))}
    </div>
  );
};
```

### [动画滑动手势](#动画滑动手势)

结合 `--radix-toast-swipe-move-[x|y]` 和 `--radix-toast-swipe-end-[x|y]` CSS 变量与 `data-swipe="[start|move|cancel|end]"` 属性来动画滑动关闭手势。以下是一个示例：

```jsx
// index.jsx

import { Toast } from "radix-ui";

import "./styles.css";

export default () => (
  <Toast.Provider swipeDirection="right">
    <Toast.Root className="ToastRoot">...</Toast.Root>

    <Toast.Viewport />
  </Toast.Provider>
);
```

```css
/* styles.css */

.ToastRoot[data-swipe="move"] {
  transform: translateX(var(--radix-toast-swipe-move-x));
}

.ToastRoot[data-swipe="cancel"] {
  transform: translateX(0);

  transition: transform 200ms ease-out;
}

.ToastRoot[data-swipe="end"] {
  animation: slideRight 100ms ease-out;
}

@keyframes slideRight {
  from {
    transform: translateX(var(--radix-toast-swipe-end-x));
  }

  to {
    transform: translateX(100%);
  }
}
```

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

遵循 [`aria-live` 要求](https://www.w3.org/TR/wai-aria/#aria-live)。

### [灵敏度](#灵敏度)

使用 `type` 属性控制提示的灵敏度以满足屏幕阅读器的要求。

对于用户操作生成的提示，选择 `foreground`。由后台任务生成的提示应使用 `background`。

#### 前景

前景提示会立即宣布。当前景提示出现时，辅助技术可能选择清除之前排队的消息。尝试避免在同一时间堆积不同的前景提示。

#### 背景

背景提示会在下一个机会优雅地宣布，例如，当屏幕阅读器结束阅读当前句子时。它们不会清除排队的消息，因此过度使用可能会被屏幕阅读器用户感知为延迟的用户体验，因为它们在响应用户交互时被使用。

```jsx
<Toast.Root type="foreground">

	<Toast.Description>文件成功移除。</Toast.Description>

	<Toast.Close>关闭</Toast.Close>

</Toast.Root>



<Toast.Root type="background">

	<Toast.Description>我们刚刚发布了 Radix 1.0。</Toast.Description>

	<Toast.Close>关闭</Toast.Close>

</Toast.Root>
```

### [替代动作](#替代动作)

在 `Action` 上使用 `altText` 属性告诉屏幕阅读器用户采取提示的替代方式。

你可以指引用户前往应用程序中的永久位置，以便他们可以进行操作，或者实现自己的自定义热键逻辑。如果实现后者，请使用 `foreground` 类型以立即宣布并增加持续时间，以给予用户充足的时间。

```jsx
<Toast.Root type="background">

	<Toast.Title>可用升级！</Toast.Title>

	<Toast.Description>我们刚刚发布了 Radix 1.0。</Toast.Description>

	<Toast.Action altText="前往账户设置以进行升级">

		升级

	</Toast.Action>

	<Toast.Close>关闭</Toast.Close>

</Toast.Root>



<Toast.Root type="foreground" duration={10000}>

	<Toast.Description>文件成功移除。</Toast.Description>

	<Toast.Action altText="撤销 (Alt+U)">

		撤销 <kbd>Alt</kbd>+<kbd>U</kbd>

	</Toast.Action>

	<Toast.Close>关闭</Toast.Close>

</Toast.Root>
```

### [关闭图标按钮](#关闭图标按钮)

当提供一个图标（或字体图标）时，请记得为屏幕阅读器用户正确标记它。

```jsx
<Toast.Root type="foreground">
  <Toast.Description>已保存!</Toast.Description>

  <Toast.Close aria-label="关闭">
    <span aria-hidden>×</span>
  </Toast.Close>
</Toast.Root>
```

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

| Key           | Description                                                       |
| ------------- | ----------------------------------------------------------------- |
| `F8`          | 聚焦于提示视口。                                                  |
| `Tab`         | 将焦点移动到下一个可聚焦元素。                                    |
| `Shift + Tab` | 将焦点移动到上一个可聚焦元素。                                    |
| `Space`       | 当焦点在 `Toast.Action` 或<!-- --> `Toast.Close` 上时，关闭提示。 |
| `Enter`       | 当焦点在 `Toast.Action` 或<!-- --> `Toast.Close` 上时，关闭提示。 |
| `Esc`         | 当焦点在 `Toast` 上时，关闭提示。                                 |

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

### [抽象部分](#抽象部分)

通过将原始部分抽象到自己的组件中，创建你自己的 API。

#### 用法

```jsx
import { Toast } from "./your-toast";

export default () => (
  <Toast title="可用升级" content="我们刚刚发布了 Radix 3.0!">
    <button onClick={handleUpgrade}>升级</button>
  </Toast>
);
```

#### 实现

```jsx
// your-toast.jsx

import { Toast as ToastPrimitive } from "radix-ui";

export const Toast = ({ title, content, children, ...props }) => {
  return (
    <ToastPrimitive.Root {...props}>
      {title && <ToastPrimitive.Title>{title}</ToastPrimitive.Title>}

      <ToastPrimitive.Description>{content}</ToastPrimitive.Description>

      {children && <ToastPrimitive.Action asChild>{children}</ToastPrimitive.Action>}

      <ToastPrimitive.Close aria-label="关闭">
        <span aria-hidden>×</span>
      </ToastPrimitive.Close>
    </ToastPrimitive.Root>
  );
};
```

### [命令式 API](#命令式-api)

创建你自己的命令式 API，以允许[提示复制](/primitives/docs/components/toast#duplicate-toasts)（如果需要）。

#### 用法

```jsx
import { Toast } from "./your-toast";

export default () => {
  const savedRef = React.useRef();

  return (
    <div>
      <form onSubmit={() => savedRef.current.publish()}>
        {/* ... */}

        <button>保存</button>
      </form>

      <Toast ref={savedRef}>成功保存!</Toast>
    </div>
  );
};
```

#### 实现

```jsx
// your-toast.jsx

import * as React from "react";

import { Toast as ToastPrimitive } from "radix-ui";

export const Toast = React.forwardRef((props, forwardedRef) => {
  const { children, ...toastProps } = props;

  const [count, setCount] = React.useState(0);

  React.useImperativeHandle(forwardedRef, () => ({
    publish: () => setCount((count) => count + 1),
  }));

  return (
    <>
      {Array.from({ length: count }).map((_, index) => (
        <ToastPrimitive.Root key={index} {...toastProps}>
          <ToastPrimitive.Description>{children}</ToastPrimitive.Description>

          <ToastPrimitive.Close>关闭</ToastPrimitive.Close>
        </ToastPrimitive.Root>
      ))}
    </>
  );
});
```

<!--$-->

<!--/$-->
