aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
317 lines (314 loc) • 10.4 kB
JavaScript
'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