/** * ToastManager - Sistema de Notificações Visuais * Gerencia notificações toast não-intrusivas para o usuário */ class ToastManager { constructor() { this.container = null; this.toasts = new Map(); this.config = { position: 'top-right', duration: 5000, maxToasts: 5, animationDuration: 300 }; this.init(); } init() { this.createContainer(); console.log('🍞 ToastManager inicializado'); } createContainer() { // Remover container existente se houver const existingContainer = document.querySelector('.toast-container'); if (existingContainer) { existingContainer.remove(); } // Criar novo container this.container = document.createElement('div'); this.container.className = `toast-container toast-${this.config.position}`; this.container.style.cssText = ` position: fixed; z-index: 10000; pointer-events: none; `; // Definir posição baseada na configuração this.setContainerPosition(); document.body.appendChild(this.container); } setContainerPosition() { if (!this.container) return; const positions = { 'top-right': { top: '20px', right: '20px', bottom: 'auto', left: 'auto' }, 'top-left': { top: '20px', left: '20px', bottom: 'auto', right: 'auto' }, 'top-center': { top: '20px', left: '50%', transform: 'translateX(-50%)', bottom: 'auto', right: 'auto' }, 'bottom-right': { bottom: '20px', right: '20px', top: 'auto', left: 'auto' }, 'bottom-left': { bottom: '20px', left: '20px', top: 'auto', right: 'auto' }, 'bottom-center': { bottom: '20px', left: '50%', transform: 'translateX(-50%)', top: 'auto', right: 'auto' } }; const pos = positions[this.config.position] || positions['top-right']; Object.assign(this.container.style, pos); } show(message, type = 'info', duration = null, options = {}) { const toast = this.createToast(message, type, options); const toastId = toast.dataset.toastId; // Adicionar ao container this.container.appendChild(toast); // Armazenar referência this.toasts.set(toastId, toast); // Limitar número de toasts this.limitToasts(); // Animar entrada requestAnimationFrame(() => { toast.classList.add('show'); }); // Configurar tempo de remoção const autoHideDuration = duration !== null ? duration : this.config.duration; if (autoHideDuration > 0) { setTimeout(() => { this.hide(toastId); }, autoHideDuration); } return toastId; } createToast(message, type, options) { const toast = document.createElement('div'); const toastId = 'toast_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); toast.dataset.toastId = toastId; toast.className = `toast toast-${type}`; toast.style.cssText = ` background: var(--toast-bg, #333); color: var(--toast-color, #fff); padding: 16px 20px; margin-bottom: 10px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; line-height: 1.4; max-width: 400px; min-width: 300px; opacity: 0; transform: translateX(100%); transition: all ${this.config.animationDuration}ms ease; pointer-events: auto; position: relative; overflow: hidden; `; // Definir cores baseadas no tipo const colors = { success: { bg: '#22c55e', color: '#fff' }, error: { bg: '#ef4444', color: '#fff' }, warning: { bg: '#f59e0b', color: '#fff' }, info: { bg: '#3b82f6', color: '#fff' } }; const colorConfig = colors[type] || colors.info; toast.style.setProperty('--toast-bg', colorConfig.bg); toast.style.setProperty('--toast-color', colorConfig.color); // Adicionar ícone baseado no tipo const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; const icon = options.icon || icons[type] || icons.info; toast.innerHTML = `
${icon}
${message}
`; return toast; } limitToasts() { if (this.toasts.size <= this.config.maxToasts) return; const toastsArray = Array.from(this.toasts.entries()); const toRemove = toastsArray.slice(0, toastsArray.length - this.config.maxToasts); toRemove.forEach(([toastId]) => { this.hide(toastId); }); } hide(toastId) { const toast = this.toasts.get(toastId); if (!toast) return; toast.classList.remove('show'); setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } this.toasts.delete(toastId); }, this.config.animationDuration); } hideAll() { this.toasts.forEach((toast, toastId) => { this.hide(toastId); }); } // Métodos de conveniência success(message, duration, options) { return this.show(message, 'success', duration, options); } error(message, duration, options) { return this.show(message, 'error', duration, options); } warning(message, duration, options) { return this.show(message, 'warning', duration, options); } info(message, duration, options) { return this.show(message, 'info', duration, options); } // Configurar posição setPosition(position) { if (['top-right', 'top-left', 'top-center', 'bottom-right', 'bottom-left', 'bottom-center'].includes(position)) { this.config.position = position; this.createContainer(); } } // Obter estatísticas getStats() { return { activeToasts: this.toasts.size, maxToasts: this.config.maxToasts, position: this.config.position, duration: this.config.duration }; } } // Tornar disponível globalmente window.ToastManager = ToastManager; window.toastManager = new ToastManager();