/* DCTS Dashboard Toast Component */ import { ICONS } from '../utils/icons.js'; const TOAST_ICONS = { success: ICONS.checkCircle({ cls: 'toast-icon', sw: 2.5 }), error: ICONS.alert({ cls: 'toast-icon', sw: 2.5 }), info: ICONS.info({ cls: 'toast-icon', sw: 2.5 }), }; export function showToast(message, type = 'info') { const container = document.getElementById('toast-container'); if (!container) { console.log(`[Toast ${type}]`, message); return; } const toast = document.createElement('div'); toast.className = `toast toast-${type}`; // role="alert" 隐含 assertive,仅用于错误;普通提示用 polite,避免与 role 冲突 if (type === 'error') { toast.setAttribute('role', 'alert'); } else { toast.setAttribute('role', 'status'); toast.setAttribute('aria-live', 'polite'); } // 消息一律走 textContent,杜绝 innerHTML 注入(message 可能携带用户输入,如工作流名) const iconSvg = TOAST_ICONS[type] || TOAST_ICONS.info; toast.innerHTML = iconSvg; const textEl = document.createElement('span'); textEl.textContent = message; toast.appendChild(textEl); // 关闭按钮:键盘可达(Tab 聚焦 + Enter/Space 触发),aria-label 供读屏播报。 // 此前仅支持点击关闭,键盘/读屏用户无法主动消除(尤其 role=alert 的错误提示)。 const closeBtn = document.createElement('button'); closeBtn.type = 'button'; closeBtn.className = 'toast-close'; closeBtn.setAttribute('aria-label', '关闭提示'); closeBtn.innerHTML = ICONS.x({ size: 14, sw: 2.2 }); toast.appendChild(closeBtn); let dismissed = false; const dismiss = () => { if (dismissed) return; dismissed = true; toast.classList.add('toast-fadeOut'); setTimeout(() => toast.remove(), 250); }; closeBtn.addEventListener('click', (e) => { e.stopPropagation(); dismiss(); }); toast.addEventListener('click', dismiss); container.appendChild(toast); setTimeout(dismiss, 3500); }