@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,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;
|