@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/forms.js CHANGED
@@ -47,12 +47,23 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
47
47
  },
48
48
  DEFAULTS: {
49
49
  DISPLACEMENT_SCALE: 70,
50
- BLUR_AMOUNT: 0,
51
- SATURATION: 140,
52
- ABERRATION_INTENSITY: 2,
50
+ get BLUR_AMOUNT() {
51
+ return .15 * this.DISPLACEMENT_SCALE;
52
+ // Dynamically computed based on displacement
53
+ },
54
+ get SATURATION() {
55
+ return 100 + .5 * this.DISPLACEMENT_SCALE;
56
+ // Saturate relative to intensity
57
+ },
58
+ get ABERRATION_INTENSITY() {
59
+ return .03 * this.DISPLACEMENT_SCALE;
60
+ // Scale aberration with displacement
61
+ },
53
62
  ELASTICITY: .15,
54
- CORNER_RADIUS: 20,
55
- // Default border-radius matching design system
63
+ get CORNER_RADIUS() {
64
+ return 16;
65
+ // Use 16 to match SCSS design system (was 20)
66
+ },
56
67
  PADDING: "0",
57
68
  MODE: "standard",
58
69
  OVER_LIGHT: !1,
@@ -74,6 +85,15 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
74
85
  MIN_BLUR: .1,
75
86
  MOUSE_INFLUENCE_DIVISOR: 100,
76
87
  EDGE_FADE_PIXELS: 2,
88
+ // Elasticity physics constants
89
+ ELASTICITY_TRANSLATION_FACTOR: .1,
90
+ ELASTICITY_DISTANCE_THRESHOLD: 200,
91
+ ELASTICITY_COMPRESSION_FACTOR: .3,
92
+ ELASTICITY_STIFFNESS: .1,
93
+ ELASTICITY_DAMPING: .76,
94
+ ELASTICITY_VELOCITY_FACTOR: .65,
95
+ ELASTICITY_STRETCH_RATIO: .45,
96
+ ELASTICITY_MAGNIFICATION_BASE: 1.02,
77
97
  // Note: This default must match the SCSS variable --atomix-radius-md
78
98
  // @see src/styles/01-settings/_settings.global.scss
79
99
  DEFAULT_CORNER_RADIUS: 16,
@@ -90,84 +110,126 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
90
110
  // Base angle for border gradients (degrees)
91
111
  ANGLE_MULTIPLIER: 1.2,
92
112
  // Multiplier for mouse influence on angle
113
+ VELOCITY_ANGLE_MULTIPLIER: 2.5,
114
+ // How much velocity affects gradient rotation
115
+ CHROMATIC_OFFSET: 1.5,
116
+ // Degree offset for chromatic rim layers
93
117
  BORDER_STOP_1: {
94
118
  MIN: 10,
95
119
  // Minimum percentage for border stop 1
96
120
  BASE: 33,
97
121
  // Base percentage for border stop 1
98
- MULTIPLIER: .3
122
+ get MULTIPLIER() {
123
+ return .009 * this.BASE;
124
+ }
99
125
  },
100
126
  BORDER_STOP_2: {
101
127
  MAX: 90,
102
128
  // Maximum percentage for border stop 2
103
129
  BASE: 66,
104
130
  // Base percentage for border stop 2
105
- MULTIPLIER: .4
131
+ get MULTIPLIER() {
132
+ return .006 * this.BASE;
133
+ }
106
134
  },
107
135
  BORDER_OPACITY: {
108
136
  BASE_1: .12,
109
137
  // Base opacity for border gradient 1
110
- BASE_2: .4,
138
+ get BASE_2() {
139
+ return 3.33 * this.BASE_1;
140
+ },
111
141
  // Base opacity for border gradient 2
112
- BASE_3: .32,
142
+ get BASE_3() {
143
+ return 2.66 * this.BASE_1;
144
+ },
113
145
  // Base opacity for border gradient 3
114
- BASE_4: .6,
146
+ get BASE_4() {
147
+ return 5 * this.BASE_1;
148
+ },
115
149
  // Base opacity for border gradient 4
116
- MULTIPLIER_LOW: .008,
150
+ get MULTIPLIER_LOW() {
151
+ return .066 * this.BASE_1;
152
+ },
117
153
  // Low multiplier for mouse influence on opacity
118
- MULTIPLIER_HIGH: .012
154
+ get MULTIPLIER_HIGH() {
155
+ return .1 * this.BASE_1;
156
+ }
119
157
  },
120
158
  CENTER_POSITION: 50,
121
159
  // Center position percentage (50%)
122
160
  HOVER_POSITION: {
123
161
  DIVISOR_1: 2,
124
162
  // Divisor for hover 1 position calculation
125
- DIVISOR_2: 1.5,
163
+ get DIVISOR_2() {
164
+ return .75 * this.DIVISOR_1;
165
+ },
126
166
  // Divisor for hover 2 position calculation
127
- MULTIPLIER_3: 1
167
+ get MULTIPLIER_3() {
168
+ return .5 * this.DIVISOR_1;
169
+ }
128
170
  },
129
- BASE_LAYER_MULTIPLIER: .5
171
+ get BASE_LAYER_MULTIPLIER() {
172
+ return .5;
173
+ }
130
174
  },
131
175
  // Gradient opacity values for hover effects
