UNPKG

aura-glass

Version:

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

317 lines (314 loc) 10.4 kB
'use client'; import { jsx } from 'react/jsx-runtime'; import React, { useState, useRef, useMemo, useEffect, useCallback } from 'react'; import { useAccessibilitySettings } from './useAccessibilitySettings.js'; import { useEnhancedPerformance } from './useEnhancedPerformance.js'; import { createGlassStyle } from '../core/mixins/glassMixins.js'; /** * Comprehensive glass optimization hook that adapts to device capabilities and user preferences */ function useGlassOptimization(baseOptions = {}, config = {}) { const { enableAdaptiveBlur = true, enablePerformanceMode = true, enableAccessibilityMode = true, enableBatching = true, maxConcurrentAnimations = 5, throttleAnimations = true } = config; const { settings: a11ySettings } = useAccessibilitySettings(); const { performanceMode, metrics } = useEnhancedPerformance(); const [optimizedOptions, setOptimizedOptions] = useState(baseOptions); const [activeAnimations, setActiveAnimations] = useState(0); const animationQueueRef = useRef([]); const batchTimeoutRef = useRef(); // Adaptive blur based on performance useMemo(() => { if (!enableAdaptiveBlur) return baseOptions.tier === 'low' ? 'subtle' : 'medium'; const frameRate = metrics?.frameRate || 60; const memoryUsage = metrics?.memoryUsage || 0; if (performanceMode === 'low' || frameRate < 30 || memoryUsage > 0.8) { return 'subtle'; } else if (performanceMode === 'high' && frameRate >= 60 && memoryUsage < 0.5) { return 'intense'; } return 'medium'; }, [enableAdaptiveBlur, baseOptions.tier, metrics, performanceMode]); // Performance-aware options const performanceAwareOptions = useMemo(() => { let options = { ...baseOptions }; if (enablePerformanceMode) { switch (performanceMode) { case 'low': options = { ...options, elevation: 'level1', tier: 'low' }; break; case 'high': options = { ...options, tier: 'high' }; break; default: options = { ...options, tier: 'medium' }; } } return options; }, [baseOptions, enablePerformanceMode, performanceMode]); // Accessibility-aware options const accessibilityAwareOptions = useMemo(() => { if (!enableAccessibilityMode) return performanceAwareOptions; let options = { ...performanceAwareOptions }; // Reduce effects for users who prefer reduced motion if (a11ySettings.reducedMotion) { options = { ...options, interactive: false, elevation: 'level1' }; } // Enhance contrast for high contrast mode if (a11ySettings.highContrast) { options = { ...options, intent: 'neutral', tier: 'high' }; } // Adjust for forced colors mode if (a11ySettings.forcedColors) { options = { ...options, intent: 'neutral', tier: 'high' }; } return options; }, [performanceAwareOptions, enableAccessibilityMode, a11ySettings]); // Update optimized options when dependencies change useEffect(() => { setOptimizedOptions(accessibilityAwareOptions); }, [accessibilityAwareOptions]); // Generate optimized styles const optimizedStyles = useMemo(() => { ({ mode: performanceMode, prefersReducedMotion: a11ySettings.reducedMotion }); const baseStyles = { ...createGlassStyle(optimizedOptions) // Performance options applied directly }; return { base: baseStyles, hover: { ...baseStyles, transform: a11ySettings.reducedMotion ? 'none' : 'translateY(-2px) scale(1.02)', boxShadow: optimizedOptions.elevation ? `0 8px 32px rgba(0, 0, 0, 0.2)` : baseStyles.boxShadow }, focus: { ...baseStyles, outline: 'none', boxShadow: `${baseStyles.boxShadow}, 0 0 0 3px var(--glass-color-primary, 0.3)` }, disabled: { ...baseStyles, opacity: 0.5, cursor: 'not-allowed', pointerEvents: 'none', filter: 'grayscale(0.3)' }, loading: { ...baseStyles, position: 'relative', overflow: 'hidden' // Note: CSS pseudo-elements cannot be applied via React.CSSProperties. // Use companion CSS modules or dedicated classnames for complex layering. } }; }, [optimizedOptions, performanceMode, a11ySettings]); // Animation batching system const batchAnimation = useCallback(animationFn => { if (!enableBatching) { animationFn(); return; } animationQueueRef.current.push(animationFn); if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current); } batchTimeoutRef.current = setTimeout(() => { const animations = animationQueueRef.current.splice(0); // Execute animations in batches to avoid overwhelming the browser const batchSize = Math.min(animations.length, maxConcurrentAnimations); for (let i = 0; i < animations.length; i += batchSize) { const batch = animations.slice(i, i + batchSize); setTimeout(() => { batch.forEach(animation => { try { animation(); } catch (error) { if (process.env.NODE_ENV === 'development') { console.warn('Batched animation failed:', error); } } }); }, i * 16); // Stagger batches by 16ms (60fps) } }, 16); // Batch animations for one frame }, [enableBatching, maxConcurrentAnimations]); // Throttled animation executor const executeAnimation = useCallback(animationFn => { if (!throttleAnimations || activeAnimations < maxConcurrentAnimations) { setActiveAnimations(prev => prev + 1); try { animationFn(); } finally { // Decrement counter after animation completes setTimeout(() => { setActiveAnimations(prev => Math.max(0, prev - 1)); }, 300); // Assume average animation duration } } else { // Queue animation for later execution batchAnimation(animationFn); } }, [throttleAnimations, activeAnimations, maxConcurrentAnimations, batchAnimation]); // Cleanup batched animations on unmount useEffect(() => { return () => { if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current); } animationQueueRef.current = []; }; }, []); return { optimizedOptions, optimizedStyles, performanceMode, isHighPerformance: performanceMode === 'high', isLowPerformance: performanceMode === 'low', shouldReduceEffects: a11ySettings.reducedMotion || performanceMode === 'low', shouldUseHighContrast: a11ySettings.highContrast, executeAnimation, batchAnimation, activeAnimations, metrics }; } /** * Hook for optimized glass component rendering */ function useOptimizedGlassComponent(Component, glassOptions = {}, optimizationConfig = {}) { const { optimizedStyles, shouldReduceEffects, executeAnimation } = useGlassOptimization(glassOptions, optimizationConfig); const OptimizedComponent = useMemo(() => { return /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef((props, ref) => { const componentRef = useRef(null); const mergedRef = useCallback(node => { if (componentRef.current !== node) { componentRef.current = node; } if (typeof ref === 'function') { ref(node); } else if (ref) { ref.current = node; } }, [ref]); // Apply optimized styles useEffect(() => { const element = componentRef.current; if (!element) return; Object.assign(element.style, optimizedStyles.base); // Set up interaction handlers if not reduced motion if (!shouldReduceEffects) { const handleMouseEnter = () => { executeAnimation(() => { Object.assign(element.style, optimizedStyles.hover); }); }; const handleMouseLeave = () => { executeAnimation(() => { Object.assign(element.style, optimizedStyles.base); }); }; element.addEventListener('mouseenter', handleMouseEnter); element.addEventListener('mouseleave', handleMouseLeave); return () => { element.removeEventListener('mouseenter', handleMouseEnter); element.removeEventListener('mouseleave', handleMouseLeave); }; } }, []); return jsx(Component, { ...props, ref: mergedRef }); })); }, [Component, optimizedStyles, shouldReduceEffects, executeAnimation]); return OptimizedComponent; } /** * Hook for adaptive component loading based on performance */ function useAdaptiveComponentLoading(heavyComponent, lightComponent, threshold = {}) { const { memoryUsage = 0.7, frameRate = 45, networkSpeed = ['4g', '5g'] } = threshold; const [Component, setComponent] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const { metrics } = useEnhancedPerformance(); useEffect(() => { const loadComponent = async () => { try { setIsLoading(true); setError(null); // Determine if we should load the heavy component const shouldLoadHeavy = !metrics || metrics.memoryUsage < memoryUsage && metrics.frameRate >= frameRate && networkSpeed.includes(metrics.networkSpeed); if (shouldLoadHeavy) { const heavyModule = await heavyComponent(); setComponent(() => heavyModule.default); } else { setComponent(() => lightComponent); } } catch (err) { setError(err); setComponent(() => lightComponent); // Fallback to light component } finally { setIsLoading(false); } }; loadComponent(); }, [heavyComponent, lightComponent, metrics, memoryUsage, frameRate, networkSpeed]); return { Component, isLoading, error }; } export { useAdaptiveComponentLoading, useGlassOptimization, useOptimizedGlassComponent }; //# sourceMappingURL=useGlassOptimization.js.map