Skip to content
Lenso UI

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 区域的放置位置
gapnumber12Toast 之间的间距(像素)
isExpandedbooleanfalse强制堆叠保持展开布局(不会暂停计时器)
maxVisibleToastsnumber3同时最多显示的 Toast 数量
hotkeystring[]["altKey", "KeyT"]将焦点移入 Toast 区域并展开堆叠的快捷键。修饰键使用 KeyboardEvent 布尔属性名(如 "altKey"),其他按键使用 event.code(如 "KeyT")。未列出的修饰键必须处于松开状态,因此 Alt+T 不会在 Ctrl+Alt+T 时触发。传 [] 可禁用
scaleFactornumber0.05堆叠 Toast 的缩放系数(0–1)
widthnumber | string460Toast 宽度(像素或 CSS 值)
queueToastQueue<T>-自定义 Toast 队列实例
childrenReactNode | ((props: {toast: QueuedToast<T>}) => ReactNode)-自定义渲染函数或子节点
classNamestring-附加的 CSS 类

Toast

Prop类型默认值描述
toastQueuedToast<T>-来自队列的 Toast 数据(必填)
variant"default" | "accent" | "success" | "warning" | "danger""default"Toast 的视觉变体
placementToastVariants["placement"]-放置位置(继承自 Provider)
scaleFactornumber-缩放系数(继承自 Provider)
classNamestring-附加的 CSS 类
childrenReactNode-Toast 内容(ToastContent、ToastIndicator 等)

Toast.Content

Prop类型默认值描述
childrenReactNode-内容(通常为 ToastTitle 与 ToastDescription)
classNamestring-附加的 CSS 类

Toast.Indicator

Prop类型默认值描述
variantToastVariants["variant"]-默认图标的变体
childrenReactNode-自定义指示图标(默认使用变体图标)
classNamestring-附加的 CSS 类

Toast.Title

Prop类型默认值描述
childrenReactNode-标题文本
classNamestring-附加的 CSS 类

Toast.Description

Prop类型默认值描述
childrenReactNode-说明文本
classNamestring-附加的 CSS 类

Toast.ActionButton

Prop类型默认值描述
childrenReactNode-操作按钮内容
classNamestring-附加的 CSS 类
All Button props--接受 Button 组件的全部 props

Toast.CloseButton

Prop类型默认值描述
classNamestring-附加的 CSS 类
All CloseButton props--接受 CloseButton 组件的全部 props

ToastQueue

ToastQueue 用于管理 <Toast.Provider> 的状态。状态存放在 React 之外,因此你可以在应用的任意位置触发 Toast。

构造选项

Option类型默认值描述
exitDurationnumber300关闭中的 Toast 为播放退出动画而保持挂载的时长(0 表示立即移除)
maxVisibleToastsnumber3同时最多显示的 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类型默认值描述
titleReactNode-Toast 标题(变体方法的第一个参数)
descriptionReactNode-可选说明文本
variant"default" | "accent" | "success" | "warning" | "danger""default"视觉变体
indicatorReactNode-自定义指示图标(null 可隐藏)
actionPropsButtonProps-操作按钮 props
isLoadingbooleanfalse使用加载指示替代指示器
timeoutnumber4000自动消失超时(毫秒)。默认 4000ms(4 秒)。设为 0 表示持久 Toast,不自动消失
onClose() => void-Toast 被关闭时触发(此时退出动画开始播放)

toast.promise Options

Option类型默认值描述
loadingReactNode-Promise pending 时显示的消息
successReactNode | ((data: T) => ReactNode)-成功时显示的消息(可为函数)
errorReactNode | ((error: Error) => ReactNode)-失败时显示的消息(可为函数)

无障碍

每个 Toast 实现 WAI-ARIA AlertDialog 模式,并分组渲染在带标签的地标区域中:

  • 地标区域:Toast 渲染在可通过 F6 到达的带标签地标区域中;Alt + T 可将焦点直接移入。快捷键可通过 hotkey prop 配置,传空数组可禁用;区域标签可通过 aria-label 覆盖
  • 键盘:Tab 可到达可见 Toast 的控件,Escape 折叠展开的堆叠;关闭聚焦的 Toast 时焦点会移至最近的剩余 Toast
  • 屏幕阅读器:每个 Toast 使用 role="alertdialog" 并关联标题与描述;新 Toast 会自动播报
  • 计时器:悬停或聚焦堆叠时、以及页面处于后台标签页时,自动关闭计时会暂停

相关组件