132
176
  GRADIENT_OPACITY: {
133
177
  HOVER_1: {
134
178
  BLACK_START: .3,
135
179
  // Start opacity for black hover 1
136
- BLACK_MID: .1,
180
+ get BLACK_MID() {
181
+ return this.BLACK_START / 3;
182
+ },
137
183
  // Mid opacity for black hover 1
138
184
  BLACK_STOP: 30,
139
185
  // Stop percentage for black hover 1
140
- BLACK_END: 60,
186
+ get BLACK_END() {
187
+ return 2 * this.BLACK_STOP;
188
+ },
141
189
  // End percentage for black hover 1
142
190
  WHITE_START: .5,
143
191
  // Start opacity for white hover 1
144
- WHITE_STOP: 50
192
+ get WHITE_STOP() {
193
+ return this.BLACK_END - 10;
194
+ }
145
195
  },
146
196
  HOVER_2: {
147
197
  BLACK_START: .4,
148
198
  // Start opacity for black hover 2
149
- BLACK_MID: .15,
199
+ get BLACK_MID() {
200
+ return .375 * this.BLACK_START;
201
+ },
150
202
  // Mid opacity for black hover 2
151
203
  BLACK_STOP: 40,
152
204
  // Stop percentage for black hover 2
153
- BLACK_END: 80,
205
+ get BLACK_END() {
206
+ return 2 * this.BLACK_STOP;
207
+ },
154
208
  // End percentage for black hover 2
155
209
  WHITE_START: 1,
156
210
  // Start opacity for white hover 2
157
- WHITE_STOP: 80
211
+ get WHITE_STOP() {
212
+ return this.BLACK_END;
213
+ }
158
214
  },
159
215
  HOVER_3: {
160
216
  BLACK_START: .5,
161
217
  // Start opacity for black hover 3
162
- BLACK_MID: .2,
218
+ get BLACK_MID() {
219
+ return .4 * this.BLACK_START;
220
+ },
163
221
  // Mid opacity for black hover 3
164
222
  BLACK_STOP: 50,
165
223
  // Stop percentage for black hover 3
166
- BLACK_END: 100,
224
+ get BLACK_END() {
225
+ return 2 * this.BLACK_STOP;
226
+ },
167
227
  // End percentage for black hover 3
168
228
  WHITE_START: 1,
169
229
  // Start opacity for white hover 3
170
- WHITE_STOP: 100
230
+ get WHITE_STOP() {
231
+ return this.BLACK_END;
232
+ }
171
233
  }
172
234
  },
173
235
  // Base and overlay gradient constants
@@ -176,34 +238,54 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
176
238
  // Gradient angle in degrees
177
239
  BLACK_START_BASE: .15,
178
240
  // Base start opacity for black
179
- BLACK_START_MULTIPLIER: .003,
241
+ get BLACK_START_MULTIPLIER() {
242
+ return .02 * this.BLACK_START_BASE;
243
+ },
180
244
  // Multiplier for mouse X influence on start
181
245
  BLACK_MID_BASE: .1,
182
246
  // Base mid opacity for black
183
- BLACK_MID_MULTIPLIER: .002,
247
+ get BLACK_MID_MULTIPLIER() {
248
+ return .02 * this.BLACK_MID_BASE;
249
+ },
184
250
  // Multiplier for mouse Y influence on mid
185
251
  BLACK_MID_STOP: 50,
186
252
  // Mid stop percentage
187
- BLACK_END_BASE: .18,
253
+ get BLACK_END_BASE() {
254
+ return 1.2 * this.BLACK_START_BASE;
255
+ },
188
256
  // Base end opacity for black
189
- BLACK_END_MULTIPLIER: .004,
257
+ get BLACK_END_MULTIPLIER() {
258
+ return .022 * this.BLACK_END_BASE;
259
+ },
190
260
  // Multiplier for mouse X influence on end
191
- WHITE_OPACITY: .1
261
+ get WHITE_OPACITY() {
262
+ return .666 * this.BLACK_START_BASE;
263
+ }
192
264
  },
193
265
  OVERLAY_GRADIENT: {
194
266
  BLACK_START_BASE: .12,
195
267
  // Base start opacity for black overlay
196
- BLACK_START_MULTIPLIER: .003,
268
+ get BLACK_START_MULTIPLIER() {
269
+ return .025 * this.BLACK_START_BASE;
270
+ },
197
271
  // Multiplier for mouse X influence on start
198
- BLACK_MID: .06,
272
+ get BLACK_MID() {
273
+ return .5 * this.BLACK_START_BASE;
274
+ },
199
275
  // Mid opacity for black overlay
200
276
  BLACK_MID_STOP: 40,
201
277
  // Mid stop percentage
202
- BLACK_END_BASE: .15,
278
+ get BLACK_END_BASE() {
279
+ return 1.25 * this.BLACK_START_BASE;
280
+ },
203
281
  // Base end opacity for black overlay
204
- BLACK_END_MULTIPLIER: .003,
282
+ get BLACK_END_MULTIPLIER() {
283
+ return .02 * this.BLACK_END_BASE;
284
+ },
205
285
  // Multiplier for mouse Y influence on end
206
- WHITE_OPACITY: .05
286
+ get WHITE_OPACITY() {
287
+ return .416 * this.BLACK_START_BASE;
288
+ }
207
289
  },
208
290
  // Overlay highlight constants
209
291
  OVERLAY_HIGHLIGHT: {
@@ -213,9 +295,13 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
213
295
  // Y position percentage
214
296
  WHITE_OPACITY: .4,
215
297
  // White opacity in gradient
216
- STOP: 60,
298
+ get STOP() {
299
+ return 150 * this.WHITE_OPACITY;
300
+ },
217
301
  // Stop percentage
218
- OPACITY_MULTIPLIER: .7
302
+ get OPACITY_MULTIPLIER() {
303
+ return 1.75 * this.WHITE_OPACITY;
304
+ }
219
305
  },
220
306
  // Displacement and aberration multipliers
221
307
  MULTIPLIERS: {
@@ -268,11 +354,7 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
268
354
  }
269
355
  }
270
356
  }
