@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.
Files changed (67) hide show
  1. package/README.md +510 -106
  2. package/dist/atomix.css +30 -24
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +6 -6
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/atomix.umd.js +1 -1
  7. package/dist/atomix.umd.js.map +1 -1
  8. package/dist/atomix.umd.min.js +1 -1
  9. package/dist/charts.d.ts +11 -2
  10. package/dist/charts.js +294 -139
  11. package/dist/charts.js.map +1 -1
  12. package/dist/core.d.ts +14 -39
  13. package/dist/core.js +297 -145
  14. package/dist/core.js.map +1 -1
  15. package/dist/forms.d.ts +11 -1
  16. package/dist/forms.js +385 -185
  17. package/dist/forms.js.map +1 -1
  18. package/dist/heavy.d.ts +9 -0
  19. package/dist/heavy.js +297 -143
  20. package/dist/heavy.js.map +1 -1
  21. package/dist/index.d.ts +156 -164
  22. package/dist/index.esm.js +391 -203
  23. package/dist/index.esm.js.map +1 -1
  24. package/dist/index.js +391 -203
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.min.js +1 -1
  27. package/dist/index.min.js.map +1 -1
  28. package/dist/theme.d.ts +14 -6
  29. package/dist/theme.js +2 -9
  30. package/dist/theme.js.map +1 -1
  31. package/package.json +26 -26
  32. package/src/components/AtomixGlass/AtomixGlass.tsx +1 -1
  33. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +8 -1
  34. package/src/components/AtomixGlass/deprecated/AtomixGlass.deprecated.tsx +390 -0
  35. package/src/components/AtomixGlass/glass-utils.ts +29 -0
  36. package/src/components/AtomixGlass/stories/Playground.stories.tsx +32 -1
  37. package/src/components/Button/Button.stories.tsx +1 -1
  38. package/src/components/Button/Button.tsx +6 -5
  39. package/src/components/Card/Card.tsx +2 -2
  40. package/src/components/Dropdown/Dropdown.tsx +1 -0
  41. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  42. package/src/components/Form/Select.test.tsx +75 -0
  43. package/src/components/Form/Select.tsx +348 -252
  44. package/src/components/Form/SelectOption.tsx +16 -10
  45. package/src/components/index.ts +1 -1
  46. package/src/layouts/CssGrid/index.ts +1 -0
  47. package/src/lib/composables/shared-mouse-tracker.ts +62 -6
  48. package/src/lib/composables/useAtomixGlass.ts +241 -139
  49. package/src/lib/composables/useAtomixGlassStyles.ts +201 -149
  50. package/src/lib/constants/components.ts +54 -35
  51. package/src/lib/theme/config/configLoader.ts +1 -1
  52. package/src/lib/theme/test/testTheme.ts +2 -2
  53. package/src/lib/theme/utils/themeUtils.ts +98 -110
  54. package/src/lib/types/components.ts +29 -65
  55. package/src/styles/01-settings/_settings.spacing.scss +6 -1
  56. package/src/styles/03-generic/_generic.reset.scss +1 -1
  57. package/src/styles/06-components/_components.atomix-glass.scss +20 -29
  58. package/src/styles/06-components/_components.data-table.scss +5 -4
  59. package/src/styles/06-components/_components.dynamic-background.scss +9 -8
  60. package/src/styles/06-components/_components.footer.scss +8 -7
  61. package/src/styles/06-components/_components.hero.scss +2 -2
  62. package/src/styles/06-components/_components.messages.scss +16 -16
  63. package/src/styles/06-components/_components.navbar.scss +2 -0
  64. package/src/styles/06-components/_components.select.scss +15 -2
  65. package/src/styles/06-components/_components.upload.scss +3 -3
  66. package/CHANGELOG.md +0 -165
  67. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +0 -215
package/dist/heavy.js CHANGED
@@ -65,12 +65,23 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
65
65
  },
