UNPKG

aura-glass

Version:

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

446 lines (443 loc) 16 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { useRef, useState, useCallback, useEffect, useMemo } from 'react'; import { useAnimation, useSpring, motion } from 'framer-motion'; const IS_TEST_ENV = typeof process !== "undefined" && process.env?.JEST_WORKER_ID !== undefined; // Default spatial position const DEFAULT_POSITION = { x: 0, y: 0, z: 0, pitch: 0, yaw: 0, roll: 0 }; // Spatial context detection const detectSpatialContext = () => { const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : ""; // Check for WebXR support const hasWebXR = typeof navigator !== "undefined" && "xr" in navigator; const isVR = hasWebXR && userAgent.includes("VR"); const isAR = hasWebXR && (userAgent.includes("AR") || userAgent.includes("Mobile")); // Basic capability detection const capabilities = { headTracking: hasWebXR, handTracking: hasWebXR && "getInputSources" in window, eyeTracking: false, // Requires specific hardware/API bodyTracking: false, // Limited availability environmentMapping: hasWebXR, occlusion: hasWebXR, lighting: hasWebXR, physics: true // Software-based physics always available }; let environment = "desktop"; if (isVR) environment = "vr-headset";else if (isAR && userAgent.includes("Mobile")) environment = "ar-phone";else if (isAR) environment = "ar-glasses";else if (userAgent.includes("Mobile")) environment = "mobile"; return { environment, capabilities, displayInfo: { fov: environment.includes("vr") ? 110 : 60, resolution: { width: window.innerWidth || 1920, height: window.innerHeight || 1080 }, refreshRate: 60 // Default assumption } }; }; const SpatialComputingEngine = ({ children, className = "", position = {}, bounds, enableGestures = true, enableAnchoring = false, enablePhysics = true, enableOcclusion = false, gestureTypes = ["tap", "pinch", "grab"], spatialId, onGesture, onPositionChange, onAnchorUpdate, showDebugHud = false }) => { useReducedMotion(); const containerRef = useRef(null); const xrSessionRef = useRef(null); // XRSession type not available in current environment const [spatialContext, setSpatialContext] = useState(detectSpatialContext()); const [currentPosition, setCurrentPosition] = useState({ ...DEFAULT_POSITION, ...position }); const [spatialAnchor, setSpatialAnchor] = useState(null); const [gestureActive, setGestureActive] = useState(null); const controls = useAnimation(); // Spatial motion values for smooth transformations const spatialX = useSpring(currentPosition.x, { stiffness: 400, damping: 40 }); const spatialY = useSpring(currentPosition.y, { stiffness: 400, damping: 40 }); const spatialZ = useSpring(currentPosition.z, { stiffness: 400, damping: 40 }); const spatialPitch = useSpring(currentPosition.pitch, { stiffness: 300, damping: 35 }); const spatialYaw = useSpring(currentPosition.yaw, { stiffness: 300, damping: 35 }); const spatialRoll = useSpring(currentPosition.roll, { stiffness: 300, damping: 35 }); // WebXR session management const initializeXRSession = useCallback(async () => { if (IS_TEST_ENV || !spatialContext.capabilities.headTracking) return; try { if ("xr" in navigator) { const xr = navigator.xr; // XRSystem type not available in current environment // Check for immersive VR support const isVRSupported = await xr.isSessionSupported("immersive-vr"); const isARSupported = await xr.isSessionSupported("immersive-ar"); if (isVRSupported || isARSupported) { const sessionMode = isVRSupported ? "immersive-vr" : "immersive-ar"; const session = await xr.requestSession(sessionMode, { requiredFeatures: ["local"], optionalFeatures: ["hand-tracking", "eye-tracking", "anchors"] }); xrSessionRef.current = session; // Set up XR frame loop const onXRFrame = (time, frame) => { // XRFrame type not available in current environment const pose = frame.getViewerPose(session.renderState.baseLayer.framebuffer); if (pose) { updateSpatialPosition(pose); } session.requestAnimationFrame(onXRFrame); }; session.requestAnimationFrame(onXRFrame); } } } catch (error) { console.warn("XR session initialization failed:", error); } }, [spatialContext]); // Update spatial position from XR pose or other input const updateSpatialPosition = useCallback(pose => { // XRPose type not available in current environment let newPosition; if (pose && "transform" in pose) { // Extract position from XR pose const { position: pos, orientation } = pose.transform; newPosition = { x: pos.x, y: pos.y, z: pos.z, pitch: Math.atan2(2 * (orientation.w * orientation.x + orientation.y * orientation.z), 1 - 2 * (orientation.x * orientation.x + orientation.y * orientation.y)) * 180 / Math.PI, yaw: Math.asin(2 * (orientation.w * orientation.y - orientation.z * orientation.x)) * 180 / Math.PI, roll: Math.atan2(2 * (orientation.w * orientation.z + orientation.x * orientation.y), 1 - 2 * (orientation.y * orientation.y + orientation.z * orientation.z)) * 180 / Math.PI }; } else { // Use provided position data newPosition = { ...currentPosition, ...pose }; } // Apply bounds constraints if (bounds) { newPosition.x = Math.max(bounds.min.x, Math.min(bounds.max.x, newPosition.x)); newPosition.y = Math.max(bounds.min.y, Math.min(bounds.max.y, newPosition.y)); newPosition.z = Math.max(bounds.min.z, Math.min(bounds.max.z, newPosition.z)); if (bounds.constraints?.lockX) newPosition.x = currentPosition.x; if (bounds.constraints?.lockY) newPosition.y = currentPosition.y; if (bounds.constraints?.lockZ) newPosition.z = currentPosition.z; if (bounds.constraints?.lockRotation) { newPosition.pitch = currentPosition.pitch; newPosition.yaw = currentPosition.yaw; newPosition.roll = currentPosition.roll; } } setCurrentPosition(newPosition); // Update motion values spatialX.set(newPosition.x); spatialY.set(newPosition.y); spatialZ.set(newPosition.z); spatialPitch.set(newPosition.pitch); spatialYaw.set(newPosition.yaw); spatialRoll.set(newPosition.roll); onPositionChange?.(newPosition); }, [currentPosition, bounds, spatialX, spatialY, spatialZ, spatialPitch, spatialYaw, spatialRoll, onPositionChange]); // Gesture recognition system const recognizeGesture = useCallback(event => { if (!enableGestures) return null; const rect = containerRef.current?.getBoundingClientRect(); if (!rect) return null; let gestureType = null; let position = { ...DEFAULT_POSITION }; let confidence = 1; if (event instanceof PointerEvent) { // Basic pointer gesture recognition const x = (event.clientX - rect.left) / rect.width - 0.5; const y = (event.clientY - rect.top) / rect.height - 0.5; position = { ...DEFAULT_POSITION, x: x * 100, y: y * 100 }; if (event.type === "pointerdown") gestureType = "tap";else if (event.pressure > 0.5) gestureType = "grab"; } else if (event instanceof TouchEvent && event.touches.length > 0) { // Multi-touch gesture recognition const touch = event.touches[0]; const x = (touch.clientX - rect.left) / rect.width - 0.5; const y = (touch.clientY - rect.top) / rect.height - 0.5; position = { ...DEFAULT_POSITION, x: x * 100, y: y * 100 }; if (event.touches.length === 1) gestureType = "tap";else if (event.touches.length === 2) gestureType = "pinch"; confidence = Math.min(1, event.touches.length / 2); } if (gestureType && gestureTypes.includes(gestureType)) { const gestureEvent = { type: gestureType, position, velocity: { ...DEFAULT_POSITION }, // Would calculate from previous positions confidence, duration: 0 // Would track duration }; return gestureEvent; } return null; }, [enableGestures, gestureTypes]); // Handle gesture events const handleGestureStart = useCallback(event => { const gesture = recognizeGesture(event); if (gesture) { setGestureActive(gesture.type); onGesture?.(gesture); // Visual feedback for gesture controls.start({ scale: 1.05, transition: { duration: 0.1 } }); } }, [recognizeGesture, onGesture, controls]); const handleGestureEnd = useCallback(() => { if (gestureActive) { setGestureActive(null); controls.start({ scale: 1, transition: { duration: 0.2 } }); } }, [gestureActive, controls]); // Spatial anchoring system const createSpatialAnchor = useCallback(async () => { if (!enableAnchoring || !spatialId) return; const anchor = { id: spatialId, position: currentPosition, persistent: true, cloudSync: false, accuracy: 1.0, lastUpdate: Date.now() }; // In a real implementation, this would persist to spatial mapping system setSpatialAnchor(anchor); onAnchorUpdate?.(anchor); }, [enableAnchoring, spatialId, currentPosition, onAnchorUpdate]); // Physics simulation for spatial objects const applyPhysics = useCallback(deltaTime => { if (!enablePhysics) return; // Simple gravity and collision detection const gravity = -9.81; // m/s² const newY = currentPosition.y + gravity * deltaTime * deltaTime / 1000; // Ground collision if (newY < 0) { updateSpatialPosition({ y: 0 }); } else { updateSpatialPosition({ y: newY }); } }, [enablePhysics, currentPosition.y, updateSpatialPosition]); // Initialize XR session on mount useEffect(() => { initializeXRSession(); // Cleanup XR session on unmount return () => { if (xrSessionRef.current) { xrSessionRef.current.end(); } }; }, [initializeXRSession]); // Set up gesture event listeners useEffect(() => { const container = containerRef.current; if (!container || !enableGestures) return; const handlePointerDown = e => handleGestureStart(e); const handleTouchStart = e => handleGestureStart(e); const handlePointerUp = () => handleGestureEnd(); const handleTouchEnd = () => handleGestureEnd(); container.addEventListener("pointerdown", handlePointerDown); container.addEventListener("touchstart", handleTouchStart); container.addEventListener("pointerup", handlePointerUp); container.addEventListener("touchend", handleTouchEnd); return () => { container.removeEventListener("pointerdown", handlePointerDown); container.removeEventListener("touchstart", handleTouchStart); container.removeEventListener("pointerup", handlePointerUp); container.removeEventListener("touchend", handleTouchEnd); }; }, [enableGestures, handleGestureStart, handleGestureEnd]); // Physics update loop useEffect(() => { if (!enablePhysics) return; if (IS_TEST_ENV) { applyPhysics(16); return; } let lastTime = Date.now(); let animationFrame; const physicsLoop = () => { const currentTime = Date.now(); const deltaTime = currentTime - lastTime; lastTime = currentTime; applyPhysics(deltaTime); animationFrame = requestAnimationFrame(physicsLoop); }; animationFrame = requestAnimationFrame(physicsLoop); return () => { if (animationFrame !== undefined) { cancelAnimationFrame(animationFrame); } }; }, [enablePhysics, applyPhysics]); // Auto-create anchor if position is stable useEffect(() => { if (!enableAnchoring || spatialAnchor) return; if (IS_TEST_ENV) { createSpatialAnchor(); return; } const timer = setTimeout(createSpatialAnchor, 5000); return () => clearTimeout(timer); }, [enableAnchoring, spatialAnchor, createSpatialAnchor]); // Transform calculations for CSS const transform3D = useMemo(() => ({ x: spatialX, y: spatialY, z: spatialZ, rotateX: spatialPitch, rotateY: spatialYaw, rotateZ: spatialRoll }), [spatialX, spatialY, spatialZ, spatialPitch, spatialYaw, spatialRoll]); return jsxs(motion.div, { ref: containerRef, className: `spatial-computing-container ${className}`, style: { position: "relative", transformStyle: "preserve-3d", perspective: "1000px", ...transform3D }, animate: controls, children: [bounds && process.env.NODE_ENV === "development" && jsx("div", { className: 'spatial-bounds-debug', style: { position: "absolute", border: "1px dashed rgba(0, 255, 0, 0.5)", left: `${bounds.min.x}px`, top: `${bounds.min.y}px`, width: `${bounds.max.x - bounds.min.x}px`, height: `${bounds.max.y - bounds.min.y}px`, pointerEvents: "none" } }), spatialAnchor && jsx("div", { className: 'spatial-anchor-indicator', style: { position: "absolute", top: "10px", left: "10px", width: "8px", height: "8px", backgroundColor: '/* Use createGlassStyle({ intent: "neutral", elevation: "level2" }) */', borderRadius: "50%", pointerEvents: "none" } }), gestureActive && jsx("div", { className: 'gesture-feedback', style: { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", background: '/* Use createGlassStyle({ intent: "neutral", elevation: "level2" }) */', borderRadius: "50%", width: "60px", height: "60px", pointerEvents: "none", animation: "pulse 1s infinite" } }), enableOcclusion && spatialContext.capabilities.occlusion && jsx("div", { className: 'occlusion-layer', style: { position: "absolute", inset: 0, background: '/* Use createGlassStyle({ intent: "primary", elevation: "level2" }) */', pointerEvents: "none", mixBlendMode: "multiply" } }), children, (showDebugHud || process.env.NODE_ENV === "development") && jsxs("div", { className: 'spatial-debug-info', style: { position: "absolute", bottom: "10px", right: "10px", background: '/* Use createGlassStyle({ intent: "primary", elevation: "level2" }) */', color: "white", padding: "8px", fontSize: "12px", borderRadius: "4px", pointerEvents: "none", fontFamily: "monospace" }, children: ["Env: ", spatialContext.environment, jsx("br", {}), "Pos: (", currentPosition.x.toFixed(1), ", ", currentPosition.y.toFixed(1), ",", " ", currentPosition.z.toFixed(1), ")", jsx("br", {}), "Rot: (", currentPosition.pitch.toFixed(1), "\u00B0,", " ", currentPosition.yaw.toFixed(1), "\u00B0, ", currentPosition.roll.toFixed(1), "\u00B0)", jsx("br", {}), gestureActive && `Gesture: ${gestureActive}`, jsx("br", {}), spatialAnchor && "Anchored"] }), jsx("style", { children: ` @keyframes pulse { 0%, 100% { opacity: 0.6; transform: translate(-50%, -50%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.2); } } ` })] }); }; export { SpatialComputingEngine, SpatialComputingEngine as default }; //# sourceMappingURL=SpatialComputingEngine.js.map