/** * Zentrales Glassmorphism-Benachrichtigungssystem für MYP Platform * Vereinheitlicht alle Notification-Typen mit modernem Design */ class GlassmorphismNotificationSystem { constructor() { this.notifications = new Map(); this.toastCounter = 0; this.soundEnabled = localStorage.getItem('myp-notification-sound') !== 'false'; this.animationsEnabled = !window.matchMedia('(prefers-reduced-motion: reduce)').matches; // Callback-Registry für Actions hinzufügen this.actionCallbacks = new Map(); this.callbackCounter = 0; this.init(); this.setupGlobalFunctions(); this.injectStyles(); } init() { // Container für Toasts erstellen this.createToastContainer(); // Event-Listener für Einstellungen this.setupEventListeners(); console.log('🎨 Glassmorphism Notification System initialisiert'); } createToastContainer() { if (!document.getElementById('glassmorphism-toast-container')) { const container = document.createElement('div'); container.id = 'glassmorphism-toast-container'; container.className = 'notifications-container'; document.body.appendChild(container); } } setupGlobalFunctions() { // Globale Funktionen überschreiben für einheitliches Design window.showFlashMessage = this.showToast.bind(this); window.showToast = this.showToast.bind(this); window.showNotification = this.showToast.bind(this); window.showSuccessMessage = (msg, duration) => this.showToast(msg, 'success', duration); window.showErrorMessage = (msg, duration) => this.showToast(msg, 'error', duration); window.showWarningMessage = (msg, duration) => this.showToast(msg, 'warning', duration); window.showInfoMessage = (msg, duration) => this.showToast(msg, 'info', duration); window.showSuccessNotification = (msg) => this.showToast(msg, 'success'); // Spezielle Funktionen window.showPersistentAlert = this.showPersistentAlert.bind(this); window.showConfirmationToast = this.showConfirmationToast.bind(this); window.showProgressToast = this.showProgressToast.bind(this); // Globale Callback-Ausführungsfunktion window.executeNotificationCallback = this.executeCallback.bind(this); } setupEventListeners() { // Keyboard Shortcuts document.addEventListener('keydown', (e) => { // ESC schließt alle Toasts if (e.key === 'Escape') { this.closeAllToasts(); } // Ctrl/Cmd + Shift + N öffnet Benachrichtigungseinstellungen if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'N') { this.showNotificationSettings(); } }); // Geräte-Rotation window.addEventListener('orientationchange', () => { setTimeout(() => this.repositionAllToasts(), 200); }); } /** * Zeigt eine Glassmorphism-Toast-Benachrichtigung */ showToast(message, type = 'info', duration = 5000, options = {}) { // DND-Check if (window.dndManager?.isEnabled) { window.dndManager.suppressNotification(message, type); return null; } const toastId = `glass-toast-${++this.toastCounter}`; const toast = this.createToastElement(toastId, message, type, duration, options); this.addToContainer(toast); this.animateIn(toast); this.scheduleRemoval(toastId, duration, options.persistent); // Sound abspielen if (this.soundEnabled && options.playSound !== false) { this.playNotificationSound(type); } // Browser-Notification (optional) if (options.browserNotification && 'Notification' in window) { this.showBrowserNotification(message, type, options); } return toastId; } createToastElement(toastId, message, type, duration, options) { const toast = document.createElement('div'); toast.id = toastId; toast.className = `glassmorphism-toast notification notification-${type}`; // Accessibility toast.setAttribute('role', 'alert'); toast.setAttribute('aria-live', 'polite'); toast.setAttribute('aria-atomic', 'true'); const iconSvg = this.getIconSvg(type); const progressBar = duration > 0 && !options.persistent ? this.createProgressBar(duration) : ''; toast.innerHTML = `
${iconSvg}
${options.title ? `
${options.title}
` : ''}
${message}
${options.actions ? this.createActionButtons(options.actions, toastId) : ''}
${progressBar}
`; // Hover-Events für Auto-Close-Pausierung if (duration > 0 && !options.persistent) { this.setupHoverEvents(toast, toastId); } this.notifications.set(toastId, { element: toast, type, message, timestamp: Date.now(), options }); return toast; } createProgressBar(duration) { return `
`; } createActionButtons(actions, toastId) { return actions.map(action => { // Callback in Registry speichern falls vorhanden let callbackId = ''; if (action.callback && typeof action.callback === 'function') { callbackId = `callback-${++this.callbackCounter}`; this.actionCallbacks.set(callbackId, action.callback); } return ` `; }).join(''); } getIconSvg(type) { const icons = { success: ` `, error: ` `, warning: ` `, info: ` `, loading: ` ` }; return icons[type] || icons.info; } addToContainer(toast) { const container = document.getElementById('glassmorphism-toast-container'); if (container) { container.appendChild(toast); this.repositionAllToasts(); } } animateIn(toast) { if (!this.animationsEnabled) { toast.classList.add('show'); return; } // Erweiterte Eingangsanimation mit Glassmorphism-Effekten toast.style.transform = 'translateX(120%) scale(0.8) rotateY(15deg)'; toast.style.opacity = '0'; toast.style.filter = 'blur(8px)'; // Trigger animation requestAnimationFrame(() => { toast.style.transition = 'all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)'; toast.style.transform = 'translateX(0) scale(1) rotateY(0deg)'; toast.style.opacity = '1'; toast.style.filter = 'blur(0px)'; toast.classList.add('show'); }); } setupHoverEvents(toast, toastId) { let timeoutId; let isPaused = false; const notification = this.notifications.get(toastId); if (!notification) return; const pauseTimer = () => { isPaused = true; clearTimeout(timeoutId); const progressBar = toast.querySelector('.toast-progress-bar'); if (progressBar) { progressBar.style.animationPlayState = 'paused'; } // Hover-Effekt verstärken toast.style.transform = 'translateY(-4px) scale(1.03)'; toast.style.filter = 'brightness(1.1) saturate(1.1)'; }; const resumeTimer = () => { isPaused = false; const progressBar = toast.querySelector('.toast-progress-bar'); if (progressBar) { progressBar.style.animationPlayState = 'running'; } // Hover-Effekt zurücksetzen toast.style.transform = 'translateY(0) scale(1)'; toast.style.filter = 'brightness(1) saturate(1)'; }; toast.addEventListener('mouseenter', pauseTimer); toast.addEventListener('mouseleave', resumeTimer); toast.addEventListener('focus', pauseTimer); toast.addEventListener('blur', resumeTimer); } scheduleRemoval(toastId, duration, persistent = false) { if (persistent || duration <= 0) return; setTimeout(() => { this.closeToast(toastId); }, duration); } closeToast(toastId) { const notification = this.notifications.get(toastId); if (!notification) return; const toast = notification.element; if (this.animationsEnabled) { // Verbesserte Ausgangsanimation toast.style.transition = 'all 0.6s cubic-bezier(0.4, 0, 1, 1)'; toast.style.transform = 'translateX(120%) scale(0.8) rotateY(-15deg)'; toast.style.opacity = '0'; toast.style.filter = 'blur(4px)'; setTimeout(() => { this.removeToast(toastId); }, 600); } else { this.removeToast(toastId); } } removeToast(toastId) { const notification = this.notifications.get(toastId); if (!notification) return; if (notification.element.parentNode) { notification.element.parentNode.removeChild(notification.element); } this.notifications.delete(toastId); // Alle zugehörigen Callbacks löschen this.actionCallbacks.forEach((callback, callbackId) => { if (callbackId.includes(toastId)) { this.actionCallbacks.delete(callbackId); } }); this.repositionAllToasts(); } repositionAllToasts() { const container = document.getElementById('glassmorphism-toast-container'); if (!container) return; const toasts = Array.from(container.children); toasts.forEach((toast, index) => { // Gestaffelte Positionierung mit smooth transition toast.style.top = `${1 + index * 3.75}rem`; toast.style.zIndex = 1000 - index; // Leichter Versatz für Tiefeneffekt if (index > 0) { toast.style.transform = `scale(${1 - index * 0.015})`; toast.style.opacity = `${1 - index * 0.08}`; } }); } closeAllToasts() { const toastIds = Array.from(this.notifications.keys()); toastIds.forEach((id, index) => { setTimeout(() => this.closeToast(id), index * 100); }); } /** * Spezielle Toast-Typen */ showConfirmationToast(message, onConfirm, onCancel = null, options = {}) { const confirmCallback = () => { if (typeof onConfirm === 'function') { try { onConfirm(); } catch (error) { console.error('Fehler beim Ausführen der Bestätigungslogik:', error); } } }; const cancelCallback = () => { if (typeof onCancel === 'function') { try { onCancel(); } catch (error) { console.error('Fehler beim Ausführen der Abbruchlogik:', error); } } }; const actions = [ { text: options.confirmText || 'Bestätigen', type: 'primary', callback: confirmCallback, closeAfter: true } ]; // Cancel-Button nur hinzufügen wenn Callback vorhanden ist if (onCancel || options.cancelText) { actions.push({ text: options.cancelText || 'Abbrechen', type: 'secondary', callback: cancelCallback, closeAfter: true }); } return this.showToast(message, 'warning', 0, { persistent: true, title: options.title || 'Bestätigung erforderlich', actions: actions, ...options }); } showProgressToast(message, type = 'info', options = {}) { const toastId = this.showToast(message, 'loading', 0, { persistent: true, title: options.title || 'Verarbeitung...', ...options }); return { id: toastId, updateProgress: (percent) => this.updateProgressToast(toastId, percent), updateMessage: (newMessage) => this.updateToastMessage(toastId, newMessage), complete: (finalMessage, finalType = 'success') => { this.closeToast(toastId); if (finalMessage) { this.showToast(finalMessage, finalType, 3000); } } }; } updateProgressToast(toastId, percent) { const notification = this.notifications.get(toastId); if (!notification) return; let progressBar = notification.element.querySelector('.toast-progress-bar'); if (!progressBar) { const progressContainer = document.createElement('div'); progressContainer.className = 'toast-progress'; progressContainer.innerHTML = `
`; notification.element.querySelector('.toast-content').appendChild(progressContainer); progressBar = progressContainer.querySelector('.toast-progress-bar'); } progressBar.style.width = `${Math.min(100, Math.max(0, percent))}%`; } updateToastMessage(toastId, newMessage) { const notification = this.notifications.get(toastId); if (!notification) return; const messageEl = notification.element.querySelector('.toast-message'); if (messageEl) { messageEl.textContent = newMessage; } } showPersistentAlert(message, type = 'warning', options = {}) { return this.showToast(message, type, 0, { persistent: true, title: options.title || 'Wichtiger Hinweis', actions: [{ text: 'Verstanden', type: 'primary', onClick: '' }], ...options }); } /** * Browser-Benachrichtigungen */ async showBrowserNotification(message, type, options = {}) { if (!('Notification' in window)) return null; if (Notification.permission === 'granted') { return new Notification(options.title || 'MYP Platform', { body: message, icon: '/static/icons/notification-icon.png', badge: '/static/icons/badge-icon.png', tag: `myp-${type}`, ...options.browserOptions }); } else if (Notification.permission === 'default') { const permission = await Notification.requestPermission(); if (permission === 'granted') { return this.showBrowserNotification(message, type, options); } } return null; } /** * Sound-System */ playNotificationSound(type) { if (!this.soundEnabled) return; try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); // Verfeinerte und melodiösere Frequenzen const frequencies = { success: [523.25, 659.25, 783.99, 880], // C-E-G-A (Dur-Akkord mit Terz) error: [440, 370, 311], // A-F#-D# (dissonanter Moll-Akkord) warning: [493.88, 587.33, 659.25], // B-D-E (warnendes Motiv) info: [523.25, 659.25], // C-E (freundlich) loading: [392, 440, 493.88, 523.25] // G-A-B-C (aufsteigende Melodie) }; const freq = frequencies[type] || frequencies.info; freq.forEach((f, i) => { setTimeout(() => { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); const filterNode = audioContext.createBiquadFilter(); // Sanfterer Sound mit Filter oscillator.connect(filterNode); filterNode.connect(gainNode); gainNode.connect(audioContext.destination); // Filtereinstellungen für weicheren Klang filterNode.type = 'lowpass'; filterNode.frequency.setValueAtTime(2000, audioContext.currentTime); filterNode.Q.setValueAtTime(1, audioContext.currentTime); oscillator.frequency.setValueAtTime(f, audioContext.currentTime); // Sanftere Wellenformen je nach Typ oscillator.type = type === 'error' ? 'triangle' : 'sine'; // Dezentere Lautstärke und sanftere Envelope const baseVolume = type === 'error' ? 0.06 : 0.08; gainNode.gain.setValueAtTime(0, audioContext.currentTime); gainNode.gain.linearRampToValueAtTime(baseVolume, audioContext.currentTime + 0.1); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.3); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.3); }, i * 120); // Langsamere Abfolge für melodiöseren Effekt }); // Zusätzlicher subtiler Reverb-Effekt für Success if (type === 'success') { setTimeout(() => { const reverb = audioContext.createConvolver(); const impulse = audioContext.createBuffer(2, audioContext.sampleRate * 0.5, audioContext.sampleRate); for (let channel = 0; channel < impulse.numberOfChannels; channel++) { const channelData = impulse.getChannelData(channel); for (let i = 0; i < channelData.length; i++) { channelData[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / channelData.length, 2); } } reverb.buffer = impulse; const finalOsc = audioContext.createOscillator(); const finalGain = audioContext.createGain(); finalOsc.connect(reverb); reverb.connect(finalGain); finalGain.connect(audioContext.destination); finalOsc.frequency.setValueAtTime(1046.5, audioContext.currentTime); // Hohe C finalOsc.type = 'sine'; finalGain.gain.setValueAtTime(0, audioContext.currentTime); finalGain.gain.linearRampToValueAtTime(0.03, audioContext.currentTime + 0.1); finalGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.8); finalOsc.start(audioContext.currentTime); finalOsc.stop(audioContext.currentTime + 0.8); }, freq.length * 120); } } catch (error) { // Silent fail } } toggleSound() { this.soundEnabled = !this.soundEnabled; localStorage.setItem('myp-notification-sound', this.soundEnabled.toString()); this.showToast( `Benachrichtigungstöne ${this.soundEnabled ? 'aktiviert' : 'deaktiviert'}`, 'info', 2000 ); } /** * Einstellungen */ showNotificationSettings() { const settingsHTML = `

Benachrichtigungseinstellungen

`; this.showToast(settingsHTML, 'info', 0, { persistent: true, title: 'Einstellungen', actions: [{ text: 'Schließen', type: 'secondary', onClick: '' }] }); } toggleAnimations() { this.animationsEnabled = !this.animationsEnabled; this.showToast( `Animationen ${this.animationsEnabled ? 'aktiviert' : 'deaktiviert'}`, 'info', 2000 ); } /** * CSS-Styles einbetten */ injectStyles() { if (document.getElementById('glassmorphism-notification-styles')) return; const styles = document.createElement('style'); styles.id = 'glassmorphism-notification-styles'; styles.textContent = ` .glassmorphism-toast { margin-bottom: 0.625rem; transform: translateX(100%); opacity: 0; transition: all 0.7s cubic-bezier(0.34, 1.56, 0.64, 1); will-change: transform, opacity, filter; /* Erweiterte Premium Glassmorphism-Eigenschaften */ backdrop-filter: blur(50px) saturate(200%) brightness(120%) contrast(110%); -webkit-backdrop-filter: blur(50px) saturate(200%) brightness(120%) contrast(110%); box-shadow: 0 32px 64px rgba(0, 0, 0, 0.1), 0 16px 32px rgba(0, 0, 0, 0.06), 0 6px 12px rgba(0, 0, 0, 0.05), inset 0 2px 0 rgba(255, 255, 255, 0.4), inset 0 1px 2px rgba(255, 255, 255, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.18); border-radius: 1.5rem; overflow: hidden; position: relative; /* Subtile Farbverläufe */ background: linear-gradient(145deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.06) 25%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.05) 75%, rgba(255, 255, 255, 0.08) 100%); } /* Erweiterte Glassmorphism-Overlay-Effekte */ .glassmorphism-toast::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.25) 0%, transparent 35%), radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.15) 0%, transparent 35%), radial-gradient(circle at 50% 10%, rgba(255, 255, 255, 0.1) 0%, transparent 40%), linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, transparent 60%), conic-gradient(from 180deg at 50% 50%, rgba(255, 255, 255, 0.05) 0deg, rgba(255, 255, 255, 0.1) 45deg, rgba(255, 255, 255, 0.05) 90deg, rgba(255, 255, 255, 0.08) 135deg, rgba(255, 255, 255, 0.05) 180deg, rgba(255, 255, 255, 0.12) 225deg, rgba(255, 255, 255, 0.05) 270deg, rgba(255, 255, 255, 0.08) 315deg, rgba(255, 255, 255, 0.05) 360deg); pointer-events: none; opacity: 0; transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1); animation: subtle-shimmer 8s ease-in-out infinite; } /* Elegante Shimmer-Animation */ @keyframes subtle-shimmer { 0%, 100% { opacity: 0; transform: scale(1) rotate(0deg); } 25% { opacity: 0.3; transform: scale(1.01) rotate(1deg); } 50% { opacity: 0.6; transform: scale(1.02) rotate(0deg); } 75% { opacity: 0.3; transform: scale(1.01) rotate(-1deg); } } .glassmorphism-toast:hover::before { opacity: 1; animation-play-state: paused; } /* Floating-Partikel-Effekt bei Hover */ .glassmorphism-toast::after { content: ''; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.3) 1px, transparent 1px), radial-gradient(circle at 60% 80%, rgba(255, 255, 255, 0.2) 1px, transparent 1px), radial-gradient(circle at 80% 30%, rgba(255, 255, 255, 0.25) 1px, transparent 1px), radial-gradient(circle at 30% 70%, rgba(255, 255, 255, 0.15) 1px, transparent 1px); opacity: 0; transition: opacity 0.6s ease; animation: floating-particles 12s linear infinite; pointer-events: none; } @keyframes floating-particles { 0% { transform: translate(0, 0) rotate(0deg); } 25% { transform: translate(-10px, -10px) rotate(90deg); } 50% { transform: translate(0, -20px) rotate(180deg); } 75% { transform: translate(10px, -10px) rotate(270deg); } 100% { transform: translate(0, 0) rotate(360deg); } } .glassmorphism-toast:hover::after { opacity: 1; } .glassmorphism-toast.show { transform: translateX(0); opacity: 1; } /* Typ-spezifische erweiterte Premium Glassmorphism-Effekte */ .glassmorphism-toast.notification-success { background: linear-gradient(145deg, rgba(34, 197, 94, 0.18) 0%, rgba(134, 239, 172, 0.12) 20%, rgba(16, 185, 129, 0.15) 40%, rgba(34, 197, 94, 0.08) 60%, rgba(134, 239, 172, 0.1) 80%, rgba(16, 185, 129, 0.06) 100%); border: 1px solid rgba(34, 197, 94, 0.3); box-shadow: 0 40px 80px rgba(34, 197, 94, 0.15), 0 20px 40px rgba(34, 197, 94, 0.08), 0 8px 16px rgba(16, 185, 129, 0.1), inset 0 3px 0 rgba(255, 255, 255, 0.6), inset 0 1px 2px rgba(134, 239, 172, 0.4), 0 0 0 1px rgba(34, 197, 94, 0.2); color: rgba(5, 95, 70, 0.95); } .dark .glassmorphism-toast.notification-success { color: rgba(134, 239, 172, 0.95); background: linear-gradient(145deg, rgba(34, 197, 94, 0.25) 0%, rgba(134, 239, 172, 0.15) 30%, rgba(34, 197, 94, 0.12) 70%, rgba(16, 185, 129, 0.08) 100%); } .glassmorphism-toast.notification-error { background: linear-gradient(145deg, rgba(239, 68, 68, 0.18) 0%, rgba(252, 165, 165, 0.12) 20%, rgba(220, 38, 38, 0.15) 40%, rgba(239, 68, 68, 0.08) 60%, rgba(252, 165, 165, 0.1) 80%, rgba(220, 38, 38, 0.06) 100%); border: 1px solid rgba(239, 68, 68, 0.3); box-shadow: 0 40px 80px rgba(239, 68, 68, 0.15), 0 20px 40px rgba(239, 68, 68, 0.08), 0 8px 16px rgba(220, 38, 38, 0.1), inset 0 3px 0 rgba(255, 255, 255, 0.6), inset 0 1px 2px rgba(252, 165, 165, 0.4), 0 0 0 1px rgba(239, 68, 68, 0.2); color: rgba(153, 27, 27, 0.95); } .dark .glassmorphism-toast.notification-error { color: rgba(252, 165, 165, 0.95); background: linear-gradient(145deg, rgba(239, 68, 68, 0.25) 0%, rgba(252, 165, 165, 0.15) 30%, rgba(239, 68, 68, 0.12) 70%, rgba(220, 38, 38, 0.08) 100%); } .glassmorphism-toast.notification-warning { background: linear-gradient(145deg, rgba(245, 158, 11, 0.18) 0%, rgba(252, 211, 77, 0.12) 20%, rgba(217, 119, 6, 0.15) 40%, rgba(245, 158, 11, 0.08) 60%, rgba(252, 211, 77, 0.1) 80%, rgba(217, 119, 6, 0.06) 100%); border: 1px solid rgba(245, 158, 11, 0.3); box-shadow: 0 40px 80px rgba(245, 158, 11, 0.15), 0 20px 40px rgba(245, 158, 11, 0.08), 0 8px 16px rgba(217, 119, 6, 0.1), inset 0 3px 0 rgba(255, 255, 255, 0.6), inset 0 1px 2px rgba(252, 211, 77, 0.4), 0 0 0 1px rgba(245, 158, 11, 0.2); color: rgba(146, 64, 14, 0.95); } .dark .glassmorphism-toast.notification-warning { color: rgba(252, 211, 77, 0.95); background: linear-gradient(145deg, rgba(245, 158, 11, 0.25) 0%, rgba(252, 211, 77, 0.15) 30%, rgba(245, 158, 11, 0.12) 70%, rgba(217, 119, 6, 0.08) 100%); } .glassmorphism-toast.notification-info { background: linear-gradient(145deg, rgba(59, 130, 246, 0.18) 0%, rgba(147, 197, 253, 0.12) 20%, rgba(37, 99, 235, 0.15) 40%, rgba(59, 130, 246, 0.08) 60%, rgba(147, 197, 253, 0.1) 80%, rgba(37, 99, 235, 0.06) 100%); border: 1px solid rgba(59, 130, 246, 0.3); box-shadow: 0 40px 80px rgba(59, 130, 246, 0.15), 0 20px 40px rgba(59, 130, 246, 0.08), 0 8px 16px rgba(37, 99, 235, 0.1), inset 0 3px 0 rgba(255, 255, 255, 0.6), inset 0 1px 2px rgba(147, 197, 253, 0.4), 0 0 0 1px rgba(59, 130, 246, 0.2); color: rgba(30, 64, 175, 0.95); } .dark .glassmorphism-toast.notification-info { color: rgba(147, 197, 253, 0.95); background: linear-gradient(145deg, rgba(59, 130, 246, 0.25) 0%, rgba(147, 197, 253, 0.15) 30%, rgba(59, 130, 246, 0.12) 70%, rgba(37, 99, 235, 0.08) 100%); } .glassmorphism-toast.notification-loading { background: linear-gradient(145deg, rgba(99, 102, 241, 0.18) 0%, rgba(165, 180, 252, 0.12) 20%, rgba(79, 70, 229, 0.15) 40%, rgba(99, 102, 241, 0.08) 60%, rgba(165, 180, 252, 0.1) 80%, rgba(79, 70, 229, 0.06) 100%); border: 1px solid rgba(99, 102, 241, 0.3); box-shadow: 0 40px 80px rgba(99, 102, 241, 0.15), 0 20px 40px rgba(99, 102, 241, 0.08), 0 8px 16px rgba(79, 70, 229, 0.1), inset 0 3px 0 rgba(255, 255, 255, 0.6), inset 0 1px 2px rgba(165, 180, 252, 0.4), 0 0 0 1px rgba(99, 102, 241, 0.2); color: rgba(55, 48, 163, 0.95); } .dark .glassmorphism-toast.notification-loading { color: rgba(165, 180, 252, 0.95); background: linear-gradient(145deg, rgba(99, 102, 241, 0.25) 0%, rgba(165, 180, 252, 0.15) 30%, rgba(99, 102, 241, 0.12) 70%, rgba(79, 70, 229, 0.08) 100%); } /* Dark Mode Premium Anpassungen */ .dark .glassmorphism-toast { backdrop-filter: blur(80px) saturate(200%) brightness(115%) contrast(125%); -webkit-backdrop-filter: blur(80px) saturate(200%) brightness(115%) contrast(125%); box-shadow: 0 40px 80px rgba(0, 0, 0, 0.4), 0 20px 40px rgba(0, 0, 0, 0.3), 0 8px 16px rgba(0, 0, 0, 0.2), inset 0 3px 0 rgba(255, 255, 255, 0.15), inset 0 1px 2px rgba(255, 255, 255, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.08); background: linear-gradient(145deg, rgba(0, 0, 0, 0.25) 0%, rgba(15, 15, 15, 0.18) 25%, rgba(0, 0, 0, 0.22) 50%, rgba(10, 10, 10, 0.15) 75%, rgba(0, 0, 0, 0.2) 100%); } .dark .glassmorphism-toast::before { background: radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.12) 0%, transparent 35%), radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.08) 0%, transparent 35%), radial-gradient(circle at 50% 10%, rgba(255, 255, 255, 0.06) 0%, transparent 40%), linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, transparent 60%), conic-gradient(from 180deg at 50% 50%, rgba(255, 255, 255, 0.03) 0deg, rgba(255, 255, 255, 0.06) 45deg, rgba(255, 255, 255, 0.03) 90deg, rgba(255, 255, 255, 0.05) 135deg, rgba(255, 255, 255, 0.03) 180deg, rgba(255, 255, 255, 0.07) 225deg, rgba(255, 255, 255, 0.03) 270deg, rgba(255, 255, 255, 0.05) 315deg, rgba(255, 255, 255, 0.03) 360deg); } .dark .glassmorphism-toast::after { background: radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.15) 1px, transparent 1px), radial-gradient(circle at 60% 80%, rgba(255, 255, 255, 0.1) 1px, transparent 1px), radial-gradient(circle at 80% 30%, rgba(255, 255, 255, 0.12) 1px, transparent 1px), radial-gradient(circle at 30% 70%, rgba(255, 255, 255, 0.08) 1px, transparent 1px); } .toast-content { position: relative; overflow: hidden; padding: 1rem; border-radius: inherit; } .toast-header { display: flex; align-items: flex-start; gap: 0.875rem; } .toast-icon { flex-shrink: 0; width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: linear-gradient(145deg, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.3) 100%); backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%); border: 1px solid rgba(255, 255, 255, 0.4); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(0, 0, 0, 0.03); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; overflow: hidden; } /* Icon-Glow-Effekt */ .toast-icon::before { content: ''; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle, rgba(255, 255, 255, 0.25) 0%, transparent 70%); opacity: 0; transition: opacity 0.3s ease; animation: icon-pulse 3s ease-in-out infinite; } @keyframes icon-pulse { 0%, 100% { opacity: 0; transform: scale(0.8); } 50% { opacity: 0.4; transform: scale(1.1); } } .toast-icon:hover::before { opacity: 1; animation-play-state: paused; } .toast-icon:hover { transform: scale(1.08) rotate(8deg); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08), 0 4px 8px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.7), inset 0 -1px 0 rgba(0, 0, 0, 0.05); } .dark .toast-icon { background: linear-gradient(145deg, rgba(0, 0, 0, 0.35) 0%, rgba(15, 15, 15, 0.25) 50%, rgba(0, 0, 0, 0.3) 100%); border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(255, 255, 255, 0.03); } .dark .toast-icon::before { background: radial-gradient(circle, rgba(255, 255, 255, 0.12) 0%, transparent 70%); } .toast-body { flex: 1; min-width: 0; } .toast-title { font-weight: 600; font-size: 0.875rem; margin-bottom: 0.375rem; line-height: 1.3; letter-spacing: 0.01em; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); background: linear-gradient(135deg, currentColor 0%, currentColor 100%); -webkit-background-clip: text; background-clip: text; } .toast-message { font-size: 0.8125rem; line-height: 1.4; opacity: 0.9; font-weight: 450; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); } .toast-actions { display: flex; gap: 0.5rem; align-items: flex-start; flex-shrink: 0; } .toast-action-btn { padding: 0.5rem 0.875rem; border-radius: 0.75rem; font-size: 0.75rem; font-weight: 500; border: none; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; align-items: center; gap: 0.375rem; background: linear-gradient(145deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.12) 50%, rgba(255, 255, 255, 0.2) 100%); color: inherit; backdrop-filter: blur(16px) saturate(130%); -webkit-backdrop-filter: blur(16px) saturate(130%); border: 1px solid rgba(255, 255, 255, 0.3); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); position: relative; overflow: hidden; } /* Button-Shimmer-Effekt */ .toast-action-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%); transition: left 0.4s cubic-bezier(0.4, 0, 0.2, 1); } .toast-action-btn:hover::before { left: 100%; } .toast-action-btn:hover { background: linear-gradient(145deg, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.3) 100%); transform: translateY(-2px) scale(1.04); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.6); border-color: rgba(255, 255, 255, 0.5); } .toast-action-btn:active { transform: translateY(-1px) scale(1.02); transition: transform 0.1s ease; } .toast-action-primary { background: linear-gradient(145deg, rgba(59, 130, 246, 0.8) 0%, rgba(37, 99, 235, 0.85) 50%, rgba(59, 130, 246, 0.75) 100%); color: white; border-color: rgba(59, 130, 246, 0.6); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2), 0 1px 3px rgba(59, 130, 246, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.25); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); } .toast-action-primary::before { background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.25) 50%, transparent 100%); } .toast-action-primary:hover { background: linear-gradient(145deg, rgba(59, 130, 246, 0.9) 0%, rgba(37, 99, 235, 0.95) 50%, rgba(59, 130, 246, 0.85) 100%); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25), 0 2px 6px rgba(59, 130, 246, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.3); } .dark .toast-action-btn { background: linear-gradient(145deg, rgba(0, 0, 0, 0.35) 0%, rgba(15, 15, 15, 0.25) 50%, rgba(0, 0, 0, 0.3) 100%); border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.08); } .dark .toast-action-btn:hover { background: linear-gradient(145deg, rgba(0, 0, 0, 0.45) 0%, rgba(15, 15, 15, 0.35) 50%, rgba(0, 0, 0, 0.4) 100%); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.12); } .toast-close { padding: 0.375rem; border-radius: 0.625rem; border: none; background: linear-gradient(145deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.12) 100%); color: inherit; cursor: pointer; opacity: 0.75; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; align-items: center; justify-content: center; backdrop-filter: blur(16px) saturate(110%); -webkit-backdrop-filter: blur(16px) saturate(110%); border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.3); position: relative; overflow: hidden; } /* Close-Button-Ripple-Effekt */ .toast-close::after { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.3); transform: translate(-50%, -50%); transition: width 0.25s ease, height 0.25s ease; } .toast-close:hover::after { width: 100%; height: 100%; } .toast-close:hover { opacity: 1; background: linear-gradient(145deg, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.22) 100%); transform: scale(1.1) rotate(90deg); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.5); } .toast-close:active { transform: scale(1.05) rotate(90deg); transition: transform 0.1s ease; } .dark .toast-close { background: linear-gradient(145deg, rgba(0, 0, 0, 0.25) 0%, rgba(15, 15, 15, 0.15) 50%, rgba(0, 0, 0, 0.2) 100%); border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.06); } .dark .toast-close::after { background: rgba(255, 255, 255, 0.15); } .dark .toast-close:hover { background: linear-gradient(145deg, rgba(0, 0, 0, 0.35) 0%, rgba(15, 15, 15, 0.25) 50%, rgba(0, 0, 0, 0.4) 100%); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.1); } .toast-progress { position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 50%, rgba(255, 255, 255, 0.08) 100%); overflow: hidden; border-radius: 0 0 1.75rem 1.75rem; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); } .toast-progress-bar { height: 100%; background: linear-gradient(90deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.8) 25%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 255, 255, 0.8) 75%, rgba(255, 255, 255, 0.6) 100%); width: 0%; transition: width 0.25s ease; position: relative; border-radius: inherit; box-shadow: 0 0 8px rgba(255, 255, 255, 0.4), 0 0 4px rgba(255, 255, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.7); overflow: hidden; } /* Progress-Bar-Animationen */ .toast-progress-bar::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.4) 25%, rgba(255, 255, 255, 0.6) 50%, rgba(255, 255, 255, 0.4) 75%, transparent 100%); animation: progress-shimmer 2s ease-in-out infinite; } .toast-progress-bar::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(45deg, transparent 25%, rgba(255, 255, 255, 0.2) 25%, rgba(255, 255, 255, 0.2) 50%, transparent 50%, transparent 75%, rgba(255, 255, 255, 0.2) 75%); background-size: 12px 12px; animation: progress-stripes 0.8s linear infinite; } @keyframes progress-shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } @keyframes progress-stripes { 0% { background-position: 0 0; } 100% { background-position: 12px 0; } } @keyframes toast-progress { from { width: 100%; } to { width: 0%; } } .notification-settings { max-width: 320px; padding: 0.875rem; background: linear-gradient(145deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.06) 50%, rgba(255, 255, 255, 0.1) 100%); border-radius: 1.25rem; backdrop-filter: blur(24px) saturate(140%); -webkit-backdrop-filter: blur(24px) saturate(140%); border: 1px solid rgba(255, 255, 255, 0.25); box-shadow: 0 16px 32px rgba(0, 0, 0, 0.08), 0 6px 12px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.4); } .dark .notification-settings { background: linear-gradient(145deg, rgba(0, 0, 0, 0.35) 0%, rgba(15, 15, 15, 0.25) 50%, rgba(0, 0, 0, 0.3) 100%); border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 16px 32px rgba(0, 0, 0, 0.25), 0 6px 12px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.06); } .notification-settings h3 { margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: inherit; text-align: center; background: linear-gradient(135deg, currentColor 0%, currentColor 100%); -webkit-background-clip: text; background-clip: text; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); } .setting-item { display: flex; align-items: center; gap: 0.75rem; margin: 1rem 0; cursor: pointer; font-size: 0.875rem; font-weight: 450; padding: 0.75rem; border-radius: 0.875rem; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); background: linear-gradient(145deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%); border: 1px solid rgba(255, 255, 255, 0.12); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); position: relative; overflow: hidden; } /* Setting-Item-Hover-Effekt */ .setting-item::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.08) 50%, transparent 100%); transition: left 0.3s ease; } .setting-item:hover::before { left: 100%; } .setting-item:hover { background: linear-gradient(145deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.08) 100%); transform: translateX(6px) scale(1.01); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25); } .dark .setting-item { background: linear-gradient(145deg, rgba(0, 0, 0, 0.18) 0%, rgba(15, 15, 15, 0.12) 100%); border: 1px solid rgba(255, 255, 255, 0.06); } .dark .setting-item:hover { background: linear-gradient(145deg, rgba(0, 0, 0, 0.25) 0%, rgba(15, 15, 15, 0.18) 100%); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.08); } .setting-item input[type="checkbox"] { margin: 0; width: 1.25rem; height: 1.25rem; accent-color: currentColor; cursor: pointer; border-radius: 0.3rem; transition: all 0.2s ease; } .setting-item input[type="checkbox"]:checked { transform: scale(1.05); box-shadow: 0 0 6px rgba(59, 130, 246, 0.3); } /* Responsive Design */ @media (max-width: 640px) { .notifications-container { left: 0.5rem; right: 0.5rem; top: 0.5rem; } .glassmorphism-toast { margin-bottom: 0.5rem; border-radius: 1.25rem; } .toast-content { padding: 0.875rem; } .toast-header { gap: 0.75rem; } .toast-icon { width: 2rem; height: 2rem; } .toast-title { font-size: 0.8125rem; } .toast-message { font-size: 0.75rem; } .toast-action-btn { padding: 0.4rem 0.7rem; font-size: 0.7rem; } .toast-close { padding: 0.3rem; } } /* High Contrast Mode */ @media (prefers-contrast: high) { .glassmorphism-toast { border: 2px solid currentColor; backdrop-filter: none; -webkit-backdrop-filter: none; background: rgba(255, 255, 255, 0.95); } .dark .glassmorphism-toast { background: rgba(0, 0, 0, 0.95); } } /* Reduced Motion */ @media (prefers-reduced-motion: reduce) { .glassmorphism-toast, .toast-icon, .toast-action-btn, .toast-close { transition: none !important; animation: none !important; } .glassmorphism-toast:hover { transform: none !important; } } `; document.head.appendChild(styles); } /** * Behandelt Action-Button-Klicks */ handleActionClick(callbackId, toastId, shouldClose = true) { // Callback ausführen falls vorhanden if (callbackId && this.actionCallbacks.has(callbackId)) { const callback = this.actionCallbacks.get(callbackId); try { callback(); } catch (error) { console.error('Fehler beim Ausführen des Action-Callbacks:', error); } // Callback nach Ausführung löschen this.actionCallbacks.delete(callbackId); } // Toast schließen falls gewünscht if (shouldClose) { this.closeToast(toastId); } } /** * Für Rückwärtskompatibilität - führt Callback-Funktionen aus */ executeCallback(callbackId) { if (this.actionCallbacks.has(callbackId)) { const callback = this.actionCallbacks.get(callbackId); try { callback(); } catch (error) { console.error('Fehler beim Ausführen des Callbacks:', error); } this.actionCallbacks.delete(callbackId); } } } // Globale Instanz erstellen const glassNotificationSystem = new GlassmorphismNotificationSystem(); // Für Rückwärtskompatibilität if (typeof window !== 'undefined') { window.glassNotificationSystem = glassNotificationSystem; window.GlassmorphismNotificationSystem = GlassmorphismNotificationSystem; } // Event für DOM-ready ohne Test-Benachrichtigung document.addEventListener('DOMContentLoaded', () => { console.log('🎨 Glassmorphism Notification System bereit'); });