/** * Lightweight toast notification system. * * Usage: * import { showToast } from '../toast.js'; * * showToast('Something went wrong', 'error'); * showToast('Merge successful!', 'success'); * showToast('Select two adjacent polygons', 'info'); */ // ── Palette ────────────────────────────────────────────────────────────────── const THEMES = { success: { bg: '#10b981', icon: '\u2705' }, // green error: { bg: '#ef4444', icon: '\u274c' }, // red warning: { bg: '#f59e0b', icon: '\u26a0\ufe0f' }, // amber info: { bg: '#0ea5e9', icon: '\u2139\ufe0f' }, // cyan }; // ── Container (created once, appended to ) ──────────────────────────── let container = null; function ensureContainer() { if (container) return container; container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 10000; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; `; document.body.appendChild(container); return container; } // ── Public API ────────────────────────────────────────────────────────────── /** * Display a toast notification. * * @param {string} message Plain-text message to show. * @param {'success'|'error'|'warning'|'info'} [type='info'] * @param {number} [duration=4000] Auto-dismiss time in ms. */ export function showToast(message, type = 'info', duration = 4000) { const parent = ensureContainer(); const theme = THEMES[type] || THEMES.info; const el = document.createElement('div'); el.style.cssText = ` background: ${theme.bg}; color: #fff; padding: 10px 18px; border-radius: 8px; font-family: var(--font-body, 'Exo', sans-serif); font-size: 13px; font-weight: 600; box-shadow: 0 4px 12px rgba(0,0,0,0.25); pointer-events: auto; cursor: pointer; opacity: 0; transition: opacity 0.25s ease, transform 0.25s ease; transform: translateY(-8px); max-width: 420px; text-align: center; line-height: 1.4; `; el.textContent = `${theme.icon} ${message}`; parent.appendChild(el); // Animate in requestAnimationFrame(() => { el.style.opacity = '1'; el.style.transform = 'translateY(0)'; }); // Dismiss helper const dismiss = () => { el.style.opacity = '0'; el.style.transform = 'translateY(-8px)'; setTimeout(() => el.remove(), 300); }; // Click to dismiss early el.addEventListener('click', dismiss); // Auto-dismiss setTimeout(dismiss, duration); }