Toast
轻提示:用 ToastProvider 承载 useToast,浮层堆叠、自动关闭、悬停暂停,兼容受控与非受控。
安装
添加组件
pnpm dlx @elyri/cli@latest add toast示例
基础用法
调用 toast({ title, description, variant }) 弹出一条提示。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { toast } = useToast();
return (
<Button
onClick={() => toast({ title: 'Saved', description: 'Saved to your workspace.', variant: 'success' })}
>
Show toast
</Button>
);
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}位置切换
切换 ToastProvider 的 position,提示会停靠在视口的六个方向之一。
import { useState } from 'react';
import { ToastProvider, useToast } from './components/elyri/Toast';
import type { ToastPosition } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
const positions: ToastPosition[] = ['top-left', 'top', 'top-right', 'bottom-left', 'bottom', 'bottom-right'];
function Trigger({ position, onPick }: { position: ToastPosition; onPick: (position: ToastPosition) => void }) {
const { toast } = useToast();
return (
<div className="demo-ui-row">
{positions.map((item) => (
<Button
key={item}
variant={item === position ? 'primary' : 'secondary'}
onClick={() => {
onPick(item);
toast({ title: 'Position', description: item, variant: 'info' });
}}
>
{item}
</Button>
))}
</div>
);
}
export function Example() {
const [position, setPosition] = useState<ToastPosition>('top');
return (
<ToastProvider position={position}>
<Trigger position={position} onPick={setPosition} />
</ToastProvider>
);
}全部变体
四种语义:info / success / warning / danger。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { toast } = useToast();
return (
<div className="demo-ui-row">
<Button onClick={() => toast({ title: 'Heads up', description: 'A neutral update.', variant: 'info' })}>
Info
</Button>
<Button onClick={() => toast({ title: 'Saved', description: 'Saved to your workspace.', variant: 'success' })}>
Success
</Button>
<Button onClick={() => toast({ title: 'Careful', description: 'This is irreversible.', variant: 'warning' })}>
Warning
</Button>
<Button onClick={() => toast({ title: 'Error', description: 'Something went wrong.', variant: 'danger' })}>
Danger
</Button>
</div>
);
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}加载到成功
先弹出 loading 提示,再用 update(id, options) 就地改成 success。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { toast, update } = useToast();
const showLoading = () => {
const id = toast({ title: 'Uploading file', description: 'Please wait…', variant: 'loading' });
setTimeout(
() => update(id, { title: 'Upload complete', description: 'Saved to your workspace.', variant: 'success' }),
1500,
);
};
return <Button onClick={showLoading}>Upload file</Button>;
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}单行 Message
不传 description 时只显示一行标题;先弹出 loading,再用 update 改成 danger。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { toast, update } = useToast();
const showMessage = () => {
const id = toast({ title: 'Connecting…', variant: 'loading' });
setTimeout(() => update(id, { title: 'Connection failed', variant: 'danger' }), 1500);
};
return <Button onClick={showMessage}>Connect</Button>;
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}Promise
promise(p, messages) 自动跟随 promise 切换 loading / success / error。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { promise } = useToast();
const showPromise = () => {
promise(new Promise((resolve) => setTimeout(resolve, 1500)), {
loading: { title: 'Submitting', description: 'Hang tight…' },
success: { title: 'Submitted', description: 'Saved to your workspace.' },
error: { title: 'Submit failed', description: 'Please try again.' },
});
};
return <Button onClick={showPromise}>Submit</Button>;
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}常驻
persistent: true 的提示不会自动关闭,只能由程序调用 dismiss(id) 关闭。
import { ToastProvider, useToast } from './components/elyri/Toast';
import { Button } from './components/elyri/Button';
function Trigger() {
const { toast, dismiss } = useToast();
const showPersistent = () => {
const id = toast({
title: 'Syncing',
description: 'Closed by the app when the task finishes.',
variant: 'loading',
persistent: true,
});
setTimeout(() => dismiss(id), 3000);
};
return <Button onClick={showPersistent}>Start sync</Button>;
}
export function Example() {
return (
<ToastProvider>
<Trigger />
</ToastProvider>
);
}属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ToastProvider.duration | number | 4000 | Provider 默认自动关闭毫秒数,0 表示不自动关闭 |
ToastProvider.limit | number | 4 | 同时最多显示的条数 |
ToastProvider.position | ToastPosition | 'top' | 视口停靠方向,支持六个方向,默认 top |
ToastProvider.label | string | 'Notifications' | 视口区域的无障碍名称 |
ToastProvider.onClose | (id, reason) => void | — | 任一提示关闭后的回调,reason 为 manual | timeout | limit |
useToast().toast | (options) => string | — | toast(options) 弹出一条提示,返回其 id |
useToast().update | (id, options) => void | — | update(id, options) 就地更新某条提示,如 loading 完成后改成 success |
useToast().dismiss | (id?: string) => void | — | dismiss(id) 手动关闭某条提示;不传 id 时关闭全部 |
useToast().promise | (p, messages) => Promise | — | promise(p, { loading, success, error }) 跟踪一个 promise,自动切换 loading / success / error |
options | ToastOptions | — | toast 选项:title / description / variant / duration / persistent / dismissible / action |
options.description | ReactNode | — | 关闭后不显示描述,只保留一行标题,形如单行 Message |
options.persistent | boolean | false | 常驻:不自动关闭、不显示关闭按钮、不受 limit 挤出,只能由程序调用 dismiss 关闭 |
options.dismissible | boolean | true | 是否显示关闭按钮,默认 true;persistent 时始终不显示 |
children | ReactNode | — | Provider 包裹应用内容 |