66
66
  DEFAULTS: {
67
67
  DISPLACEMENT_SCALE: 70,
68
- BLUR_AMOUNT: 0,
69
- SATURATION: 140,
70
- ABERRATION_INTENSITY: 2,
68
+ get BLUR_AMOUNT() {
69
+ return .15 * this.DISPLACEMENT_SCALE;
70
+ // Dynamically computed based on displacement
71
+ },
72
+ get SATURATION() {
73
+ return 100 + .5 * this.DISPLACEMENT_SCALE;
74
+ // Saturate relative to intensity
75
+ },
76
+ get ABERRATION_INTENSITY() {
77
+ return .03 * this.DISPLACEMENT_SCALE;
78
+ // Scale aberration with displacement
79
+ },
71
80
  ELASTICITY: .15,
72
- CORNER_RADIUS: 20,
73
- // Default border-radius matching design system
81
+ get CORNER_RADIUS() {
82
+ return 16;
83
+ // Use 16 to match SCSS design system (was 20)
84
+ },
74
85
  PADDING: "0",
75
86
  MODE: "standard",
76
87
  OVER_LIGHT: !1,
@@ -92,6 +103,15 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
92
103
  MIN_BLUR: .1,
93
104
  MOUSE_INFLUENCE_DIVISOR: 100,
94
105
  EDGE_FADE_PIXELS: 2,
106
+ // Elasticity physics constants
107
+ ELASTICITY_TRANSLATION_FACTOR: .1,
108
+ ELASTICITY_DISTANCE_THRESHOLD: 200,
109
+ ELASTICITY_COMPRESSION_FACTOR: .3,
110
+ ELASTICITY_STIFFNESS: .1,
111
+ ELASTICITY_DAMPING: .76,
112
+ ELASTICITY_VELOCITY_FACTOR: .65,
113
+ ELASTICITY_STRETCH_RATIO: .45,
114
+ ELASTICITY_MAGNIFICATION_BASE: 1.02,
95
115
  // Note: This default must match the SCSS variable --atomix-radius-md
96
116
  // @see src/styles/01-settings/_settings.global.scss
97
117
  DEFAULT_CORNER_RADIUS: 16,
@@ -108,84 +128,126 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
108
128
  // Base angle for border gradients (degrees)
109
129
  ANGLE_MULTIPLIER: 1.2,
110
130
  // Multiplier for mouse influence on angle
131
+ VELOCITY_ANGLE_MULTIPLIER: 2.5,
132
+ // How much velocity affects gradient rotation
133
+ CHROMATIC_OFFSET: 1.5,
134
+ // Degree offset for chromatic rim layers
111
135
  BORDER_STOP_1: {
112
136
  MIN: 10,
113
137
  // Minimum percentage for border stop 1
114
138
  BASE: 33,
115
139
  // Base percentage for border stop 1
116
- MULTIPLIER: .3
140
+ get MULTIPLIER() {
141
+ return .009 * this.BASE;
142
+ }
117
143
  },
118
144
  BORDER_STOP_2: {
119
145
  MAX: 90,
120
146
  // Maximum percentage for border stop 2
121
147
  BASE: 66,
122
148
  // Base percentage for border stop 2
123
- MULTIPLIER: .4
149
+ get MULTIPLIER() {
150
+ return .006 * this.BASE;
151
+ }
124
152
  },
125
153
  BORDER_OPACITY: {
126
154
  BASE_1: .12,
127
155
  // Base opacity for border gradient 1
128
- BASE_2: .4,
156
+ get BASE_2() {
157
+ return 3.33 * this.BASE_1;
158
+ },
129
159
  // Base opacity for border gradient 2
130
- BASE_3: .32,
160
+ get BASE_3() {
161
+ return 2.66 * this.BASE_1;
162
+ },
131
163
  // Base opacity for border gradient 3
132
- BASE_4: .6,
164
+ get BASE_4() {
165
+ return 5 * this.BASE_1;
166
+ },
133
167
  // Base opacity for border gradient 4
134
- MULTIPLIER_LOW: .008,
168
+ get MULTIPLIER_LOW() {
169
+ return .066 * this.BASE_1;
170
+ },
135
171
  // Low multiplier for mouse influence on opacity
136
- MULTIPLIER_HIGH: .012
172
+ get MULTIPLIER_HIGH() {
173
+ return .1 * this.BASE_1;
174
+ }
137
175
  },
138
176
  CENTER_POSITION: 50,
139
177
  // Center position percentage (50%)
140
178
  HOVER_POSITION: {
141
179
  DIVISOR_1: 2,
142
180
  // Divisor for hover 1 position calculation
143
- DIVISOR_2: 1.5,
181
+ get DIVISOR_2() {
182
+ return .75 * this.DIVISOR_1;
183
+ },
144
184
  // Divisor for hover 2 position calculation
145
- MULTIPLIER_3: 1
185
+ get MULTIPLIER_3() {
186
+ return .5 * this.DIVISOR_1;
187
+ }
146
188
  },
147
- BASE_LAYER_MULTIPLIER: .5
189
+ get BASE_LAYER_MULTIPLIER() {
190
+ return .5;
191
+ }
148
192
  },
149
193
  // Gradient opacity values for hover effects
150
194
  GRADIENT_OPACITY: {
151
195
  HOVER_1: {
152
196
  BLACK_START: .3,
153
197
  // Start opacity for black hover 1
154
- BLACK_MID: .1,
198
+ get BLACK_MID() {
199
+ return this.BLACK_START / 3;
200
+ },
155
201
  // Mid opacity for black hover 1
156
202
  BLACK_STOP: 30,
157
203
  // Stop percentage for black hover 1
158
- BLACK_END: 60,
204
+ get BLACK_END() {
205
+ return 2 * this.BLACK_STOP;
206
+ },
159
207
  // End percentage for black hover 1
160
208
  WHITE_START: .5,
161
209
  // Start opacity for white hover 1
162
- WHITE_STOP: 50
210
+ get WHITE_STOP() {
211
+ return this.BLACK_END - 10;
212
+ }
163
213
  },
164
214
  HOVER_2: {
165
215
  BLACK_START: .4,
166
216
  // Start opacity for black hover 2
167
- BLACK_MID: .15,
217
+ get BLACK_MID() {
218
+ return .375 * this.BLACK_START;
219
+ },
168
220
  // Mid opacity for black hover 2
169
221
  BLACK_STOP: 40,
170
222
  // Stop percentage for black hover 2
171
- BLACK_END: 80,
223
+ get BLACK_END() {
224
+ return 2 * this.BLACK_STOP;
225
+ },
172
226
  // End percentage for black hover 2
173
227
  WHITE_START: 1,
174
228
  // Start opacity for white hover 2
175
- WHITE_STOP: 80
229
+ get WHITE_STOP() {
230
+ return this.BLACK_END;
231
+ }
176
232
  },
177
233
  HOVER_3: {
178
234
  BLACK_START: .5,
179
235
  // Start opacity for black hover 3
180
- BLACK_MID: .2,
236
+ get BLACK_MID() {
237
+ return .4 * this.BLACK_START;
238
+ },
181
239
  // Mid opacity for black hover 3
182
240
  BLACK_STOP: 50,
183
241
  // Stop percentage for black hover 3
184
- BLACK_END: 100,
242
+ get BLACK_END() {
243
+ return 2 * this.BLACK_STOP;
244
+ },
185
245
  // End percentage for black hover 3
186
246
  WHITE_START: 1,
187
247
  // Start opacity for white hover 3
188
- WHITE_STOP: 100
248
+ get WHITE_STOP() {
249
+ return this.BLACK_END;
250
+ }
189
251
  }
190
252
  },
