UNPKG

aura-glass

Version:

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

180 lines (177 loc) 7.63 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { useMotionValue, useSpring, useScroll, useTransform, motion } from 'framer-motion'; import { forwardRef, useRef, useState, 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 '../../primitives/motion/MotionFramer.js'; import { useA11yId } from '../../utils/a11y.js'; const GlassParallaxLayers = /*#__PURE__*/forwardRef(({ layers, className, mouseIntensity = 0.5, scrollIntensity = 0.3, perspective = 1200, autoRotate = false, rotateSpeed = 0.5, interactive = true, debug = false, "aria-label": ariaLabel, respectMotionPreference = true }, ref) => { const containerRef = useRef(null); const [isHovered, setIsHovered] = useState(false); const componentId = useA11yId("parallax-layers"); const [prefersReducedMotion, setPrefersReducedMotion] = useState(false); // Check for reduced motion preference useEffect(() => { if (!respectMotionPreference) return; const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); setPrefersReducedMotion(mediaQuery.matches); const handleChange = e => setPrefersReducedMotion(e.matches); mediaQuery.addEventListener("change", handleChange); return () => mediaQuery.removeEventListener("change", handleChange); }, [respectMotionPreference]); // Motion values for mouse position const mouseX = useMotionValue(0); const mouseY = useMotionValue(0); // Smooth spring animation const springConfig = { damping: 25, stiffness: 150 }; const smoothMouseX = useSpring(mouseX, springConfig); const smoothMouseY = useSpring(mouseY, springConfig); // Scroll progress const { scrollYProgress } = useScroll({ target: containerRef, offset: ["start end", "end start"] }); // Auto rotation const [rotation, setRotation] = useState(0); useEffect(() => { if (!autoRotate) return; const interval = setInterval(() => { setRotation(prev => prev + rotateSpeed); }, 16); return () => clearInterval(interval); }, [autoRotate, rotateSpeed]); // Handle mouse movement const handleMouseMove = useCallback(e => { if (!interactive || !containerRef.current) return; const rect = containerRef.current.getBoundingClientRect(); const x = (e.clientX - rect.left) / rect.width - 0.5; const y = (e.clientY - rect.top) / rect.height - 0.5; mouseX.set(x); mouseY.set(y); }, [interactive, mouseX, mouseY]); const handleMouseLeave = useCallback(() => { mouseX.set(0); mouseY.set(0); setIsHovered(false); }, [mouseX, mouseY]); const handleMouseEnter = useCallback(() => { setIsHovered(true); }, []); // Sort layers by depth (furthest first for proper stacking) const sortedLayers = [...layers].sort((a, b) => b.depth - a.depth); return jsxs("div", { ref: ref || containerRef, id: componentId, className: cn("relative overflow-hidden", "transform-gpu will-change-transform", className), style: { perspective: prefersReducedMotion ? "none" : `${perspective}px`, transformStyle: prefersReducedMotion ? "flat" : "preserve-3d" }, onMouseMove: prefersReducedMotion ? undefined : handleMouseMove, onMouseEnter: prefersReducedMotion ? undefined : handleMouseEnter, onMouseLeave: prefersReducedMotion ? undefined : handleMouseLeave, role: "presentation", "aria-label": ariaLabel || "Parallax layers with interactive effects", "aria-hidden": !ariaLabel, children: [sortedLayers.map((layer, index) => { const depthFactor = layer.depth / 10; const parallaxFactor = 1 - depthFactor; // Calculate transforms based on depth const xTransform = useTransform(smoothMouseX, [-0.5, 0.5], [-50 * parallaxFactor * mouseIntensity, 50 * parallaxFactor * mouseIntensity]); useTransform(smoothMouseY, [-0.5, 0.5], [-30 * parallaxFactor * mouseIntensity, 30 * parallaxFactor * mouseIntensity]); const scrollY = useTransform(scrollYProgress, [0, 1], [100 * depthFactor * scrollIntensity, -100 * depthFactor * scrollIntensity]); const rotateXTransform = useTransform(smoothMouseY, [-0.5, 0.5], [15 * parallaxFactor * mouseIntensity, -15 * parallaxFactor * mouseIntensity]); const rotateYTransform = useTransform(smoothMouseX, [-0.5, 0.5], [-15 * parallaxFactor * mouseIntensity, 15 * parallaxFactor * mouseIntensity]); // Blur based on depth const blurAmount = Math.round(depthFactor * 4); const autoBlur = blurAmount === 0 ? "none" : blurAmount === 1 ? "subtle" : blurAmount === 2 ? "medium" : blurAmount === 3 ? "strong" : "intense"; // Map layer blur values to OptimizedGlass compatible values const mapBlurValue = blur => { switch (blur) { case "sm": return "subtle"; case "md": return "medium"; case "lg": return "strong"; case "xl": return "intense"; default: return blur; } }; const finalBlur = mapBlurValue(layer.blur) || autoBlur; return jsx(motion.div, { initial: { opacity: 0, scale: prefersReducedMotion ? 1 : 0.9 }, animate: { opacity: layer.opacity || 1 - depthFactor * 0.2, scale: layer.scale || 1 - depthFactor * 0.1 }, transition: { duration: prefersReducedMotion ? 0 : 0.6, delay: prefersReducedMotion ? 0 : index * 0.1 }, style: { x: prefersReducedMotion ? 0 : xTransform, y: prefersReducedMotion ? 0 : scrollY, rotateX: prefersReducedMotion ? 0 : rotateXTransform, rotateY: prefersReducedMotion ? 0 : autoRotate ? rotation : rotateYTransform, z: prefersReducedMotion ? 0 : layer.depth * 50, scale: layer.scale || 1 - depthFactor * 0.1, opacity: layer.opacity || 1 - depthFactor * 0.2, transformStyle: prefersReducedMotion ? "flat" : "preserve-3d" }, children: jsxs(OptimizedGlassCore, { intent: "neutral", elevation: isHovered ? "level3" : "level1", intensity: "medium", glassBlur: finalBlur, depth: Math.min(layer.depth, 5), className: cn("absolute inset-0", "transition-all duration-300", layer.className), children: [layer.content, debug && jsxs("div", { className: 'absolute glass-top-2 left-2 glass-surface-dark/20 glass-glass-glass-backdrop-blur-sm glass-contrast-guard glass-p-2 glass-radius-sm glass-text-xs text-primary/90 glass-contrast-guard', children: ["Layer ", index + 1, " | Depth: ", layer.depth] })] }) }, `layer-${index}`); }), interactive && !prefersReducedMotion && jsx(motion.div, { className: 'absolute bottom-4 right-4 text-primary/60 glass-text-xs glass-surface-dark/20 glass-glass-glass-backdrop-blur-sm glass-contrast-guard glass-p-2 glass-radius-md glass-contrast-guard', initial: { opacity: 0 }, animate: { opacity: isHovered ? 1 : 0.5 }, role: "status", "aria-live": "polite", children: isHovered ? "Move mouse to parallax" : "Hover to interact" })] }); }); export { GlassParallaxLayers }; //# sourceMappingURL=GlassParallaxLayers.js.map