/** * TXAToast - Liquid Glass Toast Notification System * Hỗ trợ đa vị trí, bypass CSP qua Trusted Types & SVG Inline */ class TXAToastSystem { constructor() { this.containers = {}; this.policy = null; this.initPolicy(); this.injectStyles(); } initPolicy() { if (typeof window !== 'undefined' && window.trustedTypes) { try { this.policy = window.trustedTypes.createPolicy('txatoast-policy', { createHTML: (string) => string }); } catch (e) { this.policy = window.trustedTypes.defaultPolicy || { createHTML: (string) => string }; } } } getContainer(position = 'top-right') { if (typeof document === 'undefined') return null; const id = `txatoast-container-${position}`; let container = document.getElementById(id); if (!container) { container = document.createElement('div'); container.id = id; const posStyles = { 'top-right': 'top: 24px; right: 24px;', 'top-left': 'top: 24px; left: 24px;', 'bottom-right': 'bottom: 24px; right: 24px;', 'bottom-left': 'bottom: 24px; left: 24px;', 'top-center': 'top: 24px; left: 50%; transform: translateX(-50%); align-items: center;', 'bottom-center': 'bottom: 24px; left: 50%; transform: translateX(-50%); align-items: center;' }; container.style.cssText = ` position: fixed; z-index: 2147483647; display: flex; flex-direction: column; gap: 14px; pointer-events: none; max-width: 380px; width: 100%; ${posStyles[position] || posStyles['top-right']} `; document.body.appendChild(container); } return container; } injectStyles() { if (typeof document === 'undefined' || document.getElementById('txatoast-styles')) return; const style = document.createElement('style'); style.id = 'txatoast-styles'; style.textContent = ` .txa-toast { pointer-events: auto; position: relative; padding: 16px 22px; border-radius: 24px; color: #ffffff; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; font-weight: 600; letter-spacing: -0.01em; display: flex; align-items: center; justify-content: space-between; gap: 14px; overflow: hidden; background: rgba(255, 255, 255, 0.02); backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px); border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.2); opacity: 0; transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); } /* Position-based Animations */ [id*="-right"] .txa-toast { transform: translateX(120%) scale(0.9); } [id*="-left"] .txa-toast { transform: translateX(-120%) scale(0.9); } [id*="-center"] .txa-toast { transform: translateY(50px) scale(0.9); } [id*="top-center"] .txa-toast { transform: translateY(-50px) scale(0.9); } .txa-toast.txa-show { transform: translate(0) scale(1); opacity: 1; } .txa-toast.txa-hide { opacity: 0; scale: 0.85; } .txa-toast::before { content: ''; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle, rgba(255,255,255,0.12) 0%, transparent 60%); pointer-events: none; mix-blend-mode: overlay; } .txa-toast-success { border-color: rgba(16, 185, 129, 0.4); box-shadow: 0 12px 35px rgba(16, 185, 129, 0.2); } .txa-toast-success .txa-icon { color: #10b981; } .txa-toast-error { border-color: rgba(239, 68, 68, 0.4); box-shadow: 0 12px 35px rgba(239, 68, 68, 0.2); } .txa-toast-error .txa-icon { color: #ef4444; } .txa-toast-info { border-color: rgba(59, 130, 246, 0.4); box-shadow: 0 12px 35px rgba(59, 130, 246, 0.2); } .txa-toast-info .txa-icon { color: #3b82f6; } .txa-toast-warning { border-color: rgba(245, 158, 11, 0.4); box-shadow: 0 12px 35px rgba(245, 158, 11, 0.2); } .txa-toast-warning .txa-icon { color: #f59e0b; } .txa-content-wrapper { display: flex; align-items: center; gap: 12px; flex-grow: 1; } .txa-icon { display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .txa-message { line-height: 1.4; text-shadow: 0 1px 2px rgba(0,0,0,0.6); } .txa-close { background: transparent; border: none; color: rgba(255, 255, 255, 0.4); cursor: pointer; padding: 6px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; border-radius: 50%; } .txa-close:hover { color: #ffffff; background: rgba(255,255,255,0.1); transform: rotate(90deg); } .txa-progress-bar { position: absolute; bottom: 0; left: 0; height: 3px; width: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent); transform-origin: left; transform: scaleX(1); } `; document.head.appendChild(style); } show(message, type = 'info', duration = 3500, position = 'top-right') { const container = this.getContainer(position); if (!container) return; const svgIcons = { success: ``, error: ``, info: ``, warning: `` }; const toast = document.createElement('div'); toast.className = `txa-toast txa-toast-${type}`; const rawHTML = `