191
253
  // Base and overlay gradient constants
@@ -194,34 +256,54 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
194
256
  // Gradient angle in degrees
195
257
  BLACK_START_BASE: .15,
196
258
  // Base start opacity for black
197
- BLACK_START_MULTIPLIER: .003,
259
+ get BLACK_START_MULTIPLIER() {
260
+ return .02 * this.BLACK_START_BASE;
261
+ },
198
262
  // Multiplier for mouse X influence on start
199
263
  BLACK_MID_BASE: .1,
200
264
  // Base mid opacity for black
201
- BLACK_MID_MULTIPLIER: .002,
265
+ get BLACK_MID_MULTIPLIER() {
266
+ return .02 * this.BLACK_MID_BASE;
267
+ },
202
268
  // Multiplier for mouse Y influence on mid
203
269
  BLACK_MID_STOP: 50,
204
270
  // Mid stop percentage
205
- BLACK_END_BASE: .18,
271
+ get BLACK_END_BASE() {
272
+ return 1.2 * this.BLACK_START_BASE;
273
+ },
206
274
  // Base end opacity for black
207
- BLACK_END_MULTIPLIER: .004,
275
+ get BLACK_END_MULTIPLIER() {
276
+ return .022 * this.BLACK_END_BASE;
277
+ },
208
278
  // Multiplier for mouse X influence on end
209
- WHITE_OPACITY: .1
279
+ get WHITE_OPACITY() {
280
+ return .666 * this.BLACK_START_BASE;
281
+ }
210
282
  },
211
283
  OVERLAY_GRADIENT: {
212
284
  BLACK_START_BASE: .12,
213
285
  // Base start opacity for black overlay
214
- BLACK_START_MULTIPLIER: .003,
286
+ get BLACK_START_MULTIPLIER() {
287
+ return .025 * this.BLACK_START_BASE;
288
+ },
215
289
  // Multiplier for mouse X influence on start
216
- BLACK_MID: .06,
290
+ get BLACK_MID() {
291
+ return .5 * this.BLACK_START_BASE;
292
+ },
217
293
  // Mid opacity for black overlay
218
294
  BLACK_MID_STOP: 40,
219
295
  // Mid stop percentage
220
- BLACK_END_BASE: .15,
296
+ get BLACK_END_BASE() {
297
+ return 1.25 * this.BLACK_START_BASE;
298
+ },
221
299
  // Base end opacity for black overlay
222
- BLACK_END_MULTIPLIER: .003,
300
+ get BLACK_END_MULTIPLIER() {
301
+ return .02 * this.BLACK_END_BASE;
302
+ },
223
303
  // Multiplier for mouse Y influence on end
224
- WHITE_OPACITY: .05
304
+ get WHITE_OPACITY() {
305
+ return .416 * this.BLACK_START_BASE;
306
+ }
225
307
  },
226
308
  // Overlay highlight constants
227
309
  OVERLAY_HIGHLIGHT: {
@@ -231,9 +313,13 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
231
313
  // Y position percentage
232
314
  WHITE_OPACITY: .4,
233
315
  // White opacity in gradient
234
- STOP: 60,
316
+ get STOP() {
317
+ return 150 * this.WHITE_OPACITY;
318
+ },
235
319
  // Stop percentage
236
- OPACITY_MULTIPLIER: .7
320
+ get OPACITY_MULTIPLIER() {
321
+ return 1.75 * this.WHITE_OPACITY;
322
+ }
237
323
  },
238
324
  // Displacement and aberration multipliers
239
325
  MULTIPLIERS: {
@@ -286,11 +372,7 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
286
372
  }
287
373
  }
288
374
  }
289
- }, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
290
- if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
291
- const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
292
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
293
- }, calculateElementCenter = rect => rect ? {
375
+ }, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
294
376
  x: rect.left + rect.width / 2,
295
377
  y: rect.top + rect.height / 2
296
378
  } : {
@@ -362,7 +444,16 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
362
444
  // Silently handle errors
363
445
  }
364
446
  return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
365
- }, lerp$1 = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
447
+ }, smoothstep = t => {
448
+ const clamped = Math.max(0, Math.min(1, t));
449
+ return clamped * clamped * (3 - 2 * clamped);
450
+ }, lerp$1 = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), calculateSpring = (current, target, velocity, stiffness = .1, damping = .8) => {
451
+ const newVelocity = (velocity + (target - current) * stiffness) * damping;
452
+ return {
453
+ value: current + newVelocity,
454
+ velocity: newVelocity
455
+ };
456
+ }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
366
457
  switch (mode) {
367
458
  case "standard":
368
459
  return displacementMap;
@@ -751,6 +842,9 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
751
842
  }), jsx("div", {
752
843
  ref: contentRef,
753
844
  className: ATOMIX_GLASS.CONTENT_CLASS,
845
+ style: {
846
+ transform: "var(--atomix-glass-child-parallax, none)"
847
+ },
754
848
  children: children
755
849
  }) ]
