/**
* 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;