@shohojdhara/atomix 0.4.4 → 0.4.6
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/dist/atomix.css +50 -11
- 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.js +184 -189
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +4 -4
- package/dist/core.js +194 -199
- package/dist/core.js.map +1 -1
- package/dist/forms.js +184 -189
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +189 -194
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +44 -47
- package/dist/index.esm.js +496 -613
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +528 -631
- 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 +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +60 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +8 -42
- package/src/components/AtomixGlass/glass-utils.ts +27 -14
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +19 -21
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1162 -515
- package/src/components/AtomixGlass/stories/shared-components.tsx +11 -3
- package/src/components/Breadcrumb/Breadcrumb.tsx +5 -5
- package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +2 -2
- package/src/components/Button/Button.tsx +6 -6
- package/src/components/Card/Card.tsx +3 -3
- package/src/components/Dropdown/Dropdown.tsx +5 -3
- package/src/components/Footer/Footer.tsx +124 -166
- package/src/components/Footer/FooterLink.tsx +16 -19
- package/src/components/Footer/FooterSection.tsx +40 -39
- package/src/components/Footer/FooterSocialLink.tsx +59 -58
- package/src/components/Footer/README.md +1 -1
- package/src/components/Hero/Hero.tsx +72 -142
- package/src/components/Navigation/Menu/MegaMenu.tsx +17 -12
- package/src/components/Navigation/Menu/Menu.tsx +49 -24
- package/src/components/Navigation/Nav/NavItem.tsx +5 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +13 -5
- package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -2
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -4
- package/src/components/Slider/Slider.tsx +7 -4
- package/src/lib/composables/index.ts +1 -2
- package/src/lib/composables/useAtomixGlass.ts +246 -222
- package/src/lib/composables/useAtomixGlassStyles.ts +46 -23
- package/src/lib/composables/useFooter.ts +117 -20
- package/src/lib/composables/useSlider.ts +3 -1
- package/src/lib/constants/components.ts +3 -1
- package/src/lib/types/components.ts +44 -12
- package/src/styles/06-components/_components.atomix-glass.scss +72 -14
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +0 -222
- package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +0 -329
- package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +0 -82
- package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +0 -153
- package/src/lib/composables/atomix-glass/useGlassOverLight.ts +0 -198
- package/src/lib/composables/atomix-glass/useGlassState.ts +0 -112
- package/src/lib/composables/atomix-glass/useGlassTransforms.ts +0 -160
- package/src/lib/composables/glass-styles.ts +0 -302
- package/src/lib/composables/useGlassContainer.ts +0 -177
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { globalMouseTracker } from '../shared-mouse-tracker';
|
|
3
|
-
import { calculateElementCenter } from '../../../components/AtomixGlass/glass-utils';
|
|
4
|
-
import type { MousePosition } from '../../types/components';
|
|
5
|
-
|
|
6
|
-
interface UseGlassMouseTrackingProps {
|
|
7
|
-
glassRef: React.RefObject<HTMLDivElement>;
|
|
8
|
-
mouseContainer?: React.RefObject<HTMLElement | null> | null;
|
|
9
|
-
externalGlobalMousePosition?: MousePosition;
|
|
10
|
-
externalMouseOffset?: MousePosition;
|
|
11
|
-
effectiveWithoutEffects?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function useGlassMouseTracking({
|
|
15
|
-
glassRef,
|
|
16
|
-
mouseContainer,
|
|
17
|
-
externalGlobalMousePosition,
|
|
18
|
-
externalMouseOffset,
|
|
19
|
-
effectiveWithoutEffects = false,
|
|
20
|
-
}: UseGlassMouseTrackingProps) {
|
|
21
|
-
const [internalGlobalMousePosition, setInternalGlobalMousePosition] = useState<MousePosition>({
|
|
22
|
-
x: 0,
|
|
23
|
-
y: 0,
|
|
24
|
-
});
|
|
25
|
-
const [internalMouseOffset, setInternalMouseOffset] = useState<MousePosition>({ x: 0, y: 0 });
|
|
26
|
-
|
|
27
|
-
// Mouse tracking using shared global tracker
|
|
28
|
-
// Cache bounding rect to avoid repeated getBoundingClientRect calls
|
|
29
|
-
const cachedRectRef = useRef<DOMRect | null>(null);
|
|
30
|
-
const updateRectRef = useRef<number | null>(null);
|
|
31
|
-
|
|
32
|
-
const globalMousePosition = useMemo(
|
|
33
|
-
() => externalGlobalMousePosition || internalGlobalMousePosition,
|
|
34
|
-
[externalGlobalMousePosition, internalGlobalMousePosition]
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const mouseOffset = useMemo(
|
|
38
|
-
() => externalMouseOffset || internalMouseOffset,
|
|
39
|
-
[externalMouseOffset, internalMouseOffset]
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// Handle mouse position updates from shared tracker
|
|
43
|
-
const handleGlobalMousePosition = useCallback(
|
|
44
|
-
(globalPos: MousePosition) => {
|
|
45
|
-
if (externalGlobalMousePosition && externalMouseOffset) {
|
|
46
|
-
// External mouse position provided, skip internal tracking
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (effectiveWithoutEffects) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
55
|
-
if (!container) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Use cached rect if available, otherwise get new one
|
|
60
|
-
let rect = cachedRectRef.current;
|
|
61
|
-
if (!rect || rect.width === 0 || rect.height === 0) {
|
|
62
|
-
rect = container.getBoundingClientRect();
|
|
63
|
-
cachedRectRef.current = rect;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (rect.width === 0 || rect.height === 0) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const center = calculateElementCenter(rect);
|
|
71
|
-
|
|
72
|
-
// Calculate offset relative to this container
|
|
73
|
-
const newOffset = {
|
|
74
|
-
x: ((globalPos.x - center.x) / rect.width) * 100,
|
|
75
|
-
y: ((globalPos.y - center.y) / rect.height) * 100,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// React 18 automatically batches these updates
|
|
79
|
-
setInternalMouseOffset(newOffset);
|
|
80
|
-
setInternalGlobalMousePosition(globalPos);
|
|
81
|
-
},
|
|
82
|
-
[
|
|
83
|
-
mouseContainer,
|
|
84
|
-
glassRef,
|
|
85
|
-
externalGlobalMousePosition,
|
|
86
|
-
externalMouseOffset,
|
|
87
|
-
effectiveWithoutEffects,
|
|
88
|
-
]
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
// Subscribe to shared mouse tracker
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
if (externalGlobalMousePosition && externalMouseOffset) {
|
|
94
|
-
// External mouse position provided, don't subscribe
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (effectiveWithoutEffects) {
|
|
99
|
-
// Effects disabled, don't subscribe
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Subscribe to shared tracker
|
|
104
|
-
const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
|
|
105
|
-
|
|
106
|
-
// Update cached rect when container size changes
|
|
107
|
-
const updateRect = () => {
|
|
108
|
-
if (updateRectRef.current !== null) {
|
|
109
|
-
cancelAnimationFrame(updateRectRef.current);
|
|
110
|
-
}
|
|
111
|
-
updateRectRef.current = requestAnimationFrame(() => {
|
|
112
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
113
|
-
if (container) {
|
|
114
|
-
cachedRectRef.current = container.getBoundingClientRect();
|
|
115
|
-
}
|
|
116
|
-
updateRectRef.current = null;
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// Use ResizeObserver to update cached rect when container size changes
|
|
121
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
122
|
-
let resizeObserver: ResizeObserver | null = null;
|
|
123
|
-
|
|
124
|
-
if (container && typeof ResizeObserver !== 'undefined') {
|
|
125
|
-
resizeObserver = new ResizeObserver(updateRect);
|
|
126
|
-
resizeObserver.observe(container);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return () => {
|
|
130
|
-
unsubscribe();
|
|
131
|
-
if (updateRectRef.current !== null) {
|
|
132
|
-
cancelAnimationFrame(updateRectRef.current);
|
|
133
|
-
updateRectRef.current = null;
|
|
134
|
-
}
|
|
135
|
-
if (resizeObserver) {
|
|
136
|
-
resizeObserver.disconnect();
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
}, [
|
|
140
|
-
handleGlobalMousePosition,
|
|
141
|
-
mouseContainer,
|
|
142
|
-
glassRef,
|
|
143
|
-
externalGlobalMousePosition,
|
|
144
|
-
externalMouseOffset,
|
|
145
|
-
effectiveWithoutEffects,
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
globalMousePosition,
|
|
150
|
-
mouseOffset,
|
|
151
|
-
cachedRectRef,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo } from 'react';
|
|
2
|
-
import { calculateMouseInfluence } from '../../../components/AtomixGlass/glass-utils';
|
|
3
|
-
import type {
|
|
4
|
-
MousePosition,
|
|
5
|
-
OverLightConfig,
|
|
6
|
-
OverLightObjectConfig,
|
|
7
|
-
} from '../../types/components';
|
|
8
|
-
|
|
9
|
-
interface UseGlassOverLightProps {
|
|
10
|
-
overLight: OverLightConfig;
|
|
11
|
-
detectedOverLight: boolean;
|
|
12
|
-
mouseOffset: MousePosition;
|
|
13
|
-
isHovered: boolean;
|
|
14
|
-
isActive: boolean;
|
|
15
|
-
debugOverLight?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useGlassOverLight({
|
|
19
|
-
overLight,
|
|
20
|
-
detectedOverLight,
|
|
21
|
-
mouseOffset,
|
|
22
|
-
isHovered,
|
|
23
|
-
isActive,
|
|
24
|
-
debugOverLight = false,
|
|
25
|
-
}: UseGlassOverLightProps) {
|
|
26
|
-
/**
|
|
27
|
-
* Get effective overLight value based on configuration
|
|
28
|
-
* - boolean: returns the boolean value directly
|
|
29
|
-
* - 'auto': returns detectedOverLight (auto-detected from background)
|
|
30
|
-
* - object: returns detectedOverLight (auto-detected, but config object provides customization)
|
|
31
|
-
*/
|
|
32
|
-
const getEffectiveOverLight = useCallback(() => {
|
|
33
|
-
if (typeof overLight === 'boolean') {
|
|
34
|
-
return overLight;
|
|
35
|
-
}
|
|
36
|
-
if (overLight === 'auto') {
|
|
37
|
-
return detectedOverLight;
|
|
38
|
-
}
|
|
39
|
-
if (typeof overLight === 'object' && overLight !== null) {
|
|
40
|
-
return detectedOverLight;
|
|
41
|
-
}
|
|
42
|
-
// Default to false for safety when overLight is undefined or invalid
|
|
43
|
-
return false;
|
|
44
|
-
}, [overLight, detectedOverLight]);
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Validate and clamp a numeric config value
|
|
48
|
-
* @param value - The value to validate
|
|
49
|
-
* @param min - Minimum allowed value
|
|
50
|
-
* @param max - Maximum allowed value
|
|
51
|
-
* @param defaultValue - Default value if validation fails
|
|
52
|
-
* @returns Validated and clamped value
|
|
53
|
-
*/
|
|
54
|
-
const validateConfigValue = useCallback(
|
|
55
|
-
(value: unknown, min: number, max: number, defaultValue: number): number => {
|
|
56
|
-
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
|
|
57
|
-
return defaultValue;
|
|
58
|
-
}
|
|
59
|
-
return Math.min(max, Math.max(min, value));
|
|
60
|
-
},
|
|
61
|
-
[]
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const overLightConfig = useMemo(() => {
|
|
65
|
-
const isOverLight = getEffectiveOverLight();
|
|
66
|
-
const mouseInfluence = calculateMouseInfluence(mouseOffset);
|
|
67
|
-
const hoverIntensity = isHovered ? 1.4 : 1;
|
|
68
|
-
const activeIntensity = isActive ? 1.6 : 1;
|
|
69
|
-
|
|
70
|
-
// More robust overlight configuration with better defaults and clamping
|
|
71
|
-
const baseOpacity = isOverLight
|
|
72
|
-
? Math.min(0.6, Math.max(0.2, 0.5 * hoverIntensity * activeIntensity))
|
|
73
|
-
: 0;
|
|
74
|
-
|
|
75
|
-
const baseConfig = {
|
|
76
|
-
isOverLight,
|
|
77
|
-
threshold: 0.7,
|
|
78
|
-
opacity: baseOpacity,
|
|
79
|
-
contrast: Math.min(1.6, Math.max(1.0, 1.4 + mouseInfluence * 0.1)),
|
|
80
|
-
brightness: Math.min(1.1, Math.max(0.8, 0.9 + mouseInfluence * 0.05)),
|
|
81
|
-
saturationBoost: 1.3, // Fixed value — dynamic saturation amplifies perceived displacement
|
|
82
|
-
shadowIntensity: Math.min(1.2, Math.max(0.5, 0.9 + mouseInfluence * 0.2)),
|
|
83
|
-
borderOpacity: Math.min(1.0, Math.max(0.3, 0.7 + mouseInfluence * 0.1)),
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
if (typeof overLight === 'object' && overLight !== null) {
|
|
87
|
-
const objConfig = overLight as OverLightObjectConfig;
|
|
88
|
-
|
|
89
|
-
// Validate and apply object config values with proper clamping
|
|
90
|
-
const validatedThreshold = validateConfigValue(
|
|
91
|
-
objConfig.threshold,
|
|
92
|
-
0.1,
|
|
93
|
-
1.0,
|
|
94
|
-
baseConfig.threshold
|
|
95
|
-
);
|
|
96
|
-
const validatedOpacity = validateConfigValue(objConfig.opacity, 0.1, 1.0, baseConfig.opacity);
|
|
97
|
-
const validatedContrast = validateConfigValue(
|
|
98
|
-
objConfig.contrast,
|
|
99
|
-
0.5,
|
|
100
|
-
2.5,
|
|
101
|
-
baseConfig.contrast
|
|
102
|
-
);
|
|
103
|
-
const validatedBrightness = validateConfigValue(
|
|
104
|
-
objConfig.brightness,
|
|
105
|
-
0.5,
|
|
106
|
-
2.0,
|
|
107
|
-
baseConfig.brightness
|
|
108
|
-
);
|
|
109
|
-
const validatedSaturationBoost = validateConfigValue(
|
|
110
|
-
objConfig.saturationBoost,
|
|
111
|
-
0.5,
|
|
112
|
-
3.0,
|
|
113
|
-
baseConfig.saturationBoost
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const finalConfig = {
|
|
117
|
-
...baseConfig,
|
|
118
|
-
threshold: validatedThreshold,
|
|
119
|
-
opacity: validatedOpacity * hoverIntensity * activeIntensity,
|
|
120
|
-
contrast: Math.min(1.6, validatedContrast + mouseInfluence * 0.1),
|
|
121
|
-
brightness: Math.min(1.1, validatedBrightness + mouseInfluence * 0.05),
|
|
122
|
-
saturationBoost: validatedSaturationBoost, // Use validated value directly, no mouse influence
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// Debug logging
|
|
126
|
-
if (
|
|
127
|
-
(typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') &&
|
|
128
|
-
debugOverLight
|
|
129
|
-
) {
|
|
130
|
-
console.log('[AtomixGlass] OverLight Config:', {
|
|
131
|
-
isOverLight,
|
|
132
|
-
config: {
|
|
133
|
-
threshold: finalConfig.threshold.toFixed(3),
|
|
134
|
-
opacity: finalConfig.opacity.toFixed(3),
|
|
135
|
-
contrast: finalConfig.contrast.toFixed(3),
|
|
136
|
-
brightness: finalConfig.brightness.toFixed(3),
|
|
137
|
-
saturationBoost: finalConfig.saturationBoost.toFixed(3),
|
|
138
|
-
shadowIntensity: finalConfig.shadowIntensity.toFixed(3),
|
|
139
|
-
borderOpacity: finalConfig.borderOpacity.toFixed(3),
|
|
140
|
-
},
|
|
141
|
-
input: {
|
|
142
|
-
threshold: objConfig.threshold,
|
|
143
|
-
opacity: objConfig.opacity,
|
|
144
|
-
contrast: objConfig.contrast,
|
|
145
|
-
brightness: objConfig.brightness,
|
|
146
|
-
saturationBoost: objConfig.saturationBoost,
|
|
147
|
-
},
|
|
148
|
-
dynamic: {
|
|
149
|
-
mouseInfluence: mouseInfluence.toFixed(3),
|
|
150
|
-
hoverIntensity: hoverIntensity.toFixed(3),
|
|
151
|
-
activeIntensity: activeIntensity.toFixed(3),
|
|
152
|
-
},
|
|
153
|
-
timestamp: new Date().toISOString(),
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return finalConfig;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Debug logging for non-object configs
|
|
161
|
-
if (
|
|
162
|
-
(typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') &&
|
|
163
|
-
debugOverLight
|
|
164
|
-
) {
|
|
165
|
-
console.log('[AtomixGlass] OverLight Config:', {
|
|
166
|
-
isOverLight,
|
|
167
|
-
configType: typeof overLight === 'boolean' ? (overLight ? 'true' : 'false') : overLight,
|
|
168
|
-
config: {
|
|
169
|
-
threshold: baseConfig.threshold.toFixed(3),
|
|
170
|
-
opacity: baseConfig.opacity.toFixed(3),
|
|
171
|
-
contrast: baseConfig.contrast.toFixed(3),
|
|
172
|
-
brightness: baseConfig.brightness.toFixed(3),
|
|
173
|
-
saturationBoost: baseConfig.saturationBoost.toFixed(3),
|
|
174
|
-
shadowIntensity: baseConfig.shadowIntensity.toFixed(3),
|
|
175
|
-
borderOpacity: baseConfig.borderOpacity.toFixed(3),
|
|
176
|
-
},
|
|
177
|
-
dynamic: {
|
|
178
|
-
mouseInfluence: mouseInfluence.toFixed(3),
|
|
179
|
-
hoverIntensity: hoverIntensity.toFixed(3),
|
|
180
|
-
activeIntensity: activeIntensity.toFixed(3),
|
|
181
|
-
},
|
|
182
|
-
timestamp: new Date().toISOString(),
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return baseConfig;
|
|
187
|
-
}, [
|
|
188
|
-
overLight,
|
|
189
|
-
getEffectiveOverLight,
|
|
190
|
-
mouseOffset,
|
|
191
|
-
isHovered,
|
|
192
|
-
isActive,
|
|
193
|
-
validateConfigValue,
|
|
194
|
-
debugOverLight,
|
|
195
|
-
]);
|
|
196
|
-
|
|
197
|
-
return { overLightConfig };
|
|
198
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import type { AtomixGlassProps } from '../../types/components';
|
|
3
|
-
|
|
4
|
-
interface UseGlassStateProps {
|
|
5
|
-
reducedMotion?: boolean;
|
|
6
|
-
highContrast?: boolean;
|
|
7
|
-
withoutEffects?: boolean;
|
|
8
|
-
onClick?: () => void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function useGlassState({
|
|
12
|
-
reducedMotion = false,
|
|
13
|
-
highContrast = false,
|
|
14
|
-
withoutEffects = false,
|
|
15
|
-
onClick,
|
|
16
|
-
}: UseGlassStateProps) {
|
|
17
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
18
|
-
const [isActive, setIsActive] = useState(false);
|
|
19
|
-
const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
|
|
20
|
-
const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
|
|
21
|
-
|
|
22
|
-
// Media query handlers
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const mediaQueryReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
30
|
-
const mediaQueryHighContrast = window.matchMedia('(prefers-contrast: high)');
|
|
31
|
-
|
|
32
|
-
setUserPrefersReducedMotion(mediaQueryReducedMotion.matches);
|
|
33
|
-
setUserPrefersHighContrast(mediaQueryHighContrast.matches);
|
|
34
|
-
|
|
35
|
-
const handleReducedMotionChange = (e: MediaQueryListEvent) => {
|
|
36
|
-
setUserPrefersReducedMotion(e.matches);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleHighContrastChange = (e: MediaQueryListEvent) => {
|
|
40
|
-
setUserPrefersHighContrast(e.matches);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
if (mediaQueryReducedMotion.addEventListener) {
|
|
44
|
-
mediaQueryReducedMotion.addEventListener('change', handleReducedMotionChange);
|
|
45
|
-
mediaQueryHighContrast.addEventListener('change', handleHighContrastChange);
|
|
46
|
-
} else if (mediaQueryReducedMotion.addListener) {
|
|
47
|
-
mediaQueryReducedMotion.addListener(handleReducedMotionChange);
|
|
48
|
-
mediaQueryHighContrast.addListener(handleHighContrastChange);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return () => {
|
|
52
|
-
try {
|
|
53
|
-
if (mediaQueryReducedMotion.removeEventListener) {
|
|
54
|
-
mediaQueryReducedMotion.removeEventListener('change', handleReducedMotionChange);
|
|
55
|
-
mediaQueryHighContrast.removeEventListener('change', handleHighContrastChange);
|
|
56
|
-
} else if (mediaQueryReducedMotion.removeListener) {
|
|
57
|
-
mediaQueryReducedMotion.removeListener(handleReducedMotionChange);
|
|
58
|
-
mediaQueryHighContrast.removeListener(handleHighContrastChange);
|
|
59
|
-
}
|
|
60
|
-
} catch (cleanupError) {
|
|
61
|
-
console.error('AtomixGlass: Error cleaning up media query listeners:', cleanupError);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.error('AtomixGlass: Error setting up media queries:', error);
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
}, []);
|
|
69
|
-
|
|
70
|
-
const effectiveReducedMotion = useMemo(
|
|
71
|
-
() => reducedMotion || userPrefersReducedMotion,
|
|
72
|
-
[reducedMotion, userPrefersReducedMotion]
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const effectiveHighContrast = useMemo(
|
|
76
|
-
() => highContrast || userPrefersHighContrast,
|
|
77
|
-
[highContrast, userPrefersHighContrast]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const effectiveWithoutEffects = useMemo(
|
|
81
|
-
() => withoutEffects || effectiveReducedMotion,
|
|
82
|
-
[withoutEffects, effectiveReducedMotion]
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const handleMouseEnter = useCallback(() => setIsHovered(true), []);
|
|
86
|
-
const handleMouseLeave = useCallback(() => setIsHovered(false), []);
|
|
87
|
-
const handleMouseDown = useCallback(() => setIsActive(true), []);
|
|
88
|
-
const handleMouseUp = useCallback(() => setIsActive(false), []);
|
|
89
|
-
|
|
90
|
-
const handleKeyDown = useCallback(
|
|
91
|
-
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
92
|
-
if (onClick && (e.key === 'Enter' || e.key === ' ')) {
|
|
93
|
-
e.preventDefault();
|
|
94
|
-
onClick();
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
[onClick]
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
isHovered,
|
|
102
|
-
isActive,
|
|
103
|
-
effectiveReducedMotion,
|
|
104
|
-
effectiveHighContrast,
|
|
105
|
-
effectiveWithoutEffects,
|
|
106
|
-
handleMouseEnter,
|
|
107
|
-
handleMouseLeave,
|
|
108
|
-
handleMouseDown,
|
|
109
|
-
handleMouseUp,
|
|
110
|
-
handleKeyDown,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useMemo } from 'react';
|
|
2
|
-
import { ATOMIX_GLASS } from '../../constants/components';
|
|
3
|
-
import {
|
|
4
|
-
calculateDistance,
|
|
5
|
-
calculateElementCenter,
|
|
6
|
-
validateGlassSize,
|
|
7
|
-
} from '../../../components/AtomixGlass/glass-utils';
|
|
8
|
-
import type { GlassSize, MousePosition, OverLightConfig } from '../../types/components';
|
|
9
|
-
|
|
10
|
-
const { CONSTANTS } = ATOMIX_GLASS;
|
|
11
|
-
|
|
12
|
-
interface UseGlassTransformsProps {
|
|
13
|
-
glassRef: React.RefObject<HTMLDivElement>;
|
|
14
|
-
globalMousePosition: MousePosition;
|
|
15
|
-
glassSize: GlassSize;
|
|
16
|
-
overLight: OverLightConfig;
|
|
17
|
-
detectedOverLight: boolean;
|
|
18
|
-
elasticity?: number;
|
|
19
|
-
effectiveWithoutEffects?: boolean;
|
|
20
|
-
isActive?: boolean;
|
|
21
|
-
onClick?: () => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function useGlassTransforms({
|
|
25
|
-
glassRef,
|
|
26
|
-
globalMousePosition,
|
|
27
|
-
glassSize,
|
|
28
|
-
overLight,
|
|
29
|
-
detectedOverLight,
|
|
30
|
-
elasticity = 0.05,
|
|
31
|
-
effectiveWithoutEffects = false,
|
|
32
|
-
isActive = false,
|
|
33
|
-
onClick,
|
|
34
|
-
}: UseGlassTransformsProps) {
|
|
35
|
-
const calculateDirectionalScale = useCallback(() => {
|
|
36
|
-
// Disable directional scaling if overLight is active (to prevent zooming/distorting the premium glass effect)
|
|
37
|
-
const isOverLightActive =
|
|
38
|
-
overLight === true ||
|
|
39
|
-
(overLight === 'auto' && detectedOverLight) ||
|
|
40
|
-
(typeof overLight === 'object' && overLight !== null && detectedOverLight);
|
|
41
|
-
|
|
42
|
-
if (isOverLightActive) {
|
|
43
|
-
return 'scale(1)';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
!globalMousePosition.x ||
|
|
48
|
-
!globalMousePosition.y ||
|
|
49
|
-
!glassRef.current ||
|
|
50
|
-
!validateGlassSize(glassSize)
|
|
51
|
-
) {
|
|
52
|
-
return 'scale(1)';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
56
|
-
const center = calculateElementCenter(rect);
|
|
57
|
-
const deltaX = globalMousePosition.x - center.x;
|
|
58
|
-
const deltaY = globalMousePosition.y - center.y;
|
|
59
|
-
|
|
60
|
-
const edgeDistanceX = Math.max(0, Math.abs(deltaX) - glassSize.width / 2);
|
|
61
|
-
const edgeDistanceY = Math.max(0, Math.abs(deltaY) - glassSize.height / 2);
|
|
62
|
-
const edgeDistance = calculateDistance({ x: edgeDistanceX, y: edgeDistanceY }, { x: 0, y: 0 });
|
|
63
|
-
|
|
64
|
-
if (edgeDistance > CONSTANTS.ACTIVATION_ZONE) {
|
|
65
|
-
return 'scale(1)';
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const fadeInFactor = 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
|
|
69
|
-
const centerDistance = calculateDistance(globalMousePosition, center);
|
|
70
|
-
|
|
71
|
-
if (centerDistance === 0) {
|
|
72
|
-
return 'scale(1)';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const normalizedX = deltaX / centerDistance;
|
|
76
|
-
const normalizedY = deltaY / centerDistance;
|
|
77
|
-
const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
|
|
78
|
-
|
|
79
|
-
const scaleX =
|
|
80
|
-
1 +
|
|
81
|
-
Math.abs(normalizedX) * stretchIntensity * 0.3 -
|
|
82
|
-
Math.abs(normalizedY) * stretchIntensity * 0.15;
|
|
83
|
-
const scaleY =
|
|
84
|
-
1 +
|
|
85
|
-
Math.abs(normalizedY) * stretchIntensity * 0.3 -
|
|
86
|
-
Math.abs(normalizedX) * stretchIntensity * 0.15;
|
|
87
|
-
|
|
88
|
-
return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
|
|
89
|
-
}, [globalMousePosition, elasticity, glassSize, glassRef, overLight, detectedOverLight]);
|
|
90
|
-
|
|
91
|
-
const calculateFadeInFactor = useCallback(() => {
|
|
92
|
-
if (
|
|
93
|
-
!globalMousePosition.x ||
|
|
94
|
-
!globalMousePosition.y ||
|
|
95
|
-
!glassRef.current ||
|
|
96
|
-
!validateGlassSize(glassSize)
|
|
97
|
-
) {
|
|
98
|
-
return 0;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
102
|
-
const center = calculateElementCenter(rect);
|
|
103
|
-
|
|
104
|
-
const edgeDistanceX = Math.max(
|
|
105
|
-
0,
|
|
106
|
-
Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2
|
|
107
|
-
);
|
|
108
|
-
const edgeDistanceY = Math.max(
|
|
109
|
-
0,
|
|
110
|
-
Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2
|
|
111
|
-
);
|
|
112
|
-
const edgeDistance = calculateDistance({ x: edgeDistanceX, y: edgeDistanceY }, { x: 0, y: 0 });
|
|
113
|
-
|
|
114
|
-
return edgeDistance > CONSTANTS.ACTIVATION_ZONE
|
|
115
|
-
? 0
|
|
116
|
-
: 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
|
|
117
|
-
}, [globalMousePosition, glassSize, glassRef]);
|
|
118
|
-
|
|
119
|
-
const calculateElasticTranslation = useCallback(() => {
|
|
120
|
-
if (!glassRef.current) {
|
|
121
|
-
return { x: 0, y: 0 };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const fadeInFactor = calculateFadeInFactor();
|
|
125
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
126
|
-
const center = calculateElementCenter(rect);
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
x: (globalMousePosition.x - center.x) * elasticity * 0.1 * fadeInFactor,
|
|
130
|
-
y: (globalMousePosition.y - center.y) * elasticity * 0.1 * fadeInFactor,
|
|
131
|
-
};
|
|
132
|
-
}, [globalMousePosition, elasticity, calculateFadeInFactor, glassRef]);
|
|
133
|
-
|
|
134
|
-
const elasticTranslation = useMemo(() => {
|
|
135
|
-
if (effectiveWithoutEffects) {
|
|
136
|
-
return { x: 0, y: 0 };
|
|
137
|
-
}
|
|
138
|
-
return calculateElasticTranslation();
|
|
139
|
-
}, [calculateElasticTranslation, effectiveWithoutEffects]);
|
|
140
|
-
|
|
141
|
-
const directionalScale = useMemo(() => {
|
|
142
|
-
if (effectiveWithoutEffects) {
|
|
143
|
-
return 'scale(1)';
|
|
144
|
-
}
|
|
145
|
-
return calculateDirectionalScale();
|
|
146
|
-
}, [calculateDirectionalScale, effectiveWithoutEffects]);
|
|
147
|
-
|
|
148
|
-
const transformStyle = useMemo(() => {
|
|
149
|
-
if (effectiveWithoutEffects) {
|
|
150
|
-
return isActive && Boolean(onClick) ? 'scale(0.98)' : 'scale(1)';
|
|
151
|
-
}
|
|
152
|
-
return `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? 'scale(0.96)' : directionalScale}`;
|
|
153
|
-
}, [elasticTranslation, isActive, onClick, directionalScale, effectiveWithoutEffects]);
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
elasticTranslation,
|
|
157
|
-
directionalScale,
|
|
158
|
-
transformStyle,
|
|
159
|
-
};
|
|
160
|
-
}
|