271
- }, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
272
- if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
273
- const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
274
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
275
- }, calculateElementCenter = rect => rect ? {
357
+ }, {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
276
358
  x: rect.left + rect.width / 2,
277
359
  y: rect.top + rect.height / 2
278
360
  } : {
@@ -344,7 +426,16 @@ import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, u
344
426
  // Silently handle errors
345
427
  }
346
428
  return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
347
- }, 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) => {
429
+ }, smoothstep = t => {
430
+ const clamped = Math.max(0, Math.min(1, t));
431
+ return clamped * clamped * (3 - 2 * clamped);
432
+ }, 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) => {
433
+ const newVelocity = (velocity + (target - current) * stiffness) * damping;
434
+ return {
435
+ value: current + newVelocity,
436
+ velocity: newVelocity
437
+ };
438
+ }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
348
439
  switch (mode) {
349
440
  case "standard":
350
441
  return displacementMap;
@@ -735,6 +826,9 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
735
826
  }), jsx("div", {
736
827
  ref: contentRef,
737
828
  className: ATOMIX_GLASS.CONTENT_CLASS,
829
+ style: {
830
+ transform: "var(--atomix-glass-child-parallax, none)"
831
+ },
738
832
  children: children
739
833
  }) ]
740
834
  })
@@ -769,9 +863,21 @@ class {
769
863
  y: this.lastEvent.clientY
770
864
  },
771
865
  // Notify all subscribers
