/** * Toast Notification System * Provides temporary notifications for game events (quest acceptance, errors, etc.) * * Usage: * showToast('Quest accepted!', 'success'); * showToast('Failed to save', 'error'); * showToast('Item added to inventory', 'info'); */ /** * Show a toast notification * @param {string} message - The message to display * @param {string} type - Type of toast: 'success', 'error', or 'info' * @param {number} duration - Duration in ms before auto-dismiss (default: 4000) */ function showToast(message, type = 'info', duration = 4000) { // Create toast container if it doesn't exist let container = document.getElementById('toast-container'); if (!container) { container = document.createElement('div'); container.id = 'toast-container'; container.className = 'toast-container'; document.body.appendChild(container); } // Create toast element const toast = document.createElement('div'); toast.className = `toast toast--${type}`; toast.innerHTML = ` ${escapeHtml(message)} `; // Add to container container.appendChild(toast); // Trigger animation (small delay for DOM to register element) requestAnimationFrame(() => { toast.classList.add('toast--visible'); }); // Auto-remove after duration setTimeout(() => { dismissToast(toast); }, duration); } /** * Dismiss a toast notification with animation * @param {HTMLElement} toast - The toast element to dismiss */ function dismissToast(toast) { if (!toast || toast.classList.contains('toast--dismissing')) { return; } toast.classList.add('toast--dismissing'); toast.classList.remove('toast--visible'); // Remove from DOM after animation completes setTimeout(() => { if (toast.parentElement) { toast.remove(); } }, 300); } /** * Escape HTML to prevent XSS * @param {string} text - Text to escape * @returns {string} Escaped text */ function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Make functions globally available window.showToast = showToast; window.dismissToast = dismissToast;