iter: d10 toast 手动关闭 + 堆叠上限

This commit is contained in:
ui-arch-bot
2026-10-23 16:53:44 +08:00
parent a11157b80a
commit a08f157e5e
10 changed files with 74 additions and 15 deletions
+18 -7
View File
@@ -3,6 +3,7 @@ import type { ToastProps } from './types.js';
let host: HTMLElement | null = null; // 全局右上角堆叠容器:首条消息出现时懒创建
let seq = 0; // 自增序号:为每条 toast 生成唯一 id,辅助无障碍定位
const MAX_STACK = 4; // 同屏最多条数:超出即挤掉最早一条,避免刷屏淹页面
/*** 确保堆叠容器存在:fixed 挂在 body 末尾,后续消息依次向上堆叠 ***/
function ensureHost(): HTMLElement {
@@ -12,19 +13,29 @@ function ensureHost(): HTMLElement {
return host;
}
/*** 退场并移除:先摘 ua-open 触发过渡,transitionend 与 320ms 超时兜底二选一 ***/
function dismiss(item: HTMLElement): void {
item.classList.remove('ua-open');
item.addEventListener('transitionend', () => item.remove(), { once: true });
window.setTimeout(() => item.isConnected && item.remove(), 320); // reduced-motion 下 transition 不触发
}
/*** 弹出一条 toast:语义色由 variant 决定,duration 毫秒后自动淡出并移除节点 ***/
export function showToast(props: ToastProps): HTMLElement {
// —— 状态集中声明 ——
const item = h('div', { class: `ua-toast ua-toast-${props.variant ?? 'info'}`, role: 'status', id: `ua-toast-${++seq}` }, props.message);
const duration = props.duration ?? 3000; // 停留时长毫秒
const item = h(
'div',
{ class: `ua-toast ua-toast-${props.variant ?? 'info'}${props.closable ? ' is-closable' : ''}`, role: 'status', id: `ua-toast-${++seq}` },
h('span', { class: 'ua-toast-msg' }, props.message),
props.closable ? h('button', { type: 'button', class: 'ua-toast-close', 'aria-label': '关闭提示', onClick: () => dismiss(item) }, '×') : null,
);
const stack = ensureHost();
if (stack.childElementCount >= MAX_STACK) stack.firstElementChild?.remove(); // 超过上限直接挤掉最早一条
ensureHost().appendChild(item);
stack.appendChild(item);
requestAnimationFrame(() => item.classList.add('ua-open')); // 下一帧入场,保证过渡生效
window.setTimeout(() => {
item.classList.remove('ua-open'); // 先退场再摘除,退场动画与 reduced-motion 兜底都安全
item.addEventListener('transitionend', () => item.remove(), { once: true });
window.setTimeout(() => item.isConnected && item.remove(), 320); // transition 不触发时的兜底移除
}, duration);
window.setTimeout(() => dismiss(item), duration);
return item;
}
+1
View File
@@ -187,6 +187,7 @@ export interface PaginationProps {
export interface ToastProps {
message: string; // 提示文案
duration?: number; // 自动消失毫秒;缺省 3000
closable?: boolean; // 是否显示关闭按钮(手动立即收起);缺省 false
variant?: 'info' | 'success' | 'warning' | 'danger'; // 语义色;缺省 info
}