Components

提示

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

import * as React from "react";
import { Toast } from "radix-ui";
import "./styles.css";
const ToastDemo = () => {
const [open, setOpen] = React.useState(false);
const eventDateRef = React.useRef(new Date());
const timerRef = React.useRef(0);
React.useEffect(() => {
return () => clearTimeout(timerRef.current);
}, []);
return (
<Toast.Provider swipeDirection="right">
<button className="Button large violet" onClick={() => { setOpen(false); window.clearTimeout(timerRef.current); timerRef.current = window.setTimeout(() => { eventDateRef.current = oneWeekAway(); setOpen(true); }, 100); }} >
Add to calendar
</button>
<Toast.Root className="ToastRoot" open={open} onOpenChange={setOpen}>
<Toast.Title className="ToastTitle">Scheduled: Catch up</Toast.Title>
<Toast.Description asChild>
<time className="ToastDescription" dateTime={eventDateRef.current.toISOString()} >
{prettyDate(eventDateRef.current)}
</time>
</Toast.Description>
<Toast.Action className="ToastAction" asChild altText="Goto schedule to undo" >
<button className="Button small green">Undo</button>
</Toast.Action>
</Toast.Root>
<Toast.Viewport className="ToastViewport" />
</Toast.Provider>
);
};
function oneWeekAway(date) {
const now = new Date();
const inOneWeek = now.setDate(now.getDate() + 7);
return new Date(inOneWeek);
}
function prettyDate(date) {
return new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "short",
}).format(date);
}
export default ToastDemo;

Features

    自动关闭。

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

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

    支持通过滑动手势关闭。

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

    可以是受控或不受控的。

安装

从命令行安装组件。

npm install @radix-ui/react-toast

结构

导入组件。

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 参考

Provider

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

PropTypeDefault
duration
number
5000
label*
string
"Notification"
swipeDirection
enum
"right"
swipeThreshold
number
50

Viewport

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

PropTypeDefault
asChild
boolean
false
hotkey
string[]
["F8"]
label
string
"Notifications ({hotkey})"

Root

自动关闭的提示。应避免被保持开启以获取用户响应

PropTypeDefault
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 attributeValues
[data-state]"open" | "closed"
[data-swipe]"start" | "move" | "cancel" | "end"
[data-swipe-direction]"up" | "down" | "left" | "right"
CSS VariableDescription
--radix-toast-swipe-move-x水平滑动时提示的偏移位置
--radix-toast-swipe-move-y垂直滑动时提示的偏移位置
--radix-toast-swipe-end-x水平滑动后提示的偏移结束位置
--radix-toast-swipe-end-y垂直滑动后提示的偏移结束位置

Title

提示的可选标题。

PropTypeDefault
asChild
boolean
false

Description

提示消息。

PropTypeDefault
asChild
boolean
false

Action

一个安全可以忽略的操作,以确保用户不需要在时间限制的情况下完成任务。

当获取用户响应是必要的时,应该将一个 AlertDialog 风格化为提示并插入到视口中。

PropTypeDefault
asChild
boolean
false
altText*
string
No default value

Close

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

PropTypeDefault
asChild
boolean
false

示例

自定义热键

使用热键重写默认热键,使用来自 keycode.info 的每个键的 event.code 值。

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

自定义持续时间

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

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

重复提示

当每次用户点击按钮时提示必须出现时,使用状态渲染多个相同提示的实例(见下文)。或者,你可以抽象部分以创建自己的命令式 API

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]" 属性来动画滑动关闭手势。以下是一个示例:

// 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>
);
/* 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 要求

灵敏度

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

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

前景

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

背景

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

<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 类型以立即宣布并增加持续时间,以给予用户充足的时间。

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

关闭图标按钮

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

<Toast.Root type="foreground">
<Toast.Description>已保存!</Toast.Description>
<Toast.Close aria-label="关闭">
<span aria-hidden>×</span>
</Toast.Close>
</Toast.Root>

键盘交互

KeyDescription
F8
聚焦于提示视口。
Tab
将焦点移动到下一个可聚焦元素。
Shift + Tab
将焦点移动到上一个可聚焦元素。
Space
当焦点在 Toast.Action Toast.Close 上时,关闭提示。
Enter
当焦点在 Toast.Action Toast.Close 上时,关闭提示。
Esc
当焦点在 Toast 上时,关闭提示。

自定义 API

抽象部分

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

用法

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

实现

// 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,以允许提示复制(如果需要)。

用法

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>
);
};

实现

// 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>
))}
</>
);
});
上一篇标签 Tabs
下一篇切换 Toggle