UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

288 lines (285 loc) 10.1 kB
'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