/** * Notification/Toast System for Hugo Blox Kit * Provides reusable notification functionality */ import {hugoEnvironment} from "@params"; // Debug mode based on environment const isDebugMode = hugoEnvironment === "development"; /** * Show a notification message using Tailwind styles * @param {string} message - The message to display * @param {string} type - The type of notification ('success', 'error', 'info', 'warning') * @param {number} duration - Duration in milliseconds (default 3000) * @returns {HTMLElement} - The notification element */ export function showNotification(message, type = "success", duration = 3000) { // Get or create the notification container const container = getOrCreateContainer(); // Remove any existing notifications with same message const existingNotification = container.querySelector(".hb-notification"); if (existingNotification?.textContent?.includes(message)) { existingNotification.remove(); } // Create notification element const notification = createNotificationElement(message, type); // Add to container container.appendChild(notification); // Handle close button const closeBtn = notification.querySelector(".hb-notification-close"); if (closeBtn) { closeBtn.addEventListener("click", () => { removeNotification(notification); }); } // Auto-remove after duration if (duration > 0) { setTimeout(() => { removeNotification(notification); }, duration); } return notification; } /** * Get or create the notification container * @returns {HTMLElement} - The container element */ function getOrCreateContainer() { let container = document.getElementById("hb-notification-container"); if (!container) { if (isDebugMode) { console.warn("Notification container not found, creating fallback"); } container = document.createElement("div"); container.id = "hb-notification-container"; container.className = "fixed top-20 right-4 z-[9999] pointer-events-none"; container.setAttribute("aria-live", "polite"); container.setAttribute("aria-atomic", "true"); document.body.appendChild(container); } return container; } /** * Create a notification element * @param {string} message - The message to display * @param {string} type - The type of notification * @returns {HTMLElement} - The notification element */ function createNotificationElement(message, type) { const notification = document.createElement("div"); notification.setAttribute("role", "alert"); // Define colors for different types const colors = { success: {bg: "#10b981", bgClass: "bg-green-500"}, error: {bg: "#ef4444", bgClass: "bg-red-500"}, info: {bg: "#3b82f6", bgClass: "bg-blue-500"}, warning: {bg: "#f59e0b", bgClass: "bg-amber-500"}, }; const color = colors[type] || colors.info; // Set classes notification.className = `hb-notification pointer-events-auto flex items-center gap-2 px-4 py-3 text-white rounded-lg shadow-lg ${color.bgClass} animate-slide-in`; // Add inline styles as fallback notification.style.cssText = ` background-color: ${color.bg}; color: white; display: flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; border-radius: 0.5rem; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); margin-bottom: 0.5rem; pointer-events: auto; `; // Add icon based on type const icons = { success: '', error: '', info: '', warning: '', }; const icon = icons[type] || icons.info; notification.innerHTML = ` ${icon} ${message} `; // Ensure animation styles exist ensureAnimationStyles(); return notification; } /** * Remove notification with animation * @param {HTMLElement} notification - The notification element to remove */ export function removeNotification(notification) { if (!notification || !notification.parentNode) return; notification.classList.remove("animate-slide-in"); notification.classList.add("animate-slide-out"); setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); } /** * Ensure animation styles are in the document */ function ensureAnimationStyles() { if (!document.querySelector("#hb-notification-styles")) { const style = document.createElement("style"); style.id = "hb-notification-styles"; style.textContent = ` @keyframes slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slide-out { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } .animate-slide-in { animation: slide-in 0.3s ease-out; } .animate-slide-out { animation: slide-out 0.3s ease-out; } `; document.head.appendChild(style); } } // Export default for convenience export default showNotification;