/** * Notification System * Accessible toast notifications */ (function () { "use strict"; class NotificationManager { constructor() { this.container = null; this.notifications = new Map(); this.init(); } init() { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => this.createContainer() ); } else { this.createContainer(); } } createContainer() { if (!document.body || this.container) return; this.container = document.createElement("div"); this.container.id = "notification-container"; this.container.setAttribute("aria-live", "polite"); this.container.setAttribute("aria-atomic", "true"); this.container.className = "notification-container"; const style = document.createElement("style"); style.textContent = ` .notification-container { position: fixed; top: 80px; right: 20px; z-index: 10000; display: flex; flex-direction: column; gap: 10px; max-width: 400px; pointer-events: none; } .notification { padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 12px; color: white; font-size: 14px; font-weight: 500; pointer-events: auto; animation: slideInRight 0.3s ease; min-width: 250px; } .notification.removing { animation: slideOutRight 0.3s ease; } .notification-success { background: #10b981; } .notification-error { background: #ef4444; } .notification-info { background: #3b82f6; } .notification-warning { background: #f59e0b; } .notification-icon { font-size: 18px; flex-shrink: 0; } .notification-message { flex: 1; } .notification-close { background: transparent; border: none; color: white; cursor: pointer; padding: 4px; opacity: 0.8; transition: opacity 0.2s; } .notification-close:hover { opacity: 1; } @keyframes slideInRight { from { transform: translateX(400px); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(400px); opacity: 0; } } @media (max-width: 640px) { .notification-container { right: 10px; left: 10px; max-width: none; } .notification { min-width: auto; } } `; document.head.appendChild(style); document.body.appendChild(this.container); } show(message, type = "info", duration = 3000) { if (!this.container) this.createContainer(); if (!this.container) return; const id = Date.now() + Math.random(); const notification = document.createElement("div"); notification.className = `notification notification-${type}`; notification.setAttribute("role", "alert"); const icons = { success: "✓", error: "✕", info: "ℹ", warning: "⚠", }; notification.innerHTML = ` ${icons[type] || icons.info} ${this.escapeHtml(message)} `; const closeBtn = notification.querySelector(".notification-close"); closeBtn.addEventListener("click", () => this.remove(id)); this.container.appendChild(notification); this.notifications.set(id, notification); if (duration > 0) { setTimeout(() => this.remove(id), duration); } return id; } remove(id) { const notification = this.notifications.get(id); if (!notification) return; notification.classList.add("removing"); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } this.notifications.delete(id); }, 300); } escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } success(message, duration) { return this.show(message, "success", duration); } error(message, duration) { return this.show(message, "error", duration); } info(message, duration) { return this.show(message, "info", duration); } warning(message, duration) { return this.show(message, "warning", duration); } } // Create global instance window.Notifications = window.Notifications || new NotificationManager(); // Legacy compatibility window.showNotification = function (message, type = "info") { window.Notifications.show(message, type); }; })();