// shared/toast.js — non-blocking notification helper available to every // tool via window.zddc.toast(msg, level, opts). Originated as classifier's // local showToast (classifier/js/excel.js); promoted here so tools that // today use alert() or silent console.error can switch to a uniform // non-blocking surface. // // Usage: // window.zddc.toast('Saved.', 'success'); // window.zddc.toast('Could not load: ' + err.message, 'error'); // window.zddc.toast('Note', 'info', { durationMs: 3000 }); // // Levels: 'info' (default) | 'success' | 'warning' | 'error'. // Each tool may also expose app.notify(msg, level) as a thin wrapper — // see ARCHITECTURE.md for the convention. (function () { 'use strict'; if (!window.zddc) window.zddc = {}; // Don't overwrite if a tool defined its own first. if (typeof window.zddc.toast === 'function') return; var DEFAULT_DURATION_MS = 5000; var FADE_MS = 300; function toast(message, level, opts) { opts = opts || {}; var lvl = (level === 'success' || level === 'error' || level === 'warning') ? level : 'info'; // Single-toast policy: dismiss any existing toast immediately // so the new one is always the most recent. Matches the // classifier's prior behavior and avoids stack-of-toasts UX. var existing = document.querySelector('.zddc-toast'); if (existing) existing.remove(); var el = document.createElement('div'); el.className = 'zddc-toast zddc-toast--' + lvl; // ARIA: errors get assertive (interrupts SR queue), others polite. el.setAttribute('role', lvl === 'error' ? 'alert' : 'status'); el.setAttribute('aria-live', lvl === 'error' ? 'assertive' : 'polite'); el.textContent = message == null ? '' : String(message); document.body.appendChild(el); var dur = typeof opts.durationMs === 'number' ? opts.durationMs : DEFAULT_DURATION_MS; var timer = setTimeout(function () { el.classList.add('zddc-toast--fade'); setTimeout(function () { if (el.parentNode) el.parentNode.removeChild(el); }, FADE_MS); }, dur); // Click-to-dismiss. Useful for sticky errors the user wants gone. el.addEventListener('click', function () { clearTimeout(timer); if (el.parentNode) el.parentNode.removeChild(el); }); return el; } window.zddc.toast = toast; })();