@shohojdhara/atomix 0.6.1 → 0.6.3
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/README.md +510 -106
- package/dist/atomix.css +30 -24
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +6 -6
- package/dist/atomix.min.css.map +1 -1
- package/dist/atomix.umd.js +1 -1
- package/dist/atomix.umd.js.map +1 -1
- package/dist/atomix.umd.min.js +1 -1
- package/dist/charts.d.ts +11 -2
- package/dist/charts.js +294 -139
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +14 -39
- package/dist/core.js +297 -145
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +11 -1
- package/dist/forms.js +385 -185
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +9 -0
- package/dist/heavy.js +297 -143
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +156 -164
- package/dist/index.esm.js +391 -203
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +391 -203
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +14 -6
- package/dist/theme.js +2 -9
- package/dist/theme.js.map +1 -1
- package/package.json +26 -26
- package/src/components/AtomixGlass/AtomixGlass.tsx +1 -1
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +8 -1
- package/src/components/AtomixGlass/deprecated/AtomixGlass.deprecated.tsx +390 -0
- package/src/components/AtomixGlass/glass-utils.ts +29 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +32 -1
- package/src/components/Button/Button.stories.tsx +1 -1
- package/src/components/Button/Button.tsx +6 -5
- package/src/components/Card/Card.tsx +2 -2
- package/src/components/Dropdown/Dropdown.tsx +1 -0
- package/src/components/EdgePanel/EdgePanel.tsx +1 -3
- package/src/components/Form/Select.test.tsx +75 -0
- package/src/components/Form/Select.tsx +348 -252
- package/src/components/Form/SelectOption.tsx +16 -10
- package/src/components/index.ts +1 -1
- package/src/layouts/CssGrid/index.ts +1 -0
- package/src/lib/composables/shared-mouse-tracker.ts +62 -6
- package/src/lib/composables/useAtomixGlass.ts +241 -139
- package/src/lib/composables/useAtomixGlassStyles.ts +201 -149
- package/src/lib/constants/components.ts +54 -35
- package/src/lib/theme/config/configLoader.ts +1 -1
- package/src/lib/theme/test/testTheme.ts +2 -2
- package/src/lib/theme/utils/themeUtils.ts +98 -110
- package/src/lib/types/components.ts +29 -65
- package/src/styles/01-settings/_settings.spacing.scss +6 -1
- package/src/styles/03-generic/_generic.reset.scss +1 -1
- package/src/styles/06-components/_components.atomix-glass.scss +20 -29
- package/src/styles/06-components/_components.data-table.scss +5 -4
- package/src/styles/06-components/_components.dynamic-background.scss +9 -8
- package/src/styles/06-components/_components.footer.scss +8 -7
- package/src/styles/06-components/_components.hero.scss +2 -2
- package/src/styles/06-components/_components.messages.scss +16 -16
- package/src/styles/06-components/_components.navbar.scss +2 -0
- package/src/styles/06-components/_components.select.scss +15 -2
- package/src/styles/06-components/_components.upload.scss +3 -3
- package/CHANGELOG.md +0 -165
- package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +0 -215
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useCallback,
|
|
3
|
-
useEffect,
|
|
4
|
-
useMemo,
|
|
5
|
-
useRef,
|
|
6
|
-
useState,
|
|
7
|
-
} from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
2
|
import type {
|
|
9
3
|
AtomixGlassProps,
|
|
10
4
|
GlassSize,
|
|
@@ -21,6 +15,9 @@ import {
|
|
|
21
15
|
extractBorderRadiusFromDOMElement,
|
|
22
16
|
validateGlassSize,
|
|
23
17
|
lerp,
|
|
18
|
+
calculateSpring,
|
|
19
|
+
calculateVelocity,
|
|
20
|
+
smoothstep,
|
|
24
21
|
} from '../../components/AtomixGlass/glass-utils';
|
|
25
22
|
import { updateAtomixGlassStyles } from './useAtomixGlassStyles';
|
|
26
23
|
// Phase 1: Time-Based Animation System
|
|
@@ -48,10 +45,7 @@ const backgroundDetectionCache = new WeakMap<HTMLElement, BackgroundDetectionCac
|
|
|
48
45
|
* Compare two OverLightConfig values for equality
|
|
49
46
|
* Handles primitives (boolean, 'auto') and objects with deep comparison
|
|
50
47
|
*/
|
|
51
|
-
const compareOverLightConfig = (
|
|
52
|
-
config1: OverLightConfig,
|
|
53
|
-
config2: OverLightConfig
|
|
54
|
-
): boolean => {
|
|
48
|
+
const compareOverLightConfig = (config1: OverLightConfig, config2: OverLightConfig): boolean => {
|
|
55
49
|
// Primitive comparison for boolean and 'auto'
|
|
56
50
|
if (typeof config1 !== 'object' || config1 === null) {
|
|
57
51
|
return config1 === config2;
|
|
@@ -158,6 +152,7 @@ interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
|
|
|
158
152
|
wrapperRef?: React.RefObject<HTMLDivElement | null>;
|
|
159
153
|
children?: React.ReactNode;
|
|
160
154
|
isFixedOrSticky?: boolean;
|
|
155
|
+
priority?: number; // Priority for z-index ordering
|
|
161
156
|
// Phase 1: Time-Based Animation System
|
|
162
157
|
withLiquidBlur?: boolean;
|
|
163
158
|
animationQuality?: 'low' | 'medium' | 'high';
|
|
@@ -226,7 +221,7 @@ export function useAtomixGlass({
|
|
|
226
221
|
reducedMotion = false,
|
|
227
222
|
highContrast = false,
|
|
228
223
|
withoutEffects = false,
|
|
229
|
-
elasticity =
|
|
224
|
+
elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY,
|
|
230
225
|
onClick,
|
|
231
226
|
debugBorderRadius = false,
|
|
232
227
|
debugOverLight = false,
|
|
@@ -236,6 +231,7 @@ export function useAtomixGlass({
|
|
|
236
231
|
padding,
|
|
237
232
|
withLiquidBlur,
|
|
238
233
|
isFixedOrSticky = false,
|
|
234
|
+
priority = 1, // Default priority
|
|
239
235
|
// Phase 1: Animation System Props
|
|
240
236
|
withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION,
|
|
241
237
|
animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED,
|
|
@@ -265,6 +261,15 @@ export function useAtomixGlass({
|
|
|
265
261
|
const [dynamicBorderRadius, setDynamicCornerRadius] = useState<number>(
|
|
266
262
|
CONSTANTS.DEFAULT_CORNER_RADIUS
|
|
267
263
|
);
|
|
264
|
+
|
|
265
|
+
// ── Physics state refs ────────────────────────────────────────────────
|
|
266
|
+
const elasticTranslationRef = useRef<MousePosition>({ x: 0, y: 0 });
|
|
267
|
+
const elasticVelocityRef = useRef<MousePosition>({ x: 0, y: 0 });
|
|
268
|
+
const directionalScaleRef = useRef<{ x: number; y: number }>({ x: 1, y: 1 });
|
|
269
|
+
const scaleVelocityRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
270
|
+
const lastMouseTimeRef = useRef<number>(0);
|
|
271
|
+
const mouseVelocityRef = useRef<MousePosition>({ x: 0, y: 0 });
|
|
272
|
+
|
|
268
273
|
const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
|
|
269
274
|
const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
|
|
270
275
|
const [detectedOverLight, setDetectedOverLight] = useState(false);
|
|
@@ -285,7 +290,7 @@ export function useAtomixGlass({
|
|
|
285
290
|
const fbmConfig = useMemo(() => {
|
|
286
291
|
// If quality preset is provided, use it as base
|
|
287
292
|
const preset = getFBMConfigForQuality(distortionQuality);
|
|
288
|
-
|
|
293
|
+
|
|
289
294
|
// Override with custom values if provided
|
|
290
295
|
return {
|
|
291
296
|
octaves: distortionOctaves ?? preset.octaves,
|
|
@@ -371,7 +376,7 @@ export function useAtomixGlass({
|
|
|
371
376
|
}
|
|
372
377
|
|
|
373
378
|
const time = shaderTimeRef.current;
|
|
374
|
-
|
|
379
|
+
|
|
375
380
|
// Apply liquid glass distortion with time
|
|
376
381
|
return liquidGlassWithTime(uv, time, fbmConfig);
|
|
377
382
|
},
|
|
@@ -388,13 +393,12 @@ export function useAtomixGlass({
|
|
|
388
393
|
return result;
|
|
389
394
|
}, [borderRadius, dynamicBorderRadius]);
|
|
390
395
|
|
|
391
|
-
const { glassSize } = useGlassSize({
|
|
392
|
-
glassRef,
|
|
393
|
-
effectiveBorderRadius,
|
|
394
|
-
cachedRectRef
|
|
396
|
+
const { glassSize } = useGlassSize({
|
|
397
|
+
glassRef,
|
|
398
|
+
effectiveBorderRadius,
|
|
399
|
+
cachedRectRef,
|
|
395
400
|
});
|
|
396
401
|
|
|
397
|
-
|
|
398
402
|
const effectiveHighContrast = useMemo(
|
|
399
403
|
() => highContrast || userPrefersHighContrast,
|
|
400
404
|
[highContrast, userPrefersHighContrast]
|
|
@@ -436,7 +440,10 @@ export function useAtomixGlass({
|
|
|
436
440
|
setDynamicCornerRadius(extractedRadius);
|
|
437
441
|
}
|
|
438
442
|
} catch (error) {
|
|
439
|
-
if (
|
|
443
|
+
if (
|
|
444
|
+
(typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') &&
|
|
445
|
+
debugBorderRadius
|
|
446
|
+
) {
|
|
440
447
|
console.error('[AtomixGlass] Error extracting corner radius:', error);
|
|
441
448
|
}
|
|
442
449
|
}
|
|
@@ -479,7 +486,8 @@ export function useAtomixGlass({
|
|
|
479
486
|
// Background detection for overLight auto-detect
|
|
480
487
|
useEffect(() => {
|
|
481
488
|
// Only run auto-detection for 'auto' mode or object config (which uses auto-detection)
|
|
482
|
-
const shouldDetect =
|
|
489
|
+
const shouldDetect =
|
|
490
|
+
overLight === 'auto' || (typeof overLight === 'object' && overLight !== null);
|
|
483
491
|
|
|
484
492
|
if (shouldDetect && glassRef.current) {
|
|
485
493
|
const element = glassRef.current;
|
|
@@ -528,7 +536,13 @@ export function useAtomixGlass({
|
|
|
528
536
|
const bgImage = computedStyle.backgroundImage;
|
|
529
537
|
|
|
530
538
|
// Check for solid color backgrounds
|
|
531
|
-
if (
|
|
539
|
+
if (
|
|
540
|
+
bgColor &&
|
|
541
|
+
bgColor !== 'rgba(0, 0, 0, 0)' &&
|
|
542
|
+
bgColor !== 'transparent' &&
|
|
543
|
+
bgColor !== 'initial' &&
|
|
544
|
+
bgColor !== 'none'
|
|
545
|
+
) {
|
|
532
546
|
const rgb = bgColor.match(/\d+/g);
|
|
533
547
|
if (rgb && rgb.length >= 3) {
|
|
534
548
|
const r = Number(rgb[0]);
|
|
@@ -554,7 +568,7 @@ export function useAtomixGlass({
|
|
|
554
568
|
hasValidBackground = true;
|
|
555
569
|
}
|
|
556
570
|
} catch (styleError) {
|
|
557
|
-
|
|
571
|
+
// Silently continue
|
|
558
572
|
}
|
|
559
573
|
|
|
560
574
|
if (currentElement) {
|
|
@@ -573,27 +587,37 @@ export function useAtomixGlass({
|
|
|
573
587
|
if (typeof overLight === 'object' && overLight !== null) {
|
|
574
588
|
const objConfig = overLight as OverLightObjectConfig;
|
|
575
589
|
if (objConfig.threshold !== undefined) {
|
|
576
|
-
|
|
590
|
+
const configThreshold =
|
|
591
|
+
typeof objConfig.threshold === 'number' && !isNaN(objConfig.threshold)
|
|
592
|
+
? objConfig.threshold
|
|
593
|
+
: 0.7;
|
|
577
594
|
threshold = Math.min(0.9, Math.max(0.1, configThreshold));
|
|
578
595
|
}
|
|
579
596
|
}
|
|
580
597
|
|
|
581
598
|
const isOverLightDetected = avgLuminance > threshold;
|
|
582
|
-
setCachedBackgroundDetection(
|
|
599
|
+
setCachedBackgroundDetection(
|
|
600
|
+
element.parentElement,
|
|
601
|
+
overLight,
|
|
602
|
+
isOverLightDetected,
|
|
603
|
+
threshold
|
|
604
|
+
);
|
|
583
605
|
setDetectedOverLight(isOverLightDetected);
|
|
584
606
|
} else {
|
|
585
607
|
const result = false;
|
|
586
|
-
const threshold =
|
|
587
|
-
|
|
588
|
-
|
|
608
|
+
const threshold =
|
|
609
|
+
typeof overLight === 'object' && overLight !== null
|
|
610
|
+
? (overLight as OverLightObjectConfig).threshold || 0.7
|
|
611
|
+
: 0.7;
|
|
589
612
|
setCachedBackgroundDetection(element.parentElement, overLight, result, threshold);
|
|
590
613
|
setDetectedOverLight(result);
|
|
591
614
|
}
|
|
592
615
|
} else {
|
|
593
616
|
const result = false;
|
|
594
|
-
const threshold =
|
|
595
|
-
|
|
596
|
-
|
|
617
|
+
const threshold =
|
|
618
|
+
typeof overLight === 'object' && overLight !== null
|
|
619
|
+
? (overLight as OverLightObjectConfig).threshold || 0.7
|
|
620
|
+
: 0.7;
|
|
597
621
|
setCachedBackgroundDetection(element.parentElement, overLight, result, threshold);
|
|
598
622
|
setDetectedOverLight(result);
|
|
599
623
|
}
|
|
@@ -749,19 +773,12 @@ export function useAtomixGlass({
|
|
|
749
773
|
}
|
|
750
774
|
|
|
751
775
|
return baseConfig;
|
|
752
|
-
}, [
|
|
753
|
-
overLight,
|
|
754
|
-
getEffectiveOverLight,
|
|
755
|
-
isHovered,
|
|
756
|
-
isActive,
|
|
757
|
-
validateConfigValue,
|
|
758
|
-
debugOverLight,
|
|
759
|
-
]);
|
|
776
|
+
}, [overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight]);
|
|
760
777
|
|
|
761
778
|
// Transform calculation (static base for React render)
|
|
762
779
|
// Mouse interactions are purely handled by imperative updates in the RAF lerp loop to prevent re-renders
|
|
763
780
|
const transformStyle = useMemo(() => {
|
|
764
|
-
return effectiveWithoutEffects || (isActive && Boolean(onClick)) ? 'scale(0.
|
|
781
|
+
return effectiveWithoutEffects || (isActive && Boolean(onClick)) ? 'scale(0.99)' : 'scale(1)';
|
|
765
782
|
}, [effectiveWithoutEffects, isActive, onClick]);
|
|
766
783
|
|
|
767
784
|
// Mouse tracking
|
|
@@ -770,7 +787,6 @@ export function useAtomixGlass({
|
|
|
770
787
|
// Derived values for imperative updates (we can use memoized ones or re-calculate)
|
|
771
788
|
// Since updateAtomixGlassStyles is called imperatively, we pass current refs and state
|
|
772
789
|
|
|
773
|
-
|
|
774
790
|
// Handle mouse position updates
|
|
775
791
|
// ── Raw mouse handler — writes to TARGET refs only ──────────────────
|
|
776
792
|
// The lerp loop (below) reads the targets and incrementally
|
|
@@ -804,82 +820,166 @@ export function useAtomixGlass({
|
|
|
804
820
|
|
|
805
821
|
const cur = internalMouseOffsetRef.current;
|
|
806
822
|
const tgt = targetMouseOffsetRef.current;
|
|
807
|
-
const dx = tgt.x - cur.x;
|
|
808
|
-
const dy = tgt.y - cur.y;
|
|
809
|
-
|
|
810
|
-
// If we're close enough, snap and park
|
|
811
|
-
if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {
|
|
812
|
-
internalMouseOffsetRef.current = { ...tgt };
|
|
813
|
-
internalGlobalMousePositionRef.current = { ...targetGlobalMousePositionRef.current };
|
|
814
|
-
|
|
815
|
-
// Final update and stop
|
|
816
|
-
updateAtomixGlassStyles(
|
|
817
|
-
wrapperRef?.current || null,
|
|
818
|
-
glassRef.current,
|
|
819
|
-
{
|
|
820
|
-
mouseOffset: internalMouseOffsetRef.current,
|
|
821
|
-
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
822
|
-
glassSize,
|
|
823
|
-
isHovered,
|
|
824
|
-
isActive,
|
|
825
|
-
isOverLight: overLightConfig.isOverLight,
|
|
826
|
-
baseOverLightConfig: overLightConfig,
|
|
827
|
-
effectiveBorderRadius,
|
|
828
|
-
effectiveWithoutEffects,
|
|
829
|
-
effectiveReducedMotion,
|
|
830
|
-
elasticity,
|
|
831
|
-
directionalScale: isActive && Boolean(onClick) ? 'scale(0.96)' : 'scale(1)',
|
|
832
|
-
onClick,
|
|
833
|
-
withLiquidBlur,
|
|
834
|
-
blurAmount,
|
|
835
|
-
saturation,
|
|
836
|
-
padding,
|
|
837
|
-
isFixedOrSticky,
|
|
838
|
-
}
|
|
839
|
-
);
|
|
840
|
-
|
|
841
|
-
stopLerpLoop();
|
|
842
|
-
return;
|
|
843
|
-
}
|
|
844
823
|
|
|
845
|
-
//
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
824
|
+
// Calculate spring-based mouse offset (keeps the liquid tracking feel)
|
|
825
|
+
const springX = calculateSpring(
|
|
826
|
+
cur.x,
|
|
827
|
+
tgt.x,
|
|
828
|
+
mouseVelocityRef.current.x,
|
|
829
|
+
CONSTANTS.LERP_FACTOR,
|
|
830
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
831
|
+
);
|
|
832
|
+
const springY = calculateSpring(
|
|
833
|
+
cur.y,
|
|
834
|
+
tgt.y,
|
|
835
|
+
mouseVelocityRef.current.y,
|
|
836
|
+
CONSTANTS.LERP_FACTOR,
|
|
837
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
internalMouseOffsetRef.current = { x: springX.value, y: springY.value };
|
|
841
|
+
mouseVelocityRef.current = { x: springX.velocity, y: springY.velocity };
|
|
842
|
+
|
|
851
843
|
const curG = internalGlobalMousePositionRef.current;
|
|
852
844
|
const tgtG = targetGlobalMousePositionRef.current;
|
|
853
845
|
internalGlobalMousePositionRef.current = {
|
|
854
|
-
x: lerp(curG.x, tgtG.x,
|
|
855
|
-
y: lerp(curG.y, tgtG.y,
|
|
846
|
+
x: lerp(curG.x, tgtG.x, CONSTANTS.LERP_FACTOR),
|
|
847
|
+
y: lerp(curG.y, tgtG.y, CONSTANTS.LERP_FACTOR),
|
|
856
848
|
};
|
|
857
849
|
|
|
858
|
-
//
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
850
|
+
// ── Calculate Elastic Physics ─────────────────────────────────────
|
|
851
|
+
let targetElasticTranslation = { x: 0, y: 0 };
|
|
852
|
+
let targetScale = { x: 1, y: 1 };
|
|
853
|
+
|
|
854
|
+
if (!effectiveWithoutEffects && glassRef.current) {
|
|
855
|
+
const rect = cachedRectRef.current || glassRef.current.getBoundingClientRect();
|
|
856
|
+
const center = calculateElementCenter(rect);
|
|
857
|
+
const globalPos = internalGlobalMousePositionRef.current;
|
|
858
|
+
|
|
859
|
+
if (globalPos.x && globalPos.y) {
|
|
860
|
+
const deltaX = globalPos.x - center.x;
|
|
861
|
+
const deltaY = globalPos.y - center.y;
|
|
862
|
+
const edgeDistanceX = Math.max(0, Math.abs(deltaX) - rect.width / 2);
|
|
863
|
+
const edgeDistanceY = Math.max(0, Math.abs(deltaY) - rect.height / 2);
|
|
864
|
+
const edgeDistance = Math.sqrt(
|
|
865
|
+
edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
const activationZone = CONSTANTS.ACTIVATION_ZONE;
|
|
869
|
+
const rawT = edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone;
|
|
870
|
+
const fadeInFactor = smoothstep(rawT);
|
|
871
|
+
|
|
872
|
+
targetElasticTranslation = {
|
|
873
|
+
x: deltaX * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor,
|
|
874
|
+
y: deltaY * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor,
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
// Scale stretch logic (liquid surface tension)
|
|
878
|
+
if (edgeDistance <= activationZone) {
|
|
879
|
+
const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
880
|
+
if (centerDistance > 0) {
|
|
881
|
+
const nx = deltaX / centerDistance;
|
|
882
|
+
const ny = deltaY / centerDistance;
|
|
883
|
+
const stretchIntensity = Math.min(centerDistance / 350, 1) * elasticity * rawT;
|
|
884
|
+
|
|
885
|
+
// Liquid magnification (lens effect)
|
|
886
|
+
const mag = 1 + stretchIntensity * 0.06;
|
|
887
|
+
|
|
888
|
+
targetScale = {
|
|
889
|
+
x: mag + Math.abs(nx) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO,
|
|
890
|
+
y: mag + Math.abs(ny) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO,
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
// Maintain liquid volume by compressing the perpendicular axis
|
|
894
|
+
targetScale.x -= Math.abs(ny) * stretchIntensity * 0.15;
|
|
895
|
+
targetScale.y -= Math.abs(nx) * stretchIntensity * 0.15;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
881
898
|
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Integrate Elastic Translation Spring
|
|
902
|
+
const springTX = calculateSpring(
|
|
903
|
+
elasticTranslationRef.current.x,
|
|
904
|
+
targetElasticTranslation.x,
|
|
905
|
+
elasticVelocityRef.current.x,
|
|
906
|
+
CONSTANTS.ELASTICITY_STIFFNESS,
|
|
907
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
882
908
|
);
|
|
909
|
+
const springTY = calculateSpring(
|
|
910
|
+
elasticTranslationRef.current.y,
|
|
911
|
+
targetElasticTranslation.y,
|
|
912
|
+
elasticVelocityRef.current.y,
|
|
913
|
+
CONSTANTS.ELASTICITY_STIFFNESS,
|
|
914
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
elasticTranslationRef.current = { x: springTX.value, y: springTY.value };
|
|
918
|
+
elasticVelocityRef.current = { x: springTX.velocity, y: springTY.velocity };
|
|
919
|
+
|
|
920
|
+
// Integrate Scale Spring
|
|
921
|
+
const springSX = calculateSpring(
|
|
922
|
+
directionalScaleRef.current.x,
|
|
923
|
+
targetScale.x,
|
|
924
|
+
scaleVelocityRef.current.x,
|
|
925
|
+
CONSTANTS.ELASTICITY_STIFFNESS,
|
|
926
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
927
|
+
);
|
|
928
|
+
const springSY = calculateSpring(
|
|
929
|
+
directionalScaleRef.current.y,
|
|
930
|
+
targetScale.y,
|
|
931
|
+
scaleVelocityRef.current.y,
|
|
932
|
+
CONSTANTS.ELASTICITY_STIFFNESS,
|
|
933
|
+
CONSTANTS.ELASTICITY_DAMPING
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
directionalScaleRef.current = { x: springSX.value, y: springSY.value };
|
|
937
|
+
scaleVelocityRef.current = { x: springSX.velocity, y: springSY.velocity };
|
|
938
|
+
|
|
939
|
+
// Imperative style update
|
|
940
|
+
updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
|
|
941
|
+
mouseOffset: internalMouseOffsetRef.current,
|
|
942
|
+
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
943
|
+
elasticTranslation: elasticTranslationRef.current,
|
|
944
|
+
elasticVelocity: elasticVelocityRef.current,
|
|
945
|
+
mouseVelocity: mouseVelocityRef.current,
|
|
946
|
+
directionalScale: directionalScaleRef.current,
|
|
947
|
+
glassSize,
|
|
948
|
+
isHovered,
|
|
949
|
+
isActive,
|
|
950
|
+
isOverLight: overLightConfig.isOverLight,
|
|
951
|
+
baseOverLightConfig: overLightConfig,
|
|
952
|
+
effectiveBorderRadius,
|
|
953
|
+
effectiveWithoutEffects,
|
|
954
|
+
effectiveReducedMotion,
|
|
955
|
+
elasticity,
|
|
956
|
+
scaleBase: isActive && Boolean(onClick) ? 0.99 : 1,
|
|
957
|
+
onClick,
|
|
958
|
+
withLiquidBlur,
|
|
959
|
+
blurAmount,
|
|
960
|
+
saturation,
|
|
961
|
+
padding,
|
|
962
|
+
isFixedOrSticky,
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
// ── Stop check ──────────────────────────────────────────────────
|
|
966
|
+
const VEL_EPS = 0.001;
|
|
967
|
+
const POS_EPS = 0.001;
|
|
968
|
+
|
|
969
|
+
const isAtRest =
|
|
970
|
+
Math.abs(mouseVelocityRef.current.x) < VEL_EPS &&
|
|
971
|
+
Math.abs(mouseVelocityRef.current.y) < VEL_EPS &&
|
|
972
|
+
Math.abs(elasticVelocityRef.current.x) < VEL_EPS &&
|
|
973
|
+
Math.abs(elasticVelocityRef.current.y) < VEL_EPS &&
|
|
974
|
+
Math.abs(scaleVelocityRef.current.x) < VEL_EPS &&
|
|
975
|
+
Math.abs(scaleVelocityRef.current.y) < VEL_EPS &&
|
|
976
|
+
Math.abs(internalMouseOffsetRef.current.x - targetMouseOffsetRef.current.x) < POS_EPS &&
|
|
977
|
+
Math.abs(internalMouseOffsetRef.current.y - targetMouseOffsetRef.current.y) < POS_EPS;
|
|
978
|
+
|
|
979
|
+
if (isAtRest) {
|
|
980
|
+
stopLerpLoop();
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
883
983
|
|
|
884
984
|
lerpRafRef.current = requestAnimationFrame(tick);
|
|
885
985
|
};
|
|
@@ -965,8 +1065,12 @@ export function useAtomixGlass({
|
|
|
965
1065
|
return undefined;
|
|
966
1066
|
}
|
|
967
1067
|
|
|
968
|
-
const unsubscribe = globalMouseTracker.subscribe(
|
|
969
|
-
|
|
1068
|
+
const unsubscribe = globalMouseTracker.subscribe(
|
|
1069
|
+
handleGlobalMousePosition,
|
|
1070
|
+
glassRef.current || undefined,
|
|
1071
|
+
300
|
|
1072
|
+
); // 300px max distance for full effect
|
|
1073
|
+
|
|
970
1074
|
// Initial start
|
|
971
1075
|
startLerpLoop();
|
|
972
1076
|
|
|
@@ -1015,29 +1119,29 @@ export function useAtomixGlass({
|
|
|
1015
1119
|
|
|
1016
1120
|
// Also call updateStyles on other state changes (hover, active, etc)
|
|
1017
1121
|
useEffect(() => {
|
|
1018
|
-
updateAtomixGlassStyles(
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1122
|
+
updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
|
|
1123
|
+
mouseOffset: externalMouseOffset || internalMouseOffsetRef.current,
|
|
1124
|
+
globalMousePosition: externalGlobalMousePosition || internalGlobalMousePositionRef.current,
|
|
1125
|
+
elasticTranslation: elasticTranslationRef.current,
|
|
1126
|
+
elasticVelocity: elasticVelocityRef.current,
|
|
1127
|
+
mouseVelocity: mouseVelocityRef.current,
|
|
1128
|
+
directionalScale: directionalScaleRef.current,
|
|
1129
|
+
scaleBase: isActive && Boolean(onClick) ? 0.96 : 1,
|
|
1130
|
+
glassSize,
|
|
1131
|
+
isHovered,
|
|
1132
|
+
isActive,
|
|
1133
|
+
isOverLight: overLightConfig.isOverLight,
|
|
1134
|
+
baseOverLightConfig: overLightConfig,
|
|
1135
|
+
effectiveBorderRadius,
|
|
1136
|
+
effectiveWithoutEffects,
|
|
1137
|
+
effectiveReducedMotion,
|
|
1138
|
+
elasticity,
|
|
1139
|
+
onClick,
|
|
1140
|
+
withLiquidBlur,
|
|
1141
|
+
blurAmount,
|
|
1142
|
+
saturation,
|
|
1143
|
+
padding,
|
|
1144
|
+
});
|
|
1041
1145
|
}, [
|
|
1042
1146
|
isHovered,
|
|
1043
1147
|
isActive,
|
|
@@ -1055,7 +1159,7 @@ export function useAtomixGlass({
|
|
|
1055
1159
|
blurAmount,
|
|
1056
1160
|
saturation,
|
|
1057
1161
|
padding,
|
|
1058
|
-
onClick
|
|
1162
|
+
onClick,
|
|
1059
1163
|
]);
|
|
1060
1164
|
|
|
1061
1165
|
// Event handlers
|
|
@@ -1064,8 +1168,6 @@ export function useAtomixGlass({
|
|
|
1064
1168
|
const handleMouseDown = useCallback(() => setIsActive(true), []);
|
|
1065
1169
|
const handleMouseUp = useCallback(() => setIsActive(false), []);
|
|
1066
1170
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
1171
|
const handleKeyDown = useCallback(
|
|
1070
1172
|
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
1071
1173
|
if (onClick && (e.key === 'Enter' || e.key === ' ')) {
|
|
@@ -1087,7 +1189,7 @@ export function useAtomixGlass({
|
|
|
1087
1189
|
effectiveWithoutEffects,
|
|
1088
1190
|
detectedOverLight,
|
|
1089
1191
|
globalMousePosition, // This is now static (refs or props) unless prop changes
|
|
1090
|
-
mouseOffset,
|
|
1192
|
+
mouseOffset, // This is now static (refs or props) unless prop changes
|
|
1091
1193
|
overLightConfig,
|
|
1092
1194
|
transformStyle,
|
|
1093
1195
|
getShaderTime,
|
|
@@ -1098,4 +1200,4 @@ export function useAtomixGlass({
|
|
|
1098
1200
|
handleMouseUp,
|
|
1099
1201
|
handleKeyDown,
|
|
1100
1202
|
};
|
|
1101
|
-
}
|
|
1203
|
+
}
|