/** * Auto-dismisses flash notices after their data-flash-timeout expires. * Supports slide-out animation and hover-pause. */ import { resolveHost } from '../core/app-dom.js'; import { postForm as postFormRequest } from '../core/app-http.js'; export function initFlashAutoDismiss(root = document, options = {}) { const { selector = '.notice[data-flash-timeout]', defaultTimeout = 0, } = options; const host = resolveHost(root); const notices = Array.from(host.querySelectorAll(selector)); if (!notices.length) { return { destroy: () => {} }; } const timers = []; let destroyed = false; const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const dismissNotice = (notice) => { if (prefersReducedMotion) { notice.remove(); return; } notice.classList.add('toast-dismissing'); notice.addEventListener('animationend', () => notice.remove(), { once: true }); setTimeout(() => { if (notice.parentNode) notice.remove(); }, 300); }; const postForm = async (form) => { const action = form.getAttribute('action'); if (!action) { return false; } try { await postFormRequest(action, new FormData(form)); return true; } catch { return false; } }; notices.forEach((notice) => { const timeout = Number.parseInt( notice.dataset.flashTimeout || `${defaultTimeout}`, 10 ); if (!timeout || timeout <= 0) { return; } notice.style.setProperty('--flash-timeout', `${timeout}ms`); notice.classList.add('flash-timed'); let timer = window.setTimeout(async () => { if (destroyed) { return; } const form = notice.querySelector('form'); if (form) { const ok = await postForm(form); if (!ok) { return; } } dismissNotice(notice); }, timeout); timers.push(timer); // Pause auto-dismiss on hover notice.addEventListener('mouseenter', () => { window.clearTimeout(timer); }); notice.addEventListener('mouseleave', () => { timer = window.setTimeout(async () => { if (destroyed) return; const form = notice.querySelector('form'); if (form) { const ok = await postForm(form); if (!ok) return; } dismissNotice(notice); }, 1000); timers.push(timer); }); }); const destroy = () => { destroyed = true; timers.forEach((timer) => window.clearTimeout(timer)); }; return { destroy }; }