Toast 轻提示
向用户展示临时通知与消息,支持自动消失与可定制的放置位置。
用法
import { Toast, toast } from '@lenso/ui';此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 toast-default (Apache-2.0).import { Persons } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function InvitationQueue() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "You have been invited to join a team", description: "Bob sent you an invitation to join HeroUI team", type: "default", data: { indicator: <Persons />, actionStyle: "tertiary" }, actionProps: { children: "Dismiss", onClick: () => manager.close(id) }, }); }} > Show toast </Button> </div> <Notifications /> </> );}
export function Default() { return ( <Toast.Provider> <InvitationQueue /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
组件结构
<Toast.Provider> <Toast> <Toast.Indicator /> <Toast.Content> <Toast.Title /> <Toast.Description /> </Toast.Content> <Toast.ActionButton /> <Toast.CloseButton /> </Toast></Toast.Provider>示例
变体
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 variants, Apache-2.0.import { HardDrive, Persons } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function Messages() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have been invited to join a team", description: "Bob sent you an invitation to join HeroUI team", type: "default", data: { indicator: <Persons />, actionStyle: "tertiary" }, actionProps: { children: "Dismiss", onClick: () => manager.close(id) }, }); }} > Default toast </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "You have 2 credits left", description: "Get a paid plan for more credits", type: "accent", actionProps: { children: "Upgrade", onClick: () => manager.close(id) }, }); }} > Accent toast </Button> <Button xstyle={styles.successText} size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have upgraded your plan", description: "You can continue using HeroUI Chat", type: "success", data: { actionStyle: "success" }, actionProps: { children: "Billing", onClick: () => manager.close(id) }, }); }} > Success toast </Button> <Button xstyle={styles.warningText} size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have no credits left", description: "Upgrade to a paid plan to continue", type: "warning", data: { actionStyle: "warning" }, actionProps: { children: "Upgrade", onClick: () => manager.close(id) }, }); }} > Warning toast </Button> <Button size="sm" variant="danger-soft" onClick={() => { const id = manager.add({ title: "Storage is full", description: "Remove files to release space. Adding more text to demonstrate longer content display", type: "danger", data: { indicator: <HardDrive />, actionStyle: "danger" }, actionProps: { children: "Remove", onClick: () => manager.close(id) }, }); }} > Danger toast </Button> </div> </div> <Notifications /> </> );}
export function Variants() { return ( <Toast.Provider> <Messages /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
放置位置
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 placements, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const placements = ["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"] as const;const placementQueues = { "top-start": Toast.createToastManager(), top: Toast.createToastManager(), "top-end": Toast.createToastManager(), "bottom-start": Toast.createToastManager(), bottom: Toast.createToastManager(), "bottom-end": Toast.createToastManager(),};
export function Placements() { return ( <div {...stylex.props(styles.placements)}> {placements.map((placement) => ( <Toast.Provider key={placement} toastManager={placementQueues[placement]} limit={3}> <Notifications placement={placement} /> </Toast.Provider> ))} <div {...stylex.props(styles.placementButtons)}> {placements.map((placement) => ( <Button key={placement} size="sm" variant="secondary" onClick={() => placementQueues[placement].add({ description: "Event has been created", title: "Event created", type: "default", }) } > {placement.replace("-", " ")} </Button> ))} </div> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
展开堆叠
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 expanded, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const queue = Toast.createToastManager();
export function Expanded() { return ( <div {...stylex.props(styles.frame)}> <Toast.Provider toastManager={queue}> <Notifications expanded aria-label="Expanded notifications" /> </Toast.Provider> <Button size="sm" variant="secondary" onClick={() => { queue.add({ title: "Simple message", type: "default" }); setTimeout(() => queue.add({ title: "Operation completed", type: "success" }), 400); setTimeout(() => queue.add({ title: "New update available", type: "accent" }), 800); }} > Show 3 toasts </Button> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
简单 Toast
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 simple, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
function Messages() { const manager = Toast.useToastManager(); return ( <> <div {...stylex.props(styles.frame)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Simple message", type: "default" })} > Default </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Operation completed", type: "success" })} > Success </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "New update available", type: "accent" })} > Info </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Please check your settings", type: "warning" })} > Warning </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Something went wrong", type: "danger" })} > Error </Button> </div> </div> <Notifications /> </> );}
export function Simple() { return ( <Toast.Provider> <Messages /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
自定义指示器
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 custom-indicator, Apache-2.0.import { Star } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function Message() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Custom icon indicator", data: { indicator: <Star /> } }) } > Custom indicator </Button> </div> <Notifications /> </> );}
export function CustomIndicator() { return ( <Toast.Provider> <Message /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
自定义 Toast 渲染
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 custom-toast, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, CloseIcon, Toast } from "@lenso/ui";import { styles } from "./_shared";
function CustomLayout() { const manager = Toast.useToastManager(); return ( <> <Toast.Portal> <Toast.Viewport placement="bottom"> {manager.toasts.map((item) => ( <Toast key={item.id} toast={item} xstyle={styles.customRoot}> <Toast.Content> <div {...stylex.props(styles.customRow)}> <Toast.Indicator xstyle={styles.accent} /> <div {...stylex.props(styles.customText)}> {item.title && <Toast.Title xstyle={styles.accent} />} {item.description && <Toast.Description />} </div> </div> </Toast.Content> <Toast.Close xstyle={styles.customClose} aria-label="Close notification"> <CloseIcon {...stylex.props(styles.customCloseIcon)} /> </Toast.Close> </Toast> ))} </Toast.Viewport> </Toast.Portal> <Button size="sm" variant="secondary" onClick={() => manager.add({ description: "This uses a custom render function", title: "Custom layout toast", type: "default", }) } > Custom toast </Button> </> );}
export function CustomToast() { return ( <div {...stylex.props(styles.frame)}> <Toast.Provider> <CustomLayout /> </Toast.Provider> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Promise 与加载中
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 promise, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const uploadFile = (): Promise<{ filename: string; size: number }> => new Promise((resolve) => { setTimeout(() => resolve({ filename: "document.pdf", size: 1024 }), 2000); });const createEvent = (): Promise<never> => new Promise((_, reject) => { setTimeout(() => reject(new Error("Network error. Please try again.")), 2000); });const saveData = (): Promise<{ count: number }> => new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) resolve({ count: 42 }); else reject(new Error("Failed to save data")); }, 2000); });const fetchUser = (): Promise<{ name: string; email: string }> => new Promise((resolve) => { setTimeout(() => resolve({ email: "[email protected]", name: "John Doe" }), 2000); });
function Workflows() { const manager = Toast.useToastManager(); return ( <> <div {...stylex.props(styles.frame, styles.promise)}> <div {...stylex.props(styles.section)}> <div {...stylex.props(styles.center)}> <h3 {...stylex.props(styles.heading)}>Using toast.promise()</h3> <p {...stylex.props(styles.help)}> Automatically handles loading, success, and error states </p> </div> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(uploadFile(), { error: { title: "Failed to upload file" }, loading: { title: "Uploading file..." }, success: (data) => ({ title: `File ${data.filename} uploaded (${data.size}KB)`, }), }) .catch(() => {}); }} > Upload file </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(createEvent(), { error: (error: Error) => ({ title: error.message }), loading: { title: "Creating event..." }, success: { title: "Event created" }, }) .catch(() => {}); }} > Create event (error) </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(saveData(), { error: (error: Error) => ({ title: error.message }), loading: { title: "Saving changes..." }, success: (data) => ({ title: `Saved ${data.count} items` }), }) .catch(() => {}); }} > Save data (random) </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(fetchUser(), { error: { title: "Failed to fetch user" }, loading: { title: "Loading user..." }, success: (data) => ({ title: `Welcome back, ${data.name}!` }), }) .catch(() => {}); }} > Fetch user </Button> </div> </div> <div {...stylex.props(styles.section)}> <div {...stylex.props(styles.center)}> <h3 {...stylex.props(styles.heading)}>Manual Loading State</h3> <p {...stylex.props(styles.help)}>Manually control loading state with isLoading prop</p> </div> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Uploading file...", description: "Please wait while we upload your file", type: "loading", timeout: 0, }); setTimeout( () => manager.update(id, { title: "File uploaded", description: "Your file has been uploaded successfully", type: "success", timeout: 5000, }), 3000, ); }} > Upload with loading </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Processing payment...", type: "loading", timeout: 0, }); setTimeout( () => manager.update(id, { title: "Payment processed", description: "Your payment has been processed successfully", type: "success", timeout: 5000, }), 2500, ); }} > Payment processing </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Saving changes...", type: "loading", timeout: 0 }); setTimeout( () => manager.update(id, { title: "Failed to save", description: "Please try again", type: "danger", timeout: 5000, }), 2000, ); }} > Loading to error </Button> </div> </div> </div> <Notifications /> </> );}
export function PromiseDemo() { return ( <Toast.Provider> <Workflows /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
回调
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 callbacks, Apache-2.0.import { useRef, useState } from "react";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
function History() { const manager = Toast.useToastManager(); const [closedHistory, setClosedHistory] = useState< Array<{ id: number; message: string; time: string }> >([]); const nextId = useRef(0); const addToHistory = (message: string) => { const time = new Date().toLocaleTimeString(); const id = nextId.current++; setClosedHistory((previous) => [{ id, message, time }, ...previous].slice(0, 5)); }; return ( <> <div {...stylex.props(styles.frame, styles.large)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "File saved", onClose: () => addToHistory("File saved (closed after 3 seconds)"), timeout: 3000, }) } > Custom timeout (3s) </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Changes saved", onClose: () => addToHistory("Changes saved (closed after 10 seconds)"), timeout: 10000, }) } > Custom timeout (10s) </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Event created", type: "success", onClose: () => addToHistory("Event created (closed after default timeout)"), }) } > With onClose callback </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Important notification", description: "This toast will stay until dismissed", onClose: () => addToHistory("Important notification (manually closed)"), timeout: 0, }) } > Persistent toast </Button> </div> <div {...stylex.props(styles.history)}> <div {...stylex.props(styles.historyHeading)}> <h3 {...stylex.props(styles.heading)}>Closed History</h3> {closedHistory.length > 0 && ( <Button xstyle={styles.clear} size="sm" variant="tertiary" onClick={() => setClosedHistory([])} > Clear </Button> )} </div> <div {...stylex.props(styles.historyPanel)}> {closedHistory.length === 0 ? ( <p {...stylex.props(styles.empty)}>No toasts closed yet. Try closing one above!</p> ) : ( closedHistory.map((item, index) => ( <div key={item.id} {...stylex.props(styles.historyItem, styles.historyDelay(index * 50))} > <div {...stylex.props(styles.historyText)}> <span {...stylex.props(styles.medium)}>{item.message}</span> <span {...stylex.props(styles.time)}>({item.time})</span> </div> <div {...stylex.props(styles.check)}> <svg {...stylex.props(styles.checkIcon)} fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24" aria-hidden="true" > <path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round" /> </svg> </div> </div> )) )} </div> </div> </div> <Notifications /> </> );}
export function Callbacks() { return ( <Toast.Provider> <History /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
自定义队列
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 custom-queue, Apache-2.0.import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
export function CustomQueue() { const [notificationQueue] = useState(() => Toast.createToastManager()); const [errorQueue] = useState(() => Toast.createToastManager()); const [successQueue] = useState(() => Toast.createToastManager()); return ( <div {...stylex.props(styles.queues)}> <Toast.Provider toastManager={notificationQueue} limit={2}> <Notifications placement="bottom" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button size="sm" variant="secondary" onClick={() => notificationQueue.add({ description: "You have a new message", title: "New notification", type: "default", }) } > Add notification (max 2) </Button> </div> <Toast.Provider toastManager={errorQueue} limit={3}> <Notifications placement="bottom-start" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button size="sm" variant="danger-soft" onClick={() => errorQueue.add({ description: "Failed to save changes", title: "Error occurred", type: "danger", }) } > Add error (max 3) </Button> </div> <Toast.Provider toastManager={successQueue} limit={1}> <Notifications placement="bottom-end" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button xstyle={styles.successText} size="sm" variant="secondary" onClick={() => successQueue.add({ description: `Operation ${Date.now()}`, title: "Success!", type: "success", }) } > Add success (max 1) </Button> </div> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
配置
在应用根节点渲染 Provider。
import { Toast, Button, toast } from '@lenso/ui';
function App() { return ( <div> <Toast.Provider /> <Button onPress={() => toast("Simple message")}> Show toast </Button> </div> );}自定义样式
Tailwind CSS
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 custom-styles, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { styles } from "./_shared";
function StyledNotifications() { const manager = Toast.useToastManager(); return ( <> <Toast.Portal> <Toast.Viewport placement="bottom"> {manager.toasts.map((item) => ( <Toast key={item.id} toast={item} xstyle={styles.styledRoot}> <Toast.Content> <div {...stylex.props(styles.styledRow)}> <Toast.Indicator xstyle={styles.neutral} /> <div {...stylex.props(styles.styledText)}> {item.title && <Toast.Title xstyle={styles.styledTitle} />} {item.description && <Toast.Description xstyle={styles.styledDescription} />} </div> </div> </Toast.Content> </Toast> ))} </Toast.Viewport> </Toast.Portal> <Button size="sm" variant="secondary" onClick={() => manager.add({ description: "Draft synced", title: "Saved", type: "default" }) } > Show toast </Button> </> );}
export function CustomStyles() { return ( <div {...stylex.props(styles.customFrame)}> <Toast.Provider> <StyledNotifications /> </Toast.Provider> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
全局 CSS
若要自定义组件类,可使用 @layer components 指令。了解更多。
@layer components { .toast { @apply rounded-xl shadow-lg; }
.toast__content { @apply gap-2; }}样式参考
HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。
CSS 类
Toast 使用以下 CSS 类(查看源码样式):
基础类 [!toc]
.toast— Toast 根容器.toast-region— Toast 区域容器.toast__content— 包裹标题与说明的内容容器.toast__indicator— 图标/指示器容器.toast__title— Toast 标题文本.toast__description— Toast 说明文本.toast__action— 操作按钮容器.toast__close-button— 关闭按钮容器
变体类 [!toc]
.toast--default— 默认灰色变体.toast--accent— 强调蓝色变体.toast--success— 成功绿色变体.toast--warning— 警告黄/橙色变体.toast--danger— 危险红色变体
交互状态
组件支持多种状态:
- 最前:
[data-frontmost]— 应用于堆叠中最上层可见的 Toast - 索引:
[data-index]— 基于 Toast 在堆叠中的位置 - 放置:
[data-placement="*"]— 基于 Toast 区域的放置位置 - 进入中:
[data-entering]— Toast 首次绘制时应用 - 退出中:
[data-exiting]— 关闭中的 Toast 播放退出过渡时应用 - 展开:
[data-expanded]— 堆叠展开时应用于每个 Toast - 隐藏:
[data-hidden]— 应用于超出maxVisibleToasts的 Toast
API 参考
Toast.Provider
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
placement | "top start" | "top" | "top end" | "bottom start" | "bottom" | "bottom end" | "bottom" | Toast 区域的放置位置 |
gap | number | 12 | Toast 之间的间距(像素) |
isExpanded | boolean | false | 强制堆叠保持展开布局(不会暂停计时器) |
maxVisibleToasts | number | 3 | 同时最多显示的 Toast 数量 |
hotkey | string[] | ["altKey", "KeyT"] | 将焦点移入 Toast 区域并展开堆叠的快捷键。修饰键使用 KeyboardEvent 布尔属性名(如 "altKey"),其他按键使用 event.code(如 "KeyT")。未列出的修饰键必须处于松开状态,因此 Alt+T 不会在 Ctrl+Alt+T 时触发。传 [] 可禁用 |
scaleFactor | number | 0.05 | 堆叠 Toast 的缩放系数(0–1) |
width | number | string | 460 | Toast 宽度(像素或 CSS 值) |
queue | ToastQueue<T> | - | 自定义 Toast 队列实例 |
children | ReactNode | ((props: {toast: QueuedToast<T>}) => ReactNode) | - | 自定义渲染函数或子节点 |
className | string | - | 附加的 CSS 类 |
Toast
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
toast | QueuedToast<T> | - | 来自队列的 Toast 数据(必填) |
variant | "default" | "accent" | "success" | "warning" | "danger" | "default" | Toast 的视觉变体 |
placement | ToastVariants["placement"] | - | 放置位置(继承自 Provider) |
scaleFactor | number | - | 缩放系数(继承自 Provider) |
className | string | - | 附加的 CSS 类 |
children | ReactNode | - | Toast 内容(ToastContent、ToastIndicator 等) |
Toast.Content
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
children | ReactNode | - | 内容(通常为 ToastTitle 与 ToastDescription) |
className | string | - | 附加的 CSS 类 |
Toast.Indicator
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
variant | ToastVariants["variant"] | - | 默认图标的变体 |
children | ReactNode | - | 自定义指示图标(默认使用变体图标) |
className | string | - | 附加的 CSS 类 |
Toast.Title
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
children | ReactNode | - | 标题文本 |
className | string | - | 附加的 CSS 类 |
Toast.Description
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
children | ReactNode | - | 说明文本 |
className | string | - | 附加的 CSS 类 |
Toast.ActionButton
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
children | ReactNode | - | 操作按钮内容 |
className | string | - | 附加的 CSS 类 |
All Button props | - | - | 接受 Button 组件的全部 props |
Toast.CloseButton
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
className | string | - | 附加的 CSS 类 |
All CloseButton props | - | - | 接受 CloseButton 组件的全部 props |
ToastQueue
ToastQueue 用于管理 <Toast.Provider> 的状态。状态存放在 React 之外,因此你可以在应用的任意位置触发 Toast。
构造选项
| Option | 类型 | 默认值 | 描述 |
|---|---|---|---|
exitDuration | number | 300 | 关闭中的 Toast 为播放退出动画而保持挂载的时长(0 表示立即移除) |
maxVisibleToasts | number | 3 | 同时最多显示的 Toast 数量(仅视觉) |
wrapUpdate | (fn: () => void) => void | - | 包裹状态更新的函数(例如 document.startViewTransition);默认直接应用更新并由 CSS transition 驱动动画 |
方法
| Method | 参数 | 返回值 | 描述 |
|---|---|---|---|
add | (content: T, options?: ToastOptions) | string | 将 Toast 加入队列,返回 Toast key |
update | (key: string, content: T, options?: {timeout?: number; onClose?: () => void}) | boolean | 原地更新 Toast,保持其在堆叠中的位置;timeout 重新开始倒计时(0 表示保持打开),省略则沿用当前倒计时。Toast 已不存在时返回 false |
close | (key: string) | void | 按 key 关闭 Toast |
pauseAll | () | void | 暂停所有 Toast 计时器 |
resumeAll | () | void | 恢复所有 Toast 计时器 |
clear | () | void | 关闭所有 Toast(每个 Toast 播放退出动画并触发各自的 onClose) |
subscribe | (fn: () => void) | () => void | 订阅队列变化,返回取消订阅函数 |
toast 函数
默认 toast 函数提供便捷方法用于显示 Toast:
import { toast } from '@lenso/ui';
// 基础 toast(默认约 4 秒后自动消失)toast("Event has been created");
// 变体方法(默认同样约 4 秒后自动消失)toast.success("File saved");toast.info("New update available");toast.warning("Please check your settings");toast.danger("Something went wrong");
// 传入 options。利用返回的 id,操作按钮可以关闭它所属的 Toast。const eventId = toast("Event has been created", { description: "Your event has been scheduled for tomorrow", variant: "default", timeout: 5000, // 自定义超时:5 秒 onClose: () => console.log("Closed"), actionProps: { children: "View", onPress: () => toast.close(eventId), }, indicator: <CustomIcon />,});
// 原地更新已有 Toast(保持其在堆叠中的位置)。// 省略的选项会被继承,因此若原 Toast 是持久的,需要显式传入 `timeout` 才会开始倒计时。const id = toast("Saving…", { timeout: 0 });toast.update(id, "Saved", { variant: "success", timeout: 4000 });
// Promise 支持(自动显示加载指示)。Promise 结束时加载 Toast 会原地更新 —// 同一个 Toast、同一个堆叠位置;自动关闭倒计时从这一刻开始。toast.promise( uploadFile(), { loading: "Uploading file...", success: (data) => `File ${data.filename} uploaded`, error: "Failed to upload file", });
// 手动加载状态(持久 toast:不自动消失)const loadingId = toast("Creating event...", { isLoading: true, timeout: 0, // 持久 toast:不自动消失});
// 随后原地更新,并开始自动关闭倒计时toast.update(loadingId, "Event created", { variant: "success", timeout: 4000 });
// 队列方法toast.close(key);toast.clear();toast.pauseAll();toast.resumeAll();toast Options
| Option | 类型 | 默认值 | 描述 |
|---|---|---|---|
title | ReactNode | - | Toast 标题(变体方法的第一个参数) |
description | ReactNode | - | 可选说明文本 |
variant | "default" | "accent" | "success" | "warning" | "danger" | "default" | 视觉变体 |
indicator | ReactNode | - | 自定义指示图标(null 可隐藏) |
actionProps | ButtonProps | - | 操作按钮 props |
isLoading | boolean | false | 使用加载指示替代指示器 |
timeout | number | 4000 | 自动消失超时(毫秒)。默认 4000ms(4 秒)。设为 0 表示持久 Toast,不自动消失 |
onClose | () => void | - | Toast 被关闭时触发(此时退出动画开始播放) |
toast.promise Options
| Option | 类型 | 默认值 | 描述 |
|---|---|---|---|
loading | ReactNode | - | Promise pending 时显示的消息 |
success | ReactNode | ((data: T) => ReactNode) | - | 成功时显示的消息(可为函数) |
error | ReactNode | ((error: Error) => ReactNode) | - | 失败时显示的消息(可为函数) |
无障碍
每个 Toast 实现 WAI-ARIA AlertDialog 模式,并分组渲染在带标签的地标区域中:
- 地标区域:Toast 渲染在可通过
F6到达的带标签地标区域中;Alt+T可将焦点直接移入。快捷键可通过hotkeyprop 配置,传空数组可禁用;区域标签可通过aria-label覆盖 - 键盘:
Tab可到达可见 Toast 的控件,Escape折叠展开的堆叠;关闭聚焦的 Toast 时焦点会移至最近的剩余 Toast - 屏幕阅读器:每个 Toast 使用
role="alertdialog"并关联标题与描述;新 Toast 会自动播报 - 计时器:悬停或聚焦堆叠时、以及页面处于后台标签页时,自动关闭计时会暂停