aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
288 lines (285 loc) • 10.1 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useContext, useState, forwardRef, createContext } from 'react';
import { cn } from '../../lib/utilsComprehensive.js';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import { MotionFramer } from '../../primitives/motion/MotionFramer.js';
const NotificationContext = /*#__PURE__*/createContext(undefined);
const defaultNotificationContext = {
notifications: [],
addNotification: () => {},
removeNotification: () => {},
clearAll: () => {}
};
const useNotifications = () => {
const context = useContext(NotificationContext);
return context ?? defaultNotificationContext;
};
const GlassNotificationProvider = ({
children
}) => {
const [notifications, setNotifications] = useState([]);
const addNotification = notification => {
const id = Date.now().toString() + Math.random().toString(36).substr(2, 9);
const newNotification = {
...notification,
id
};
setNotifications(prev => [newNotification, ...prev]);
// Auto-remove non-persistent notifications
if (!newNotification.persistent) {
const duration = newNotification.duration || 5000;
setTimeout(() => {
removeNotification(id);
}, duration);
}
};
const removeNotification = id => {
setNotifications(prev => prev.filter(notification => notification.id !== id));
};
const clearAll = () => {
setNotifications([]);
};
return jsx(NotificationContext.Provider, {
"data-glass-component": true,
value: {
notifications,
addNotification,
removeNotification,
clearAll
},
children: children
});
};
/**
* GlassNotificationCenter component
* A notification center with glassmorphism styling for managing toast notifications
*/
const GlassNotificationCenter = /*#__PURE__*/forwardRef(({
// TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance
position = "top-right",
maxNotifications = 5,
autoHideDelay = 5000,
animation = "slide",
showClearAll = true,
className,
...props
}, ref) => {
const {
notifications,
removeNotification,
clearAll
} = useNotifications();
const positionClasses = {
"top-right": "top-4 right-4",
"top-left": "top-4 left-4",
"bottom-right": "bottom-4 right-4",
"bottom-left": "bottom-4 left-4",
"top-center": "top-4 left-1/2 transform -translate-x-1/2",
"bottom-center": "bottom-4 left-1/2 transform -translate-x-1/2"
};
const displayedNotifications = notifications.slice(0, maxNotifications);
const getTypeStyles = type => {
switch (type) {
case "success":
return {
icon: "✓",
bgClass: "border-green-500/20 bg-green-500/10",
iconClass: "text-green-400"
};
case "error":
return {
icon: "✕",
bgClass: "border-red-500/20 bg-red-500/10",
iconClass: "text-red-400"
};
case "warning":
return {
icon: "⚠",
bgClass: "border-yellow-500/20 bg-yellow-500/10",
iconClass: "text-yellow-400"
};
case "info":
default:
return {
icon: "ℹ",
bgClass: "border-blue-500/20 bg-blue-500/10",
iconClass: "text-blue-400"
};
}
};
const containerClassName = cn("fixed z-50 glass-gap-2", positionClasses[position], className);
if (displayedNotifications.length === 0) {
return jsx("div", {
ref: ref,
className: containerClassName,
"aria-live": "polite",
"data-empty": true,
...props,
children: jsx("span", {
className: "sr-only",
children: "No notifications"
})
});
}
return jsxs("div", {
ref: ref,
className: containerClassName,
"aria-live": "polite",
...props,
children: [showClearAll && notifications.length > 1 && jsxs(OptimizedGlassCore, {
elevation: "level1",
intensity: "medium",
depth: 1,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "low",
className: 'glass-px-3 glass-py-1 glass-radius-full glass-text-xs cursor-pointer hover:glass-surface-subtle/10 transition-colors',
onClick: clearAll,
children: ["Clear All (", notifications.length, ")"]
}), displayedNotifications.map((notification, index) => {
const typeStyles = getTypeStyles(notification.type);
return jsx(MotionFramer, {
delay: index * 100,
children: jsxs(OptimizedGlassCore, {
elevation: "level2",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
className: cn("min-w-80 max-w-sm glass-p-4 glass-radius-lg border glass-backdrop-blur-md", typeStyles.bgClass),
children: [jsxs("div", {
className: "glass-flex glass-items-start glass-gap-3",
children: [jsx("div", {
className: `flex-shrink-0 w-6 h-6 glass-radius-full flex items-center justify-center glass-text-sm font-bold ${typeStyles.iconClass}`,
children: typeStyles.icon
}), jsxs("div", {
className: "glass-flex-1 glass-min-w-0",
children: [jsx("h4", {
className: 'glass-text-sm font-semibold text-primary',
children: notification.title
}), notification.message && jsx("p", {
className: 'glass-mt-1 glass-text-sm text-primary/80',
children: notification.message
}), notification.action && jsx("button", {
onClick: notification.action.onClick,
className: 'glass-mt-2 glass-text-sm font-medium text-primary hover:glass-text-secondary transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: notification.action.label
})]
}), jsx("button", {
onClick: e => removeNotification(notification.id),
className: 'glass-flex-shrink-0 w-5 h-5 glass-radius-full glass-flex glass-items-center glass-justify-center text-primary/60 hover:text-primary/90 hover:glass-surface-subtle/10 transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "\u2715"
})]
}), !notification.persistent && notification.duration && jsx("div", {
className: 'mt-3 h-1 glass-surface-subtle/20 glass-radius-full overflow-hidden',
children: jsx("div", {
className: 'glass-h-full glass-surface-subtle/40 glass-radius-full transition-all duration-100 ease-linear',
style: {
animation: `shrink ${notification.duration}ms linear forwards`
}
})
})]
})
}, notification.id);
})]
});
});
GlassNotificationCenter.displayName = "GlassNotificationCenter";
const GlassNotificationItem = /*#__PURE__*/forwardRef(({
notification,
onClose,
className,
...props
}, ref) => {
const typeStyles = getTypeStyles(notification.type);
return jsx(OptimizedGlassCore, {
ref: ref,
elevation: "level1",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
className: cn("glass-p-4 glass-radius-lg border", typeStyles.bgClass, className),
...props,
children: jsxs("div", {
className: "glass-flex glass-items-start glass-gap-3",
children: [jsx("div", {
className: `w-6 h-6 glass-radius-full flex items-center justify-center glass-text-sm ${typeStyles.iconClass}`,
children: typeStyles.icon
}), jsxs("div", {
className: "glass-flex-1",
children: [jsx("h4", {
className: 'font-semibold',
children: notification.title
}), notification.message && jsx("p", {
className: 'glass-text-sm opacity-80',
children: notification.message
})]
}), jsx("button", {
onClick: onClose,
className: 'glass-contrast-guard glass-focus glass-touch-target hover:text-primary text-primary/60',
children: "\u2715"
})]
})
});
});
GlassNotificationItem.displayName = "GlassNotificationItem";
// Helper function for notification styles
const getTypeStyles = type => {
switch (type) {
case "success":
return {
icon: "✓",
bgClass: "border-green-500/20 bg-green-500/10",
iconClass: "text-green-400"
};
case "error":
return {
icon: "✕",
bgClass: "border-red-500/20 bg-red-500/10",
iconClass: "text-red-400"
};
case "warning":
return {
icon: "⚠",
bgClass: "border-yellow-500/20 bg-yellow-500/10",
iconClass: "text-yellow-400"
};
case "info":
default:
return {
icon: "ℹ",
bgClass: "border-blue-500/20 bg-blue-500/10",
iconClass: "text-blue-400"
};
}
};
// Add shrink animation for progress bar
const shrinkKeyframes = `
@keyframes shrink {
from { width: 100%; }
to { width: 0%; }
}
`;
// Inject keyframes safely with SSR check
if (typeof document !== "undefined" && typeof window !== "undefined") {
const existingStyle = document.querySelector("#glass-notification-styles");
if (!existingStyle) {
const style = document.createElement("style");
style.id = "glass-notification-styles";
style.textContent = shrinkKeyframes;
document.head.appendChild(style);
}
}
export { GlassNotificationCenter, GlassNotificationItem, GlassNotificationProvider, useNotifications };
//# sourceMappingURL=GlassNotificationCenter.js.map