(() => { let toaster; const toasts = new WeakMap(); let isPaused = false; const ICONS = { success: '', error: '', info: '', warning: '' }; function initToaster(toasterElement) { if (toasterElement.dataset.toasterInitialized) return; toaster = toasterElement; toaster.addEventListener('mouseenter', pauseAllTimeouts); toaster.addEventListener('mouseleave', resumeAllTimeouts); toaster.addEventListener('click', (event) => { const actionLink = event.target.closest('.toast footer a'); const actionButton = event.target.closest('.toast footer button'); if (actionLink || actionButton) { closeToast(event.target.closest('.toast')); } }); toaster.querySelectorAll('.toast:not([data-toast-initialized])').forEach(initToast); toaster.dataset.toasterInitialized = 'true'; toaster.dispatchEvent(new CustomEvent('basecoat:initialized')); } function initToast(element) { if (element.dataset.toastInitialized) return; const duration = parseInt(element.dataset.duration); const timeoutDuration = duration !== -1 ? duration || (element.dataset.category === 'error' ? 5000 : 3000) : -1; const state = { remainingTime: timeoutDuration, timeoutId: null, startTime: null, }; if (timeoutDuration !== -1) { if (isPaused) { state.timeoutId = null; } else { state.startTime = Date.now(); state.timeoutId = setTimeout(() => closeToast(element), timeoutDuration); } } toasts.set(element, state); element.dataset.toastInitialized = 'true'; } function pauseAllTimeouts() { if (isPaused) return; isPaused = true; toaster.querySelectorAll('.toast:not([aria-hidden="true"])').forEach(element => { if (!toasts.has(element)) return; const state = toasts.get(element); if (state.timeoutId) { clearTimeout(state.timeoutId); state.timeoutId = null; state.remainingTime -= Date.now() - state.startTime; } }); } function resumeAllTimeouts() { if (!isPaused) return; isPaused = false; toaster.querySelectorAll('.toast:not([aria-hidden="true"])').forEach(element => { if (!toasts.has(element)) return; const state = toasts.get(element); if (state.remainingTime !== -1 && !state.timeoutId) { if (state.remainingTime > 0) { state.startTime = Date.now(); state.timeoutId = setTimeout(() => closeToast(element), state.remainingTime); } else { closeToast(element); } } }); } function closeToast(element) { if (!toasts.has(element)) return; const state = toasts.get(element); clearTimeout(state.timeoutId); toasts.delete(element); if (element.contains(document.activeElement)) document.activeElement.blur(); element.setAttribute('aria-hidden', 'true'); element.addEventListener('transitionend', () => element.remove(), { once: true }); } function executeAction(button, toast) { const actionString = button.dataset.toastAction; if (!actionString) return; try { const func = new Function('close', actionString); func(() => closeToast(toast)); } catch (event) { console.error('Error executing toast action:', event); } } function createToast(config) { const { category = 'info', title, description, action, cancel, duration, icon, } = config; const iconHtml = icon || (category && ICONS[category]) || ''; const titleHtml = title ? `

${title}

` : ''; const descriptionHtml = description ? `

${description}

` : ''; const actionHtml = action?.href ? `${action.label}` : action?.onclick ? `` : ''; const cancelHtml = cancel ? `` : ''; const footerHtml = actionHtml || cancelHtml ? `` : ''; const html = `
${iconHtml}
${titleHtml} ${descriptionHtml}
${footerHtml}
`; const template = document.createElement('template'); template.innerHTML = html.trim(); return template.content.firstChild; } document.addEventListener('basecoat:toast', (event) => { if (!toaster) { console.error('Cannot create toast: toaster container not found on page.'); return; } const config = event.detail?.config || {}; const toastElement = createToast(config); toaster.appendChild(toastElement); }); if (window.basecoat) { window.basecoat.register('toaster', '#toaster:not([data-toaster-initialized])', initToaster); window.basecoat.register('toast', '.toast:not([data-toast-initialized])', initToast); } })();