UNPKG

aura-glass

Version:

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

549 lines (546 loc) 20.7 kB
'use client'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { X } from 'lucide-react'; import React, { forwardRef, useState, useRef, useEffect, useCallback } from 'react'; 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'; import { LiquidGlassMaterial } from '../../primitives/LiquidGlassMaterial.js'; import { GlassButton } from '../button/GlassButton.js'; import { trapFocus } from '../../utils/focus.js'; /** * GlassDialog component * Modal dialog with glassmorphism styling and comprehensive functionality */ const GlassDialog = /*#__PURE__*/forwardRef(({ open = false, onOpenChange, title, description, children, size = "md", variant = "default", material = "glass", materialProps, closeOnBackdropClick = true, closeOnEscape = true, showCloseButton = true, animation = "scale", footer, header, modal = true, backdropBlur = true, elevation = "modal", zIndex = 50, className, contentClassName, // Consciousness features consciousness, predictive, adaptive, eyeTracking, spatialAudio, trackAchievements, ...props }, ref) => { const [isVisible, setIsVisible] = useState(open); const dialogRef = useRef(null); const previouslyFocusedRef = useRef(null); // Consciousness state const [interactionCount, setInteractionCount] = useState(0); const [dialogEngagement, setDialogEngagement] = useState({ timeSpent: 0, contentScrolled: false, interactionCount: 0 }); const [dialogInsights, setDialogInsights] = useState(null); const eyeTracker = eyeTracking ? { startTracking: handler => {}, stopTracking: () => {} } : null; const biometricAdapter = adaptive ? { getCurrentBiometrics: () => ({ stressLevel: 0.3 }) } : null; const spatialAudioEngine = spatialAudio ? { playSound: (sound, config) => {} } : null; const interactionRecorder = consciousness ? { recordInteraction: (type, data) => {} } : null; const achievementTracker = trackAchievements ? { recordInteraction: (type, data) => {} } : null; // Handle escape key useEffect(() => { if (!closeOnEscape || !open) return; const handleEscape = event => { if (event.key === "Escape") { onOpenChange?.(false); } }; document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); }, [closeOnEscape, open, onOpenChange]); // Handle body scroll lock useEffect(() => { if (modal && open) { document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = ""; }; } }, [modal, open]); // Focus management: trap focus and restore on close useEffect(() => { if (!open || !dialogRef.current) return; // Store previously focused element previouslyFocusedRef.current = document.activeElement; // Set up focus trap const releaseFocus = trapFocus(dialogRef.current, { returnFocus: false, // We'll handle this manually for better control escapeDeactivates: false, // We handle escape separately allowOutsideClick: true // We handle backdrop clicks separately }); return () => { releaseFocus(); // Restore focus to previously focused element if (previouslyFocusedRef.current) { setTimeout(() => { previouslyFocusedRef.current?.focus(); }, 0); } }; }, [open]); // Handle visibility state useEffect(() => { if (open) { setIsVisible(true); } else { // Delay hiding to allow exit animation const timer = setTimeout(() => setIsVisible(false), 200); return () => clearTimeout(timer); } }, [open]); // Consciousness effects // Dialog lifecycle tracking with spatial audio useEffect(() => { if (open) { const openTime = Date.now(); setInteractionCount(prev => prev + 1); // Record dialog opening if (consciousness && interactionRecorder) { interactionRecorder.recordInteraction("dialog_open", { title: title?.toString() || "Dialog", size, variant, timestamp: openTime }); } // Track achievement for dialog interaction if (trackAchievements && achievementTracker) { achievementTracker.recordInteraction("dialog_opened", { dialogTitle: title?.toString() || "Dialog", timestamp: openTime }); } // Play spatial audio for dialog opening if (spatialAudio && spatialAudioEngine) { spatialAudioEngine.playSound("dialog-open", { volume: 0.5, position: "center", reverb: 0.3 }); } // Start tracking time const timeTracker = setInterval(() => { setDialogEngagement(prev => ({ ...prev, timeSpent: Date.now() - openTime })); }, 1000); return () => clearInterval(timeTracker); } else { // Record dialog closing if (consciousness && interactionRecorder && dialogEngagement.timeSpent > 0) { interactionRecorder.recordInteraction("dialog_close", { title: title?.toString() || "Dialog", timeSpent: dialogEngagement.timeSpent, interactions: dialogEngagement.interactionCount, contentScrolled: dialogEngagement.contentScrolled, timestamp: Date.now() }); } // Play spatial audio for dialog closing if (spatialAudio && spatialAudioEngine) { spatialAudioEngine.playSound("dialog-close", { volume: 0.4, position: "center", reverb: 0.2 }); } } }, [open, consciousness, interactionRecorder, trackAchievements, achievementTracker, spatialAudio, spatialAudioEngine, title, size, variant, dialogEngagement]); // Eye tracking for dialog engagement useEffect(() => { if (!eyeTracking || !eyeTracker || !open) return; const handleGazeData = gazeData => { // Track if user is looking at dialog content const dialogElement = document.querySelector('[data-consciousness-dialog="true"]'); if (dialogElement) { const rect = dialogElement.getBoundingClientRect(); const isLookingAtDialog = gazeData.x >= rect.left && gazeData.x <= rect.right && gazeData.y >= rect.top && gazeData.y <= rect.bottom; if (isLookingAtDialog && trackAchievements && achievementTracker) { achievementTracker.recordInteraction("dialog_gaze_engagement", { dialogTitle: title?.toString(), gazeTime: Date.now() }); } } }; eyeTracker.startTracking(handleGazeData); return () => eyeTracker.stopTracking(); }, [eyeTracking, eyeTracker, open, trackAchievements, achievementTracker, title]); // Biometric adaptation for dialog behavior useEffect(() => { if (!adaptive || !biometricAdapter || !open) return; const updateAdaptiveFeatures = () => { const biometrics = biometricAdapter.getCurrentBiometrics(); setDialogInsights({ urgency: biometrics.stressLevel > 0.7 ? "high" : biometrics.stressLevel > 0.4 ? "medium" : "low", complexity: (children?.toString().length || 0) > 300 ? 0.8 : 0.4, userStress: biometrics.stressLevel }); if (trackAchievements && achievementTracker) { achievementTracker.recordInteraction("dialog_biometric_adaptation", { stressLevel: biometrics.stressLevel, dialogComplexity: (children?.toString().length || 0) > 300 ? "high" : "low" }); } }; const interval = setInterval(updateAdaptiveFeatures, 3000); updateAdaptiveFeatures(); return () => clearInterval(interval); }, [adaptive, biometricAdapter, open, children, trackAchievements, achievementTracker]); // Enhanced handlers with consciousness tracking const handleBackdropClick = useCallback(event => { if (closeOnBackdropClick && event.target === event.currentTarget) { // Record backdrop click interaction if (consciousness && interactionRecorder) { interactionRecorder.recordInteraction("dialog_backdrop_click", { dialogTitle: title?.toString(), timestamp: Date.now() }); } // Play spatial audio for backdrop close if (spatialAudio && spatialAudioEngine) { spatialAudioEngine.playSound("dialog-backdrop-close", { volume: 0.3, position: "left", reverb: 0.1 }); } onOpenChange?.(false); props.onClose?.(); } }, [closeOnBackdropClick, onOpenChange, props, consciousness, interactionRecorder, title, spatialAudio, spatialAudioEngine]); const handleClose = useCallback(() => { // Record close button interaction if (consciousness && interactionRecorder) { interactionRecorder.recordInteraction("dialog_close_button", { dialogTitle: title?.toString(), timestamp: Date.now() }); } // Play spatial audio for button close if (spatialAudio && spatialAudioEngine) { spatialAudioEngine.playSound("dialog-button-close", { volume: 0.3, position: "right", reverb: 0.1 }); } onOpenChange?.(false); props.onClose?.(); }, [onOpenChange, props, consciousness, interactionRecorder, title, spatialAudio, spatialAudioEngine]); // Size classes const sizeClasses = { xs: "max-w-xs", sm: "max-w-sm", md: "max-w-md", lg: "max-w-lg", xl: "max-w-xl", "2xl": "max-w-2xl", full: "max-w-full glass-mx-4" }; // Animation presets const getAnimationPreset = () => { switch (animation) { case "fade": return "fadeIn"; case "scale": return "scaleIn"; case "slide": return "slideUp"; case "flip": return "rotateIn"; default: return "scaleIn"; } }; if (!isVisible) return null; return jsxs("div", { "data-glass-component": true, className: cn("fixed inset-0 flex items-center justify-center", variant === "fullscreen" ? "glass-p-0" : "glass-p-4", `z-${zIndex}`, consciousness && "consciousness-dialog-container", adaptive && dialogInsights?.urgency === "high" && "consciousness-urgent-dialog", eyeTracking && "consciousness-eye-trackable"), onClick: handleBackdropClick, role: "dialog", "aria-modal": modal, "aria-labelledby": title ? "dialog-title" : undefined, "aria-describedby": description ? "dialog-description" : undefined, "data-consciousness-dialog": "true", "data-consciousness-active": String(!!consciousness), "data-dialog-title": title?.toString(), "data-dialog-urgency": dialogInsights?.urgency, "data-user-stress": dialogInsights?.userStress, "data-interaction-count": interactionCount, children: [jsx(MotionFramer, { preset: "fadeIn", duration: 200, className: cn("absolute inset-0 bg-black/20", backdropBlur && "glass-backdrop-blur-md") }), jsx(MotionFramer, { ref: dialogRef, preset: getAnimationPreset(), duration: 200, className: cn("relative w-full", variant === "fullscreen" ? "h-full" : "max-h-[90vh]", sizeClasses[size]), children: material === "liquid" ? jsxs(LiquidGlassMaterial, { ior: materialProps?.ior || 1.48, thickness: materialProps?.thickness || 10, tint: materialProps?.tint || { r: 0, g: 0, b: 0, a: 0.08 }, variant: materialProps?.variant || "regular", quality: materialProps?.quality || "high", environmentAdaptation: true, motionResponsive: true, ref: ref, className: cn("w-full overflow-hidden liquid-glass-dialog-surface", variant === "fullscreen" && "h-full rounded-none", consciousness && "consciousness-dialog-glass", eyeTracking && "consciousness-eye-trackable-content", adaptive && dialogInsights?.urgency === "high" && "consciousness-urgent-glass", predictive && dialogInsights && "consciousness-predictive-glass", className), style: { "--liquid-glass-dialog-density": "0.9", "--liquid-glass-adaptive-tint": dialogInsights?.urgency === "high" ? "rgba(220, 38, 38, 0.12)" : "rgba(0, 0, 0, 0.08)" }, "data-liquid-glass-dialog": "true", "data-dialog-urgency": dialogInsights?.urgency, ...props, children: [(header || title || description || showCloseButton) && jsxs("div", { className: "glass-flex glass-items-start glass-justify-between glass-p-6 glass-border-b glass-border-glass-border/10", children: [jsx("div", { className: "glass-flex-1 glass-min-w-0", children: header || jsxs(Fragment, { children: [title && jsx("h2", { id: "dialog-title", className: 'glass-text-lg font-semibold text-primary mb-1', children: title }), description && jsx("p", { id: "dialog-description", className: 'glass-text-sm text-muted-foreground', children: description })] }) }), showCloseButton && jsx(GlassButton, { variant: "ghost", size: "sm", iconOnly: true, onClick: handleClose, "aria-label": "Close dialog", className: "glass-flex-shrink-0 glass-ml-4", children: "\u00D7" })] }), jsx("div", { className: cn("flex-1 overflow-y-auto glass-p-6", contentClassName), children: children }), footer && jsx("div", { className: "glass-p-6 glass-border-t glass-border-glass-border/10", children: footer })] }) : jsxs(OptimizedGlassCore, { intent: "neutral", elevation: "level2", intensity: "medium", depth: 2, tint: "neutral", border: "subtle", animation: "none", performanceMode: "medium", ref: ref, liftOnHover: true, hoverSheen: true, className: cn("w-full overflow-hidden border border-border/20 glass-radial-reveal", variant === "fullscreen" && "h-full rounded-none", consciousness && "consciousness-dialog-glass", eyeTracking && "consciousness-eye-trackable-content", adaptive && dialogInsights?.urgency === "high" && "consciousness-urgent-glass", predictive && dialogInsights && "consciousness-predictive-glass", className), ...props, children: [(header || title || description || showCloseButton) && jsxs("div", { className: "glass-flex glass-items-start glass-justify-between glass-p-6 glass-border-b glass-border-glass-border/10", children: [jsx("div", { className: "glass-flex-1 glass-min-w-0", children: header || jsxs(Fragment, { children: [title && jsx("h2", { id: "dialog-title", className: 'glass-text-lg font-semibold text-primary mb-1', children: title }), description && jsx("p", { id: "dialog-description", className: "glass-text-sm glass-text-secondary", children: description })] }) }), showCloseButton && jsx(GlassButton, { type: "button", className: cn("glass-ml-4 glass-p-2 glass-radius-lg glass-text-secondary hover:text-foreground hover:bg-muted/20 transition-colors", consciousness && "consciousness-close-button", adaptive && dialogInsights?.urgency === "high" && "consciousness-urgent-close"), onClick: handleClose, "aria-label": "Close dialog", consciousness: consciousness, adaptive: adaptive, spatialAudio: spatialAudio, trackAchievements: trackAchievements, "data-consciousness-close": "true", children: jsx(X, { className: 'w-4 h-4' }) })] }), children && jsxs("div", { className: cn("flex-1 overflow-y-auto glass-p-6", consciousness && "consciousness-dialog-body", eyeTracking && "consciousness-eye-trackable-body", adaptive && dialogInsights?.urgency === "high" && "consciousness-urgent-body"), "data-consciousness-body": "true", "data-content-complexity": dialogInsights?.complexity, onScroll: e => { setDialogEngagement(prev => ({ ...prev, contentScrolled: true, interactionCount: prev.interactionCount + 1 })); if (consciousness && interactionRecorder) { interactionRecorder.recordInteraction("dialog_scroll", { dialogTitle: title?.toString(), timestamp: Date.now() }); } }, onClick: e => { setDialogEngagement(prev => ({ ...prev, interactionCount: prev.interactionCount + 1 })); if (consciousness && interactionRecorder) { interactionRecorder.recordInteraction("dialog_content_click", { dialogTitle: title?.toString(), timestamp: Date.now() }); } }, children: [children, predictive && dialogInsights && jsx("div", { className: "glass-mt-4 glass-p-3 glass-surface-primary/10 glass-radius-lg glass-border glass-border-primary/20 glass-text-xs", children: jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsx("span", { className: 'text-primary', children: "Dialog Insights" }), jsxs("div", { className: "glass-flex glass-gap-2", children: [jsxs("span", { className: cn("glass-px-2 glass-py-1 glass-radius-md", dialogInsights.urgency === "high" ? "bg-red-500/20 text-red-300" : dialogInsights.urgency === "medium" ? "bg-yellow-500/20 text-yellow-300" : "bg-green-500/20 text-green-300"), children: [dialogInsights.urgency, " urgency"] }), dialogInsights.userStress > 0.7 && jsx("span", { className: "glass-px-2 glass-py-1 glass-radius-md glass-surface-primary/20 glass-text-secondary", children: "high stress" })] })] }) })] }), footer && jsx("div", { className: "glass-p-6 glass-border-t glass-border-glass-border/10", children: footer })] }) })] }); }); GlassDialog.displayName = "GlassDialog"; const DialogTrigger = /*#__PURE__*/forwardRef(({ children, asChild = false, ...props }, ref) => { if (asChild) { return /*#__PURE__*/React.cloneElement(children, { ref, ...props }); } return jsx(GlassButton, { ref: ref, ...props, children: children }); }); DialogTrigger.displayName = "DialogTrigger"; const DialogHeader = /*#__PURE__*/forwardRef(({ children, className, ...props }, ref) => { return jsx("div", { ref: ref, className: cn("flex flex-col glass-gap-1.5 text-center sm:text-left", className), ...props, children: children }); }); DialogHeader.displayName = "DialogHeader"; const DialogTitle = /*#__PURE__*/forwardRef(({ children, className, ...props }, ref) => { return jsx("h2", { ref: ref, className: cn("glass-text-lg font-semibold leading-none tracking-tight", className), ...props, children: children }); }); DialogTitle.displayName = "DialogTitle"; const DialogDescription = /*#__PURE__*/forwardRef(({ children, className, ...props }, ref) => { return jsx("p", { ref: ref, className: cn("glass-text-sm glass-text-secondary", className), ...props, children: children }); }); DialogDescription.displayName = "DialogDescription"; const DialogFooter = /*#__PURE__*/forwardRef(({ children, className, ...props }, ref) => { return jsx("div", { ref: ref, className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:glass-gap-2", className), ...props, children: children }); }); DialogFooter.displayName = "DialogFooter"; export { DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, GlassDialog }; //# sourceMappingURL=GlassDialog.js.map