@shohojdhara/atomix 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/atomix.config.ts +58 -1
- package/dist/atomix.css +148 -120
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1227 -122
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1052 -41
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2086 -1035
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1620 -600
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +441 -270
- package/dist/index.esm.js +1900 -638
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1935 -670
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +6 -3
- package/scripts/atomix-cli.js +148 -4
- package/scripts/cli/__tests__/basic.test.js +3 -2
- package/scripts/cli/__tests__/clean.test.js +278 -0
- package/scripts/cli/__tests__/component-validator.test.js +433 -0
- package/scripts/cli/__tests__/generator.test.js +613 -0
- package/scripts/cli/__tests__/glass-motion.test.js +256 -0
- package/scripts/cli/__tests__/integration.test.js +719 -108
- package/scripts/cli/__tests__/migrate.test.js +74 -0
- package/scripts/cli/__tests__/security.test.js +206 -0
- package/scripts/cli/__tests__/test-setup.js +3 -1
- package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
- package/scripts/cli/__tests__/token-provider.test.js +361 -0
- package/scripts/cli/__tests__/utils.test.js +5 -5
- package/scripts/cli/commands/benchmark.js +105 -0
- package/scripts/cli/commands/build-theme.js +4 -1
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +135 -14
- package/scripts/cli/commands/init.js +45 -18
- package/scripts/cli/commands/migrate.js +106 -0
- package/scripts/cli/commands/sync-tokens.js +206 -0
- package/scripts/cli/commands/theme-bridge.js +248 -0
- package/scripts/cli/commands/tokens.js +157 -0
- package/scripts/cli/commands/validate.js +194 -0
- package/scripts/cli/internal/ai-engine.js +156 -0
- package/scripts/cli/internal/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +102 -2
- package/scripts/cli/internal/generator.js +359 -39
- package/scripts/cli/internal/glass-generator.js +398 -0
- package/scripts/cli/internal/hook-generator.js +369 -0
- package/scripts/cli/internal/hooks.js +61 -0
- package/scripts/cli/internal/itcss-generator.js +565 -0
- package/scripts/cli/internal/motion-generator.js +679 -0
- package/scripts/cli/internal/template-engine.js +301 -0
- package/scripts/cli/internal/theme-bridge.js +664 -0
- package/scripts/cli/internal/tokens/engine.js +122 -0
- package/scripts/cli/internal/tokens/provider.js +34 -0
- package/scripts/cli/internal/tokens/providers/figma.js +50 -0
- package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
- package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
- package/scripts/cli/internal/tokens/token-provider.js +443 -0
- package/scripts/cli/internal/tokens/token-validator.js +513 -0
- package/scripts/cli/internal/validator.js +276 -0
- package/scripts/cli/internal/wizard.js +60 -6
- package/scripts/cli/mappings.js +23 -0
- package/scripts/cli/migration-tools.js +164 -94
- package/scripts/cli/plugins/style-dictionary.js +46 -0
- package/scripts/cli/templates/README.md +525 -95
- package/scripts/cli/templates/common-templates.js +40 -14
- package/scripts/cli/templates/components/react-component.ts +282 -0
- package/scripts/cli/templates/config/project-config.ts +112 -0
- package/scripts/cli/templates/hooks/use-component.ts +477 -0
- package/scripts/cli/templates/index.js +19 -4
- package/scripts/cli/templates/index.ts +171 -0
- package/scripts/cli/templates/next-templates.js +72 -0
- package/scripts/cli/templates/react-templates.js +70 -126
- package/scripts/cli/templates/scss-templates.js +35 -35
- package/scripts/cli/templates/stories/storybook-story.ts +241 -0
- package/scripts/cli/templates/styles/scss-component.ts +255 -0
- package/scripts/cli/templates/tests/vitest-test.ts +229 -0
- package/scripts/cli/templates/token-templates.js +337 -1
- package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
- package/scripts/cli/templates/types/component-types.ts +145 -0
- package/scripts/cli/templates/utils/testing-utils.ts +144 -0
- package/scripts/cli/templates/vanilla-templates.js +39 -0
- package/scripts/cli/token-manager.js +8 -2
- package/scripts/cli/utils/cache-manager.js +240 -0
- package/scripts/cli/utils/detector.js +46 -0
- package/scripts/cli/utils/diagnostics.js +289 -0
- package/scripts/cli/utils/error.js +45 -3
- package/scripts/cli/utils/helpers.js +24 -0
- package/scripts/cli/utils/logger.js +1 -1
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +4 -38
- package/scripts/cli/utils.js +46 -0
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.test.tsx +0 -17
- package/src/components/Accordion/Accordion.tsx +0 -4
- package/src/components/AtomixGlass/AtomixGlass.tsx +102 -2
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +125 -12
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- package/src/components/AtomixGlass/animation-system.ts +578 -0
- package/src/components/AtomixGlass/shader-utils.ts +4 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
- package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
- package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
- package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
- package/src/components/Button/Button.stories.tsx +10 -0
- package/src/components/Button/Button.test.tsx +16 -11
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +12 -12
- package/src/components/Form/Select.tsx +62 -3
- package/src/components/Modal/Modal.tsx +14 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +38 -0
- package/src/components/Steps/Steps.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +77 -8
- package/src/components/Testimonial/Testimonial.tsx +1 -1
- package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
- package/src/components/TypedButton/TypedButton.tsx +39 -0
- package/src/components/TypedButton/index.ts +2 -0
- package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
- package/src/lib/composables/index.ts +4 -7
- package/src/lib/composables/types.ts +45 -0
- package/src/lib/composables/useAccordion.ts +0 -7
- package/src/lib/composables/useAtomixGlass.ts +144 -5
- package/src/lib/composables/useChartExport.ts +3 -13
- package/src/lib/composables/useDropdown.ts +66 -0
- package/src/lib/composables/useFocusTrap.ts +80 -0
- package/src/lib/composables/usePerformanceMonitor.ts +448 -0
- package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
- package/src/lib/composables/useResponsiveGlass.ts +441 -0
- package/src/lib/composables/useTooltip.ts +16 -0
- package/src/lib/composables/useTypedButton.ts +66 -0
- package/src/lib/config/index.ts +62 -5
- package/src/lib/constants/components.ts +55 -0
- package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
- package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
- package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
- package/src/lib/types/components.ts +37 -11
- package/src/lib/types/glass.ts +35 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/utils/displacement-generator.ts +1 -1
- package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
- package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
- package/src/styles/06-components/_components.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- package/src/styles/06-components/_components.typedbutton.scss +212 -0
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
- package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
- package/src/styles/06-components/old.chart.styles.scss +0 -2788
package/dist/core.js
CHANGED
|
@@ -92,7 +92,7 @@ var match, version, createPropertyDescriptor$2 = function(bitmap, value) {
|
|
|
92
92
|
return "object" == typeof it ? null !== it : isCallable$7(it);
|
|
93
93
|
}, path$3 = {}, path$2 = path$3, globalThis$a = globalThis_1, isCallable$6 = isCallable$8, aFunction = function(variable) {
|
|
94
94
|
return isCallable$6(variable) ? variable : void 0;
|
|
95
|
-
}, navigator = globalThis_1.navigator, userAgent$1 = navigator && navigator.userAgent, globalThis$8 = globalThis_1, userAgent = userAgent$1 ? String(userAgent$1) : "", process$1 = globalThis$8.process, Deno = globalThis$8.Deno, versions = process$1 && process$1.versions || Deno && Deno.version, v8 = versions && versions.v8;
|
|
95
|
+
}, navigator$1 = globalThis_1.navigator, userAgent$1 = navigator$1 && navigator$1.userAgent, globalThis$8 = globalThis_1, userAgent = userAgent$1 ? String(userAgent$1) : "", process$1 = globalThis$8.process, Deno = globalThis$8.Deno, versions = process$1 && process$1.versions || Deno && Deno.version, v8 = versions && versions.v8;
|
|
96
96
|
|
|
97
97
|
v8 && (
|
|
98
98
|
// in old Chrome, versions of V8 isn't V8 = Chrome / 10
|
|
@@ -564,7 +564,16 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
564
564
|
PADDING: "0",
|
|
565
565
|
MODE: "standard",
|
|
566
566
|
OVER_LIGHT: !1,
|
|
567
|
-
ENABLE_OVER_LIGHT_LAYERS: !0
|
|
567
|
+
ENABLE_OVER_LIGHT_LAYERS: !0,
|
|
568
|
+
// Phase 1: Time-Based Animation System defaults
|
|
569
|
+
WITH_TIME_ANIMATION: !0,
|
|
570
|
+
ANIMATION_SPEED: 1,
|
|
571
|
+
// Phase 1: Multi-Layer Distortion System defaults
|
|
572
|
+
WITH_MULTI_LAYER_DISTORTION: !1,
|
|
573
|
+
DISTORTION_OCTAVES: 5,
|
|
574
|
+
DISTORTION_LACUNARITY: 2,
|
|
575
|
+
DISTORTION_GAIN: .5,
|
|
576
|
+
DISTORTION_QUALITY: "high"
|
|
568
577
|
},
|
|
569
578
|
CONSTANTS: {
|
|
570
579
|
ACTIVATION_ZONE: 200,
|
|
@@ -727,6 +736,44 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
727
736
|
// Saturation constants
|
|
728
737
|
SATURATION: {
|
|
729
738
|
HIGH_CONTRAST: 200
|
|
739
|
+
},
|
|
740
|
+
// Phase 1: Animation System Constants
|
|
741
|
+
ANIMATION: {
|
|
742
|
+
// Breathing effect timing (in milliseconds)
|
|
743
|
+
BREATHING_CYCLE: 2e3,
|
|
744
|
+
// 2-second breathing cycle
|
|
745
|
+
// Flow animation speed
|
|
746
|
+
FLOW_SPEED_X: .1,
|
|
747
|
+
// Horizontal flow speed
|
|
748
|
+
FLOW_SPEED_Y: .15,
|
|
749
|
+
// Vertical flow speed
|
|
750
|
+
// Wave propagation
|
|
751
|
+
WAVE_SPEED: .05,
|
|
752
|
+
// Radial wave speed
|
|
753
|
+
WAVE_AMPLITUDE: .02
|
|
754
|
+
},
|
|
755
|
+
// Phase 1: Multi-Layer Distortion Quality Presets
|
|
756
|
+
DISTORTION_QUALITY_PRESETS: {
|
|
757
|
+
low: {
|
|
758
|
+
octaves: 2,
|
|
759
|
+
lacunarity: 2,
|
|
760
|
+
gain: .5
|
|
761
|
+
},
|
|
762
|
+
medium: {
|
|
763
|
+
octaves: 4,
|
|
764
|
+
lacunarity: 2,
|
|
765
|
+
gain: .5
|
|
766
|
+
},
|
|
767
|
+
high: {
|
|
768
|
+
octaves: 5,
|
|
769
|
+
lacunarity: 2,
|
|
770
|
+
gain: .5
|
|
771
|
+
},
|
|
772
|
+
ultra: {
|
|
773
|
+
octaves: 7,
|
|
774
|
+
lacunarity: 2,
|
|
775
|
+
gain: .5
|
|
776
|
+
}
|
|
730
777
|
}
|
|
731
778
|
}
|
|
732
779
|
}, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
|
|
@@ -805,7 +852,7 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
805
852
|
// Silently handle errors
|
|
806
853
|
}
|
|
807
854
|
return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
|
|
808
|
-
}, lerp = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
|
|
855
|
+
}, lerp$1 = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
|
|
809
856
|
switch (mode) {
|
|
810
857
|
case "standard":
|
|
811
858
|
return displacementMap;
|
|
@@ -978,14 +1025,16 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
|
|
|
978
1025
|
}, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
|
|
979
1026
|
width: 0,
|
|
980
1027
|
height: 0
|
|
981
|
-
}, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1,
|
|
1028
|
+
}, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1,
|
|
1029
|
+
// Phase 1: Animation System props
|
|
1030
|
+
shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
|
|
982
1031
|
// Generate a stable, deterministic ID for SSR compatibility
|
|
983
1032
|
// Use a module-level counter that's consistent across server and client
|
|
984
|
-
const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null);
|
|
1033
|
+
const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
|
|
985
1034
|
// Lazy load shader utilities only when shader mode is needed
|
|
986
1035
|
useEffect((() => {
|
|
987
1036
|
"shader" === mode ?
|
|
988
|
-
// Dynamic import shader utilities
|
|
1037
|
+
// Dynamic import shader utilities with animation support
|
|
989
1038
|
Promise.resolve().then((() => shaderUtils)).then((shaderUtils => {
|
|
990
1039
|
shaderUtilsRef.current = {
|
|
991
1040
|
ShaderDisplacementGenerator: shaderUtils.ShaderDisplacementGenerator,
|
|
@@ -1000,7 +1049,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
|
|
|
1000
1049
|
// Generate shader map with debouncing and caching
|
|
1001
1050
|
useEffect((() => {
|
|
1002
1051
|
// Enhanced validation for shader mode
|
|
1003
|
-
if ("shader" === mode && glassSize && validateGlassSize(glassSize)
|
|
1052
|
+
if ("shader" === mode && glassSize && validateGlassSize(glassSize)) {
|
|
1004
1053
|
// Create cache key from size and variant
|
|
1005
1054
|
const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
|
|
1006
1055
|
const entry = sharedShaderCache.get(key);
|
|
@@ -1022,11 +1071,9 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
|
|
|
1022
1071
|
width: glassSize.width,
|
|
1023
1072
|
height: glassSize.height,
|
|
1024
1073
|
fragment: selectedShader
|
|
1025
|
-
}),
|
|
1026
|
-
// Defer shader generation with longer delay to avoid blocking
|
|
1027
|
-
setTimeout((() => {
|
|
1074
|
+
}), shaderUpdateTimeoutRef.current = setTimeout((() => {
|
|
1028
1075
|
const url = shaderGeneratorRef.current?.updateShader() || "";
|
|
1029
|
-
((key, url) => {
|
|
1076
|
+
url && ((key, url) => {
|
|
1030
1077
|
// Evict oldest entries if at capacity
|
|
1031
1078
|
if (sharedShaderCache.size >= 15) {
|
|
1032
1079
|
const entries = Array.from(sharedShaderCache.entries());
|
|
@@ -1058,7 +1105,8 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
|
|
|
1058
1105
|
// Cleanup function with error handling
|
|
1059
1106
|
return () => {
|
|
1060
1107
|
shaderDebounceTimeoutRef.current && (clearTimeout(shaderDebounceTimeoutRef.current),
|
|
1061
|
-
shaderDebounceTimeoutRef.current = null)
|
|
1108
|
+
shaderDebounceTimeoutRef.current = null), shaderUpdateTimeoutRef.current && (clearTimeout(shaderUpdateTimeoutRef.current),
|
|
1109
|
+
shaderUpdateTimeoutRef.current = null);
|
|
1062
1110
|
try {
|
|
1063
1111
|
shaderGeneratorRef.current?.destroy();
|
|
1064
1112
|
} catch (error) {
|
|
@@ -1067,7 +1115,37 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
|
|
|
1067
1115
|
shaderGeneratorRef.current = null;
|
|
1068
1116
|
}
|
|
1069
1117
|
};
|
|
1070
|
-
}), [ mode, glassSize, shaderVariant ])
|
|
1118
|
+
}), [ mode, glassSize, shaderVariant ]),
|
|
1119
|
+
// Phase 1: Time-Based Animation Loop - Continuous shader regeneration
|
|
1120
|
+
useEffect((() => {
|
|
1121
|
+
// Only run animations in shader mode with time animation enabled
|
|
1122
|
+
if ("shader" !== mode || !withTimeAnimation || effectiveReducedMotion || effectiveWithoutEffects)
|
|
1123
|
+
// Cancel any existing animation frame
|
|
1124
|
+
return void (null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
|
|
1125
|
+
animationFrameRef.current = null));
|
|
1126
|
+
const baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed || 1)), complexity = withMultiLayerDistortion ? Math.max(1, (distortionOctaves || 3) / 3 + .25 * Math.max(0, (distortionLacunarity || 2) - 2) + Math.max(0, (distortionGain || .5) - .5)) : 1, frameInterval = 1e3 / Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
|
|
1127
|
+
let lastUpdate = 0, isCancelled = !1;
|
|
1128
|
+
const animate = currentTime => {
|
|
1129
|
+
if (!isCancelled) {
|
|
1130
|
+
if (currentTime - lastUpdate >= frameInterval && shaderGeneratorRef.current) {
|
|
1131
|
+
lastUpdate = currentTime;
|
|
1132
|
+
try {
|
|
1133
|
+
const animatedShaderUrl = shaderGeneratorRef.current.updateShader();
|
|
1134
|
+
animatedShaderUrl && setShaderMapUrl(animatedShaderUrl);
|
|
1135
|
+
} catch (error) {
|
|
1136
|
+
console.warn("AtomixGlassContainer: Error in animation loop", error);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
// Start animation loop
|
|
1143
|
+
// Cleanup animation on unmount or dependency change
|
|
1144
|
+
return animationFrameRef.current = requestAnimationFrame(animate), () => {
|
|
1145
|
+
isCancelled = !0, null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
|
|
1146
|
+
animationFrameRef.current = null);
|
|
1147
|
+
};
|
|
1148
|
+
}), [ mode, withTimeAnimation, animationSpeed, displacementScale, withMultiLayerDistortion, distortionOctaves, distortionLacunarity, distortionGain, distortionQuality, effectiveReducedMotion, effectiveWithoutEffects, glassSize ]);
|
|
1071
1149
|
// Removed forced reflow to avoid layout thrash and potential feedback sizing loops
|
|
1072
1150
|
const [rectCache, setRectCache] = useState(null);
|
|
1073
1151
|
useEffect((() => {
|
|
@@ -1401,12 +1479,144 @@ class {
|
|
|
1401
1479
|
style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
|
|
1402
1480
|
style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)");
|
|
1403
1481
|
}
|
|
1404
|
-
}
|
|
1482
|
+
};
|
|
1405
1483
|
|
|
1406
1484
|
/**
|
|
1407
1485
|
* Updates the styles of the AtomixGlass wrapper and container elements imperatively
|
|
1408
1486
|
* to avoid React re-renders on mouse movement.
|
|
1409
|
-
*/
|
|
1487
|
+
*/
|
|
1488
|
+
/**
|
|
1489
|
+
* Animation System for AtomixGlass Component
|
|
1490
|
+
*
|
|
1491
|
+
* Implements Phase 1 features from the AtomixGlass Feature Implementation Roadmap:
|
|
1492
|
+
* - Feature 1.1: Time-Based Animation System
|
|
1493
|
+
* - Feature 1.2: Multi-Layer Distortion System (FBM)
|
|
1494
|
+
*
|
|
1495
|
+
* @packageDocumentation
|
|
1496
|
+
*/
|
|
1497
|
+
// ============================================================================
|
|
1498
|
+
// Noise Functions for FBM (Feature 1.2)
|
|
1499
|
+
// ============================================================================
|
|
1500
|
+
/**
|
|
1501
|
+
* Perlin noise implementation for smooth gradient noise
|
|
1502
|
+
*
|
|
1503
|
+
* @param x - X coordinate
|
|
1504
|
+
* @param y - Y coordinate
|
|
1505
|
+
* @returns Noise value in range [0, 1]
|
|
1506
|
+
*/
|
|
1507
|
+
function perlinNoise(x, y) {
|
|
1508
|
+
// Simplified Perlin noise using pseudo-random gradients
|
|
1509
|
+
const X = 255 & Math.floor(x), Y = 255 & Math.floor(y), xf = x - Math.floor(x), yf = y - Math.floor(y), u = fade(xf), v = fade(yf), A = p[X] + Y & 255, B = p[X + 1] + Y & 255, ga = grad(p[A], xf, yf), gb = grad(p[B], xf - 1, yf), gc = grad(p[A + 1 & 255], xf, yf - 1), gd = grad(p[B + 1 & 255], xf - 1, yf - 1), lerpX1 = lerp(ga, gb, u), lerpX2 = lerp(gc, gd, u);
|
|
1510
|
+
// Scale to [0, 1] range
|
|
1511
|
+
return (lerp(lerpX1, lerpX2, v) + 1) / 2;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// ============================================================================
|
|
1515
|
+
// Fractal Brownian Motion (FBM) Engine (Feature 1.2)
|
|
1516
|
+
// ============================================================================
|
|
1517
|
+
/**
|
|
1518
|
+
* Creates an FBM engine with configurable parameters
|
|
1519
|
+
*
|
|
1520
|
+
* @param config - FBM configuration (octaves, lacunarity, gain)
|
|
1521
|
+
* @returns Object with fbm function
|
|
1522
|
+
*
|
|
1523
|
+
* @example
|
|
1524
|
+
* ```typescript
|
|
1525
|
+
* const fbmEngine = createFBMEngine({ octaves: 5, lacunarity: 2, gain: 0.5 });
|
|
1526
|
+
*
|
|
1527
|
+
* // Generate noise at position (0.5, 0.5) with time animation
|
|
1528
|
+
* const noiseValue = fbmEngine.fbm(0.5, 0.5, Date.now());
|
|
1529
|
+
* ```
|
|
1530
|
+
*/ function createFBMEngine(config) {
|
|
1531
|
+
/**
|
|
1532
|
+
* Fractal Brownian Motion function
|
|
1533
|
+
* Combines multiple octaves of noise for complex, natural patterns
|
|
1534
|
+
*
|
|
1535
|
+
* @param x - X coordinate
|
|
1536
|
+
* @param y - Y coordinate
|
|
1537
|
+
* @param time - Optional time value for animation
|
|
1538
|
+
* @returns FBM noise value in range [0, 1]
|
|
1539
|
+
*/
|
|
1540
|
+
const fbm = (x, y, time = 0) => {
|
|
1541
|
+
let value = 0, amplitude = .5, frequency = 1, phase = .001 * time;
|
|
1542
|
+
// Convert to seconds for reasonable animation speed
|
|
1543
|
+
for (let i = 0; i < config.octaves; i++)
|
|
1544
|
+
// Apply time-based phase shift to all octaves
|
|
1545
|
+
value += perlinNoise(x * frequency + phase, y * frequency + phase) * amplitude,
|
|
1546
|
+
frequency *= config.lacunarity, // Increase frequency
|
|
1547
|
+
amplitude *= config.gain;
|
|
1548
|
+
return value;
|
|
1549
|
+
};
|
|
1550
|
+
/**
|
|
1551
|
+
* Get FBM with simple time factor
|
|
1552
|
+
*/ return {
|
|
1553
|
+
fbm: fbm,
|
|
1554
|
+
fbmWithTime: (x, y, time) => fbm(x, y, time)
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* Gets optimal FBM config based on quality preset
|
|
1560
|
+
*
|
|
1561
|
+
* @param quality - Quality preset level
|
|
1562
|
+
* @returns FBM configuration for the quality level
|
|
1563
|
+
*/ const fbmEngineCache = new Map;
|
|
1564
|
+
|
|
1565
|
+
// ============================================================================
|
|
1566
|
+
// Shader Utility Functions for Time-Based Effects
|
|
1567
|
+
// ============================================================================
|
|
1568
|
+
/**
|
|
1569
|
+
* Liquid glass distortion with time-based animation
|
|
1570
|
+
* Uses FBM to create organic, flowing liquid effects
|
|
1571
|
+
*
|
|
1572
|
+
* @param uv - UV coordinates (normalized 0-1)
|
|
1573
|
+
* @param time - Elapsed time in milliseconds
|
|
1574
|
+
* @param config - FBM configuration
|
|
1575
|
+
* @returns Distorted UV coordinates
|
|
1576
|
+
*/ function liquidGlassWithTime(uv, time, config) {
|
|
1577
|
+
const configKey = `${config.octaves}-${config.lacunarity}-${config.gain}`;
|
|
1578
|
+
let fbmEngine = fbmEngineCache.get(configKey);
|
|
1579
|
+
fbmEngine || (fbmEngine = createFBMEngine(config), fbmEngineCache.set(configKey, fbmEngine));
|
|
1580
|
+
// Animate noise with time
|
|
1581
|
+
const animatedNoise = fbmEngine.fbmWithTime(2 * uv.x + 1e-4 * time, 2 * uv.y + 15e-5 * time, time);
|
|
1582
|
+
return {
|
|
1583
|
+
x: uv.x + .04 * (animatedNoise - .5),
|
|
1584
|
+
y: uv.y + .04 * (animatedNoise - .5)
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// ============================================================================
|
|
1589
|
+
// Helper Functions
|
|
1590
|
+
// ============================================================================
|
|
1591
|
+
/**
|
|
1592
|
+
* Fade curve for smooth interpolation (Perlin's fade function)
|
|
1593
|
+
*/ function fade(t) {
|
|
1594
|
+
return t * t * t * (t * (6 * t - 15) + 10);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Linear interpolation
|
|
1599
|
+
*/ function lerp(a, b, t) {
|
|
1600
|
+
return a + t * (b - a);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* Gradient calculation for Perlin noise
|
|
1605
|
+
*/ function grad(hash, x, y) {
|
|
1606
|
+
const h = 15 & hash, u = h < 8 ? x : y, v = h < 4 ? y : 12 === h || 14 === h ? x : 0;
|
|
1607
|
+
return (1 & h ? -u : u) + (2 & h ? -v : v);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* Permutation table for Perlin noise
|
|
1612
|
+
*/ const p = (() => {
|
|
1613
|
+
const permutation = [];
|
|
1614
|
+
for (let i = 0; i < 256; i++) permutation[i] = Math.floor(256 * Math.random());
|
|
1615
|
+
// Duplicate for overflow handling
|
|
1616
|
+
return [ ...permutation, ...permutation ];
|
|
1617
|
+
})(), {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
|
|
1618
|
+
|
|
1619
|
+
const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
|
|
1410
1620
|
parentElement && backgroundDetectionCache.set(parentElement, {
|
|
1411
1621
|
result: result,
|
|
1412
1622
|
timestamp: Date.now(),
|
|
@@ -1419,7 +1629,9 @@ class {
|
|
|
1419
1629
|
* Composable hook for AtomixGlass component logic
|
|
1420
1630
|
* Manages all state, calculations, and event handlers
|
|
1421
1631
|
*/
|
|
1422
|
-
function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1
|
|
1632
|
+
function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, withTimeAnimation:
|
|
1633
|
+
// Phase 1: Animation System Props
|
|
1634
|
+
withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED, withMultiLayerDistortion: withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION, distortionOctaves: distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES, distortionLacunarity: distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY, distortionGain: distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN, distortionQuality: distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY}) {
|
|
1423
1635
|
// State
|
|
1424
1636
|
const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
|
|
1425
1637
|
x: 0,
|
|
@@ -1433,7 +1645,47 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1433
1645
|
}), targetGlobalMousePositionRef = useRef({
|
|
1434
1646
|
x: 0,
|
|
1435
1647
|
y: 0
|
|
1436
|
-
}), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1),
|
|
1648
|
+
}), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), animationFrameIdRef = useRef(null), animationStartTimeRef = useRef(0), elapsedTimeRef = useRef(0), shaderTimeRef = useRef(0), fbmConfig = useMemo((() => {
|
|
1649
|
+
// If quality preset is provided, use it as base
|
|
1650
|
+
const preset = (quality = distortionQuality, ATOMIX_GLASS.CONSTANTS.DISTORTION_QUALITY_PRESETS[quality]);
|
|
1651
|
+
// Override with custom values if provided
|
|
1652
|
+
var quality;
|
|
1653
|
+
return {
|
|
1654
|
+
octaves: distortionOctaves ?? preset.octaves,
|
|
1655
|
+
lacunarity: distortionLacunarity ?? preset.lacunarity,
|
|
1656
|
+
gain: distortionGain ?? preset.gain
|
|
1657
|
+
};
|
|
1658
|
+
}), [ distortionQuality, distortionOctaves, distortionLacunarity, distortionGain ]), fbmEngine = useMemo((() => withMultiLayerDistortion ? createFBMEngine(fbmConfig) : null), [ withMultiLayerDistortion, fbmConfig ]), effectiveReducedMotion = useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveWithTimeAnimation = useMemo((() => withTimeAnimation && !effectiveReducedMotion), [ withTimeAnimation, effectiveReducedMotion ]);
|
|
1659
|
+
/**
|
|
1660
|
+
* Animation loop for time-based effects
|
|
1661
|
+
*/
|
|
1662
|
+
useEffect((() => {
|
|
1663
|
+
if (!effectiveWithTimeAnimation || "undefined" == typeof window) return;
|
|
1664
|
+
let lastFrameTime = performance.now();
|
|
1665
|
+
/**
|
|
1666
|
+
* Animation frame handler
|
|
1667
|
+
*/ const animate = currentTime => {
|
|
1668
|
+
// Calculate delta time
|
|
1669
|
+
const deltaTime = currentTime - lastFrameTime;
|
|
1670
|
+
lastFrameTime = currentTime;
|
|
1671
|
+
// Apply animation speed multiplier
|
|
1672
|
+
const scaledDelta = deltaTime * animationSpeed;
|
|
1673
|
+
elapsedTimeRef.current += scaledDelta, shaderTimeRef.current = elapsedTimeRef.current,
|
|
1674
|
+
// Continue animation loop
|
|
1675
|
+
animationFrameIdRef.current = requestAnimationFrame(animate);
|
|
1676
|
+
};
|
|
1677
|
+
// Start animation
|
|
1678
|
+
// Cleanup
|
|
1679
|
+
return animationStartTimeRef.current = performance.now(), animationFrameIdRef.current = requestAnimationFrame(animate),
|
|
1680
|
+
() => {
|
|
1681
|
+
null !== animationFrameIdRef.current && (cancelAnimationFrame(animationFrameIdRef.current),
|
|
1682
|
+
animationFrameIdRef.current = null);
|
|
1683
|
+
};
|
|
1684
|
+
}), [ effectiveWithTimeAnimation, animationSpeed ]);
|
|
1685
|
+
/**
|
|
1686
|
+
* Get current shader time for animations
|
|
1687
|
+
*/
|
|
1688
|
+
const getShaderTime = useCallback((() => shaderTimeRef.current), []), applyTimeBasedDistortion = useCallback((uv => effectiveWithTimeAnimation && fbmEngine ? liquidGlassWithTime(uv, shaderTimeRef.current, fbmConfig) : uv), [ effectiveWithTimeAnimation, fbmEngine, fbmConfig ]), effectiveBorderRadius = useMemo((() => void 0 !== borderRadius ? Math.max(0, borderRadius) : Math.max(0, dynamicBorderRadius)), [ borderRadius, dynamicBorderRadius ]), {glassSize: glassSize} = function({glassRef: glassRef, effectiveBorderRadius: effectiveBorderRadius, cachedRectRef: cachedRectRef}) {
|
|
1437
1689
|
const [glassSize, setGlassSize] = useState({
|
|
1438
1690
|
width: 270,
|
|
1439
1691
|
height: 69
|
|
@@ -1492,7 +1744,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1492
1744
|
glassRef: glassRef,
|
|
1493
1745
|
effectiveBorderRadius: effectiveBorderRadius,
|
|
1494
1746
|
cachedRectRef: cachedRectRef
|
|
1495
|
-
}),
|
|
1747
|
+
}), effectiveHighContrast = useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveWithoutEffects = useMemo((() => withoutEffects || effectiveReducedMotion), [ withoutEffects, effectiveReducedMotion ]), globalMousePosition = externalGlobalMousePosition || internalGlobalMousePositionRef.current, mouseOffset = externalMouseOffset || internalMouseOffsetRef.current;
|
|
1748
|
+
/**
|
|
1749
|
+
* Apply time-based distortion to UV coordinates
|
|
1750
|
+
*/
|
|
1496
1751
|
// Extract border-radius from children
|
|
1497
1752
|
useEffect((() => {
|
|
1498
1753
|
const extractRadius = () => {
|
|
@@ -1695,6 +1950,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1695
1950
|
lerpActiveRef.current = !0;
|
|
1696
1951
|
const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
|
|
1697
1952
|
if (!lerpActiveRef.current) return;
|
|
1953
|
+
// Add ref validity check to prevent memory leaks
|
|
1954
|
+
if (!glassRef.current || !wrapperRef?.current) return void (lerpActiveRef.current = !1);
|
|
1698
1955
|
const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
|
|
1699
1956
|
// If we're close enough, snap and park
|
|
1700
1957
|
if (Math.abs(dx) < .05 && Math.abs(dy) < .05) internalMouseOffsetRef.current = {
|
|
@@ -1703,17 +1960,17 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1703
1960
|
...targetGlobalMousePositionRef.current
|
|
1704
1961
|
}; else {
|
|
1705
1962
|
internalMouseOffsetRef.current = {
|
|
1706
|
-
x: lerp(cur.x, tgt.x, LERP_T),
|
|
1707
|
-
y: lerp(cur.y, tgt.y, LERP_T)
|
|
1963
|
+
x: lerp$1(cur.x, tgt.x, LERP_T),
|
|
1964
|
+
y: lerp$1(cur.y, tgt.y, LERP_T)
|
|
1708
1965
|
};
|
|
1709
1966
|
const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
|
|
1710
1967
|
internalGlobalMousePositionRef.current = {
|
|
1711
|
-
x: lerp(curG.x, tgtG.x, LERP_T),
|
|
1712
|
-
y: lerp(curG.y, tgtG.y, LERP_T)
|
|
1968
|
+
x: lerp$1(curG.x, tgtG.x, LERP_T),
|
|
1969
|
+
y: lerp$1(curG.y, tgtG.y, LERP_T)
|
|
1713
1970
|
};
|
|
1714
1971
|
}
|
|
1715
1972
|
// Imperative style update with the smoothed values
|
|
1716
|
-
updateAtomixGlassStyles(wrapperRef
|
|
1973
|
+
updateAtomixGlassStyles(wrapperRef.current, glassRef.current, {
|
|
1717
1974
|
mouseOffset: internalMouseOffsetRef.current,
|
|
1718
1975
|
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
1719
1976
|
glassSize: glassSize,
|
|
@@ -1804,6 +2061,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1804
2061
|
// This is now static (refs or props) unless prop changes
|
|
1805
2062
|
overLightConfig: overLightConfig,
|
|
1806
2063
|
transformStyle: transformStyle,
|
|
2064
|
+
getShaderTime: getShaderTime,
|
|
2065
|
+
applyTimeBasedDistortion: applyTimeBasedDistortion,
|
|
1807
2066
|
handleMouseEnter: handleMouseEnter,
|
|
1808
2067
|
handleMouseLeave: handleMouseLeave,
|
|
1809
2068
|
handleMouseDown: handleMouseDown,
|
|
@@ -1812,6 +2071,358 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1812
2071
|
};
|
|
1813
2072
|
}
|
|
1814
2073
|
|
|
2074
|
+
/**
|
|
2075
|
+
* Default responsive breakpoints configuration
|
|
2076
|
+
*
|
|
2077
|
+
* These breakpoints are optimized for glass effect performance across device classes:
|
|
2078
|
+
* - Mobile: Reduced complexity for 60 FPS target
|
|
2079
|
+
* - Tablet: Balanced quality and performance
|
|
2080
|
+
* - Desktop: Full fidelity effects
|
|
2081
|
+
*/ const DEFAULT_BREAKPOINTS = {
|
|
2082
|
+
mobile: {
|
|
2083
|
+
maxWidth: 640,
|
|
2084
|
+
params: {
|
|
2085
|
+
distortionOctaves: 3,
|
|
2086
|
+
displacementScale: .7,
|
|
2087
|
+
blurAmount: .8,
|
|
2088
|
+
animationSpeed: .8,
|
|
2089
|
+
chromaticIntensity: .5
|
|
2090
|
+
}
|
|
2091
|
+
},
|
|
2092
|
+
tablet: {
|
|
2093
|
+
minWidth: 641,
|
|
2094
|
+
maxWidth: 1024,
|
|
2095
|
+
params: {
|
|
2096
|
+
distortionOctaves: 4,
|
|
2097
|
+
displacementScale: .85,
|
|
2098
|
+
blurAmount: .9,
|
|
2099
|
+
animationSpeed: .9,
|
|
2100
|
+
chromaticIntensity: .75
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
desktop: {
|
|
2104
|
+
minWidth: 1025,
|
|
2105
|
+
params: {
|
|
2106
|
+
distortionOctaves: 5,
|
|
2107
|
+
displacementScale: 1,
|
|
2108
|
+
blurAmount: 1,
|
|
2109
|
+
animationSpeed: 1,
|
|
2110
|
+
chromaticIntensity: 1
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2115
|
+
/**
|
|
2116
|
+
* Device performance tier detection
|
|
2117
|
+
*
|
|
2118
|
+
* Uses Device Memory API and Hardware Concurrency API to classify devices
|
|
2119
|
+
* into performance tiers for automatic quality adjustment.
|
|
2120
|
+
*
|
|
2121
|
+
* @returns Performance tier classification
|
|
2122
|
+
*/
|
|
2123
|
+
/**
|
|
2124
|
+
* PerformanceDashboard - Real-time performance monitoring overlay
|
|
2125
|
+
*
|
|
2126
|
+
* Displays:
|
|
2127
|
+
* - Current FPS with color coding
|
|
2128
|
+
* - Frame time statistics
|
|
2129
|
+
* - Quality level indicator
|
|
2130
|
+
* - GPU memory usage (if available)
|
|
2131
|
+
* - Auto-scaling status
|
|
2132
|
+
*/
|
|
2133
|
+
const PerformanceDashboard = ({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
|
|
2134
|
+
// Get color for FPS display
|
|
2135
|
+
const getFpsColor = fps => fps >= 58 ? "#4ade80" : // Green - good
|
|
2136
|
+
fps >= 45 ? "#fbbf24" : "#ef4444" // Red - critical
|
|
2137
|
+
, dashboardStyle = useMemo((() => ({
|
|
2138
|
+
position: "fixed",
|
|
2139
|
+
top: "16px",
|
|
2140
|
+
right: "16px",
|
|
2141
|
+
padding: "12px 16px",
|
|
2142
|
+
backgroundColor: "rgba(17, 24, 39, 0.95)",
|
|
2143
|
+
borderRadius: "8px",
|
|
2144
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
2145
|
+
fontFamily: "monospace",
|
|
2146
|
+
fontSize: "12px",
|
|
2147
|
+
color: "#fff",
|
|
2148
|
+
zIndex: 9999,
|
|
2149
|
+
minWidth: "200px",
|
|
2150
|
+
backdropFilter: "blur(8px)",
|
|
2151
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2152
|
+
transition: "opacity 0.3s ease",
|
|
2153
|
+
opacity: isVisible ? 1 : 0,
|
|
2154
|
+
pointerEvents: isVisible ? "auto" : "none"
|
|
2155
|
+
})), [ isVisible ]), headerStyle = useMemo((() => ({
|
|
2156
|
+
display: "flex",
|
|
2157
|
+
justifyContent: "space-between",
|
|
2158
|
+
alignItems: "center",
|
|
2159
|
+
marginBottom: "8px",
|
|
2160
|
+
paddingBottom: "8px",
|
|
2161
|
+
borderBottom: "1px solid rgba(255, 255, 255, 0.1)"
|
|
2162
|
+
})), []), titleStyle = useMemo((() => ({
|
|
2163
|
+
fontWeight: "bold",
|
|
2164
|
+
fontSize: "13px",
|
|
2165
|
+
color: "#fff"
|
|
2166
|
+
})), []), closeButtonStyle = useMemo((() => ({
|
|
2167
|
+
background: "transparent",
|
|
2168
|
+
border: "none",
|
|
2169
|
+
color: "#9ca3af",
|
|
2170
|
+
cursor: "pointer",
|
|
2171
|
+
fontSize: "16px",
|
|
2172
|
+
padding: "0",
|
|
2173
|
+
lineHeight: "1"
|
|
2174
|
+
})), []), metricRowStyle = useMemo((() => ({
|
|
2175
|
+
display: "flex",
|
|
2176
|
+
justifyContent: "space-between",
|
|
2177
|
+
alignItems: "center",
|
|
2178
|
+
marginBottom: "6px"
|
|
2179
|
+
})), []), labelStyle = useMemo((() => ({
|
|
2180
|
+
color: "#9ca3af",
|
|
2181
|
+
marginRight: "12px"
|
|
2182
|
+
})), []), valueStyle = useMemo((() => ({
|
|
2183
|
+
fontWeight: "bold"
|
|
2184
|
+
})), []);
|
|
2185
|
+
// Get quality level badge color
|
|
2186
|
+
return isVisible ? jsxs("div", {
|
|
2187
|
+
style: dashboardStyle,
|
|
2188
|
+
children: [ jsxs("div", {
|
|
2189
|
+
style: headerStyle,
|
|
2190
|
+
children: [ jsx("span", {
|
|
2191
|
+
style: titleStyle,
|
|
2192
|
+
children: "Performance Monitor"
|
|
2193
|
+
}), onClose && jsx("button", {
|
|
2194
|
+
style: closeButtonStyle,
|
|
2195
|
+
onClick: onClose,
|
|
2196
|
+
"aria-label": "Close performance dashboard",
|
|
2197
|
+
children: "×"
|
|
2198
|
+
}) ]
|
|
2199
|
+
}), jsxs("div", {
|
|
2200
|
+
style: metricRowStyle,
|
|
2201
|
+
children: [ jsx("span", {
|
|
2202
|
+
style: labelStyle,
|
|
2203
|
+
children: "FPS"
|
|
2204
|
+
}), jsx("span", {
|
|
2205
|
+
style: {
|
|
2206
|
+
...valueStyle,
|
|
2207
|
+
color: getFpsColor(metrics.fps)
|
|
2208
|
+
},
|
|
2209
|
+
children: Math.round(metrics.fps)
|
|
2210
|
+
}) ]
|
|
2211
|
+
}), jsxs("div", {
|
|
2212
|
+
style: metricRowStyle,
|
|
2213
|
+
children: [ jsx("span", {
|
|
2214
|
+
style: labelStyle,
|
|
2215
|
+
children: "Frame Time"
|
|
2216
|
+
}), jsxs("span", {
|
|
2217
|
+
style: valueStyle,
|
|
2218
|
+
children: [ metrics.frameTime.toFixed(2), "ms" ]
|
|
2219
|
+
}) ]
|
|
2220
|
+
}), jsxs("div", {
|
|
2221
|
+
style: metricRowStyle,
|
|
2222
|
+
children: [ jsx("span", {
|
|
2223
|
+
style: labelStyle,
|
|
2224
|
+
children: "Quality"
|
|
2225
|
+
}), jsx("span", {
|
|
2226
|
+
style: {
|
|
2227
|
+
...valueStyle,
|
|
2228
|
+
color: (quality => {
|
|
2229
|
+
switch (quality) {
|
|
2230
|
+
case "high":
|
|
2231
|
+
return "#4ade80";
|
|
2232
|
+
|
|
2233
|
+
case "medium":
|
|
2234
|
+
return "#fbbf24";
|
|
2235
|
+
|
|
2236
|
+
case "low":
|
|
2237
|
+
return "#ef4444";
|
|
2238
|
+
|
|
2239
|
+
default:
|
|
2240
|
+
return "#9ca3af";
|
|
2241
|
+
}
|
|
2242
|
+
})(metrics.qualityLevel),
|
|
2243
|
+
textTransform: "uppercase",
|
|
2244
|
+
fontSize: "11px"
|
|
2245
|
+
},
|
|
2246
|
+
children: metrics.qualityLevel
|
|
2247
|
+
}) ]
|
|
2248
|
+
}), metrics.gpuMemory && jsxs("div", {
|
|
2249
|
+
style: metricRowStyle,
|
|
2250
|
+
children: [ jsx("span", {
|
|
2251
|
+
style: labelStyle,
|
|
2252
|
+
children: "GPU Memory"
|
|
2253
|
+
}), jsxs("span", {
|
|
2254
|
+
style: valueStyle,
|
|
2255
|
+
children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
|
|
2256
|
+
}) ]
|
|
2257
|
+
}), metrics.isAutoScaling && jsx("div", {
|
|
2258
|
+
style: {
|
|
2259
|
+
marginTop: "8px",
|
|
2260
|
+
paddingTop: "8px",
|
|
2261
|
+
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2262
|
+
fontSize: "10px",
|
|
2263
|
+
color: "#6b7280",
|
|
2264
|
+
textAlign: "center"
|
|
2265
|
+
},
|
|
2266
|
+
children: "Auto-scaling active"
|
|
2267
|
+
}), jsxs("div", {
|
|
2268
|
+
style: {
|
|
2269
|
+
marginTop: "8px",
|
|
2270
|
+
paddingTop: "8px",
|
|
2271
|
+
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2272
|
+
display: "flex",
|
|
2273
|
+
alignItems: "center",
|
|
2274
|
+
gap: "6px"
|
|
2275
|
+
},
|
|
2276
|
+
children: [ jsx("div", {
|
|
2277
|
+
style: {
|
|
2278
|
+
width: "8px",
|
|
2279
|
+
height: "8px",
|
|
2280
|
+
borderRadius: "50%",
|
|
2281
|
+
backgroundColor: getFpsColor(metrics.fps),
|
|
2282
|
+
animation: metrics.fps < 45 ? "pulse 1s infinite" : "none"
|
|
2283
|
+
}
|
|
2284
|
+
}), jsx("span", {
|
|
2285
|
+
style: {
|
|
2286
|
+
fontSize: "10px",
|
|
2287
|
+
color: metrics.fps >= 58 ? "#4ade80" : metrics.fps >= 45 ? "#fbbf24" : "#ef4444"
|
|
2288
|
+
},
|
|
2289
|
+
children: metrics.fps >= 58 ? "Optimal" : metrics.fps >= 45 ? "Warning" : "Critical"
|
|
2290
|
+
}) ]
|
|
2291
|
+
}) ]
|
|
2292
|
+
}) : null;
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2295
|
+
// Add pulse animation for critical FPS
|
|
2296
|
+
if ("undefined" != typeof document) {
|
|
2297
|
+
const styleSheet = document.createElement("style");
|
|
2298
|
+
styleSheet.textContent = "\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n ",
|
|
2299
|
+
document.head.appendChild(styleSheet);
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
/**
|
|
2303
|
+
* Mobile optimization presets
|
|
2304
|
+
*
|
|
2305
|
+
* These presets adjust glass effect parameters based on device performance tier
|
|
2306
|
+
* to ensure smooth animations and responsive interactions.
|
|
2307
|
+
*/
|
|
2308
|
+
/**
|
|
2309
|
+
* Performance preset - Maximum FPS, reduced quality
|
|
2310
|
+
* Best for low-end devices or when battery saving is priority
|
|
2311
|
+
*/ const PERFORMANCE_PRESET = {
|
|
2312
|
+
distortionOctaves: 2,
|
|
2313
|
+
// Minimal FBM layers
|
|
2314
|
+
displacementScale: 50,
|
|
2315
|
+
// Subtle displacement
|
|
2316
|
+
blurAmount: 5,
|
|
2317
|
+
// Light blur
|
|
2318
|
+
saturation: 80,
|
|
2319
|
+
// Reduced saturation
|
|
2320
|
+
aberrationIntensity: .3,
|
|
2321
|
+
// Minimal chromatic aberration
|
|
2322
|
+
animationSpeed: .8,
|
|
2323
|
+
// Slightly slower for performance
|
|
2324
|
+
chromaticIntensity: .3,
|
|
2325
|
+
// Low chromatic effect
|
|
2326
|
+
distortionLacunarity: 1.5,
|
|
2327
|
+
// Simpler noise pattern
|
|
2328
|
+
distortionGain: .3
|
|
2329
|
+
}, BALANCED_PRESET = {
|
|
2330
|
+
distortionOctaves: 3,
|
|
2331
|
+
// Moderate FBM layers
|
|
2332
|
+
displacementScale: 75,
|
|
2333
|
+
// Medium displacement
|
|
2334
|
+
blurAmount: 8,
|
|
2335
|
+
// Moderate blur
|
|
2336
|
+
saturation: 90,
|
|
2337
|
+
// Near-full saturation
|
|
2338
|
+
aberrationIntensity: .5,
|
|
2339
|
+
// Moderate chromatic aberration
|
|
2340
|
+
animationSpeed: 1,
|
|
2341
|
+
// Normal speed
|
|
2342
|
+
chromaticIntensity: .5,
|
|
2343
|
+
// Moderate chromatic effect
|
|
2344
|
+
distortionLacunarity: 2,
|
|
2345
|
+
// Standard noise pattern
|
|
2346
|
+
distortionGain: .4
|
|
2347
|
+
}, QUALITY_PRESET = {
|
|
2348
|
+
distortionOctaves: 4,
|
|
2349
|
+
// More FBM layers for detail
|
|
2350
|
+
displacementScale: 100,
|
|
2351
|
+
// Stronger displacement
|
|
2352
|
+
blurAmount: 12,
|
|
2353
|
+
// Smoother blur
|
|
2354
|
+
saturation: 100,
|
|
2355
|
+
// Full saturation
|
|
2356
|
+
aberrationIntensity: .7,
|
|
2357
|
+
// Pronounced chromatic aberration
|
|
2358
|
+
animationSpeed: 1.2,
|
|
2359
|
+
// Slightly faster for drama
|
|
2360
|
+
chromaticIntensity: .7,
|
|
2361
|
+
// Strong chromatic effect
|
|
2362
|
+
distortionLacunarity: 2.2,
|
|
2363
|
+
// Richer noise pattern
|
|
2364
|
+
distortionGain: .5
|
|
2365
|
+
}, MOBILE_OPTIMIZED_BREAKPOINTS = {
|
|
2366
|
+
/** Desktop - Full quality */
|
|
2367
|
+
desktop: {
|
|
2368
|
+
minWidth: 1024,
|
|
2369
|
+
params: {
|
|
2370
|
+
distortionOctaves: 6,
|
|
2371
|
+
displacementScale: 150,
|
|
2372
|
+
blurAmount: 15,
|
|
2373
|
+
saturation: 100,
|
|
2374
|
+
aberrationIntensity: 1,
|
|
2375
|
+
animationSpeed: 1,
|
|
2376
|
+
chromaticIntensity: 1,
|
|
2377
|
+
distortionLacunarity: 2.5,
|
|
2378
|
+
distortionGain: .6
|
|
2379
|
+
}
|
|
2380
|
+
},
|
|
2381
|
+
/** Laptop - High quality */
|
|
2382
|
+
laptop: {
|
|
2383
|
+
minWidth: 768,
|
|
2384
|
+
params: {
|
|
2385
|
+
...QUALITY_PRESET,
|
|
2386
|
+
distortionOctaves: 5,
|
|
2387
|
+
displacementScale: 120
|
|
2388
|
+
}
|
|
2389
|
+
},
|
|
2390
|
+
/** Tablet - Balanced quality */
|
|
2391
|
+
tablet: {
|
|
2392
|
+
minWidth: 640,
|
|
2393
|
+
params: {
|
|
2394
|
+
...BALANCED_PRESET,
|
|
2395
|
+
distortionOctaves: 4,
|
|
2396
|
+
displacementScale: 90
|
|
2397
|
+
}
|
|
2398
|
+
},
|
|
2399
|
+
/** Mobile - Performance optimized */
|
|
2400
|
+
mobile: {
|
|
2401
|
+
maxWidth: 639,
|
|
2402
|
+
params: {
|
|
2403
|
+
...PERFORMANCE_PRESET,
|
|
2404
|
+
distortionOctaves: 3,
|
|
2405
|
+
displacementScale: 75,
|
|
2406
|
+
blurAmount: 6
|
|
2407
|
+
}
|
|
2408
|
+
},
|
|
2409
|
+
/** Small mobile - Maximum performance */
|
|
2410
|
+
mobileSmall: {
|
|
2411
|
+
maxWidth: 375,
|
|
2412
|
+
params: {
|
|
2413
|
+
...PERFORMANCE_PRESET,
|
|
2414
|
+
distortionOctaves: 2,
|
|
2415
|
+
displacementScale: 50,
|
|
2416
|
+
blurAmount: 4,
|
|
2417
|
+
saturation: 70
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
|
|
2422
|
+
/**
|
|
2423
|
+
* Balanced preset - Good quality with reasonable performance
|
|
2424
|
+
* Default preset for most mobile devices
|
|
2425
|
+
*/
|
|
1815
2426
|
/**
|
|
1816
2427
|
* AtomixGlass - A high-performance glass morphism component with liquid distortion effects
|
|
1817
2428
|
*
|
|
@@ -1826,6 +2437,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1826
2437
|
* - Focus ring support for keyboard navigation
|
|
1827
2438
|
* - Responsive breakpoints for mobile optimization
|
|
1828
2439
|
* - Enhanced ARIA attributes for screen readers
|
|
2440
|
+
* - Time-based animation system with FBM distortion
|
|
2441
|
+
* - Device preset optimization for performance/quality balance
|
|
1829
2442
|
*
|
|
1830
2443
|
* Design System Compliance:
|
|
1831
2444
|
* - Uses design tokens for opacity, spacing, and colors
|
|
@@ -1882,8 +2495,15 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1882
2495
|
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
1883
2496
|
* <div>Content with debug logging enabled</div>
|
|
1884
2497
|
* </AtomixGlass>
|
|
1885
|
-
|
|
1886
|
-
|
|
2498
|
+
*
|
|
2499
|
+
* @example
|
|
2500
|
+
* // Performance-optimized for mobile devices
|
|
2501
|
+
* <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
|
|
2502
|
+
* <div>Mobile-optimized glass effect</div>
|
|
2503
|
+
* </AtomixGlass>
|
|
2504
|
+
*/
|
|
2505
|
+
function AtomixGlass({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, ...rest}) {
|
|
2506
|
+
const glassRef = useRef(null), contentRef = useRef(null), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, applyTimeBasedDistortion: applyTimeBasedDistortion, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
|
|
1887
2507
|
glassRef: glassRef,
|
|
1888
2508
|
contentRef: contentRef,
|
|
1889
2509
|
borderRadius: borderRadius,
|
|
@@ -1896,7 +2516,6 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1896
2516
|
withoutEffects: withoutEffects,
|
|
1897
2517
|
elasticity: elasticity,
|
|
1898
2518
|
onClick: onClick,
|
|
1899
|
-
debugBorderRadius: debugBorderRadius,
|
|
1900
2519
|
debugOverLight: debugOverLight,
|
|
1901
2520
|
debugPerformance: debugPerformance,
|
|
1902
2521
|
children: children,
|
|
@@ -1905,8 +2524,389 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
1905
2524
|
withLiquidBlur: withLiquidBlur,
|
|
1906
2525
|
padding: padding,
|
|
1907
2526
|
style: style,
|
|
1908
|
-
isFixedOrSticky: isFixedOrSticky
|
|
1909
|
-
|
|
2527
|
+
isFixedOrSticky: isFixedOrSticky,
|
|
2528
|
+
// Phase 1: Animation System props
|
|
2529
|
+
withTimeAnimation: withTimeAnimation,
|
|
2530
|
+
animationSpeed: animationSpeed,
|
|
2531
|
+
withMultiLayerDistortion: withMultiLayerDistortion,
|
|
2532
|
+
distortionOctaves: distortionOctaves,
|
|
2533
|
+
distortionLacunarity: distortionLacunarity,
|
|
2534
|
+
distortionGain: distortionGain,
|
|
2535
|
+
distortionQuality: distortionQuality
|
|
2536
|
+
});
|
|
2537
|
+
// Re-calculate only when devicePreset changes
|
|
2538
|
+
// Responsive breakpoint system - automatically adjusts parameters based on viewport
|
|
2539
|
+
!
|
|
2540
|
+
/**
|
|
2541
|
+
* Responsive Glass Parameters Hook
|
|
2542
|
+
*
|
|
2543
|
+
* Automatically adjusts glass effect parameters based on:
|
|
2544
|
+
* 1. Screen size (mobile/tablet/desktop breakpoints)
|
|
2545
|
+
* 2. Device performance (RAM and CPU detection)
|
|
2546
|
+
* 3. Custom breakpoint configuration
|
|
2547
|
+
*
|
|
2548
|
+
* Features:
|
|
2549
|
+
* - Debounced resize handling
|
|
2550
|
+
* - Performance-based quality adjustment
|
|
2551
|
+
* - Smooth parameter transitions
|
|
2552
|
+
* - Debug mode for development
|
|
2553
|
+
*
|
|
2554
|
+
* @example
|
|
2555
|
+
* ```typescript
|
|
2556
|
+
* const { responsiveParams, currentBreakpoint } = useResponsiveGlass({
|
|
2557
|
+
* baseParams: {
|
|
2558
|
+
* distortionOctaves: 5,
|
|
2559
|
+
* displacementScale: 20,
|
|
2560
|
+
* blurAmount: 10,
|
|
2561
|
+
* },
|
|
2562
|
+
* debug: true,
|
|
2563
|
+
* });
|
|
2564
|
+
* ```
|
|
2565
|
+
*
|
|
2566
|
+
* @param options Hook configuration options
|
|
2567
|
+
* @returns Responsive parameters and metadata
|
|
2568
|
+
*/
|
|
2569
|
+
function({baseParams: baseParams, breakpoints: breakpoints = DEFAULT_BREAKPOINTS, enabled: enabled = !0, enablePerformanceAdjustment: enablePerformanceAdjustment = !0, debug: debug = !1}) {
|
|
2570
|
+
const [responsiveParams, setResponsiveParams] = useState(baseParams), [currentBreakpoint, setCurrentBreakpoint] = useState("desktop"), [performanceTier, setPerformanceTier] = useState("high"), [isActive, setIsActive] = useState(enabled), baseParamsRef = useRef(baseParams), breakpointsRef = useRef(breakpoints);
|
|
2571
|
+
// Update refs when props change
|
|
2572
|
+
baseParamsRef.current = baseParams, breakpointsRef.current = breakpoints;
|
|
2573
|
+
/**
|
|
2574
|
+
* Calculate and apply responsive parameters
|
|
2575
|
+
*/
|
|
2576
|
+
const calculateParams = useCallback((() => {
|
|
2577
|
+
if (!enabled || "undefined" == typeof window) return setIsActive(!1), setResponsiveParams(baseParamsRef.current),
|
|
2578
|
+
void setCurrentBreakpoint("disabled");
|
|
2579
|
+
setIsActive(!0);
|
|
2580
|
+
// Get current screen width
|
|
2581
|
+
const width = window.innerWidth, {name: name, params: breakpointParams} = ((width, breakpoints) => {
|
|
2582
|
+
// Convert breakpoints to array and sort by minWidth descending
|
|
2583
|
+
const sortedBreakpoints = Object.entries(breakpoints).filter((([_, bp]) => void 0 !== bp.minWidth)).sort(((a, b) => (b[1].minWidth || 0) - (a[1].minWidth || 0)));
|
|
2584
|
+
// Find first breakpoint where width >= minWidth
|
|
2585
|
+
for (const [name, bp] of sortedBreakpoints) if (width >= (bp.minWidth || 0)) return {
|
|
2586
|
+
name: name,
|
|
2587
|
+
params: bp.params
|
|
2588
|
+
};
|
|
2589
|
+
// If no minWidth matched, check maxWidth breakpoints
|
|
2590
|
+
const maxWidthBreakpoints = Object.entries(breakpoints).filter((([_, bp]) => void 0 !== bp.maxWidth)).sort(((a, b) => (a[1].maxWidth || 1 / 0) - (b[1].maxWidth || 1 / 0)));
|
|
2591
|
+
for (const [name, bp] of maxWidthBreakpoints) if (width <= (bp.maxWidth || 1 / 0)) return {
|
|
2592
|
+
name: name,
|
|
2593
|
+
params: bp.params
|
|
2594
|
+
};
|
|
2595
|
+
// Fallback to first available breakpoint
|
|
2596
|
+
const entries = Object.entries(breakpoints);
|
|
2597
|
+
if (0 === entries.length)
|
|
2598
|
+
// Ultimate fallback - return sensible defaults
|
|
2599
|
+
return {
|
|
2600
|
+
name: "desktop",
|
|
2601
|
+
params: {
|
|
2602
|
+
distortionOctaves: 5,
|
|
2603
|
+
displacementScale: 1,
|
|
2604
|
+
blurAmount: 1
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
const firstEntry = entries[0];
|
|
2608
|
+
if (!firstEntry) return {
|
|
2609
|
+
name: "desktop",
|
|
2610
|
+
params: {
|
|
2611
|
+
distortionOctaves: 5,
|
|
2612
|
+
displacementScale: 1,
|
|
2613
|
+
blurAmount: 1
|
|
2614
|
+
}
|
|
2615
|
+
};
|
|
2616
|
+
const [fallbackName, fallbackBreakpoint] = firstEntry;
|
|
2617
|
+
return {
|
|
2618
|
+
name: fallbackName,
|
|
2619
|
+
params: fallbackBreakpoint.params
|
|
2620
|
+
};
|
|
2621
|
+
})(width, breakpointsRef.current);
|
|
2622
|
+
// Determine current breakpoint
|
|
2623
|
+
setCurrentBreakpoint(name);
|
|
2624
|
+
// Merge base params with breakpoint params
|
|
2625
|
+
let mergedParams = ((baseParams, breakpointParams) => {
|
|
2626
|
+
const result = {
|
|
2627
|
+
...baseParams
|
|
2628
|
+
}, scaleProperties = [ "displacementScale", "blurAmount", "saturation", "aberrationIntensity", "animationSpeed", "chromaticIntensity" ];
|
|
2629
|
+
// Apply scaling for specific properties
|
|
2630
|
+
for (const prop of scaleProperties) void 0 !== breakpointParams[prop] && void 0 !== baseParams[prop] && (result[prop] = baseParams[prop] * breakpointParams[prop]);
|
|
2631
|
+
// Override properties that should be set directly (not scaled)
|
|
2632
|
+
const overrideProperties = [ "distortionOctaves", "distortionLacunarity", "distortionGain" ];
|
|
2633
|
+
for (const prop of overrideProperties) void 0 !== breakpointParams[prop] && (result[prop] = breakpointParams[prop]);
|
|
2634
|
+
return result;
|
|
2635
|
+
})(baseParamsRef.current, breakpointParams);
|
|
2636
|
+
// Apply performance adjustments if enabled
|
|
2637
|
+
if (enablePerformanceAdjustment) {
|
|
2638
|
+
const tier = (() => {
|
|
2639
|
+
// Check if we're in a browser environment
|
|
2640
|
+
if ("undefined" == typeof window || "undefined" == typeof navigator) return "high"; // Default to high for SSR
|
|
2641
|
+
// Device Memory API (Chrome, Edge, Opera)
|
|
2642
|
+
// Returns RAM in GB: 0.25, 0.5, 1, 2, 4, 8
|
|
2643
|
+
const deviceMemory = navigator.deviceMemory || 4, hardwareConcurrency = navigator.hardwareConcurrency || 4;
|
|
2644
|
+
// Hardware Concurrency API (logical CPU cores)
|
|
2645
|
+
// Low-end: ≤2GB RAM OR ≤2 CPU cores
|
|
2646
|
+
return deviceMemory <= 2 || hardwareConcurrency <= 2 ? "low" :
|
|
2647
|
+
// High-end: ≥4GB RAM AND ≥4 CPU cores
|
|
2648
|
+
deviceMemory >= 4 && hardwareConcurrency >= 4 ? "high" : "medium";
|
|
2649
|
+
})();
|
|
2650
|
+
setPerformanceTier(tier), mergedParams = ((baseParams, performanceTier) => {
|
|
2651
|
+
if ("high" === performanceTier) return baseParams;
|
|
2652
|
+
// No adjustment needed
|
|
2653
|
+
const multiplier = "low" === performanceTier ? .7 : .85;
|
|
2654
|
+
return {
|
|
2655
|
+
...baseParams,
|
|
2656
|
+
distortionOctaves: Math.max(2, Math.round((baseParams.distortionOctaves || 5) * multiplier)),
|
|
2657
|
+
displacementScale: (baseParams.displacementScale || 1) * multiplier,
|
|
2658
|
+
blurAmount: (baseParams.blurAmount || 1) * multiplier,
|
|
2659
|
+
animationSpeed: (baseParams.animationSpeed || 1) * multiplier,
|
|
2660
|
+
chromaticIntensity: (baseParams.chromaticIntensity || 1) * multiplier
|
|
2661
|
+
};
|
|
2662
|
+
})(mergedParams, tier);
|
|
2663
|
+
}
|
|
2664
|
+
setResponsiveParams(mergedParams);
|
|
2665
|
+
}), [ enabled, enablePerformanceAdjustment, debug ]), debouncedCalculate = (func => {
|
|
2666
|
+
const timeoutRef = useRef(null);
|
|
2667
|
+
return useEffect((() => () => {
|
|
2668
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
2669
|
+
}), []), useCallback(((...args) => {
|
|
2670
|
+
timeoutRef.current && clearTimeout(timeoutRef.current), timeoutRef.current = setTimeout((() => {
|
|
2671
|
+
func(...args);
|
|
2672
|
+
}), 200);
|
|
2673
|
+
}), [ func, 200 ]);
|
|
2674
|
+
})(calculateParams);
|
|
2675
|
+
/**
|
|
2676
|
+
* Debounced parameter calculation for resize events
|
|
2677
|
+
*/
|
|
2678
|
+
/**
|
|
2679
|
+
* Handle window resize
|
|
2680
|
+
*/
|
|
2681
|
+
useEffect((() => {
|
|
2682
|
+
if (enabled)
|
|
2683
|
+
// Cleanup
|
|
2684
|
+
// Initial calculation
|
|
2685
|
+
return calculateParams(),
|
|
2686
|
+
// Listen for resize events
|
|
2687
|
+
window.addEventListener("resize", debouncedCalculate), () => {
|
|
2688
|
+
window.removeEventListener("resize", debouncedCalculate);
|
|
2689
|
+
};
|
|
2690
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2691
|
+
}), [ enabled ]), useCallback((() => {
|
|
2692
|
+
calculateParams();
|
|
2693
|
+
}), [ calculateParams ]);
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Get GPU memory info if available (Chrome DevTools only)
|
|
2697
|
+
*/ ({
|
|
2698
|
+
baseParams: {
|
|
2699
|
+
...useMemo((() =>
|
|
2700
|
+
/**
|
|
2701
|
+
* Get preset by name
|
|
2702
|
+
*/
|
|
2703
|
+
function(presetName) {
|
|
2704
|
+
switch (presetName) {
|
|
2705
|
+
case "performance":
|
|
2706
|
+
return PERFORMANCE_PRESET;
|
|
2707
|
+
|
|
2708
|
+
case "balanced":
|
|
2709
|
+
default:
|
|
2710
|
+
return BALANCED_PRESET;
|
|
2711
|
+
|
|
2712
|
+
case "quality":
|
|
2713
|
+
return QUALITY_PRESET;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
/**
|
|
2717
|
+
* Mobile-optimized responsive breakpoints
|
|
2718
|
+
* Automatically applies appropriate presets based on viewport size
|
|
2719
|
+
*/ (devicePreset)), [ devicePreset ]),
|
|
2720
|
+
distortionOctaves: Math.round((displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE) / 25),
|
|
2721
|
+
displacementScale: displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
|
|
2722
|
+
blurAmount: blurAmount || ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
|
|
2723
|
+
saturation: saturation || ATOMIX_GLASS.DEFAULTS.SATURATION,
|
|
2724
|
+
aberrationIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
|
|
2725
|
+
animationSpeed: 1,
|
|
2726
|
+
chromaticIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY
|
|
2727
|
+
},
|
|
2728
|
+
breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
|
|
2729
|
+
enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
|
|
2730
|
+
// Enable unless disabled
|
|
2731
|
+
debug: !1
|
|
2732
|
+
});
|
|
2733
|
+
// Performance monitoring - tracks FPS, frame time, memory usage
|
|
2734
|
+
const {metrics: performanceMetrics, recommendedQuality: recommendedQuality, isUnderperforming: isUnderperforming, setQualityLevel: setQualityLevel, toggleMonitoring: toggleMonitoring} =
|
|
2735
|
+
/**
|
|
2736
|
+
* Performance Monitor Hook
|
|
2737
|
+
*
|
|
2738
|
+
* Real-time performance tracking with automatic quality scaling.
|
|
2739
|
+
* Monitors FPS, frame time, and GPU memory to optimize glass effects.
|
|
2740
|
+
*
|
|
2741
|
+
* Features:
|
|
2742
|
+
* - Real-time FPS measurement
|
|
2743
|
+
* - Frame timing analysis
|
|
2744
|
+
* - Automatic quality scaling
|
|
2745
|
+
* - Debug overlay option
|
|
2746
|
+
* - Manual override capability
|
|
2747
|
+
*
|
|
2748
|
+
* @example
|
|
2749
|
+
* ```typescript
|
|
2750
|
+
* const { metrics, recommendedQuality, setQualityLevel } = usePerformanceMonitor({
|
|
2751
|
+
* targetFps: 60,
|
|
2752
|
+
* minFps: 45,
|
|
2753
|
+
* debug: true,
|
|
2754
|
+
* });
|
|
2755
|
+
* ```
|
|
2756
|
+
*
|
|
2757
|
+
* @param config Monitor configuration
|
|
2758
|
+
* @returns Performance metrics and controls
|
|
2759
|
+
*/
|
|
2760
|
+
function(config = {}) {
|
|
2761
|
+
const {enabled: enabled = !0, targetFps: targetFps = 60, minFps: minFps = 45, scaleUpThreshold: scaleUpThreshold = 58, lowFpsFrames: lowFpsFrames = 3, highFpsFrames: highFpsFrames = 10, debug: debug = !1, showOverlay: showOverlay = !1} = config, [metrics, setMetrics] = useState({
|
|
2762
|
+
fps: 0,
|
|
2763
|
+
frameTime: 0,
|
|
2764
|
+
gpuMemory: null,
|
|
2765
|
+
qualityLevel: "medium",
|
|
2766
|
+
timestamp: 0,
|
|
2767
|
+
isAutoScaling: !0,
|
|
2768
|
+
lowFpsCount: 0
|
|
2769
|
+
}), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled), frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
|
|
2770
|
+
setMetrics((prev => ({
|
|
2771
|
+
...prev,
|
|
2772
|
+
...newMetrics,
|
|
2773
|
+
timestamp: performance.now()
|
|
2774
|
+
})));
|
|
2775
|
+
}), []), applyAutoScaling = useCallback((currentFps => {
|
|
2776
|
+
if (manualOverride) return;
|
|
2777
|
+
const currentQuality = qualityLevelRef.current;
|
|
2778
|
+
// Check for low FPS
|
|
2779
|
+
if (currentFps < minFps) lowFpsCountRef.current++, highFpsCountRef.current = 0,
|
|
2780
|
+
// Scale down after N consecutive low-FPS frames
|
|
2781
|
+
lowFpsCountRef.current >= lowFpsFrames && "low" !== currentQuality && (qualityLevelRef.current = "low",
|
|
2782
|
+
updateMetrics({
|
|
2783
|
+
qualityLevel: "low",
|
|
2784
|
+
lowFpsCount: lowFpsCountRef.current
|
|
2785
|
+
})); else if (currentFps >= scaleUpThreshold) {
|
|
2786
|
+
// Scale up after N consecutive high-FPS frames
|
|
2787
|
+
if (highFpsCountRef.current++, lowFpsCountRef.current = 0, highFpsCountRef.current >= highFpsFrames) {
|
|
2788
|
+
const newQuality = "low" === currentQuality ? "medium" : "high";
|
|
2789
|
+
qualityLevelRef.current = newQuality, updateMetrics({
|
|
2790
|
+
qualityLevel: newQuality,
|
|
2791
|
+
lowFpsCount: 0
|
|
2792
|
+
}), highFpsCountRef.current = 0;
|
|
2793
|
+
}
|
|
2794
|
+
} else
|
|
2795
|
+
// FPS in normal range, reset counters
|
|
2796
|
+
lowFpsCountRef.current = 0, highFpsCountRef.current = 0;
|
|
2797
|
+
}), [ manualOverride, minFps, scaleUpThreshold, lowFpsFrames, highFpsFrames, debug, updateMetrics ]), measureFrame = useCallback((currentTime => {
|
|
2798
|
+
if (!isEnabled) return;
|
|
2799
|
+
frameCountRef.current++;
|
|
2800
|
+
// Calculate frame time
|
|
2801
|
+
const frameTime = currentTime - lastFrameTimeRef.current;
|
|
2802
|
+
// Update FPS every 100ms for responsiveness
|
|
2803
|
+
if (lastFrameTimeRef.current = currentTime, currentTime - lastFpsUpdateRef.current >= 100) {
|
|
2804
|
+
const elapsed = currentTime - lastFpsUpdateRef.current, fps = Math.round(1e3 * frameCountRef.current / elapsed);
|
|
2805
|
+
// Apply auto-scaling
|
|
2806
|
+
applyAutoScaling(fps), updateMetrics({
|
|
2807
|
+
fps: fps,
|
|
2808
|
+
frameTime: frameTime,
|
|
2809
|
+
qualityLevel: qualityLevelRef.current,
|
|
2810
|
+
lowFpsCount: lowFpsCountRef.current
|
|
2811
|
+
}),
|
|
2812
|
+
// Reset for next measurement period
|
|
2813
|
+
frameCountRef.current = 0, lastFpsUpdateRef.current = currentTime;
|
|
2814
|
+
}
|
|
2815
|
+
// Continue measurement loop
|
|
2816
|
+
animationFrameRef.current = requestAnimationFrame(measureFrame);
|
|
2817
|
+
}), [ isEnabled, applyAutoScaling, updateMetrics ]);
|
|
2818
|
+
/**
|
|
2819
|
+
* Initialize GPU memory tracking
|
|
2820
|
+
*/
|
|
2821
|
+
useEffect((() => {
|
|
2822
|
+
if (!isEnabled || "undefined" == typeof window) return;
|
|
2823
|
+
let mounted = !0;
|
|
2824
|
+
return (async () => {
|
|
2825
|
+
const memory = await new Promise((resolve => {
|
|
2826
|
+
// Check for WebGL debug renderer info
|
|
2827
|
+
if ("undefined" != typeof window && "undefined" != typeof document) try {
|
|
2828
|
+
const canvas = document.createElement("canvas"), gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|
2829
|
+
if (gl) {
|
|
2830
|
+
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
2831
|
+
if (debugInfo) {
|
|
2832
|
+
var _context, _context2, _context3;
|
|
2833
|
+
// Note: Actual memory info is not directly available via WebGL
|
|
2834
|
+
// We estimate based on renderer
|
|
2835
|
+
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
2836
|
+
// Rough estimation based on renderer type
|
|
2837
|
+
return void ((null == (_context = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context), _context))?.("Integrated") ? resolve(256) : (null == (_context2 = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context2), _context2))?.("AMD") || (null == (_context3 = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context3), _context3))?.("NVIDIA") ? resolve(512) : resolve(null));
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
} catch (e) {
|
|
2841
|
+
// WebGL not available or error occurred
|
|
2842
|
+
}
|
|
2843
|
+
resolve(null);
|
|
2844
|
+
}));
|
|
2845
|
+
mounted && updateMetrics({
|
|
2846
|
+
gpuMemory: memory
|
|
2847
|
+
});
|
|
2848
|
+
})(), () => {
|
|
2849
|
+
mounted = !1;
|
|
2850
|
+
};
|
|
2851
|
+
}), [ isEnabled, updateMetrics ]),
|
|
2852
|
+
/**
|
|
2853
|
+
* Start/stop monitoring based on enabled state
|
|
2854
|
+
*/
|
|
2855
|
+
useEffect((() => {
|
|
2856
|
+
if (isEnabled)
|
|
2857
|
+
// Cleanup
|
|
2858
|
+
// Initialize
|
|
2859
|
+
return lastFpsUpdateRef.current = performance.now(), lastFrameTimeRef.current = performance.now(),
|
|
2860
|
+
animationFrameRef.current = requestAnimationFrame(measureFrame), () => {
|
|
2861
|
+
null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
|
|
2862
|
+
animationFrameRef.current = null);
|
|
2863
|
+
};
|
|
2864
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2865
|
+
null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
|
|
2866
|
+
animationFrameRef.current = null);
|
|
2867
|
+
}), [ isEnabled ]);
|
|
2868
|
+
// measureFrame is stable via useCallback, avoid re-creating RAF loop
|
|
2869
|
+
/**
|
|
2870
|
+
* Manually set quality level (disables auto-scaling)
|
|
2871
|
+
*/
|
|
2872
|
+
const setQualityLevel = useCallback((level => {
|
|
2873
|
+
setManualOverride(!0), qualityLevelRef.current = level, updateMetrics({
|
|
2874
|
+
qualityLevel: level,
|
|
2875
|
+
isAutoScaling: !1
|
|
2876
|
+
});
|
|
2877
|
+
}), [ updateMetrics, debug ]), resetAutoScaling = useCallback((() => {
|
|
2878
|
+
setManualOverride(!1), lowFpsCountRef.current = 0, highFpsCountRef.current = 0,
|
|
2879
|
+
updateMetrics({
|
|
2880
|
+
isAutoScaling: !0,
|
|
2881
|
+
lowFpsCount: 0
|
|
2882
|
+
});
|
|
2883
|
+
}), [ updateMetrics, debug ]), toggleMonitoring = useCallback((() => {
|
|
2884
|
+
setIsEnabled((prev => !prev));
|
|
2885
|
+
}), []);
|
|
2886
|
+
/**
|
|
2887
|
+
* Reset to auto-scaling mode
|
|
2888
|
+
*/ var fps, currentQuality;
|
|
2889
|
+
return {
|
|
2890
|
+
metrics: metrics,
|
|
2891
|
+
recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
|
|
2892
|
+
isUnderperforming: metrics.fps < minFps,
|
|
2893
|
+
setQualityLevel: setQualityLevel,
|
|
2894
|
+
resetAutoScaling: resetAutoScaling,
|
|
2895
|
+
toggleMonitoring: toggleMonitoring
|
|
2896
|
+
};
|
|
2897
|
+
}({
|
|
2898
|
+
enabled: !1,
|
|
2899
|
+
// We'll toggle manually based on prop
|
|
2900
|
+
debug: !1,
|
|
2901
|
+
showOverlay: !1
|
|
2902
|
+
});
|
|
2903
|
+
// Auto-start performance monitoring if enabled (only in development)
|
|
2904
|
+
React.useEffect((() => {
|
|
2905
|
+
"development" === process.env.NODE_ENV && window?.enablePerformanceMonitoring && toggleMonitoring();
|
|
2906
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2907
|
+
}), []);
|
|
2908
|
+
// Only run once on mount
|
|
2909
|
+
const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
|
|
1910
2910
|
if (!isFixedOrSticky) return {};
|
|
1911
2911
|
const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
|
|
1912
2912
|
return {
|
|
@@ -2092,6 +3092,15 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
2092
3092
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2093
3093
|
shaderVariant: shaderVariant,
|
|
2094
3094
|
withLiquidBlur: withLiquidBlur,
|
|
3095
|
+
// Phase 1: Animation System props
|
|
3096
|
+
shaderTime: getShaderTime(),
|
|
3097
|
+
withTimeAnimation: withTimeAnimation,
|
|
3098
|
+
animationSpeed: animationSpeed,
|
|
3099
|
+
withMultiLayerDistortion: withMultiLayerDistortion,
|
|
3100
|
+
distortionOctaves: distortionOctaves,
|
|
3101
|
+
distortionLacunarity: distortionLacunarity,
|
|
3102
|
+
distortionGain: distortionGain,
|
|
3103
|
+
distortionQuality: distortionQuality,
|
|
2095
3104
|
children: children
|
|
2096
3105
|
}), Boolean(onClick) && jsxs(Fragment, {
|
|
2097
3106
|
children: [ jsx("div", {
|
|
@@ -2115,6 +3124,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
|
|
|
2115
3124
|
}), jsx("span", {
|
|
2116
3125
|
className: ATOMIX_GLASS.BORDER_2_CLASS
|
|
2117
3126
|
}) ]
|
|
3127
|
+
}), debugPerformance && performanceMetrics && jsx(PerformanceDashboard, {
|
|
3128
|
+
metrics: performanceMetrics,
|
|
3129
|
+
isVisible: !0,
|
|
3130
|
+
onClose: () => {}
|
|
2118
3131
|
}) ]
|
|
2119
3132
|
});
|
|
2120
3133
|
}
|
|
@@ -2181,7 +3194,7 @@ const AccordionBody = forwardRef((({children: children, className: className =
|
|
|
2181
3194
|
|
|
2182
3195
|
AccordionBody.displayName = "AccordionBody";
|
|
2183
3196
|
|
|
2184
|
-
const AccordionImpl = memo((({title: title, children: children, defaultOpen: defaultOpen = !1, isOpen: controlledOpen, onOpenChange: onOpenChange,
|
|
3197
|
+
const AccordionImpl = memo((({title: title, children: children, defaultOpen: defaultOpen = !1, isOpen: controlledOpen, onOpenChange: onOpenChange, disabled: disabled = !1, iconPosition: iconPosition = "right", icon: icon, className: className = "", style: style, glass: glass}) => {
|
|
2185
3198
|
// Generate unique IDs for accessibility
|
|
2186
3199
|
const instanceId = useId(), buttonId = `accordion-header-${instanceId}`, panelId = `accordion-panel-${instanceId}`, {state: state, toggle: toggle, updatePanelHeight: updatePanelHeight, panelRef: panelRef, contentRef: contentRef, generateClassNames: generateClassNames, generateHeaderClassNames: generateHeaderClassNames} =
|
|
2187
3200
|
/**
|
|
@@ -2225,9 +3238,7 @@ const AccordionImpl = memo((({title: title, children: children, defaultOpen: de
|
|
|
2225
3238
|
toggle: () => {
|
|
2226
3239
|
if (!defaultProps.disabled) {
|
|
2227
3240
|
const nextOpen = !isOpen;
|
|
2228
|
-
isControlled || setInternalOpen(nextOpen), defaultProps.onOpenChange?.(nextOpen)
|
|
2229
|
-
// Call legacy handlers
|
|
2230
|
-
nextOpen ? defaultProps.onOpen?.() : defaultProps.onClose?.();
|
|
3241
|
+
isControlled || setInternalOpen(nextOpen), defaultProps.onOpenChange?.(nextOpen);
|
|
2231
3242
|
}
|
|
2232
3243
|
},
|
|
2233
3244
|
updatePanelHeight: updatePanelHeight,
|
|
@@ -2241,9 +3252,7 @@ const AccordionImpl = memo((({title: title, children: children, defaultOpen: de
|
|
|
2241
3252
|
disabled: disabled,
|
|
2242
3253
|
iconPosition: iconPosition,
|
|
2243
3254
|
isOpen: controlledOpen,
|
|
2244
|
-
onOpenChange: onOpenChange
|
|
2245
|
-
onOpen: onOpen,
|
|
2246
|
-
onClose: onClose
|
|
3255
|
+
onOpenChange: onOpenChange
|
|
2247
3256
|
}), headerClassNames = generateHeaderClassNames(), panelClassNames = ACCORDION.SELECTORS.PANEL.replace(".", ""), hasCompoundComponents = React.Children.toArray(children).some((child => {
|
|
2248
3257
|
var _context;
|
|
2249
3258
|
|
|
@@ -2767,7 +3776,7 @@ class ThemeNaming {
|
|
|
2767
3776
|
ThemeNaming.prefix = "atomix";
|
|
2768
3777
|
|
|
2769
3778
|
const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, ...props}, ref) => {
|
|
2770
|
-
const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href
|
|
3779
|
+
const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsx(Icon, {
|
|
2771
3780
|
name: iconName,
|
|
2772
3781
|
size: iconSize
|
|
2773
3782
|
}) : icon, buttonClass = [ BUTTON.BASE_CLASS, ThemeNaming.variantClass("btn", variant), "md" !== size ? ThemeNaming.sizeClass("btn", size) : "", iconOnly ? ThemeNaming.stateClass("btn", "icon") : "", rounded ? ThemeNaming.stateClass("btn", "rounded") : "", isDisabled ? ThemeNaming.stateClass("btn", "disabled") : "", glass ? ThemeNaming.stateClass("btn", "glass") : "", loading ? BUTTON.CLASSES.LOADING : "", fullWidth ? BUTTON.CLASSES.FULL_WIDTH : "", block ? BUTTON.CLASSES.BLOCK : "", active ? BUTTON.CLASSES.ACTIVE : "", selected ? BUTTON.CLASSES.SELECTED : "", className ].filter(Boolean).join(" "), handleClickEvent = useCallback((event => {
|
|
@@ -2823,8 +3832,8 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
|
|
|
2823
3832
|
...buttonProps,
|
|
2824
3833
|
ref: ref,
|
|
2825
3834
|
// linkComponent usually forwards ref to anchor
|
|
2826
|
-
href: href,
|
|
2827
|
-
to: href,
|
|
3835
|
+
href: isDisabled ? void 0 : href,
|
|
3836
|
+
to: isDisabled ? void 0 : href,
|
|
2828
3837
|
target: target,
|
|
2829
3838
|
rel: "_blank" === target ? "noopener noreferrer" : void 0
|
|
2830
3839
|
};
|
|
@@ -2837,7 +3846,7 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
|
|
|
2837
3846
|
content = jsx("a", {
|
|
2838
3847
|
...buttonProps,
|
|
2839
3848
|
ref: ref,
|
|
2840
|
-
href: href,
|
|
3849
|
+
href: isDisabled ? void 0 : href,
|
|
2841
3850
|
target: target,
|
|
2842
3851
|
rel: "_blank" === target ? "noopener noreferrer" : void 0,
|
|
2843
3852
|
children: buttonContent
|
|
@@ -2879,7 +3888,7 @@ row: row = !1, flat: flat = !1,
|
|
|
2879
3888
|
// States
|
|
2880
3889
|
active: active = !1, disabled: disabled = !1, loading: loading = !1, selected: selected = !1, interactive: interactive = !1,
|
|
2881
3890
|
// Content
|
|
2882
|
-
header: header, image: image, imageAlt: imageAlt = "", title: title, text: text, actions: actions, icon: icon, footer: footer, children: children,
|
|
3891
|
+
header: header, image: image, imageAlt: imageAlt = "Card image", title: title, text: text, actions: actions, icon: icon, footer: footer, children: children,
|
|
2883
3892
|
// Interaction
|
|
2884
3893
|
onClick: onClick, onHover: onHover, onFocus: onFocus, href: href, target: target,
|
|
2885
3894
|
// Custom Link
|
|
@@ -3684,7 +4693,9 @@ const smoothStep = (a, b, t) => {
|
|
|
3684
4693
|
return this.canvasDPI;
|
|
3685
4694
|
}
|
|
3686
4695
|
},
|
|
3687
|
-
|
|
4696
|
+
createFBMEngine: createFBMEngine,
|
|
4697
|
+
fragmentShaders: fragmentShaders,
|
|
4698
|
+
liquidGlassWithTime: liquidGlassWithTime
|
|
3688
4699
|
}, Symbol.toStringTag, {
|
|
3689
4700
|
value: "Module"
|
|
3690
4701
|
}));
|