772
- this.listeners.forEach((callback => {
866
+ this.listeners.forEach((listener => {
773
867
  try {
774
- callback(this.position);
868
+ // If the listener has an element, calculate distance-based attenuation
869
+ if (listener.element) {
870
+ const elementRect = listener.element.getBoundingClientRect(), elementCenter = {
871
+ x: elementRect.left + elementRect.width / 2,
872
+ y: elementRect.top + elementRect.height / 2
873
+ }, distance = this.calculateDistance(this.position, elementCenter), maxDistance = listener.maxDistance || 300, attenuation = Math.max(0, 1 - distance / maxDistance), attenuatedRelativePosition = {
874
+ x: (this.position.x - elementCenter.x) / elementRect.width * 100 * attenuation,
875
+ y: (this.position.y - elementCenter.y) / elementRect.height * 100 * attenuation
876
+ };
877
+ listener.callback(attenuatedRelativePosition);
878
+ } else
879
+ // Send original position for listeners without distance-based attenuation
880
+ listener.callback(this.position);
775
881
  } catch (error) {
776
882
  console.error("GlobalMouseTracker: Error in subscriber callback", error);
777
883
  }
@@ -782,10 +888,17 @@ class {
782
888
  /**
783
889
  * Subscribe to mouse position updates
784
890
  * @param callback Function to call when mouse position changes
891
+ * @param element Optional element for distance-based attenuation
892
+ * @param maxDistance Optional maximum distance for full effect
785
893
  * @returns Unsubscribe function
786
- */ subscribe(callback) {
894
+ */ subscribe(callback, element, maxDistance) {
895
+ const listener = {
896
+ callback: callback,
897
+ element: element,
898
+ maxDistance: maxDistance
899
+ };
787
900
  // Return unsubscribe function
788
- return this.listeners.add(callback),
901
+ return this.listeners.add(listener),
789
902
  // Start tracking if this is the first subscriber
790
903
  1 === this.listeners.size && this.startTracking(),
791
904
  // Immediately notify with current position
@@ -796,9 +909,13 @@ class {
796
909
  /**
797
910
  * Unsubscribe from mouse position updates
798
911
  */ unsubscribe(callback) {
799
- this.listeners.delete(callback),
912
+ // Find and remove the listener with the given callback
913
+ for (const listener of this.listeners) if (listener.callback === callback) {
914
+ this.listeners.delete(listener);
915
+ break;
916
+ }
800
917
  // Stop tracking if no more subscribers
801
- 0 === this.listeners.size && this.stopTracking();
918
+ 0 === this.listeners.size && this.stopTracking();
802
919
  }
803
920
  /**
804
921
  * Start tracking mouse movement
@@ -817,6 +934,12 @@ class {
817
934
  null !== this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null), this.lastEvent = null);
818
935
  }
819
936
  /**
937
+ * Calculate distance between two points
938
+ */ calculateDistance(point1, point2) {
939
+ const dx = point1.x - point2.x, dy = point1.y - point2.y;
940
+ return Math.sqrt(dx * dx + dy * dy);
941
+ }
942
+ /**
820
943
  * Get current mouse position (synchronous)
821
944
  */ getPosition() {
822
945
  return {
@@ -830,51 +953,26 @@ class {
830
953
  }
831
954
  }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
832
955
  if (!wrapperElement && !containerElement) return;
833
- 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 = {
956
+ if (!validateGlassSize(params.glassSize)) return;
957
+ 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 = {
834
958
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
835
959
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
836
960
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
837
961
  shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
838
962
  borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
839
963
  saturationBoost: baseOverLightConfig.saturationBoost
840
- };
841
- // Calculate mouse influence
842
- let computedDirectionalScale = directionalScale, elasticTranslation = {
964
+ }, 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) => {
965
+ if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
966
+ const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
967
+ return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
968
+ })({
843
969
  x: 0,
844
970
  y: 0
845
- };
846
- // Calculate elastic translation and directional scale
847
- if (!effectiveWithoutEffects && wrapperElement) {
848
- const rect = wrapperElement.getBoundingClientRect(), center = calculateElementCenter(rect);
849
- // Mouse presence and edge distance logic
850
- if (globalMousePosition.x && globalMousePosition.y && validateGlassSize(glassSize)) {
851
- 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({
852
- x: edgeDistanceX,
853
- y: edgeDistanceY
854
- }, {
855
- x: 0,
856
- y: 0
857
- }), rawT = edgeDistance > ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE, fadeInFactor = (t => {
858
- const clamped = Math.max(0, Math.min(1, t));
859
- return clamped * clamped * (3 - 2 * clamped);
860
- })(rawT);
861
- // Directional scale
862
- if (elasticTranslation = {
863
- x: deltaX * elasticity * .1 * fadeInFactor,
864
- y: deltaY * elasticity * .1 * fadeInFactor
865
- }, !isOverLight && edgeDistance <= ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE) {
866
- const centerDistance = calculateDistance(globalMousePosition, center);
867
- if (centerDistance > 0) {
868
- 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);
869
- computedDirectionalScale = `scaleX(${Math.max(.85, softScaleX)}) scaleY(${Math.max(.85, softScaleY)})`;
870
- }
871
- }
872
- }
873
- }
874
- 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}`;
875
- // Update Wrapper Styles (glassVars)
876
- if (wrapperElement) {
877
- 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 = {
971
+ }, elasticTranslation), tensionFactor = smoothstep(stretchMagnitude / 80), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
972
+ // Calculate mouse influence
973
+ // Update Wrapper Styles (glassVars)
974
+ if (wrapperElement) {
975
+ 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 = {
878
976
  hover1: {
879
977
  x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
880
978
  y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
@@ -897,10 +995,16 @@ class {
897
995
  base: isOverLight ? overLightConfig.opacity : 0,
898
996
  over: isOverLight ? 1.1 * overLightConfig.opacity : 0
899
997
  }, style = wrapperElement.style;
900
- style.setProperty("--atomix-glass-transform", transformStyle || "none"),
901
- // Gradients
902
- 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%)`),
903
- 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%)`),
998
+ style.setProperty("--atomix-glass-transform", transformStyle || "none");
999
+ // Parallax for content (liquid refraction feel)
1000
+ const parallaxFactor = .38 + .12 * tensionFactor;
1001
+ style.setProperty("--atomix-glass-child-parallax", `translate(${elasticTranslation.x * -parallaxFactor}px, ${elasticTranslation.y * -parallaxFactor}px)`),
1002
+ style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString()),
1003
+ // ── Chromatic Rim Lighting ──────────────────────────────────────
1004
+ // Layer 1: Core White/Blue highlight
1005
+ 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%)`),
1006
+ // Layer 2: Subtle Red/Warm highlight (offset angle)
1007
+ 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%)`),
904
1008
  // Hover gradients
905
1009
  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}%)`),
906
1010
  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}%)`),
@@ -928,7 +1032,7 @@ class {
928
1032
  flowBlur: blurAmount * FLOW_BLUR_MULTIPLIER
929
1033
  };
930
1034
  if (withLiquidBlur && rect) {
931
- 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);
1035
+ 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);
932
1036
  liquidBlur = {
933
1037
  baseBlur: clampBlur(baseBlur),
934
1038
  edgeBlur: clampBlur(edgeBlur),
@@ -937,9 +1041,10 @@ class {
937
1041
  };
938
1042
  }
939
1043
  // Backdrop filter
940
- let backdropFilterString = `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`;
941
- const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), area = rect ? rect.width * rect.height : 0;
942
- 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})`;
1044
+ const dynamicSaturation = saturation + 40 * tensionFactor + 15 * (liquidBlur.baseBlur || 0);
1045
+ let backdropFilterString = "";
1046
+ const area = rect ? rect.width * rect.height : 0;
1047
+ 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})`;
943
1048
  // Container variables
944
1049
  const style = containerElement.style;
945
1050
  style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
@@ -1101,7 +1206,8 @@ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new Weak
1101
1206
  * Composable hook for AtomixGlass component logic
1102
1207
  * Manages all state, calculations, and event handlers
1103
1208
  */
1104
- 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:
1209
+ 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:
1210
+ // Default priority
1105
1211
  // Phase 1: Animation System Props
1106
1212
  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}) {
1107
1213
  // State
@@ -1117,7 +1223,24 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1117
1223
  }), targetGlobalMousePositionRef = useRef({
1118
1224
  x: 0,
1119
1225
  y: 0
1120
- }), 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((() => {
1226
+ }), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), elasticTranslationRef = useRef({
1227
+ x: 0,
1228
+ y: 0
1229
+ }), elasticVelocityRef = useRef({
1230
+ x: 0,
1231
+ y: 0
1232
+ }), directionalScaleRef = useRef({
1233
+ x: 1,
1234
+ y: 1
1235
+ }), scaleVelocityRef = useRef({
1236
+ x: 0,
1237
+ y: 0
1238
+ });
1239
+ useRef(0);
1240
+ const mouseVelocityRef = useRef({
1241
+ x: 0,
1242
+ y: 0
1243
+ }), [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((() => {
1121
1244
  // If quality preset is provided, use it as base
1122
1245
  const preset = (quality = distortionQuality, ATOMIX_GLASS.CONSTANTS.DISTORTION_QUALITY_PRESETS[quality]);
1123
1246
  // Override with custom values if provided
@@ -1402,57 +1525,85 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1402
1525
  return "undefined" == typeof process || process.env, finalConfig;
1403
1526
  }
1404
1527
  return "undefined" == typeof process || process.env, baseConfig;
1405
- }), [ 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((() => {
1528
+ }), [ 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((() => {
1406
1529
  lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
1407
1530
  lerpRafRef.current = null);
1408
1531
  }), []), startLerpLoop = useCallback((() => {
1409
1532
  if (lerpActiveRef.current) return;
1410
- lerpActiveRef.current = !0;
1411
- const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
1533
+ lerpActiveRef.current = !0, CONSTANTS.LERP_FACTOR;
1534
+ // 0.08 lower = more viscous
1535
+ const tick = () => {
1412
1536
  if (!lerpActiveRef.current) return;
1413
1537
  if (!glassRef.current) return void (lerpActiveRef.current = !1);
1414
- const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
1415
- // If we're close enough, snap and park
1416
- if (Math.abs(dx) < .01 && Math.abs(dy) < .01) return internalMouseOffsetRef.current = {
1417
- ...tgt
1418
- }, internalGlobalMousePositionRef.current = {
1419
- ...targetGlobalMousePositionRef.current
1420
- },
1421
- // Final update and stop
1422
- updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1423
- mouseOffset: internalMouseOffsetRef.current,
1424
- globalMousePosition: internalGlobalMousePositionRef.current,
1425
- glassSize: glassSize,
1426
- isHovered: isHovered,
1427
- isActive: isActive,
1428
- isOverLight: overLightConfig.isOverLight,
1429
- baseOverLightConfig: overLightConfig,
1430
- effectiveBorderRadius: effectiveBorderRadius,
1431
- effectiveWithoutEffects: effectiveWithoutEffects,
1432
- effectiveReducedMotion: effectiveReducedMotion,
1433
- elasticity: elasticity,
1434
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1435
- onClick: onClick,
1436
- withLiquidBlur: withLiquidBlur,
1437
- blurAmount: blurAmount,
1438
- saturation: saturation,
1439
- padding: padding,
1440
- isFixedOrSticky: isFixedOrSticky
1441
- }), void stopLerpLoop();
1442
- // Smooth step
1443
- internalMouseOffsetRef.current = {
1444
- x: lerp$1(cur.x, tgt.x, LERP_T),
1445
- y: lerp$1(cur.y, tgt.y, LERP_T)
1538
+ 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);
1539
+ internalMouseOffsetRef.current = {
1540
+ x: springX.value,
1541
+ y: springY.value
1542
+ }, mouseVelocityRef.current = {
1543
+ x: springX.velocity,
1544
+ y: springY.velocity
1446
1545
  };
1447
1546
  const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
1448
1547
  internalGlobalMousePositionRef.current = {
1449
- x: lerp$1(curG.x, tgtG.x, LERP_T),
1450
- y: lerp$1(curG.y, tgtG.y, LERP_T)
1548
+ x: lerp$1(curG.x, tgtG.x, CONSTANTS.LERP_FACTOR),
1549
+ y: lerp$1(curG.y, tgtG.y, CONSTANTS.LERP_FACTOR)
1550
+ };
1551
+ // ── Calculate Elastic Physics ─────────────────────────────────────
1552
+ let targetElasticTranslation = {
1553
+ x: 0,
1554
+ y: 0
1555
+ }, targetScale = {
1556
+ x: 1,
1557
+ y: 1
1558
+ };
1559
+ if (!effectiveWithoutEffects && glassRef.current) {
1560
+ const rect = cachedRectRef.current || glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), globalPos = internalGlobalMousePositionRef.current;
1561
+ if (globalPos.x && globalPos.y) {
1562
+ 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);
1563
+ // Scale stretch logic (liquid surface tension)
1564
+ if (targetElasticTranslation = {
1565
+ x: deltaX * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor,
1566
+ y: deltaY * elasticity * CONSTANTS.ELASTICITY_TRANSLATION_FACTOR * fadeInFactor
1567
+ }, edgeDistance <= activationZone) {
1568
+ const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
1569
+ if (centerDistance > 0) {
1570
+ const nx = deltaX / centerDistance, ny = deltaY / centerDistance, stretchIntensity = Math.min(centerDistance / 350, 1) * elasticity * rawT, mag = 1 + .06 * stretchIntensity;
1571
+ targetScale = {
1572
+ x: mag + Math.abs(nx) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO,
1573
+ y: mag + Math.abs(ny) * stretchIntensity * CONSTANTS.ELASTICITY_STRETCH_RATIO
1574
+ },
1575
+ // Maintain liquid volume by compressing the perpendicular axis
1576
+ targetScale.x -= Math.abs(ny) * stretchIntensity * .15, targetScale.y -= Math.abs(nx) * stretchIntensity * .15;
1577
+ }
1578
+ }
1579
+ }
1580
+ }
1581
+ // Integrate Elastic Translation Spring
1582
+ 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);
1583
+ elasticTranslationRef.current = {
1584
+ x: springTX.value,
1585
+ y: springTY.value
1586
+ }, elasticVelocityRef.current = {
1587
+ x: springTX.velocity,
1588
+ y: springTY.velocity
1589
+ };
1590
+ // Integrate Scale Spring
1591
+ 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);
1592
+ directionalScaleRef.current = {
1593
+ x: springSX.value,
1594
+ y: springSY.value
1595
+ }, scaleVelocityRef.current = {
1596
+ x: springSX.velocity,
1597
+ y: springSY.velocity
1451
1598
  },
1452
1599
  // Imperative style update
1453
1600
  updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1454
1601
  mouseOffset: internalMouseOffsetRef.current,
1455
1602
  globalMousePosition: internalGlobalMousePositionRef.current,
1603
+ elasticTranslation: elasticTranslationRef.current,
1604
+ elasticVelocity: elasticVelocityRef.current,
1605
+ mouseVelocity: mouseVelocityRef.current,
1606
+ directionalScale: directionalScaleRef.current,
1456
1607
  glassSize: glassSize,
1457
1608
  isHovered: isHovered,
1458
1609
  isActive: isActive,
@@ -1462,17 +1613,16 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1462
1613
  effectiveWithoutEffects: effectiveWithoutEffects,
1463
1614
  effectiveReducedMotion: effectiveReducedMotion,
1464
1615
  elasticity: elasticity,
1465
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1616
+ scaleBase: isActive && Boolean(onClick) ? .99 : 1,
1466
1617
  onClick: onClick,
1467
1618
  withLiquidBlur: withLiquidBlur,
1468
1619
  blurAmount: blurAmount,
1469
1620
  saturation: saturation,
1470
1621
  padding: padding,
1471
1622
  isFixedOrSticky: isFixedOrSticky
1472
- }), lerpRafRef.current = requestAnimationFrame(tick);
1623
+ }), 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);
1473
1624
  };
1474
- // 0.08 – lower = more viscous
1475
- lerpRafRef.current = requestAnimationFrame(tick);
1625
+ lerpRafRef.current = requestAnimationFrame(tick);
1476
1626
  }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
1477
1627
  if (externalGlobalMousePosition && externalMouseOffset) return;
1478
1628
  if (effectiveWithoutEffects) return;
@@ -1498,7 +1648,8 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1498
1648
  useEffect((() => {
1499
1649
  if (externalGlobalMousePosition && externalMouseOffset) return;
1500
1650
  if (effectiveWithoutEffects) return;
1501
- const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
1651
+ const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition, glassRef.current || void 0, 300);
1652
+ // 300px max distance for full effect
1502
1653
  // Initial start
1503
1654
  startLerpLoop();
1504
1655
  const container = mouseContainer?.current || glassRef.current;
@@ -1518,6 +1669,11 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1518
1669
  updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
1519
1670
  mouseOffset: externalMouseOffset || internalMouseOffsetRef.current,
1520
1671
  globalMousePosition: externalGlobalMousePosition || internalGlobalMousePositionRef.current,
1672
+ elasticTranslation: elasticTranslationRef.current,
1673
+ elasticVelocity: elasticVelocityRef.current,
1674
+ mouseVelocity: mouseVelocityRef.current,
1675
+ directionalScale: directionalScaleRef.current,
1676
+ scaleBase: isActive && Boolean(onClick) ? .96 : 1,
1521
1677
  glassSize: glassSize,
1522
1678
  isHovered: isHovered,
1523
1679
  isActive: isActive,
@@ -1527,7 +1683,6 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
1527
1683
  effectiveWithoutEffects: effectiveWithoutEffects,
1528
1684
  effectiveReducedMotion: effectiveReducedMotion,
1529
1685
  elasticity: elasticity,
1530
- directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
1531
1686
  onClick: onClick,
1532
1687
  withLiquidBlur: withLiquidBlur,
1533
1688
  blurAmount: blurAmount,
@@ -2795,7 +2950,7 @@ const PERFORMANCE_PRESET = {
2795
2950
  "aria-label": ariaLabel,
2796
2951
  "aria-describedby": ariaDescribedBy,
2797
2952
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
2798
- "aria-pressed": void 0,
2953
+ "aria-pressed": onClick ? isActive : void 0,
2799
2954
  onKeyDown: onClick ? handleKeyDown : void 0,
2800
2955
  children: [ jsx(AtomixGlassContainer, {
2801
2956
  ref: glassRef,
@@ -3265,26 +3420,25 @@ const Radio = memo((({label: label, checked: checked = !1, onChange: onChange,
3265
3420
 
3266
3421
  Radio.displayName = "Radio";
3267
3422
 
3268
- const SelectContext = createContext(null), SelectOption = memo((({value: value, children: children, disabled: disabled = !1, className: className = "", style: style}) => {
3269
- const context = useContext(SelectContext), label = "string" == typeof children ? children : value;
3270
- // We assume children is the label if it's a string, or we need a way to get label.
3271
- // For simplicity, we use children as label for registration if it's a string.
3272
- if (useEffect((() => {
3423
+ const SelectContext = createContext(null), SelectOption = memo((({value: value, label: label, children: children, disabled: disabled = !1, className: className = "", style: style}) => {
3424
+ const context = useContext(SelectContext), displayLabel = label || ("string" == typeof children ? children : value);
3425
+ if (useEffect((() => {
3273
3426
  if (context) return context.registerOption({
3274
3427
  value: value,
3275
- label: label,
3428
+ label: displayLabel,
3276
3429
  disabled: disabled
3277
3430
  }), () => {
3278
3431
  context.unregisterOption(value);
3279
3432
  };
3280
- }), [ context, value, label, disabled ]), !context) return console.warn("SelectOption must be used within a Select component"),
3433
+ }), [ context, value, displayLabel, disabled ]), !context) return console.warn("SelectOption must be used within a Select component"),
3281
3434
  null;
3282
- const {selectedValue: selectedValue, onSelect: onSelect} = context, isSelected = Array.isArray(selectedValue) ? _includesInstanceProperty(selectedValue).call(selectedValue, value) : selectedValue === value;
3435
+ const {selectedValue: selectedValue, onSelect: onSelect, focusedValue: focusedValue, id: id} = context, isSelected = Array.isArray(selectedValue) ? _includesInstanceProperty(selectedValue).call(selectedValue, value) : selectedValue === value;
3283
3436
  return jsx("li", {
3284
- className: `${SELECT_CLASSES_SELECT_ITEM} ${className}`.trim(),
3437
+ id: id ? `${id}-opt-${value}` : void 0,
3438
+ className: `${SELECT_CLASSES_SELECT_ITEM} ${focusedValue === value ? "is-focused" : ""} ${isSelected ? "is-selected" : ""} ${className}`.trim(),
3285
3439
  "data-value": value,
3286
3440
  onClick: e => {
3287
- e.preventDefault(), e.stopPropagation(), disabled || onSelect(value, label);
3441
+ e.preventDefault(), e.stopPropagation(), disabled || onSelect(value, displayLabel);
3288
3442
  },
3289
3443
  style: style,
3290
3444
  role: "option",
@@ -3304,7 +3458,7 @@ const SelectContext = createContext(null), SelectOption = memo((({value: value
3304
3458
  tabIndex: -1
3305
3459
  }), jsx("div", {
3306
3460
  className: "c-select__item-label",
3307
- children: children
3461
+ children: children || displayLabel
3308
3462
  }) ]
3309
3463
  })
3310
3464
  });
@@ -3360,18 +3514,18 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3360
3514
  disabled: disabled,
3361
3515
  invalid: invalid,
3362
3516
  valid: valid
3363
- }), [isOpen, setIsOpen] = useState(!1), [selectedLabel, setSelectedLabel] = useState(placeholder), dropdownRef = useRef(null), panelRef = useRef(null), bodyRef = useRef(null), nativeSelectRef = useRef(null), [registeredOptions, setRegisteredOptions] = useState([]), registerOption = useCallback((option => {
3517
+ }), [isOpen, setIsOpen] = useState(!1), [focusedIndex, setFocusedIndex] = useState(-1), dropdownRef = useRef(null), panelRef = useRef(null), bodyRef = useRef(null), nativeSelectRef = useRef(null), [registeredOptions, setRegisteredOptions] = useState([]), registerOption = useCallback((option => {
3364
3518
  setRegisteredOptions((prev => prev.some((o => o.value === option.value)) ? prev : [ ...prev, option ]));
3365
3519
  }), []), unregisterOption = useCallback((value => {
3366
3520
  setRegisteredOptions((prev => prev.filter((o => o.value !== value))));
3367
- }), []), hasOptionsProp = options && options.length > 0, activeOptions = hasOptionsProp ? options : registeredOptions;
3368
- // Update selected label when value changes
3369
- useEffect((() => {
3370
- if (value) {
3521
+ }), []), hasOptionsProp = options && options.length > 0, activeOptions = hasOptionsProp ? options : registeredOptions, selectedLabel = useMemo((() => {
3522
+ if (multiple && Array.isArray(value)) return 0 === value.length ? placeholder : activeOptions.filter((opt => _includesInstanceProperty(value).call(value, opt.value))).map((opt => opt.label)).join(", ");
3523
+ if (value && "string" == typeof value) {
3371
3524
  const selectedOption = activeOptions.find((opt => opt.value === value));
3372
- selectedOption && setSelectedLabel(selectedOption.label);
3373
- } else setSelectedLabel(placeholder);
3374
- }), [ value, activeOptions, placeholder ]),
3525
+ return selectedOption ? selectedOption.label : placeholder;
3526
+ }
3527
+ return placeholder;
3528
+ }), [ value, activeOptions, placeholder, multiple ]);
3375
3529
  // Handle click outside to close dropdown
3376
3530
  useEffect((() => {
3377
3531
  const handleClickOutside = event => {
@@ -3384,31 +3538,54 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3384
3538
  }), []);
3385
3539
  // Toggle dropdown
3386
3540
  const handleToggle = () => {
3387
- disabled || (!isOpen && bodyRef.current && panelRef.current ? bodyRef.current.style.height = `${panelRef.current.clientHeight}px` : bodyRef.current && (bodyRef.current.style.height = "0px"),
3388
- setIsOpen(!isOpen));
3389
- }, handleItemClick = useCallback((option => {
3390
- if (setSelectedLabel(option.label), setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"),
3391
- nativeSelectRef.current && (nativeSelectRef.current.value = option.value), onChange) {
3392
- // Create a synthetic event
3393
- const event = {
3394
- target: {
3395
- name: name,
3396
- value: option.value
3397
- }
3398
- };
3399
- onChange(event);
3541
+ if (!disabled) {
3542
+ const nextOpen = !isOpen;
3543
+ if (nextOpen && bodyRef.current && panelRef.current)
3544
+ // Set focused index to current selection or first item
3545
+ if (bodyRef.current.style.height = `${panelRef.current.clientHeight}px`, value && !multiple && "string" == typeof value) {
3546
+ const index = activeOptions.findIndex((opt => opt.value === value));
3547
+ setFocusedIndex(index >= 0 ? index : 0);
3548
+ } else if (multiple && Array.isArray(value) && value.length > 0) {
3549
+ const index = activeOptions.findIndex((opt => _includesInstanceProperty(value).call(value, opt.value)));
3550
+ setFocusedIndex(index >= 0 ? index : 0);
3551
+ } else setFocusedIndex(0); else bodyRef.current && (bodyRef.current.style.height = "0px",
3552
+ setFocusedIndex(-1));
3553
+ setIsOpen(nextOpen);
3400
3554
  }
3401
- }), [ onChange, name ]), onSelect = useCallback(((val, label) => {
3555
+ }, handleItemClick = useCallback((option => {
3556
+ let newValue;
3557
+ if (multiple) {
3558
+ const currentValues = Array.isArray(value) ? value : value ? [ value ] : [];
3559
+ newValue = _includesInstanceProperty(currentValues).call(currentValues, option.value) ? currentValues.filter((v => v !== option.value)) : [ ...currentValues, option.value ];
3560
+ } else newValue = option.value, setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px");
3561
+ onChange && (
3562
+ // Sync native select before firing onChange
3563
+ nativeSelectRef.current && (multiple && Array.isArray(newValue) ? Array.from(nativeSelectRef.current.options).forEach((opt => {
3564
+ opt.selected = _includesInstanceProperty(newValue).call(newValue, opt.value);
3565
+ })) : "string" == typeof newValue && (nativeSelectRef.current.value = newValue)),
3566
+ onChange({
3567
+ target: {
3568
+ name: name,
3569
+ value: newValue
3570
+ }
3571
+ }));
3572
+ }), [ onChange, name, multiple, value ]), onSelect = useCallback(((val, label) => {
3402
3573
  handleItemClick({
3403
3574
  value: val,
3404
3575
  label: label
3405
3576
  });
3406
- }), [ handleItemClick ]), contextValue = React.useMemo((() => ({
3577
+ }), [ handleItemClick ]), focusedValue = useMemo((() => {
3578
+ if (focusedIndex >= 0 && focusedIndex < activeOptions.length) return activeOptions[focusedIndex]?.value;
3579
+ }), [ focusedIndex, activeOptions ]), focusedOptionId = useMemo((() => {
3580
+ if (isOpen && focusedValue) return `${id || "select"}-opt-${focusedValue}`;
3581
+ }), [ isOpen, focusedValue, id ]), contextValue = React.useMemo((() => ({
3407
3582
  registerOption: registerOption,
3408
3583
  unregisterOption: unregisterOption,
3409
3584
  selectedValue: value,
3410
- onSelect: onSelect
3411
- })), [ registerOption, unregisterOption, value, onSelect ]), selectContent = jsx(SelectContext.Provider, {
3585
+ onSelect: onSelect,
3586
+ focusedValue: focusedValue,
3587
+ id: id || "select"
3588
+ })), [ registerOption, unregisterOption, value, onSelect, focusedValue, id ]), selectContent = jsx(SelectContext.Provider, {
3412
3589
  value: contextValue,
3413
3590
  children: jsxs("div", {
3414
3591
  className: `${selectClass} ${isOpen ? SELECT_CLASSES_IS_OPEN : ""}`,
@@ -3456,7 +3633,12 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3456
3633
  if (!disabled) switch (event.key) {
3457
3634
  case "Enter":
3458
3635
  case " ":
3459
- event.preventDefault(), handleToggle();
3636
+ if (event.preventDefault(), isOpen) {
3637
+ if (focusedIndex >= 0 && focusedIndex < activeOptions.length) {
3638
+ const option = activeOptions[focusedIndex];
3639
+ option && !option.disabled && handleItemClick(option);
3640
+ }
3641
+ } else handleToggle();
3460
3642
  break;
3461
3643
 
3462
3644
  case "Escape":
@@ -3464,8 +3646,23 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3464
3646
  break;
3465
3647
 
3466
3648
  case "ArrowDown":
3649
+ event.preventDefault(), isOpen ? setFocusedIndex((prev => prev < activeOptions.length - 1 ? prev + 1 : prev)) : handleToggle();
3650
+ break;
3651
+
3467
3652
  case "ArrowUp":
3468
- isOpen || (event.preventDefault(), handleToggle());
3653
+ event.preventDefault(), isOpen ? setFocusedIndex((prev => prev > 0 ? prev - 1 : 0)) : handleToggle();
3654
+ break;
3655
+
3656
+ case "Home":
3657
+ isOpen && (event.preventDefault(), setFocusedIndex(0));
3658
+ break;
3659
+
3660
+ case "End":
3661
+ isOpen && (event.preventDefault(), setFocusedIndex(activeOptions.length - 1));
3662
+ break;
3663
+
3664
+ case "Tab":
3665
+ isOpen && (setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"));
3469
3666
  }
3470
3667
  },
3471
3668
  "aria-disabled": disabled,
@@ -3474,7 +3671,11 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3474
3671
  "aria-haspopup": "listbox",
3475
3672
  "aria-expanded": isOpen,
3476
3673
  "aria-controls": id ? `${id}-listbox` : void 0,
3477
- children: selectedLabel
3674
+ "aria-activedescendant": focusedOptionId,
3675
+ children: jsx("div", {
3676
+ className: "c-select__selected-text",
3677
+ children: selectedLabel
3678
+ })
3478
3679
  }), jsx("i", {
3479
3680
  className: `${SELECT_CLASSES_ICON_CARET} ${SELECT_CLASSES_TOGGLE_ICON}`
3480
3681
  }), jsx("div", {
@@ -3492,11 +3693,13 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3492
3693
  id: id ? `${id}-listbox` : void 0,
3493
3694
  "aria-labelledby": id,
3494
3695
  children: hasOptionsProp ? options.map(((option, index) => jsx("li", {
3495
- className: SELECT_CLASSES_SELECT_ITEM,
3696
+ id: `${id || "select"}-opt-${option.value}`,
3697
+ className: `${SELECT_CLASSES_SELECT_ITEM} ${focusedIndex === index ? "is-focused" : ""} ${multiple && Array.isArray(value) && _includesInstanceProperty(value).call(value, option.value) || value === option.value ? "is-selected" : ""}`,
3496
3698
  "data-value": option.value,
3497
3699
  onClick: () => !option.disabled && handleItemClick(option),
3700
+ onMouseEnter: () => setFocusedIndex(index),
3498
3701
  role: "option",
3499
- "aria-selected": value === option.value,
3702
+ "aria-selected": multiple && Array.isArray(value) && _includesInstanceProperty(value).call(value, option.value) || value === option.value,
3500
3703
  "aria-disabled": option.disabled,
3501
3704
  children: jsxs("label", {
3502
3705
  htmlFor: `SelectItem${index}`,
@@ -3505,7 +3708,7 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3505
3708
  type: "checkbox",
3506
3709
  id: `SelectItem${index}`,
3507
3710
  className: "c-checkbox__input c-select__item-input",
3508
- checked: value === option.value,
3711
+ checked: multiple && Array.isArray(value) && _includesInstanceProperty(value).call(value, option.value) || value === option.value,
3509
3712
  readOnly: !0,
3510
3713
  disabled: option.disabled
3511
3714
  }), jsx("div", {
@@ -3524,10 +3727,7 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
3524
3727
  // Default glass settings for select components
3525
3728
  const defaultGlassProps = {
3526
3729
  displacementScale: 60,
3527
- blurAmount: 1,
3528
- saturation: 180,
3529
- aberrationIntensity: .2,
3530
- borderRadius: 12,
3730
+ blurAmount: 10,
3531
3731
  mode: "shader"
3532
3732
  }, glassProps = !0 === glass ? defaultGlassProps : {
3533
3733
  ...defaultGlassProps,