Toast
- 自动关闭。
- 鼠标悬停、聚焦和窗口失焦时暂停关闭。
- 支持快捷键跳转到 Toast 视口。
- 支持通过滑动手势关闭。
- 公开 CSS 变量用于滑动手势动画。
- 可受控或非受控。
安装
从命令行安装此组件。
$ npm add reka-ui
结构
导入组件。
<script setup lang="ts">
import { ToastAction, ToastClose, ToastDescription, ToastProvider, ToastRoot, ToastTitle, ToastViewport } from 'reka-ui'
</script>
<template>
<ToastProvider>
<ToastRoot>
<ToastTitle />
<ToastDescription />
<ToastAction />
<ToastClose />
</ToastRoot>
<ToastViewport />
</ToastProvider>
</template>
API 参考
Provider
包裹 Toast 和 Toast 视口的提供者。通常用于包裹整个应用程序。
属性 | 默认 | 类型 |
---|---|---|
duration | 5000 | 数字 每个 Toast 应保持可见的毫秒时间。 |
label | 'Notification' | string 每个 Toast 的作者本地化标签。用于帮助屏幕阅读器用户将中断与 Toast 关联起来。 |
swipeDirection | 'right' | 'right' | 'left' | 'up' | 'down' 应关闭 Toast 的指针滑动方向。 |
swipeThreshold | 50 | 数字 滑动必须经过的像素距离,超过此距离将触发关闭。 |
视口
Toast 出现的固定区域。用户可以通过按快捷键跳转到该视口。您有责任确保键盘用户能发现此快捷键。
属性 | 默认 | 类型 |
---|---|---|
as | 'ol' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
hotkey | ['F8'] | string[] 用作键盘快捷键的按键,将焦点移动到 Toast 视口。 |
label | 'Notifications ({hotkey})' | string | ((hotkey: string) => string) Toast 视口的作者本地化标签,用于在屏幕阅读器用户导航页面地标时提供上下文。可用的 `{hotkey}` 占位符将为您替换。或者,您可以传入自定义函数来生成标签。 |
Root
自动关闭的 Toast。不应保持打开状态以获取用户响应。
属性 | 默认 | 类型 |
---|---|---|
as | 'li' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
defaultOpen | true | 布尔值 对话框初始渲染时的打开状态。当您不需要控制其打开状态时使用。 |
duration | 数字 Toast 应保持可见的毫秒时间。覆盖 `ToastProvider` 提供的值。 | |
forceMount | 布尔值 当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 | |
open | 布尔值 对话框的受控打开状态。可以绑定为 `v-model:open`。 | |
type | 'foreground' | 'foreground' | 'background' 控制 Toast 的敏感度以满足可访问性目的。 对于用户操作产生的 Toast,请选择 `foreground`。由后台任务生成的 Toast 应使用 `background`。 |
事件触发 | 载荷 |
---|---|
escapeKeyDown | [事件: KeyboardEvent] 当按下 Esc 键时调用的事件处理程序。可以通过调用 `event.preventDefault` 来阻止其默认行为。 |
pause | [] 当关闭计时器暂停时调用的事件处理程序。当鼠标指针移到视口上方、视口获得焦点或窗口失焦时会发生此情况。 |
resume | [] 当关闭计时器恢复时调用的事件处理程序。当鼠标指针移离视口、视口失去焦点或窗口获得焦点时会发生此情况。 |
swipeCancel | [event: SwipeEvent] 当滑动交互被取消时调用的事件处理程序。可以通过调用 `event.preventDefault` 来阻止其默认行为。 |
swipeEnd | [event: SwipeEvent] 在滑动交互结束时调用的事件处理程序。可以通过调用 `event.preventDefault` 来阻止其默认行为。 |
swipeMove | [event: SwipeEvent] 在滑动交互期间调用的事件处理程序。可以通过调用 `event.preventDefault` 来阻止其默认行为。 |
swipeStart | [event: SwipeEvent] 当开始滑动交互时调用的事件处理程序。可以通过调用 `event.preventDefault` 来阻止其默认行为。 |
update:open | [value: boolean] 当打开状态改变时调用的事件处理程序 |
插槽(默认) | 载荷 |
---|---|
open | 布尔值 当前打开状态 |
remaining | 数字 剩余时间(毫秒) |
duration | 数字 Toast 将保持可见的总时间(毫秒) |
数据属性 | 值 |
---|---|
[data-state] | "打开" | "关闭" |
[data-swipe] | "start" | "move" | "cancel" | "end" |
[data-swipe-direction] | "up" | "down" | "left" | "right" |
CSS 变量 | 描述 |
---|---|
--reka-toast-swipe-move-x | Toast 水平滑动时的偏移位置 |
--reka-toast-swipe-move-y | Toast 垂直滑动时的偏移位置 |
--reka-toast-swipe-end-x | Toast 水平滑动后的结束偏移位置 |
--reka-toast-swipe-end-y | Toast 垂直滑动后的结束偏移位置 |
Portal
使用时,将内容部分传送到 body
中。
属性 | 默认 | 类型 |
---|---|---|
defer | 布尔值 延迟解析 Teleport 目标,直到应用程序的其他部分挂载(需要 Vue 3.5.0+) | |
disabled | 布尔值 禁用传送并内联渲染组件 | |
forceMount | 布尔值 当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 | |
to | 字符串 | HTMLElement Vue 原生传送组件属性 |
标题
Toast 的可选标题
属性 | 默认 | 类型 |
---|---|---|
as | 'div' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
描述
Toast 消息。
属性 | 默认 | 类型 |
---|---|---|
as | 'div' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
操作
一个可以安全忽略的操作,以确保用户不会因为时间限制而被迫完成具有意外副作用的任务。
当需要获取用户响应时,请将一个样式为 Toast 的 "AlertDialog" 传送至视口中。
属性 | 默认 | 类型 |
---|---|---|
altText* | string 执行此操作的替代方式的简短描述。适用于无法轻松/快速导航到按钮的屏幕阅读器用户。 | |
as | 'div' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
关闭
一个允许用户在 Toast 持续时间结束前关闭它的按钮。
属性 | 默认 | 类型 |
---|---|---|
as | 'button' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
示例
自定义快捷键
使用 keycode.info 中每个键的 `event.code` 值来覆盖默认快捷键。
<template>
<ToastProvider>
...
<ToastViewport :hotkey="['altKey', 'KeyT']" />
</ToastProvider>
</template>
自定义持续时间
自定义 Toast 的持续时间以覆盖 Provider 值。
<template>
<ToastRoot :duration="3000">
<ToastDescription>Saved!</ToastDescription>
</ToastRoot>
</template>
重复 Toast
当用户每次点击按钮时都必须显示 Toast 时,请使用状态来渲染同一 Toast 的多个实例(见下文)。或者,您可以抽象出部分内容来创建自己的命令式 API。
<template>
<div>
<form @submit="count++">
...
<button>save</button>
</form>
<ToastRoot v-for="(_, index) in count" :key="index">
<ToastDescription>Saved!</ToastDescription>
</ToastRoot>
</div>
</template>
动画滑动手势
将 `--reka-toast-swipe-move-[x|y]` 和 `--reka-toast-swipe-end-[x|y]` CSS 变量与 `data-swipe="[start|move|cancel|end]"` 属性结合使用,以实现滑动关闭手势的动画效果。示例如下:
<template>
<ToastProvider swipe-direction="right">
<ToastRoot class="ToastRoot">
...
</ToastRoot>
<ToastViewport />
</ToastProvider>
</template>
/* styles.css */
.ToastRoot[data-swipe='move'] {
transform: translateX(var(--reka-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(--reka-toast-swipe-end-x));
}
to {
transform: translateX(100%);
}
}
可访问性
遵循 `aria-live` 要求。
敏感度
使用 `type` 属性控制 Toast 对屏幕阅读器的敏感度。
对于用户操作产生的 Toast,请选择 `foreground`。由后台任务生成的 Toast 应使用 `background`。
前景
前景 Toast 会立即宣布。当前景 Toast 出现时,辅助技术可能会选择清除之前排队的消息。尽量避免同时堆叠不同的前景 Toast。
背景
背景 Toast 会在下一个合适的时机宣布,例如,当屏幕阅读器完成阅读当前句子时。它们不会清除排队的消息,因此在响应用户交互时过度使用它们可能会被屏幕阅读器用户视为迟滞的用户体验。
<template>
<ToastRoot type="foreground">
<ToastDescription>File removed successfully.</ToastDescription>
<ToastClose>Dismiss</ToastClose>
</ToastRoot>
<ToastRoot type="background">
<ToastDescription>We've just released Reka UI 2.0.</ToastDescription>
<ToastClose>Dismiss</ToastClose>
</ToastRoot>
</template>
替代操作
在 `Action` 上使用 `altText` 属性来指示屏幕阅读器用户执行 Toast 的替代方式。
您可以引导用户到应用程序中一个固定的位置执行操作,或者实现您自己的自定义快捷键逻辑。如果实现后者,请使用 `foreground` 类型立即宣布并增加持续时间以给用户充足的时间。
<template>
<ToastRoot type="background">
<ToastTitle>Upgrade Available!</ToastTitle>
<ToastDescription>We've just released Reka UI 2.0.</ToastDescription>
<ToastAction alt-text="Goto account settings to upgrade">
Upgrade
</ToastAction>
<ToastClose>Dismiss</ToastClose>
</ToastRoot>
<ToastRoot type="foreground" :duration="10000">
<ToastDescription>File removed successfully.</ToastDescription>
<ToastAction alt-text="Undo (Alt+U)">
Undo <kbd>Alt</kbd>+<kbd>U</kbd>
</ToastAction>
<ToastClose>Dismiss</ToastClose>
</ToastRoot>
</template>
关闭图标按钮
提供图标(或字体图标)时,请记住为屏幕阅读器用户正确标记它。
<template>
<ToastRoot type="foreground">
<ToastDescription>Saved!</ToastDescription>
<ToastClose aria-label="Close">
<span aria-hidden="true">×</span>
</ToastClose>
</ToastRoot>
</template>
键盘交互
键 | 描述 |
---|---|
F8 | 聚焦 Toast 视口。 |
Tab | 将焦点移动到下一个可聚焦元素。 |
Shift + Tab | 将焦点移至上一个可聚焦元素。 |
空格键 |
当焦点位于 `ToastAction` 或 `ToastClose` 上时,关闭 Toast |
回车键 |
当焦点位于 `ToastAction` 或 `ToastClose` 上时,关闭 Toast |
Esc键 |
当焦点位于 `Toast` 上时,关闭 Toast |
自定义 API
抽象部分
通过将原始部件抽象到您自己的组件中来创建您自己的 API。
用法
<script setup lang="ts">
import Toast from './your-toast.vue'
</script>
<template>
<Toast
title="Upgrade available"
content="We've just released Radix 3.0!"
>
<button @click="handleUpgrade">
Upgrade
</button>
</Toast>
</template>
实现
// your-toast.vue
<script setup lang="ts">
import { ToastAction, ToastClose, ToastDescription, ToastRoot, ToastTitle } from 'reka-ui'
defineProps<{
title: string
content: string
}>()
</script>
<template>
<ToastRoot>
<ToastTitle v-if="title">
{{ title }}
</ToastTitle>
<ToastDescription v-if="content">
{{ content }}
</ToastDescription>
<ToastAction
as-child
alt-text="toast"
>
<slot />
</ToastAction>
<ToastClose aria-label="Close">
<span aria-hidden="true">×</span>
</ToastClose>
</ToastRoot>
</template>
命令式 API
如果需要,可以创建自己的命令式 API 以允许Toast 重复。
用法
<script setup lang="ts">
import Toast from './your-toast.vue'
const savedRef = ref<InstanceType<typeof Toast>>()
</script>
<template>
<div>
<form @submit="savedRef.publish()">
...
</form>
<Toast ref="savedRef">
Saved successfully!
</Toast>
</div>
</template>
实现
// your-toast.vue
<script setup lang="ts">
import { ToastClose, ToastDescription, ToastRoot, ToastTitle } from 'reka-ui'
import { ref } from 'vue'
const count = ref(0)
function publish() {
count.value++
}
defineExpose({
publish
})
</script>
<template>
<ToastRoot
v-for="index in count"
:key="index"
>
<ToastDescription>
<slot />
</ToastDescription>
<ToastClose>Dismiss</ToastClose>
</ToastRoot>
</template>