@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.
Files changed (62) hide show
  1. package/dist/atomix.css +50 -11
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +1 -1
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.js +184 -189
  6. package/dist/charts.js.map +1 -1
  7. package/dist/core.d.ts +4 -4
  8. package/dist/core.js +194 -199
  9. package/dist/core.js.map +1 -1
  10. package/dist/forms.js +184 -189
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js +189 -194
  13. package/dist/heavy.js.map +1 -1
  14. package/dist/index.d.ts +44 -47
  15. package/dist/index.esm.js +496 -613
  16. package/dist/index.esm.js.map +1 -1
  17. package/dist/index.js +528 -631
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.min.js +1 -1
  20. package/dist/index.min.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/AtomixGlass/AtomixGlass.tsx +60 -39
  23. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +8 -42
  24. package/src/components/AtomixGlass/glass-utils.ts +27 -14
  25. package/src/components/AtomixGlass/stories/Overview.stories.tsx +19 -21
  26. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1162 -515
  27. package/src/components/AtomixGlass/stories/shared-components.tsx +11 -3
  28. package/src/components/Breadcrumb/Breadcrumb.tsx +5 -5
  29. package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +2 -2
  30. package/src/components/Button/Button.tsx +6 -6
  31. package/src/components/Card/Card.tsx +3 -3
  32. package/src/components/Dropdown/Dropdown.tsx +5 -3
  33. package/src/components/Footer/Footer.tsx +124 -166
  34. package/src/components/Footer/FooterLink.tsx +16 -19
  35. package/src/components/Footer/FooterSection.tsx +40 -39
  36. package/src/components/Footer/FooterSocialLink.tsx +59 -58
  37. package/src/components/Footer/README.md +1 -1
  38. package/src/components/Hero/Hero.tsx +72 -142
  39. package/src/components/Navigation/Menu/MegaMenu.tsx +17 -12
  40. package/src/components/Navigation/Menu/Menu.tsx +49 -24
  41. package/src/components/Navigation/Nav/NavItem.tsx +5 -3
  42. package/src/components/Navigation/Navbar/Navbar.tsx +13 -5
  43. package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -2
  44. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -4
  45. package/src/components/Slider/Slider.tsx +7 -4
  46. package/src/lib/composables/index.ts +1 -2
  47. package/src/lib/composables/useAtomixGlass.ts +246 -222
  48. package/src/lib/composables/useAtomixGlassStyles.ts +46 -23
  49. package/src/lib/composables/useFooter.ts +117 -20
  50. package/src/lib/composables/useSlider.ts +3 -1
  51. package/src/lib/constants/components.ts +3 -1
  52. package/src/lib/types/components.ts +44 -12
  53. package/src/styles/06-components/_components.atomix-glass.scss +72 -14
  54. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +0 -222
  55. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +0 -329
  56. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +0 -82
  57. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +0 -153
  58. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +0 -198
  59. package/src/lib/composables/atomix-glass/useGlassState.ts +0 -112
  60. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +0 -160
  61. package/src/lib/composables/glass-styles.ts +0 -302
  62. 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
- }