@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,302 +0,0 @@
1
- import type { CSSProperties } from 'react';
2
- import type {
3
- GlassSize,
4
- MousePosition,
5
- } from '../types/components';
6
- import { ATOMIX_GLASS } from '../constants/components';
7
- import { calculateMouseInfluence, clampBlur } from '../../components/AtomixGlass/glass-utils';
8
-
9
- const { CONSTANTS } = ATOMIX_GLASS;
10
-
11
- export interface ResolvedOverLightConfig {
12
- isOverLight: boolean;
13
- threshold: number;
14
- opacity: number;
15
- contrast: number;
16
- brightness: number;
17
- saturationBoost: number;
18
- shadowIntensity: number;
19
- borderOpacity: number;
20
- }
21
-
22
- interface CalculateGlassVarsOptions {
23
- mouseOffset: MousePosition;
24
- overLightConfig: ResolvedOverLightConfig;
25
- effectiveBorderRadius: number;
26
- transformStyle: string;
27
- adjustedSize: { width: string | number; height: string | number };
28
- positionStyles: { position: any; top: any; left: any };
29
- style: CSSProperties;
30
- isOverLight: boolean;
31
- isActive: boolean;
32
- isHovered: boolean;
33
- }
34
-
35
- export const calculateGlassVars = ({
36
- mouseOffset,
37
- overLightConfig,
38
- effectiveBorderRadius,
39
- transformStyle,
40
- adjustedSize,
41
- positionStyles,
42
- style,
43
- isOverLight,
44
- isActive,
45
- isHovered,
46
- }: CalculateGlassVarsOptions): CSSProperties => {
47
- const mx = mouseOffset.x;
48
- const my = mouseOffset.y;
49
- const absMx = Math.abs(mx);
50
- const absMy = Math.abs(my);
51
- const GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
52
-
53
- // Gradient Values
54
- const borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER;
55
- const borderStop1 = Math.max(
56
- GRADIENT.BORDER_STOP_1.MIN,
57
- GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
58
- );
59
- const borderStop2 = Math.min(
60
- GRADIENT.BORDER_STOP_2.MAX,
61
- GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
62
- );
63
- const borderOpacities = [
64
- GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
65
- GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
66
- GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
67
- GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
68
- ];
69
-
70
- const hoverPositions = {
71
- hover1: {
72
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
73
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1,
74
- },
75
- hover2: {
76
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
77
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2,
78
- },
79
- hover3: {
80
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
81
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
82
- },
83
- };
84
-
85
- const basePosition = {
86
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
87
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER,
88
- };
89
-
90
- // Opacity Values
91
- const overLightOpacity = overLightConfig.opacity;
92
- const BASE_OVER_LIGHT_OPACITY = 0.4;
93
- const OVER_OPACITY_MULTIPLIER = 1.1;
94
-
95
- const opacityValues = {
96
- hover1: isHovered || isActive ? 0.5 : 0,
97
- hover2: isActive ? 0.5 : 0,
98
- hover3: isHovered ? 0.4 : isActive ? 0.8 : 0,
99
- base: isOverLight ? overLightOpacity || BASE_OVER_LIGHT_OPACITY : 0,
100
- over: isOverLight
101
- ? (overLightOpacity || BASE_OVER_LIGHT_OPACITY) * OVER_OPACITY_MULTIPLIER
102
- : 0,
103
- };
104
-
105
- const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE;
106
- const blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK;
107
- const configBorderOpacity = overLightConfig?.borderOpacity ?? 1;
108
-
109
- return {
110
- '--atomix-glass-radius': `${effectiveBorderRadius}px`,
111
- '--atomix-glass-transform': transformStyle || 'none',
112
- '--atomix-glass-position': positionStyles.position,
113
- '--atomix-glass-top': positionStyles.top !== 'fixed' ? `${positionStyles.top}px` : '0',
114
- '--atomix-glass-left': positionStyles.left !== 'fixed' ? `${positionStyles.left}px` : '0',
115
- '--atomix-glass-width':
116
- style.position !== 'fixed' ? adjustedSize.width : `${adjustedSize.width}px`,
117
- '--atomix-glass-height':
118
- style.position !== 'fixed' ? adjustedSize.height : `${adjustedSize.height}px`,
119
- '--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
120
- '--atomix-glass-blend-mode': isOverLight ? 'multiply' : 'overlay',
121
- '--atomix-glass-border-gradient-1': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
122
- '--atomix-glass-border-gradient-2': `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
123
- '--atomix-glass-hover-1-opacity': opacityValues.hover1,
124
- '--atomix-glass-hover-1-gradient': isOverLight
125
- ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)`
126
- : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`,
127
- '--atomix-glass-hover-2-opacity': opacityValues.hover2,
128
- '--atomix-glass-hover-2-gradient': isOverLight
129
- ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)`
130
- : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`,
131
- '--atomix-glass-hover-3-opacity': opacityValues.hover3,
132
- '--atomix-glass-hover-3-gradient': isOverLight
133
- ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)`
134
- : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`,
135
- '--atomix-glass-base-opacity': opacityValues.base,
136
- '--atomix-glass-base-gradient': isOverLight
137
- ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
138
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
139
- '--atomix-glass-overlay-opacity': opacityValues.over,
140
- '--atomix-glass-overlay-gradient': isOverLight
141
- ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
142
- : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
143
- } as CSSProperties;
144
- };
145
-
146
- interface CalculateContainerVarsOptions {
147
- mouseOffset: MousePosition;
148
- glassSize: GlassSize;
149
- padding: string;
150
- borderRadius: number;
151
- overLightConfig: ResolvedOverLightConfig;
152
- isOverLight: boolean;
153
- effectiveWithoutEffects: boolean;
154
- effectiveReducedMotion: boolean;
155
- withLiquidBlur: boolean;
156
- blurAmount: number;
157
- saturation: number;
158
- }
159
-
160
- export const calculateContainerVars = ({
161
- mouseOffset,
162
- glassSize,
163
- padding,
164
- borderRadius,
165
- overLightConfig,
166
- isOverLight,
167
- effectiveWithoutEffects,
168
- effectiveReducedMotion,
169
- withLiquidBlur,
170
- blurAmount,
171
- saturation,
172
- }: CalculateContainerVarsOptions): CSSProperties => {
173
- // Pre-calculate static multipliers
174
- const EDGE_BLUR_MULTIPLIER = 1.25;
175
- const CENTER_BLUR_MULTIPLIER = 1.1;
176
- const FLOW_BLUR_MULTIPLIER = 1.2;
177
- const MOUSE_INFLUENCE_BLUR_FACTOR = 0.15;
178
- const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
179
- const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
180
- // Maximum blur multiplier relative to base — prevents runaway blur
181
- const MAX_BLUR_RELATIVE = 2;
182
-
183
- const defaultBlur = {
184
- baseBlur: blurAmount,
185
- edgeBlur: blurAmount * EDGE_BLUR_MULTIPLIER,
186
- centerBlur: blurAmount * CENTER_BLUR_MULTIPLIER,
187
- flowBlur: blurAmount * FLOW_BLUR_MULTIPLIER,
188
- };
189
-
190
- let liquidBlur = defaultBlur;
191
-
192
- if (
193
- withLiquidBlur &&
194
- mouseOffset &&
195
- typeof mouseOffset.x === 'number' &&
196
- typeof mouseOffset.y === 'number' &&
197
- !isNaN(mouseOffset.x) &&
198
- !isNaN(mouseOffset.y)
199
- ) {
200
- const mouseInfluence = calculateMouseInfluence(mouseOffset);
201
- const maxBlur = blurAmount * MAX_BLUR_RELATIVE;
202
-
203
- const baseBlur = Math.min(
204
- maxBlur,
205
- blurAmount + mouseInfluence * blurAmount * MOUSE_INFLUENCE_BLUR_FACTOR
206
- );
207
- const edgeIntensity = mouseInfluence * EDGE_INTENSITY_MOUSE_FACTOR;
208
- const edgeBlur = Math.min(maxBlur, baseBlur * (0.8 + edgeIntensity * 0.4));
209
- const centerIntensity = mouseInfluence * CENTER_INTENSITY_MOUSE_FACTOR;
210
- const centerBlur = Math.min(maxBlur, baseBlur * (0.3 + centerIntensity * 0.3));
211
- const flowBlur = Math.min(maxBlur, baseBlur * FLOW_BLUR_MULTIPLIER);
212
-
213
- liquidBlur = {
214
- baseBlur: clampBlur(baseBlur),
215
- edgeBlur: clampBlur(edgeBlur),
216
- centerBlur: clampBlur(centerBlur),
217
- flowBlur: clampBlur(flowBlur),
218
- };
219
- }
220
-
221
- const dynamicSaturation = saturation + (liquidBlur.baseBlur || 0) * 20;
222
-
223
- // Validate blur values before using them
224
- const validatedBaseBlur =
225
- typeof liquidBlur.baseBlur === 'number' && !isNaN(liquidBlur.baseBlur)
226
- ? liquidBlur.baseBlur
227
- : 0;
228
- const validatedEdgeBlur =
229
- typeof liquidBlur.edgeBlur === 'number' && !isNaN(liquidBlur.edgeBlur)
230
- ? liquidBlur.edgeBlur
231
- : 0;
232
- const validatedCenterBlur =
233
- typeof liquidBlur.centerBlur === 'number' && !isNaN(liquidBlur.centerBlur)
234
- ? liquidBlur.centerBlur
235
- : 0;
236
- const validatedFlowBlur =
237
- typeof liquidBlur.flowBlur === 'number' && !isNaN(liquidBlur.flowBlur)
238
- ? liquidBlur.flowBlur
239
- : 0;
240
-
241
- // Adaptive strategy: prefer single-pass blur for large areas or when effects are reduced
242
- const area = glassSize ? glassSize.width * glassSize.height : 0;
243
- const areaIsLarge = area > 180000; // ~600x300 threshold; tune as needed
244
- const devicePrefersPerformance = effectiveReducedMotion || effectiveWithoutEffects;
245
- const useMultiPass = withLiquidBlur && !devicePrefersPerformance && !areaIsLarge;
246
-
247
- let backdropFilter = `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`;
248
-
249
- if (useMultiPass) {
250
- const weightedBlur = clampBlur(
251
- validatedBaseBlur * 0.4 +
252
- validatedEdgeBlur * 0.25 +
253
- validatedCenterBlur * 0.15 +
254
- validatedFlowBlur * 0.2
255
- );
256
-
257
- backdropFilter = `blur(${weightedBlur}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`;
258
- } else {
259
- // Single-pass fallback: stronger radius to match perceived blur of multi-pass
260
- const effectiveBlur = clampBlur(
261
- Math.max(
262
- validatedBaseBlur,
263
- validatedEdgeBlur * 0.8,
264
- validatedCenterBlur * 1.1,
265
- validatedFlowBlur * 0.9
266
- )
267
- );
268
-
269
- backdropFilter = `blur(${effectiveBlur}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`;
270
- }
271
-
272
- const mx =
273
- mouseOffset && typeof mouseOffset.x === 'number' && !isNaN(mouseOffset.x) ? mouseOffset.x : 0;
274
- const my =
275
- mouseOffset && typeof mouseOffset.y === 'number' && !isNaN(mouseOffset.y) ? mouseOffset.y : 0;
276
-
277
- return {
278
- '--atomix-glass-container-width': `${glassSize?.width}`,
279
- '--atomix-glass-container-height': `${glassSize?.height}`,
280
- '--atomix-glass-container-padding': padding || '0 0',
281
- '--atomix-glass-container-radius': `${typeof borderRadius === 'number' && !isNaN(borderRadius) ? borderRadius : 0}px`,
282
- '--atomix-glass-container-backdrop': backdropFilter,
283
- '--atomix-glass-container-shadow': isOverLight
284
- ? [
285
- `inset 0 1px 0 rgba(255, 255, 255, ${(0.4 + mx * 0.002) * (overLightConfig?.shadowIntensity || 1)})`,
286
- `inset 0 -1px 0 rgba(0, 0, 0, ${(0.2 + Math.abs(my) * 0.001) * (overLightConfig?.shadowIntensity || 1)})`,
287
- `inset 0 0 20px rgba(0, 0, 0, ${(0.08 + Math.abs(mx + my) * 0.001) * (overLightConfig?.shadowIntensity || 1)})`,
288
- `0 2px 12px rgba(0, 0, 0, ${(0.12 + Math.abs(my) * 0.002) * (overLightConfig?.shadowIntensity || 1)})`,
289
- ].join(', ')
290
- : '0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset',
291
- '--atomix-glass-container-shadow-opacity': effectiveWithoutEffects ? 0 : 1,
292
- '--atomix-glass-container-bg': isOverLight
293
- ? `linear-gradient(${180 + mx * 0.5}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)`
294
- : 'none',
295
- '--atomix-glass-container-text-shadow': isOverLight
296
- ? '0px 2px 12px rgba(0, 0, 0, 0)'
297
- : '0px 2px 12px rgba(0, 0, 0, 0.4)',
298
- '--atomix-glass-container-box-shadow': isOverLight
299
- ? '0px 16px 70px rgba(0, 0, 0, 0.75)'
300
- : '0px 12px 40px rgba(0, 0, 0, 0.25)',
301
- } as CSSProperties;
302
- };
@@ -1,177 +0,0 @@
1
- import { useCallback, useEffect, useId, useRef, useState } from 'react';
2
- import { GlassContainerProps, MousePosition } from '../types/components';
3
-
4
- /**
5
- * Custom hook for managing GlassContainer state and interactions
6
- */
7
- export function useGlassContainer(props: GlassContainerProps) {
8
- const {
9
- glassSize = { width: 270, height: 69 },
10
- elasticity = 0.15,
11
- mouseContainer,
12
- globalMousePos: externalGlobalMousePos,
13
- mouseOffset: externalMouseOffset,
14
- } = props;
15
-
16
- const filterId = useId();
17
- const glassRef = useRef<HTMLDivElement>(null);
18
- const [isHovered, setIsHovered] = useState(false);
19
- const [isActive, setIsActive] = useState(false);
20
- const [currentGlassSize, setCurrentGlassSize] = useState(glassSize);
21
- const [internalGlobalMousePos, setInternalGlobalMousePos] = useState<MousePosition>({
22
- x: 0,
23
- y: 0,
24
- });
25
- const [internalMouseOffset, setInternalMouseOffset] = useState<MousePosition>({ x: 0, y: 0 });
26
-
27
- // Use external mouse position if provided, otherwise use internal
28
- const globalMousePos = externalGlobalMousePos || internalGlobalMousePos;
29
- const mouseOffset = externalMouseOffset || internalMouseOffset;
30
-
31
- // Internal mouse tracking
32
- const handleMouseMove = useCallback(
33
- (e: MouseEvent) => {
34
- const container = mouseContainer?.current || glassRef.current;
35
- if (!container) return undefined;
36
-
37
- const rect = container.getBoundingClientRect();
38
- const centerX = rect.left + rect.width / 2;
39
- const centerY = rect.top + rect.height / 2;
40
-
41
- setInternalMouseOffset({
42
- x: ((e.clientX - centerX) / rect.width) * 100,
43
- y: ((e.clientY - centerY) / rect.height) * 100,
44
- });
45
-
46
- setInternalGlobalMousePos({
47
- x: e.clientX,
48
- y: e.clientY,
49
- });
50
- },
51
- [mouseContainer]
52
- );
53
-
54
- // Set up mouse tracking if no external mouse position is provided
55
- useEffect(() => {
56
- if (externalGlobalMousePos && externalMouseOffset) return undefined;
57
-
58
- const container = mouseContainer?.current || glassRef.current;
59
- if (!container) return undefined;
60
-
61
- container.addEventListener('mousemove', handleMouseMove);
62
- return () => container.removeEventListener('mousemove', handleMouseMove);
63
- }, [handleMouseMove, mouseContainer, externalGlobalMousePos, externalMouseOffset]);
64
-
65
- // Calculate directional scaling based on mouse position
66
- const calculateDirectionalScale = useCallback(() => {
67
- if (!globalMousePos.x || !globalMousePos.y || !glassRef.current) {
68
- return 'scale(1)';
69
- }
70
-
71
- const rect = glassRef.current.getBoundingClientRect();
72
- const pillCenterX = rect.left + rect.width / 2;
73
- const pillCenterY = rect.top + rect.height / 2;
74
- const pillWidth = currentGlassSize.width;
75
- const pillHeight = currentGlassSize.height;
76
-
77
- const deltaX = globalMousePos.x - pillCenterX;
78
- const deltaY = globalMousePos.y - pillCenterY;
79
-
80
- const edgeDistanceX = Math.max(0, Math.abs(deltaX) - pillWidth / 2);
81
- const edgeDistanceY = Math.max(0, Math.abs(deltaY) - pillHeight / 2);
82
- const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
83
-
84
- const activationZone = 200;
85
- if (edgeDistance > activationZone) return 'scale(1)';
86
-
87
- const fadeInFactor = 1 - edgeDistance / activationZone;
88
- const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
89
- if (centerDistance === 0) return 'scale(1)';
90
-
91
- const normalizedX = deltaX / centerDistance;
92
- const normalizedY = deltaY / centerDistance;
93
- const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
94
-
95
- const scaleX =
96
- 1 +
97
- Math.abs(normalizedX) * stretchIntensity * 0.3 -
98
- Math.abs(normalizedY) * stretchIntensity * 0.15;
99
- const scaleY =
100
- 1 +
101
- Math.abs(normalizedY) * stretchIntensity * 0.3 -
102
- Math.abs(normalizedX) * stretchIntensity * 0.15;
103
-
104
- return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
105
- }, [globalMousePos, elasticity, currentGlassSize]);
106
-
107
- // Calculate elastic translation
108
- const calculateElasticTranslation = useCallback(() => {
109
- if (!glassRef.current) return { x: 0, y: 0 };
110
-
111
- const rect = glassRef.current.getBoundingClientRect();
112
- const pillCenterX = rect.left + rect.width / 2;
113
- const pillCenterY = rect.top + rect.height / 2;
114
- const pillWidth = currentGlassSize.width;
115
- const pillHeight = currentGlassSize.height;
116
-
117
- const edgeDistanceX = Math.max(0, Math.abs(globalMousePos.x - pillCenterX) - pillWidth / 2);
118
- const edgeDistanceY = Math.max(0, Math.abs(globalMousePos.y - pillCenterY) - pillHeight / 2);
119
- const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
120
-
121
- const activationZone = 200;
122
- const fadeInFactor = edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone;
123
-
124
- return {
125
- x: (globalMousePos.x - pillCenterX) * elasticity * 0.1 * fadeInFactor,
126
- y: (globalMousePos.y - pillCenterY) * elasticity * 0.1 * fadeInFactor,
127
- };
128
- }, [globalMousePos, elasticity, currentGlassSize]);
129
-
130
- // Update glass size
131
- useEffect(() => {
132
- const updateGlassSize = () => {
133
- if (glassRef.current) {
134
- const rect = glassRef.current.getBoundingClientRect();
135
- setCurrentGlassSize({ width: rect.width, height: rect.height });
136
- }
137
- };
138
-
139
- updateGlassSize();
140
- window.addEventListener('resize', updateGlassSize);
141
- return () => window.removeEventListener('resize', updateGlassSize);
142
- }, []);
143
-
144
- const handleMouseEnter = useCallback(() => {
145
- setIsHovered(true);
146
- }, []);
147
-
148
- const handleMouseLeave = useCallback(() => {
149
- setIsHovered(false);
150
- }, []);
151
-
152
- const handleMouseDown = useCallback(() => {
153
- setIsActive(true);
154
- }, []);
155
-
156
- const handleMouseUp = useCallback(() => {
157
- setIsActive(false);
158
- }, []);
159
-
160
- return {
161
- filterId,
162
- glassRef,
163
- isHovered,
164
- isActive,
165
- currentGlassSize,
166
- globalMousePos,
167
- mouseOffset,
168
- calculateDirectionalScale,
169
- calculateElasticTranslation,
170
- handleMouseEnter,
171
- handleMouseLeave,
172
- handleMouseDown,
173
- handleMouseUp,
174
- };
175
- }
176
-
177
- export default useGlassContainer;