(() => { const TOAST_STACK_ID = 'installer-toast-stack'; const DEFAULT_TIMEOUT = 5000; const MAX_TOASTS = 3; const ICONS = { error: '', close: '' }; const getStack = () => document.getElementById(TOAST_STACK_ID); const dismissToast = (toast) => { if (!toast) return; if (toast.classList.contains('is-leaving')) return; toast.classList.add('is-leaving'); const remove = () => toast.remove(); toast.addEventListener('transitionend', remove, { once: true }); setTimeout(remove, 450); }; const showToast = ({ title = '', description = '', status = 'error', dismissible = true, timeout = DEFAULT_TIMEOUT } = {}) => { const stack = getStack(); if (!stack) return; const visibleToasts = Array.from( stack.querySelectorAll('.installer-toast:not(.is-leaving)') ); if (visibleToasts.length >= MAX_TOASTS) { dismissToast(visibleToasts[0]); } const toast = document.createElement('div'); toast.className = 'installer-toast is-entering'; toast.dataset.status = status; toast.setAttribute('role', status === 'error' ? 'alert' : 'status'); const content = document.createElement('div'); content.className = 'installer-toast-content'; const icon = document.createElement('span'); icon.className = 'installer-toast-icon'; icon.dataset.status = status; icon.innerHTML = ICONS.error; content.appendChild(icon); const body = document.createElement('section'); body.className = 'installer-toast-body'; if (title) { const titleNode = document.createElement('p'); titleNode.className = 'installer-toast-title typography-text-m-500'; titleNode.textContent = title; body.appendChild(titleNode); } if (description) { const descNode = document.createElement('p'); descNode.className = 'installer-toast-description typography-text-m-400'; descNode.textContent = description; body.appendChild(descNode); } content.appendChild(body); toast.appendChild(content); if (dismissible) { const close = document.createElement('button'); close.type = 'button'; close.className = 'installer-toast-close'; close.setAttribute('aria-label', 'Dismiss notification'); close.innerHTML = ICONS.close; close.addEventListener('click', () => dismissToast(toast)); toast.appendChild(close); } stack.appendChild(toast); toast.getBoundingClientRect(); requestAnimationFrame(() => { toast.classList.remove('is-entering'); }); if (timeout > 0) { setTimeout(() => dismissToast(toast), timeout); } }; window.InstallerToast = Object.freeze({ showToast }); })();