756
850
  })
@@ -785,9 +879,21 @@ class {
785
879
  y: this.lastEvent.clientY
786
880
  },
787
881
  // Notify all subscribers
788
- this.listeners.forEach((callback => {
882
+ this.listeners.forEach((listener => {
789
883
  try {
790
- callback(this.position);
884
+ // If the listener has an element, calculate distance-based attenuation
885
+ if (listener.element) {
886
+ const elementRect = listener.element.getBoundingClientRect(), elementCenter = {
887
+ x: elementRect.left + elementRect.width / 2,
888
+ y: elementRect.top + elementRect.height / 2
889
+ }, distance = this.calculateDistance(this.position, elementCenter), maxDistance = listener.maxDistance || 300, attenuation = Math.max(0, 1 - distance / maxDistance), attenuatedRelativePosition = {
890
+ x: (this.position.x - elementCenter.x) / elementRect.width * 100 * attenuation,
891
+ y: (this.position.y - elementCenter.y) / elementRect.height * 100 * attenuation
892
+ };
893
+ listener.callback(attenuatedRelativePosition);
894
+ } else
895
+ // Send original position for listeners without distance-based attenuation
896
+ listener.callback(this.position);
791
897
  } catch (error) {
792
898
  console.error("GlobalMouseTracker: Error in subscriber callback", error);
793
899
  }
@@ -798,10 +904,17 @@ class {
798
904
  /**
799
905
  * Subscribe to mouse position updates
800
906
  * @param callback Function to call when mouse position changes
907
+ * @param element Optional element for distance-based attenuation
908
+ * @param maxDistance Optional maximum distance for full effect
801
909
  * @returns Unsubscribe function
802
- */ subscribe(callback) {
910
+ */ subscribe(callback, element, maxDistance) {
911
+ const listener = {
912
+ callback: callback,
913
+ element: element,
914
+ maxDistance: maxDistance
915
+ };
803
916
  // Return unsubscribe function
804
- return this.listeners.add(callback),
917
+ return this.listeners.add(listener),
805
918
  // Start tracking if this is the first subscriber
806
919
  1 === this.listeners.size && this.startTracking(),
807
920
  // Immediately notify with current position
@@ -812,9 +925,13 @@ class {
812
925
  /**
813
926
  * Unsubscribe from mouse position updates
814
927
  */ unsubscribe(callback) {
815
- this.listeners.delete(callback),
928
+ // Find and remove the listener with the given callback
929
+ for (const listener of this.listeners) if (listener.callback === callback) {
930
+ this.listeners.delete(listener);
931
+ break;
932
+ }
816
933
  // Stop tracking if no more subscribers
817
- 0 === this.listeners.size && this.stopTracking();
934
+ 0 === this.listeners.size && this.stopTracking();
818
935
  }
819
936
  /**
820
937
  * Start tracking mouse movement
@@ -833,6 +950,12 @@ class {
833
950
  null !== this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null), this.lastEvent = null);
834
951
  }
835
952
  /**
953
+ * Calculate distance between two points
954
+ */ calculateDistance(point1, point2) {
955
+ const dx = point1.x - point2.x, dy = point1.y - point2.y;
956
+ return Math.sqrt(dx * dx + dy * dy);
957
+ }
958
+ /**
836
959
  * Get current mouse position (synchronous)
837
960
  */ getPosition() {
838
961
  return {
@@ -846,51 +969,26 @@ class {
846
969
  }
847
970
  }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
848
971
  if (!wrapperElement && !containerElement) return;
849
- const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, directionalScale: directionalScale, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, isFixedOrSticky: isFixedOrSticky = !1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
972
+ if (!validateGlassSize(params.glassSize)) return;
973
+ const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, elasticTranslation: elasticTranslation, elasticVelocity: elasticVelocity, mouseVelocity: mouseVelocity, directionalScale: directionalScale, scaleBase: scaleBase, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, isFixedOrSticky: isFixedOrSticky = !1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
850
974
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
851
975
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
852
976
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
853
977
  shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
854
978
  borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
855
979
  saturationBoost: baseOverLightConfig.saturationBoost
856
- };
857
- // Calculate mouse influence
858
- let computedDirectionalScale = directionalScale, elasticTranslation = {
980
+ }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, stretchMagnitude = ((pos1, pos2) => {
981
+ if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
982
+ const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
983
+ return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
984
+ })({
859
985
  x: 0,
860
986
  y: 0
861
- };
862
- // Calculate elastic translation and directional scale
863
- if (!effectiveWithoutEffects && wrapperElement) {
864
- const rect = wrapperElement.getBoundingClientRect(), center = calculateElementCenter(rect);
865
- // Mouse presence and edge distance logic
866
- if (globalMousePosition.x && globalMousePosition.y && validateGlassSize(glassSize)) {
867
- const deltaX = globalMousePosition.x - center.x, deltaY = globalMousePosition.y - center.y, edgeDistanceX = Math.max(0, Math.abs(deltaX) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(deltaY) - glassSize.height / 2), edgeDistance = calculateDistance({
868
- x: edgeDistanceX,
869
- y: edgeDistanceY
870
- }, {
871
- x: 0,
872
- y: 0
873
- }), rawT = edgeDistance > ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE, fadeInFactor = (t => {
874
- const clamped = Math.max(0, Math.min(1, t));
875
- return clamped * clamped * (3 - 2 * clamped);
876
- })(rawT);
877
- // Directional scale
878
- if (elasticTranslation = {
879
- x: deltaX * elasticity * .1 * fadeInFactor,
880
- y: deltaY * elasticity * .1 * fadeInFactor
881
- }, !isOverLight && edgeDistance <= ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE) {
882
- const centerDistance = calculateDistance(globalMousePosition, center);
883
- if (centerDistance > 0) {
884
- const normalizedX = deltaX / centerDistance, normalizedY = deltaY / centerDistance, stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * rawT, scaleX = 1 + Math.abs(normalizedX) * stretchIntensity * .3 - Math.abs(normalizedY) * stretchIntensity * .15, scaleY = 1 + Math.abs(normalizedY) * stretchIntensity * .3 - Math.abs(normalizedX) * stretchIntensity * .15, softScaleX = 1 - softClamp(Math.max(0, 1 - scaleX), .2), softScaleY = 1 - softClamp(Math.max(0, 1 - scaleY), .2);
885
- computedDirectionalScale = `scaleX(${Math.max(.85, softScaleX)}) scaleY(${Math.max(.85, softScaleY)})`;
886
- }
887
- }
888
- }
889
- }
890
- const transformStyle = effectiveWithoutEffects ? isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)" : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? "scale(0.96)" : computedDirectionalScale}`;
891
- // Update Wrapper Styles (glassVars)
892
- if (wrapperElement) {
893
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), borderOpacities = [ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH ], configBorderOpacity = overLightConfig.borderOpacity, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
987
+ }, elasticTranslation), tensionFactor = smoothstep(stretchMagnitude / 80), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
988
+ // Calculate mouse influence
989
+ // Update Wrapper Styles (glassVars)
990
+ if (wrapperElement) {
991
+ const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, velocityRotation = (mouseVelocity.x + elasticVelocity.x) * (GRADIENT.VELOCITY_ANGLE_MULTIPLIER || 2.5), borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER + velocityRotation, chromaticOffset = GRADIENT.CHROMATIC_OFFSET || 1.5, angleR = borderGradientAngle - chromaticOffset, angleB = borderGradientAngle + chromaticOffset, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), tensionGlow = 1 + .5 * tensionFactor, borderOpacities = [ (GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow ], configBorderOpacity = overLightConfig.borderOpacity, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
894
992
  hover1: {
895
993
  x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
896
994
  y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
@@ -913,10 +1011,16 @@ class {
913
1011
  base: isOverLight ? overLightConfig.opacity : 0,
914
1012
  over: isOverLight ? 1.1 * overLightConfig.opacity : 0
915
1013
  }, style = wrapperElement.style;
916
- style.setProperty("--atomix-glass-transform", transformStyle || "none"),
917
- // Gradients
918
- style.setProperty("--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%)`),
919
- style.setProperty("--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%)`),
1014
+ style.setProperty("--atomix-glass-transform", transformStyle || "none");
1015
+ // Parallax for content (liquid refraction feel)
1016
+ const parallaxFactor = .38 + .12 * tensionFactor;
1017
+ style.setProperty("--atomix-glass-child-parallax", `translate(${elasticTranslation.x * -parallaxFactor}px, ${elasticTranslation.y * -parallaxFactor}px)`),
1018
+ style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString()),
1019
+ // ── Chromatic Rim Lighting ──────────────────────────────────────
1020
+ // Layer 1: Core White/Blue highlight
1021
+ style.setProperty("--atomix-glass-border-gradient-1", `linear-gradient(${angleB}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
1022
+ // Layer 2: Subtle Red/Warm highlight (offset angle)
1023
+ style.setProperty("--atomix-glass-border-gradient-2", `linear-gradient(${angleR}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
920
1024
  // Hover gradients
921
1025
  style.setProperty("--atomix-glass-hover-1-gradient", isOverLight ? `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}%)` : `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}%)`),
922
1026
  style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `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}%)` : `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}%)`),
@@ -944,7 +1048,7 @@ class {
944
1048
  flowBlur: blurAmount * FLOW_BLUR_MULTIPLIER
945
1049
  };
946
1050
  if (withLiquidBlur && rect) {
947
- const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = blurAmount * MAX_BLUR_RELATIVE, baseBlur = Math.min(maxBlur, blurAmount + mouseInfluence * blurAmount * MOUSE_INFLUENCE_BLUR_FACTOR), edgeIntensity = mouseInfluence * EDGE_INTENSITY_MOUSE_FACTOR, edgeBlur = Math.min(maxBlur, baseBlur * (.8 + .4 * edgeIntensity)), centerIntensity = mouseInfluence * CENTER_INTENSITY_MOUSE_FACTOR, centerBlur = Math.min(maxBlur, baseBlur * (.3 + .3 * centerIntensity)), flowBlur = Math.min(maxBlur, baseBlur * FLOW_BLUR_MULTIPLIER);
1051
+ const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = blurAmount * MAX_BLUR_RELATIVE, baseBlur = softClamp(blurAmount + mouseInfluence * blurAmount * MOUSE_INFLUENCE_BLUR_FACTOR, maxBlur), edgeBlur = softClamp(baseBlur * (.8 + mouseInfluence * EDGE_INTENSITY_MOUSE_FACTOR * .4), maxBlur), centerBlur = softClamp(baseBlur * (.3 + mouseInfluence * CENTER_INTENSITY_MOUSE_FACTOR * .3), maxBlur), flowBlur = softClamp(baseBlur * FLOW_BLUR_MULTIPLIER, maxBlur);
948
1052
  liquidBlur = {
949
1053
  baseBlur: clampBlur(baseBlur),
950
1054
  edgeBlur: clampBlur(edgeBlur),
@@ -953,9 +1057,10 @@ class {
953
1057
  };
954
1058
  }
955
1059
  // Backdrop filter
956
- let backdropFilterString = `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`;
957
- const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), area = rect ? rect.width * rect.height : 0;
958
- backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
1060
+ const dynamicSaturation = saturation + 40 * tensionFactor + 15 * (liquidBlur.baseBlur || 0);
1061
+ let backdropFilterString = "";
1062
+ const area = rect ? rect.width * rect.height : 0;
1063
+ backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})`;
959
1064
  // Container variables
960
1065
  const style = containerElement.style;
961
1066
  style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
@@ -1117,7 +1222,8 @@ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new Weak
1117
1222
  * Composable hook for AtomixGlass component logic
1118
1223
  * Manages all state, calculations, and event handlers
1119
1224
  */
1120
- function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, withTimeAnimation:
1225
+ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, priority: priority = 1, withTimeAnimation:
1226
+ // Default priority
1121
1227
  // Phase 1: Animation System Props
1122
1228
  withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED, withMultiLayerDistortion: withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION, distortionOctaves: distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES, distortionLacunarity: distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY, distortionGain: distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN, distortionQuality: distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY}) {
1123
1229
  // State
@@ -1133,7 +1239,24 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1133
1239
  }), targetGlobalMousePositionRef = useRef({
1134
1240
  x: 0,
1135
1241
  y: 0
1136
- }), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), animationFrameIdRef = useRef(null), animationStartTimeRef = useRef(0), elapsedTimeRef = useRef(0), shaderTimeRef = useRef(0), fbmConfig = useMemo((() => {
1242
+ }), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), elasticTranslationRef = useRef({
1243
+ x: 0,
1244
+ y: 0
1245
+ }), elasticVelocityRef = useRef({
1246
+ x: 0,
1247
+ y: 0
1248
+ }), directionalScaleRef = useRef({
1249
+ x: 1,
1250
+ y: 1
1251
+ }), scaleVelocityRef = useRef({
1252
+ x: 0,
1253
+ y: 0
1254
+ });
1255
+ useRef(0);
1256
+ const mouseVelocityRef = useRef({
1257
+ x: 0,
1258
+ y: 0
1259
+ }), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), animationFrameIdRef = useRef(null), animationStartTimeRef = useRef(0), elapsedTimeRef = useRef(0), shaderTimeRef = useRef(0), fbmConfig = useMemo((() => {
1137
1260
  // If quality preset is provided, use it as base
1138
1261
  const preset = (quality = distortionQuality, ATOMIX_GLASS.CONSTANTS.DISTORTION_QUALITY_PRESETS[quality]);
1139
1262
  // Override with custom values if provided
@@ -1418,57 +1541,85 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1418
1541
  return "undefined" == typeof process || process.env, finalConfig;
1419
1542
  }
1420
1543
  return "undefined" == typeof process || process.env, baseConfig;
1421
- }), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null), stopLerpLoop = useCallback((() => {
1544
+ }), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.99)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null), stopLerpLoop = useCallback((() => {
1422
1545
  lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
1423
1546
  lerpRafRef.current = null);
1424
1547
  }), []), startLerpLoop = useCallback((() => {
1425
1548
  if (lerpActiveRef.current) return;
1426
- lerpActiveRef.current = !0;
1427
- const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
1549
+ lerpActiveRef.current = !0, CONSTANTS.LERP_FACTOR;
1550
+ // 0.08 lower = more viscous
1551
+ const tick = () => {
1428
1552
  if (!lerpActiveRef.current) return;
1429
1553
  if (!glassRef.current) return void (lerpActiveRef.current = !1);
1430
- const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
1431
- // If we're close enough, snap and park
1432
- if (Math.abs(dx) < .01 && Math.abs(dy) < .01) return internalMouseOffsetRef.current = {
1433
- ...tgt
1434
- }, internalGlobalMousePositionRef.current = {
1435
- ...targetGlobalMousePositionRef.current
1436
- },
1437
- // Final update and stop
1438
- updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1439
- mouseOffset: internalMouseOffsetRef.current,
1440
- globalMousePosition: internalGlobalMousePositionRef.current,
1441
- glassSize: glassSize,
1442
- isHovered: isHovered,
1443
- isActive: isActive,
1444
- isOverLight: overLightConfig.isOverLight,
1445
- baseOverLightConfig: overLightConfig,
1446
- effectiveBorderRadius: effectiveBorderRadius,
1447
- effectiveWithoutEffects: effectiveWithoutEffects,
1448
- effectiveReducedMotion: effectiveReducedMotion,
1449
- elasticity: elasticity,
1450
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1451
- onClick: onClick,
1452
- withLiquidBlur: withLiquidBlur,
1453
- blurAmount: blurAmount,
1454
- saturation: saturation,
1455
- padding: padding,
1456
- isFixedOrSticky: isFixedOrSticky
1457
- }), void stopLerpLoop();
1458
- // Smooth step
1459
- internalMouseOffsetRef.current = {
1460
- x: lerp$1(cur.x, tgt.x, LERP_T),
1461
- y: lerp$1(cur.y, tgt.y, LERP_T)
1554
+ const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, springX = calculateSpring(cur.x, tgt.x, mouseVelocityRef.current.x, CONSTANTS.LERP_FACTOR, CONSTANTS.ELASTICITY_DAMPING), springY = calculateSpring(cur.y, tgt.y, mouseVelocityRef.current.y, CONSTANTS.LERP_FACTOR, CONSTANTS.ELASTICITY_DAMPING);
1555
+ internalMouseOffsetRef.current = {
1556
+ x: springX.value,
1557
+ y: springY.value
1558
+ }, mouseVelocityRef.current = {
1559
+ x: springX.velocity,
1560
+ y: springY.velocity
1462
1561
  };
1463
1562
  const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
1464
1563
  internalGlobalMousePositionRef.current = {
1465
- x: lerp$1(curG.x, tgtG.x, LERP_T),
1466
- y: lerp$1(curG.y, tgtG.y, LERP_T)
1564
+ x: lerp$1(curG.x, tgtG.x, CONSTANTS.LERP_FACTOR),
1565
+ y: lerp$1(curG.y, tgtG.y, CONSTANTS.LERP_FACTOR)
1566
+ };
1567
+ // ── Calculate Elastic Physics ─────────────────────────────────────
1568
+ let targetElasticTranslation = {
1569
+ x: 0,
1570
+ y: 0
1571
+ }, targetScale = {
1572
+ x: 1,
1573
+ y: 1
1574
+ };
1575
+ if (!effectiveWithoutEffects && glassRef.current) {
1576
+ const rect = cachedRectRef.current || glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), globalPos = internalGlobalMousePositionRef.current;
1577
+ if (globalPos.x && globalPos.y) {
1578
+ const deltaX = globalPos.x - center.x, deltaY = globalPos.y - center.y, edgeDistanceX = Math.max(0, Math.abs(deltaX) - rect.width / 2), edgeDistanceY = Math.max(0, Math.abs(deltaY) - rect.height / 2), edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY), activationZone = CONSTANTS.ACTIVATION_ZONE, rawT = edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone, fadeInFactor = smoothstep(rawT);
1579
+ // Scale stretch logic (liquid surface tension)
1580
+ if (targetElasticTranslation = {
1581
+ x: deltaX * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor,
1582
+ y: deltaY * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor
1583
+ }, edgeDistance <= activationZone) {
1584
+ const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
1585
+ if (centerDistance > 0) {
1586
+ const nx = deltaX / centerDistance, ny = deltaY / centerDistance, stretchIntensity = Math.min(centerDistance / 350, 1) * elasticity * rawT, mag = 1 + .06 * stretchIntensity;
1587
+ targetScale = {
1588
+ x: mag + Math.abs(nx) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO,
1589
+ y: mag + Math.abs(ny) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO
1590
+ },
1591
+ // Maintain liquid volume by compressing the perpendicular axis
1592
+ targetScale.x -= Math.abs(ny) * stretchIntensity * .15, targetScale.y -= Math.abs(nx) * stretchIntensity * .15;
1593
+ }
1594
+ }
1595
+ }
1596
+ }
1597
+ // Integrate Elastic Translation Spring
1598
+ const springTX = calculateSpring(elasticTranslationRef.current.x, targetElasticTranslation.x, elasticVelocityRef.current.x, CONSTANTS.ELASTICITY_STIFFNESS, CONSTANTS.ELASTICITY_DAMPING), springTY = calculateSpring(elasticTranslationRef.current.y, targetElasticTranslation.y, elasticVelocityRef.current.y, CONSTANTS.ELASTICITY_STIFFNESS, CONSTANTS.ELASTICITY_DAMPING);
1599
+ elasticTranslationRef.current = {
1600
+ x: springTX.value,
1601
+ y: springTY.value
1602
+ }, elasticVelocityRef.current = {
1603
+ x: springTX.velocity,
1604
+ y: springTY.velocity
1605
+ };
1606
+ // Integrate Scale Spring
1607
+ const springSX = calculateSpring(directionalScaleRef.current.x, targetScale.x, scaleVelocityRef.current.x, CONSTANTS.ELASTICITY_STIFFNESS, CONSTANTS.ELASTICITY_DAMPING), springSY = calculateSpring(directionalScaleRef.current.y, targetScale.y, scaleVelocityRef.current.y, CONSTANTS.ELASTICITY_STIFFNESS, CONSTANTS.ELASTICITY_DAMPING);
1608
+ directionalScaleRef.current = {
1609
+ x: springSX.value,
1610
+ y: springSY.value
1611
+ }, scaleVelocityRef.current = {
1612
+ x: springSX.velocity,
1613
+ y: springSY.velocity
1467
1614
  },
1468
1615
  // Imperative style update
1469
1616
  updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1470
1617
  mouseOffset: internalMouseOffsetRef.current,
1471
1618
  globalMousePosition: internalGlobalMousePositionRef.current,
1619
+ elasticTranslation: elasticTranslationRef.current,
1620
+ elasticVelocity: elasticVelocityRef.current,
1621
+ mouseVelocity: mouseVelocityRef.current,
1622
+ directionalScale: directionalScaleRef.current,
1472
1623
  glassSize: glassSize,
1473
1624
  isHovered: isHovered,
1474
1625
  isActive: isActive,
@@ -1478,17 +1629,16 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1478
1629
  effectiveWithoutEffects: effectiveWithoutEffects,
1479
1630
  effectiveReducedMotion: effectiveReducedMotion,
1480
1631
  elasticity: elasticity,
1481
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1632
+ scaleBase: isActive && Boolean(onClick) ? .99 : 1,
1482
1633
  onClick: onClick,
1483
1634
  withLiquidBlur: withLiquidBlur,
1484
1635
  blurAmount: blurAmount,
1485
1636
  saturation: saturation,
1486
1637
  padding: padding,
1487
1638
  isFixedOrSticky: isFixedOrSticky
1488
- }), lerpRafRef.current = requestAnimationFrame(tick);
1639
+ }), Math.abs(mouseVelocityRef.current.x) < .001 && Math.abs(mouseVelocityRef.current.y) < .001 && Math.abs(elasticVelocityRef.current.x) < .001 && Math.abs(elasticVelocityRef.current.y) < .001 && Math.abs(scaleVelocityRef.current.x) < .001 && Math.abs(scaleVelocityRef.current.y) < .001 && Math.abs(internalMouseOffsetRef.current.x - targetMouseOffsetRef.current.x) < .001 && Math.abs(internalMouseOffsetRef.current.y - targetMouseOffsetRef.current.y) < .001 ? stopLerpLoop() : lerpRafRef.current = requestAnimationFrame(tick);
1489
1640
  };
1490
- // 0.08 – lower = more viscous
1491
- lerpRafRef.current = requestAnimationFrame(tick);
1641
+ lerpRafRef.current = requestAnimationFrame(tick);
1492
1642
  }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
1493
1643
  if (externalGlobalMousePosition && externalMouseOffset) return;
1494
1644
  if (effectiveWithoutEffects) return;
@@ -1514,7 +1664,8 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1514
1664
  useEffect((() => {
1515
1665
  if (externalGlobalMousePosition && externalMouseOffset) return;
1516
1666
  if (effectiveWithoutEffects) return;
1517
- const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
1667
+ const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition, glassRef.current || void 0, 300);
1668
+ // 300px max distance for full effect
1518
1669
  // Initial start
1519
1670
  startLerpLoop();
1520
1671
  const container = mouseContainer?.current || glassRef.current;
@@ -1534,6 +1685,11 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1534
1685
  updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1535
1686
  mouseOffset: externalMouseOffset || internalMouseOffsetRef.current,
1536
1687
  globalMousePosition: externalGlobalMousePosition || internalGlobalMousePositionRef.current,
1688
+ elasticTranslation: elasticTranslationRef.current,
1689
+ elasticVelocity: elasticVelocityRef.current,
1690
+ mouseVelocity: mouseVelocityRef.current,
1691
+ directionalScale: directionalScaleRef.current,
1692
+ scaleBase: isActive && Boolean(onClick) ? .96 : 1,
1537
1693
  glassSize: glassSize,
1538
1694
  isHovered: isHovered,
1539
1695
  isActive: isActive,
@@ -1543,7 +1699,6 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1543
1699
  effectiveWithoutEffects: effectiveWithoutEffects,
1544
1700
  effectiveReducedMotion: effectiveReducedMotion,
1545
1701
  elasticity: elasticity,
1546
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1547
1702
  onClick: onClick,
1548
1703
  withLiquidBlur: withLiquidBlur,
1549
1704
  blurAmount: blurAmount,
@@ -2811,7 +2966,7 @@ const PERFORMANCE_PRESET = {
2811
2966
  "aria-label": ariaLabel,
2812
2967
  "aria-describedby": ariaDescribedBy,
2813
2968
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
2814
- "aria-pressed": void 0,
2969
+ "aria-pressed": onClick ? isActive : void 0,
2815
2970
  onKeyDown: onClick ? handleKeyDown : void 0,
2816
2971
  children: [ jsx(AtomixGlassContainer, {
2817
2972
  ref: glassRef,
@@ -4416,10 +4571,10 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4416
4571
  children: renderSlot(slots?.spinner, {
4417
4572
  className: ThemeNaming.bemClass("btn", "spinner"),
4418
4573
  size: spinnerSize,
4419
- variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
4574
+ variant: "link" === variant || "ghost" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "danger" : variant
4420
4575
  }, jsx(Spinner, {
4421
4576
  size: spinnerSize,
4422
- variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
4577
+ variant: "link" === variant || "ghost" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "danger" : variant
4423
4578
  }))
4424
4579
  }), iconElement && !loading && jsx("span", {
4425
4580
  className: ThemeNaming.bemClass("btn", "icon"),
@@ -4508,8 +4663,7 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4508
4663
  const defaultGlassProps = {
4509
4664
  displacementScale: 20,
4510
4665
  blurAmount: 0,
4511
- saturation: 200,
4512
- elasticity: 0
4666
+ saturation: 200
4513
4667
  }, glassProps = !0 === glass ? defaultGlassProps : {
4514
4668
  ...defaultGlassProps,
4515
4669
  ...glass