@shohojdhara/atomix 0.2.4 → 0.2.5

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 (211) hide show
  1. package/README.md +19 -0
  2. package/dist/atomix.css +1266 -1412
  3. package/dist/atomix.min.css +3 -3
  4. package/dist/index.d.ts +1232 -876
  5. package/dist/index.esm.js +16212 -26364
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +15652 -22298
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/dist/themes/applemix.css +15008 -0
  12. package/dist/themes/applemix.min.css +72 -0
  13. package/dist/themes/boomdevs.css +1266 -1413
  14. package/dist/themes/boomdevs.min.css +3 -3
  15. package/dist/themes/esrar.css +1267 -1413
  16. package/dist/themes/esrar.min.css +3 -3
  17. package/dist/themes/flashtrade.css +15159 -0
  18. package/dist/themes/flashtrade.min.css +86 -0
  19. package/dist/themes/mashroom.css +1264 -1410
  20. package/dist/themes/mashroom.min.css +5 -5
  21. package/dist/themes/shaj-default.css +1266 -1412
  22. package/dist/themes/shaj-default.min.css +3 -3
  23. package/package.json +6 -17
  24. package/src/components/Accordion/Accordion.stories.tsx +4 -26
  25. package/src/components/Accordion/Accordion.tsx +21 -12
  26. package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +487 -1215
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
  29. package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
  30. package/src/components/AtomixGlass/README.md +124 -2
  31. package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
  32. package/src/components/AtomixGlass/glass-utils.ts +263 -0
  33. package/src/components/AtomixGlass/shader-utils.ts +404 -236
  34. package/src/components/AtomixGlass/{AtomixGlass.stories.tsx → stories/AtomixGlass.stories.tsx} +55 -35
  35. package/src/components/AtomixGlass/stories/Examples.stories.tsx +57 -89
  36. package/src/components/AtomixGlass/stories/Modes.stories.tsx +149 -149
  37. package/src/components/AtomixGlass/stories/Playground.stories.tsx +95 -32
  38. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +0 -2
  39. package/src/components/AtomixGlass/stories/shared-components.tsx +9 -18
  40. package/src/components/AtomixGlass/utils.ts +3 -3
  41. package/src/components/Avatar/Avatar.tsx +3 -0
  42. package/src/components/Avatar/AvatarGroup.tsx +2 -1
  43. package/src/components/Badge/Badge.stories.tsx +74 -54
  44. package/src/components/Badge/Badge.tsx +8 -12
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
  46. package/src/components/Button/Button.tsx +3 -5
  47. package/src/components/Callout/Callout.stories.tsx +86 -35
  48. package/src/components/Callout/Callout.tsx +4 -0
  49. package/src/components/Card/Card.stories.tsx +89 -85
  50. package/src/components/Card/Card.tsx +15 -4
  51. package/src/components/Card/ElevationCard.tsx +2 -0
  52. package/src/components/Chart/AnimatedChart.tsx +179 -156
  53. package/src/components/Chart/AreaChart.tsx +123 -12
  54. package/src/components/Chart/BarChart.tsx +91 -100
  55. package/src/components/Chart/BaseChart.tsx +80 -0
  56. package/src/components/Chart/BubbleChart.tsx +114 -290
  57. package/src/components/Chart/CandlestickChart.tsx +282 -622
  58. package/src/components/Chart/Chart.stories.tsx +576 -179
  59. package/src/components/Chart/Chart.tsx +374 -75
  60. package/src/components/Chart/ChartRenderer.tsx +371 -220
  61. package/src/components/Chart/ChartToolbar.tsx +372 -61
  62. package/src/components/Chart/ChartTooltip.tsx +33 -18
  63. package/src/components/Chart/DonutChart.tsx +172 -254
  64. package/src/components/Chart/FunnelChart.tsx +169 -240
  65. package/src/components/Chart/GaugeChart.tsx +224 -392
  66. package/src/components/Chart/HeatmapChart.tsx +302 -440
  67. package/src/components/Chart/LineChart.tsx +148 -103
  68. package/src/components/Chart/MultiAxisChart.tsx +267 -395
  69. package/src/components/Chart/PieChart.tsx +114 -64
  70. package/src/components/Chart/RadarChart.tsx +202 -218
  71. package/src/components/Chart/ScatterChart.tsx +111 -97
  72. package/src/components/Chart/TreemapChart.tsx +147 -222
  73. package/src/components/Chart/WaterfallChart.tsx +253 -291
  74. package/src/components/Chart/index.ts +11 -9
  75. package/src/components/Chart/types.ts +85 -9
  76. package/src/components/Chart/utils.ts +66 -0
  77. package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
  78. package/src/components/Countdown/Countdown.tsx +4 -0
  79. package/src/components/DataTable/DataTable.tsx +2 -1
  80. package/src/components/DatePicker/DatePicker.stories.tsx +0 -11
  81. package/src/components/DatePicker/DatePicker.tsx +3 -9
  82. package/src/components/DatePicker/types.ts +5 -0
  83. package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
  84. package/src/components/Dropdown/Dropdown.tsx +26 -28
  85. package/src/components/EdgePanel/EdgePanel.stories.tsx +13 -15
  86. package/src/components/EdgePanel/EdgePanel.tsx +20 -5
  87. package/src/components/Footer/Footer.stories.tsx +187 -60
  88. package/src/components/Footer/Footer.test.tsx +134 -0
  89. package/src/components/Footer/Footer.tsx +133 -34
  90. package/src/components/Footer/FooterLink.tsx +1 -1
  91. package/src/components/Footer/FooterSection.tsx +53 -36
  92. package/src/components/Footer/FooterSocialLink.tsx +32 -29
  93. package/src/components/Footer/README.md +82 -3
  94. package/src/components/Footer/index.ts +1 -1
  95. package/src/components/Form/Checkbox.stories.tsx +13 -5
  96. package/src/components/Form/Checkbox.tsx +3 -6
  97. package/src/components/Form/Form.stories.tsx +10 -3
  98. package/src/components/Form/Form.tsx +2 -0
  99. package/src/components/Form/FormGroup.tsx +2 -1
  100. package/src/components/Form/Input.stories.tsx +12 -11
  101. package/src/components/Form/Input.tsx +97 -95
  102. package/src/components/Form/Radio.stories.tsx +22 -7
  103. package/src/components/Form/Radio.tsx +3 -6
  104. package/src/components/Form/Select.stories.tsx +21 -6
  105. package/src/components/Form/Select.tsx +3 -5
  106. package/src/components/Form/Textarea.stories.tsx +13 -11
  107. package/src/components/Form/Textarea.tsx +88 -86
  108. package/src/components/Hero/Hero.stories.tsx +2 -3
  109. package/src/components/Hero/Hero.tsx +5 -6
  110. package/src/components/Icon/Icon.tsx +12 -1
  111. package/src/components/List/List.tsx +2 -1
  112. package/src/components/List/ListGroup.tsx +2 -1
  113. package/src/components/Messages/Messages.tsx +3 -2
  114. package/src/components/Modal/Modal.stories.tsx +48 -34
  115. package/src/components/Modal/Modal.tsx +19 -23
  116. package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
  117. package/src/components/Navigation/Menu/Menu.tsx +2 -2
  118. package/src/components/Navigation/Nav/Nav.tsx +6 -1
  119. package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
  120. package/src/components/Navigation/Navbar/Navbar.tsx +4 -1
  121. package/src/components/Navigation/SideMenu/SideMenu.tsx +3 -2
  122. package/src/components/Pagination/Pagination.stories.tsx +13 -6
  123. package/src/components/Pagination/Pagination.tsx +7 -6
  124. package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
  125. package/src/components/Popover/Popover.stories.tsx +32 -24
  126. package/src/components/Popover/Popover.tsx +4 -1
  127. package/src/components/ProductReview/ProductReview.tsx +8 -2
  128. package/src/components/Progress/Progress.tsx +2 -1
  129. package/src/components/Rating/Rating.stories.tsx +11 -6
  130. package/src/components/Rating/Rating.tsx +3 -5
  131. package/src/components/River/River.tsx +5 -5
  132. package/src/components/SectionIntro/SectionIntro.tsx +8 -2
  133. package/src/components/Slider/Slider.stories.tsx +4 -4
  134. package/src/components/Slider/Slider.tsx +4 -3
  135. package/src/components/Spinner/Spinner.tsx +2 -1
  136. package/src/components/Steps/Steps.stories.tsx +5 -4
  137. package/src/components/Steps/Steps.tsx +8 -5
  138. package/src/components/Tab/Tab.stories.tsx +4 -3
  139. package/src/components/Tab/Tab.tsx +8 -6
  140. package/src/components/Testimonial/Testimonial.tsx +8 -2
  141. package/src/components/Todo/Todo.tsx +2 -1
  142. package/src/components/Toggle/Toggle.stories.tsx +5 -4
  143. package/src/components/Toggle/Toggle.tsx +8 -5
  144. package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
  145. package/src/components/Tooltip/Tooltip.tsx +9 -2
  146. package/src/components/Upload/Upload.stories.tsx +252 -0
  147. package/src/components/Upload/Upload.tsx +92 -53
  148. package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
  149. package/src/components/index.ts +0 -4
  150. package/src/layouts/Grid/Grid.stories.tsx +10 -23
  151. package/src/layouts/Grid/Grid.tsx +20 -1
  152. package/src/layouts/Grid/GridCol.tsx +76 -48
  153. package/src/lib/composables/useAtomixGlass.ts +861 -44
  154. package/src/lib/composables/useBarChart.ts +13 -6
  155. package/src/lib/composables/useChart.ts +17 -13
  156. package/src/lib/composables/useChartExport.ts +19 -78
  157. package/src/lib/composables/useChartToolbar.ts +0 -1
  158. package/src/lib/composables/useEdgePanel.ts +111 -103
  159. package/src/lib/composables/useFooter.ts +3 -3
  160. package/src/lib/composables/useGlassContainer.ts +16 -7
  161. package/src/lib/composables/useLineChart.ts +8 -1
  162. package/src/lib/composables/useRiver.ts +5 -0
  163. package/src/lib/composables/useSlider.ts +62 -24
  164. package/src/lib/composables/useVideoPlayer.ts +60 -63
  165. package/src/lib/constants/components.ts +146 -32
  166. package/src/lib/types/components.ts +258 -10
  167. package/src/lib/utils/displacement-generator.ts +55 -49
  168. package/src/lib/utils/icons.ts +1 -1
  169. package/src/lib/utils/index.ts +16 -10
  170. package/src/styles/01-settings/_settings.accordion.scss +19 -19
  171. package/src/styles/01-settings/_settings.animations.scss +5 -5
  172. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  173. package/src/styles/01-settings/_settings.avatar.scss +17 -17
  174. package/src/styles/01-settings/_settings.background.scss +1 -4
  175. package/src/styles/01-settings/_settings.badge.scss +1 -1
  176. package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
  177. package/src/styles/01-settings/_settings.card.scss +1 -1
  178. package/src/styles/01-settings/_settings.chart.scss +65 -2
  179. package/src/styles/01-settings/_settings.dropdown.scss +1 -1
  180. package/src/styles/01-settings/_settings.footer.scss +35 -42
  181. package/src/styles/01-settings/_settings.input.scss +1 -1
  182. package/src/styles/01-settings/_settings.list.scss +1 -1
  183. package/src/styles/01-settings/_settings.rating.scss +1 -1
  184. package/src/styles/01-settings/_settings.tabs.scss +1 -1
  185. package/src/styles/01-settings/_settings.upload.scss +6 -5
  186. package/src/styles/02-tools/_tools.animations.scss +4 -5
  187. package/src/styles/02-tools/_tools.background.scss +1 -13
  188. package/src/styles/02-tools/_tools.glass.scss +0 -1
  189. package/src/styles/02-tools/_tools.utility-api.scss +42 -34
  190. package/src/styles/03-generic/_generic.root.scss +1 -4
  191. package/src/styles/04-elements/_elements.body.scss +0 -1
  192. package/src/styles/06-components/_components.atomix-glass.scss +216 -39
  193. package/src/styles/06-components/_components.badge.scss +6 -8
  194. package/src/styles/06-components/_components.button.scss +8 -3
  195. package/src/styles/06-components/_components.card.scss +2 -14
  196. package/src/styles/06-components/_components.chart.scss +969 -1449
  197. package/src/styles/06-components/_components.dropdown.scss +19 -7
  198. package/src/styles/06-components/_components.edge-panel.scss +4 -2
  199. package/src/styles/06-components/_components.footer.scss +166 -85
  200. package/src/styles/06-components/_components.input.scss +8 -9
  201. package/src/styles/06-components/_components.list.scss +1 -0
  202. package/src/styles/06-components/_components.modal.scss +5 -3
  203. package/src/styles/06-components/_components.skeleton.scss +8 -6
  204. package/src/styles/06-components/_components.upload.scss +219 -4
  205. package/src/styles/06-components/old.chart.styles.scss +1 -30
  206. package/src/styles/99-utilities/_utilities.opacity.scss +1 -1
  207. package/src/styles/99-utilities/_utilities.scss +1 -1
  208. package/src/components/Chart/AdvancedChart.tsx +0 -624
  209. package/src/components/Chart/LineChartNew.tsx +0 -167
  210. package/src/components/Chart/RealTimeChart.tsx +0 -436
  211. package/src/components/DatePicker/DatePicker copy.tsx +0 -551
@@ -0,0 +1,1266 @@
1
+ import {
2
+ type CSSProperties,
3
+ forwardRef,
4
+ useCallback,
5
+ useEffect,
6
+ useId,
7
+ useRef,
8
+ useState,
9
+ useMemo,
10
+ } from 'react';
11
+ import { ShaderDisplacementGenerator, fragmentShaders } from './shader-utils';
12
+ import { displacementMap, polarDisplacementMap, prominentDisplacementMap } from './utils';
13
+
14
+ const generateShaderDisplacementMap = (width: number, height: number): string => {
15
+ try {
16
+ const generator = new ShaderDisplacementGenerator({
17
+ width,
18
+ height,
19
+ fragment: fragmentShaders.liquidGlass,
20
+ });
21
+
22
+ const dataUrl = generator.updateShader();
23
+ generator.destroy();
24
+
25
+ return dataUrl;
26
+ } catch (error) {
27
+ return displacementMap;
28
+ }
29
+ };
30
+
31
+ const getMap = (mode: 'standard' | 'polar' | 'prominent' | 'shader', shaderMapUrl?: string) => {
32
+ switch (mode) {
33
+ case 'standard':
34
+ return displacementMap;
35
+ case 'polar':
36
+ return polarDisplacementMap;
37
+ case 'prominent':
38
+ return prominentDisplacementMap;
39
+ case 'shader':
40
+ return shaderMapUrl || displacementMap;
41
+ default:
42
+ throw new Error(`Invalid mode: ${mode}`);
43
+ }
44
+ };
45
+
46
+ const GlassFilter: React.FC<{
47
+ id: string;
48
+ displacementScale: number;
49
+ aberrationIntensity: number;
50
+ width: number;
51
+ height: number;
52
+ mode: 'standard' | 'polar' | 'prominent' | 'shader';
53
+ shaderMapUrl?: string;
54
+ }> = ({ id, displacementScale, aberrationIntensity, width, height, mode, shaderMapUrl }) => (
55
+ <svg style={{ position: 'absolute', width, height, inset: 0 }} aria-hidden="true">
56
+ <defs>
57
+ <radialGradient id={`${id}-edge-mask`} cx="50%" cy="50%" r="50%">
58
+ <stop offset="0%" stopColor="black" stopOpacity="0" />
59
+ <stop
60
+ offset={`${Math.max(30, 80 - aberrationIntensity * 2)}%`}
61
+ stopColor="black"
62
+ stopOpacity="0"
63
+ />
64
+ <stop offset="100%" stopColor="white" stopOpacity="1" />
65
+ </radialGradient>
66
+ <filter id={id} x="-35%" y="-35%" width="170%" height="170%" colorInterpolationFilters="sRGB">
67
+ <feImage
68
+ id="feimage"
69
+ x="0"
70
+ y="0"
71
+ width="100%"
72
+ height="100%"
73
+ result="DISPLACEMENT_MAP"
74
+ href={getMap(mode, shaderMapUrl)}
75
+ preserveAspectRatio="xMidYMid slice"
76
+ />
77
+
78
+ <feColorMatrix
79
+ in="DISPLACEMENT_MAP"
80
+ type="matrix"
81
+ values="0.3 0.3 0.3 0 0
82
+ 0.3 0.3 0.3 0 0
83
+ 0.3 0.3 0.3 0 0
84
+ 0 0 0 1 0"
85
+ result="EDGE_INTENSITY"
86
+ />
87
+ <feComponentTransfer in="EDGE_INTENSITY" result="EDGE_MASK">
88
+ <feFuncA type="discrete" tableValues={`0 ${aberrationIntensity * 0.05} 1`} />
89
+ </feComponentTransfer>
90
+
91
+ <feOffset in="SourceGraphic" dx="0" dy="0" result="CENTER_ORIGINAL" />
92
+
93
+ <feDisplacementMap
94
+ in="SourceGraphic"
95
+ in2="DISPLACEMENT_MAP"
96
+ scale={displacementScale * (mode === 'shader' ? 1 : -1)}
97
+ xChannelSelector="R"
98
+ yChannelSelector="B"
99
+ result="RED_DISPLACED"
100
+ />
101
+ <feColorMatrix
102
+ in="RED_DISPLACED"
103
+ type="matrix"
104
+ values="1 0 0 0 0
105
+ 0 0 0 0 0
106
+ 0 0 0 0 0
107
+ 0 0 0 1 0"
108
+ result="RED_CHANNEL"
109
+ />
110
+
111
+ <feDisplacementMap
112
+ in="SourceGraphic"
113
+ in2="DISPLACEMENT_MAP"
114
+ scale={displacementScale * ((mode === 'shader' ? 1 : -1) - aberrationIntensity * 0.05)}
115
+ xChannelSelector="R"
116
+ yChannelSelector="B"
117
+ result="GREEN_DISPLACED"
118
+ />
119
+ <feColorMatrix
120
+ in="GREEN_DISPLACED"
121
+ type="matrix"
122
+ values="0 0 0 0 0
123
+ 0 1 0 0 0
124
+ 0 0 0 0 0
125
+ 0 0 0 1 0"
126
+ result="GREEN_CHANNEL"
127
+ />
128
+
129
+ <feDisplacementMap
130
+ in="SourceGraphic"
131
+ in2="DISPLACEMENT_MAP"
132
+ scale={displacementScale * ((mode === 'shader' ? 1 : -1) - aberrationIntensity * 0.1)}
133
+ xChannelSelector="R"
134
+ yChannelSelector="B"
135
+ result="BLUE_DISPLACED"
136
+ />
137
+ <feColorMatrix
138
+ in="BLUE_DISPLACED"
139
+ type="matrix"
140
+ values="0 0 0 0 0
141
+ 0 0 0 0 0
142
+ 0 0 1 0 0
143
+ 0 0 0 1 0"
144
+ result="BLUE_CHANNEL"
145
+ />
146
+
147
+ <feBlend in="GREEN_CHANNEL" in2="BLUE_CHANNEL" mode="screen" result="GB_COMBINED" />
148
+ <feBlend in="RED_CHANNEL" in2="GB_COMBINED" mode="screen" result="RGB_COMBINED" />
149
+
150
+ <feGaussianBlur
151
+ in="RGB_COMBINED"
152
+ stdDeviation={Math.max(0.1, 0.5 - aberrationIntensity * 0.1)}
153
+ result="ABERRATED_BLURRED"
154
+ />
155
+
156
+ <feComposite
157
+ in="ABERRATED_BLURRED"
158
+ in2="EDGE_MASK"
159
+ operator="in"
160
+ result="EDGE_ABERRATION"
161
+ />
162
+
163
+ <feComponentTransfer in="EDGE_MASK" result="INVERTED_MASK">
164
+ <feFuncA type="table" tableValues="1 0" />
165
+ </feComponentTransfer>
166
+ <feComposite in="CENTER_ORIGINAL" in2="INVERTED_MASK" operator="in" result="CENTER_CLEAN" />
167
+
168
+ <feComposite in="EDGE_ABERRATION" in2="CENTER_CLEAN" operator="over" />
169
+ </filter>
170
+ </defs>
171
+ </svg>
172
+ );
173
+
174
+ const GlassContainer = forwardRef<
175
+ HTMLDivElement,
176
+ React.PropsWithChildren<{
177
+ className?: string;
178
+ style?: React.CSSProperties;
179
+ displacementScale?: number;
180
+ blurAmount?: number;
181
+ saturation?: number;
182
+ aberrationIntensity?: number;
183
+ mouseOffset?: { x: number; y: number };
184
+ globalMousePos?: { x: number; y: number };
185
+ onMouseLeave?: () => void;
186
+ onMouseEnter?: () => void;
187
+ onMouseDown?: () => void;
188
+ onMouseUp?: () => void;
189
+ active?: boolean;
190
+ isHovered?: boolean;
191
+ isActive?: boolean;
192
+ overLight?: boolean;
193
+ cornerRadius?: number;
194
+ padding?: string;
195
+ glassSize?: { width: number; height: number };
196
+
197
+ onClick?: () => void;
198
+ mode?: 'standard' | 'polar' | 'prominent' | 'shader';
199
+ transform?: string;
200
+ }>
201
+ >(
202
+ (
203
+ {
204
+ children,
205
+ className = '',
206
+ style,
207
+ displacementScale = 25,
208
+ blurAmount = 12,
209
+ saturation = 180,
210
+ aberrationIntensity = 2,
211
+ mouseOffset = { x: 0, y: 0 },
212
+ globalMousePos = { x: 0, y: 0 },
213
+ onMouseEnter,
214
+ onMouseLeave,
215
+ onMouseDown,
216
+ onMouseUp,
217
+ active = false,
218
+ isHovered = false,
219
+ isActive = false,
220
+ overLight = false,
221
+ cornerRadius = 0,
222
+ padding = '0 0',
223
+ glassSize = { width: 0, height: 0 },
224
+ onClick,
225
+ mode = 'standard',
226
+ transform = 'none',
227
+ },
228
+ ref
229
+ ) => {
230
+ const filterId = useId();
231
+ const [shaderMapUrl, setShaderMapUrl] = useState<string>('');
232
+
233
+ const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
234
+
235
+ useEffect(() => {
236
+ if (mode === 'shader' && glassSize.width > 0 && glassSize.height > 0) {
237
+ try {
238
+ const url = generateShaderDisplacementMap(glassSize.width, glassSize.height);
239
+ setShaderMapUrl(url);
240
+ } catch (error) {
241
+ console.warn('Failed to generate shader displacement map:', error);
242
+ }
243
+ }
244
+ }, [mode, glassSize.width, glassSize.height]);
245
+
246
+ useEffect(() => {
247
+ if (!ref || typeof ref === 'function') return;
248
+
249
+ const element = (ref as React.RefObject<HTMLDivElement>).current;
250
+ if (!element) return;
251
+
252
+ const timeoutId = setTimeout(() => {
253
+ try {
254
+ element.offsetHeight;
255
+ } catch (error) {
256
+ console.warn('AtomixGlass: Error in GlassContainer size sync:', error);
257
+ }
258
+ }, 0);
259
+
260
+ return () => clearTimeout(timeoutId);
261
+ }, [cornerRadius, glassSize.width, glassSize.height]);
262
+
263
+ const liquidBlur = useMemo(() => {
264
+ if (!ref || !globalMousePos.x || !globalMousePos.y) {
265
+ return {
266
+ baseBlur: blurAmount,
267
+ edgeBlur: blurAmount * 0.5,
268
+ centerBlur: blurAmount * 0.2,
269
+ flowBlur: blurAmount * 0.3,
270
+ };
271
+ }
272
+
273
+ const rect = (ref as React.RefObject<HTMLDivElement>).current?.getBoundingClientRect();
274
+ if (!rect) {
275
+ return {
276
+ baseBlur: blurAmount,
277
+ edgeBlur: blurAmount * 0.5,
278
+ centerBlur: blurAmount * 0.2,
279
+ flowBlur: blurAmount * 0.3,
280
+ };
281
+ }
282
+ const centerX = rect.left + rect.width / 2;
283
+ const centerY = rect.top + rect.height / 2;
284
+
285
+ const deltaX = globalMousePos.x - centerX;
286
+ const deltaY = globalMousePos.y - centerY;
287
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
288
+
289
+ const maxDistance = Math.sqrt(rect.width * rect.width + rect.height * rect.height) / 2;
290
+ const normalizedDistance = Math.min(distance / maxDistance, 1);
291
+
292
+ const mouseInfluence =
293
+ Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / 100;
294
+
295
+ const baseBlur = blurAmount + mouseInfluence * blurAmount * 0.4;
296
+
297
+ const edgeIntensity = normalizedDistance * 1.5 + mouseInfluence * 0.3;
298
+ const edgeBlur = baseBlur * (0.8 + edgeIntensity * 0.6);
299
+
300
+ const centerIntensity = (1 - normalizedDistance) * 0.3 + mouseInfluence * 0.2;
301
+ const centerBlur = baseBlur * (0.3 + centerIntensity * 0.4);
302
+
303
+ const flowDirection = Math.atan2(deltaY, deltaX);
304
+ const flowIntensity = Math.sin(flowDirection + mouseInfluence * Math.PI) * 0.5 + 0.5;
305
+ const flowBlur = baseBlur * (0.4 + flowIntensity * 0.6);
306
+
307
+ const hoverMultiplier = isHovered ? 1.2 : 1;
308
+ const activeMultiplier = isActive ? 1.4 : 1;
309
+ const stateMultiplier = hoverMultiplier * activeMultiplier;
310
+
311
+ return {
312
+ baseBlur: Math.max(0.1, baseBlur * stateMultiplier),
313
+ edgeBlur: Math.max(0.1, edgeBlur * stateMultiplier),
314
+ centerBlur: Math.max(0.1, centerBlur * stateMultiplier),
315
+ flowBlur: Math.max(0.1, flowBlur * stateMultiplier),
316
+ };
317
+ }, [blurAmount, globalMousePos, mouseOffset, isHovered, isActive, ref]);
318
+
319
+ const backdropStyle = useMemo(() => {
320
+ const dynamicSaturation = saturation + liquidBlur.baseBlur * 20;
321
+
322
+ const blurLayers = [
323
+ `blur(${liquidBlur.baseBlur}px)`,
324
+ // `blur(${liquidBlur.edgeBlur}px)`,
325
+ // `blur(${liquidBlur.centerBlur}px)`,
326
+ `blur(${liquidBlur.flowBlur}px)`,
327
+ ];
328
+
329
+ return {
330
+ filter: `url(#${filterId})`,
331
+ backdropFilter: `${blurLayers.join(' ')} saturate(${Math.min(dynamicSaturation, 200)}%)`,
332
+ };
333
+ }, [filterId, liquidBlur, saturation]);
334
+
335
+ return (
336
+ <div
337
+ ref={ref}
338
+ className={` ${className} ${active ? 'active' : ''}`}
339
+ style={style}
340
+ onClick={onClick}
341
+ >
342
+ <div
343
+ className="glass"
344
+ style={{
345
+ position: 'relative',
346
+ padding,
347
+ borderRadius: `${cornerRadius}px`,
348
+ transition: 'all 0.2s ease-out',
349
+ boxShadow: overLight
350
+ ? '0px 16px 70px rgba(0, 0, 0, 0.75)'
351
+ : '0px 12px 40px rgba(0, 0, 0, 0.25)',
352
+ }}
353
+ onMouseEnter={onMouseEnter}
354
+ onMouseLeave={onMouseLeave}
355
+ onMouseDown={onMouseDown}
356
+ onMouseUp={onMouseUp}
357
+ >
358
+ <GlassFilter
359
+ mode={mode}
360
+ id={filterId}
361
+ displacementScale={displacementScale}
362
+ aberrationIntensity={aberrationIntensity}
363
+ width={glassSize.width}
364
+ height={glassSize.height}
365
+ shaderMapUrl={shaderMapUrl}
366
+ />
367
+ <span
368
+ className="glass__warp"
369
+ style={
370
+ {
371
+ ...backdropStyle,
372
+ borderRadius: `${cornerRadius}px`,
373
+ position: 'absolute',
374
+ inset: '0',
375
+ } as CSSProperties
376
+ }
377
+ />
378
+
379
+ <div
380
+ style={{
381
+ position: 'relative',
382
+ zIndex: 1,
383
+ textShadow: overLight
384
+ ? '0px 2px 12px rgba(0, 0, 0, 0)'
385
+ : '0px 2px 12px rgba(0, 0, 0, 0.4)',
386
+ }}
387
+ >
388
+ {children}
389
+ </div>
390
+ </div>
391
+ </div>
392
+ );
393
+ }
394
+ );
395
+
396
+ GlassContainer.displayName = 'GlassContainer';
397
+
398
+ interface AtomixGlassProps {
399
+ children: React.ReactNode;
400
+ displacementScale?: number;
401
+ blurAmount?: number;
402
+ saturation?: number;
403
+ aberrationIntensity?: number;
404
+ elasticity?: number;
405
+ cornerRadius?: number;
406
+ globalMousePos?: { x: number; y: number };
407
+ mouseOffset?: { x: number; y: number };
408
+ mouseContainer?: React.RefObject<HTMLElement | null> | null;
409
+ className?: string;
410
+ padding?: string;
411
+ style?: React.CSSProperties;
412
+ overLight?: boolean;
413
+ mode?: 'standard' | 'polar' | 'prominent' | 'shader';
414
+ onClick?: () => void;
415
+
416
+ /**
417
+ * Accessibility props
418
+ */
419
+ 'aria-label'?: string;
420
+ 'aria-describedby'?: string;
421
+ role?: string;
422
+ tabIndex?: number;
423
+
424
+ /**
425
+ * Performance and accessibility options
426
+ */
427
+ reducedMotion?: boolean;
428
+ highContrast?: boolean;
429
+ disableEffects?: boolean;
430
+
431
+ /**
432
+ * Performance monitoring
433
+ */
434
+ enablePerformanceMonitoring?: boolean;
435
+ }
436
+
437
+ export function AtomixGlass({
438
+ children,
439
+ displacementScale = 70,
440
+ blurAmount = 0,
441
+ saturation = 140,
442
+ aberrationIntensity = 2,
443
+ elasticity = 0.15,
444
+ cornerRadius = 20,
445
+ globalMousePos: externalGlobalMousePos,
446
+ mouseOffset: externalMouseOffset,
447
+ mouseContainer = null,
448
+ className = '',
449
+ padding = '0 0',
450
+ overLight = false,
451
+ style = {},
452
+ mode = 'standard',
453
+ onClick,
454
+
455
+ 'aria-label': ariaLabel,
456
+ 'aria-describedby': ariaDescribedBy,
457
+ role,
458
+ tabIndex,
459
+
460
+ reducedMotion = false,
461
+ highContrast = false,
462
+ disableEffects = false,
463
+
464
+ enablePerformanceMonitoring = false,
465
+ }: AtomixGlassProps) {
466
+ const glassRef = useRef<HTMLDivElement>(null);
467
+ const [isHovered, setIsHovered] = useState(false);
468
+ const [isActive, setIsActive] = useState(false);
469
+ const [glassSize, setGlassSize] = useState({ width: 270, height: 69 });
470
+ const [internalGlobalMousePos, setInternalGlobalMousePos] = useState({ x: 0, y: 0 });
471
+ const [internalMouseOffset, setInternalMouseOffset] = useState({ x: 0, y: 0 });
472
+
473
+ const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
474
+ const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
475
+
476
+ useEffect(() => {
477
+ if (typeof window.matchMedia !== 'function') {
478
+ console.warn('AtomixGlass: matchMedia not supported, using default preferences');
479
+ return;
480
+ }
481
+
482
+ try {
483
+ const mediaQueryReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
484
+ const mediaQueryHighContrast = window.matchMedia('(prefers-contrast: high)');
485
+
486
+ setUserPrefersReducedMotion(mediaQueryReducedMotion.matches);
487
+ setUserPrefersHighContrast(mediaQueryHighContrast.matches);
488
+
489
+ const handleReducedMotionChange = (e: MediaQueryListEvent) => {
490
+ setUserPrefersReducedMotion(e.matches);
491
+ };
492
+
493
+ const handleHighContrastChange = (e: MediaQueryListEvent) => {
494
+ setUserPrefersHighContrast(e.matches);
495
+ };
496
+
497
+ if (mediaQueryReducedMotion.addEventListener) {
498
+ mediaQueryReducedMotion.addEventListener('change', handleReducedMotionChange);
499
+ mediaQueryHighContrast.addEventListener('change', handleHighContrastChange);
500
+ } else if (mediaQueryReducedMotion.addListener) {
501
+ mediaQueryReducedMotion.addListener(handleReducedMotionChange);
502
+ mediaQueryHighContrast.addListener(handleHighContrastChange);
503
+ }
504
+
505
+ return () => {
506
+ if (mediaQueryReducedMotion.removeEventListener) {
507
+ mediaQueryReducedMotion.removeEventListener('change', handleReducedMotionChange);
508
+ mediaQueryHighContrast.removeEventListener('change', handleHighContrastChange);
509
+ } else if (mediaQueryReducedMotion.removeListener) {
510
+ mediaQueryReducedMotion.removeListener(handleReducedMotionChange);
511
+ mediaQueryHighContrast.removeListener(handleHighContrastChange);
512
+ }
513
+ };
514
+ } catch (error) {
515
+ console.warn('AtomixGlass: Error setting up media queries:', error);
516
+ }
517
+ }, []);
518
+
519
+ const effectiveReducedMotion = reducedMotion || userPrefersReducedMotion;
520
+ const effectiveHighContrast = highContrast || userPrefersHighContrast;
521
+ const effectiveDisableEffects = disableEffects || effectiveReducedMotion;
522
+
523
+ const globalMousePos = externalGlobalMousePos || internalGlobalMousePos;
524
+ const mouseOffset = externalMouseOffset || internalMouseOffset;
525
+
526
+ const mouseMoveThrottleRef = useRef<number | null>(null);
527
+ const lastMouseEventRef = useRef<MouseEvent | null>(null);
528
+
529
+ const handleMouseMove = useCallback(
530
+ (e: MouseEvent) => {
531
+ lastMouseEventRef.current = e;
532
+
533
+ if (mouseMoveThrottleRef.current === null) {
534
+ mouseMoveThrottleRef.current = requestAnimationFrame(() => {
535
+ const event = lastMouseEventRef.current;
536
+ if (!event) {
537
+ mouseMoveThrottleRef.current = null;
538
+ return;
539
+ }
540
+
541
+ const container = mouseContainer?.current || glassRef.current;
542
+ if (!container) {
543
+ mouseMoveThrottleRef.current = null;
544
+ return;
545
+ }
546
+
547
+ try {
548
+ const startTime = enablePerformanceMonitoring ? performance.now() : 0;
549
+
550
+ const rect = container.getBoundingClientRect();
551
+ const centerX = rect.left + rect.width / 2;
552
+ const centerY = rect.top + rect.height / 2;
553
+
554
+ setInternalMouseOffset({
555
+ x: ((event.clientX - centerX) / rect.width) * 100,
556
+ y: ((event.clientY - centerY) / rect.height) * 100,
557
+ });
558
+
559
+ setInternalGlobalMousePos({
560
+ x: event.clientX,
561
+ y: event.clientY,
562
+ });
563
+
564
+ if (enablePerformanceMonitoring) {
565
+ const endTime = performance.now();
566
+ const duration = endTime - startTime;
567
+ if (duration > 5) {
568
+ console.warn(`AtomixGlass: Mouse tracking took ${duration.toFixed(2)}ms`);
569
+ }
570
+ }
571
+ } catch (error) {
572
+ console.warn('AtomixGlass: Error in mouse tracking:', error);
573
+ } finally {
574
+ mouseMoveThrottleRef.current = null;
575
+ }
576
+ });
577
+ }
578
+ },
579
+ [mouseContainer]
580
+ );
581
+
582
+ useEffect(() => {
583
+ if (externalGlobalMousePos && externalMouseOffset) {
584
+ return;
585
+ }
586
+
587
+ if (effectiveDisableEffects) {
588
+ return;
589
+ }
590
+
591
+ const container = mouseContainer?.current || glassRef.current;
592
+ if (!container) {
593
+ return;
594
+ }
595
+
596
+ container.addEventListener('mousemove', handleMouseMove, { passive: true });
597
+
598
+ return () => {
599
+ container.removeEventListener('mousemove', handleMouseMove);
600
+ if (mouseMoveThrottleRef.current) {
601
+ cancelAnimationFrame(mouseMoveThrottleRef.current);
602
+ mouseMoveThrottleRef.current = null;
603
+ }
604
+ };
605
+ }, [
606
+ handleMouseMove,
607
+ mouseContainer,
608
+ externalGlobalMousePos,
609
+ externalMouseOffset,
610
+ effectiveDisableEffects,
611
+ ]);
612
+
613
+ const calculateDirectionalScale = useCallback(() => {
614
+ if (!globalMousePos.x || !globalMousePos.y || !glassRef.current) {
615
+ return 'scale(1)';
616
+ }
617
+
618
+ const rect = glassRef.current.getBoundingClientRect();
619
+ const pillCenterX = rect.left + rect.width / 2;
620
+ const pillCenterY = rect.top + rect.height / 2;
621
+ const pillWidth = glassSize.width;
622
+ const pillHeight = glassSize.height;
623
+
624
+ const deltaX = globalMousePos.x - pillCenterX;
625
+ const deltaY = globalMousePos.y - pillCenterY;
626
+
627
+ const edgeDistanceX = Math.max(0, Math.abs(deltaX) - pillWidth / 2);
628
+ const edgeDistanceY = Math.max(0, Math.abs(deltaY) - pillHeight / 2);
629
+ const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
630
+
631
+ const activationZone = 200;
632
+
633
+ if (edgeDistance > activationZone) {
634
+ return 'scale(1)';
635
+ }
636
+
637
+ const fadeInFactor = 1 - edgeDistance / activationZone;
638
+
639
+ const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
640
+ if (centerDistance === 0) {
641
+ return 'scale(1)';
642
+ }
643
+
644
+ const normalizedX = deltaX / centerDistance;
645
+ const normalizedY = deltaY / centerDistance;
646
+
647
+ const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
648
+
649
+ const scaleX =
650
+ 1 +
651
+ Math.abs(normalizedX) * stretchIntensity * 0.3 -
652
+ Math.abs(normalizedY) * stretchIntensity * 0.15;
653
+
654
+ const scaleY =
655
+ 1 +
656
+ Math.abs(normalizedY) * stretchIntensity * 0.3 -
657
+ Math.abs(normalizedX) * stretchIntensity * 0.15;
658
+
659
+ return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
660
+ }, [globalMousePos, elasticity, glassSize]);
661
+
662
+ const calculateFadeInFactor = useCallback(() => {
663
+ if (!globalMousePos.x || !globalMousePos.y || !glassRef.current) {
664
+ return 0;
665
+ }
666
+
667
+ const rect = glassRef.current.getBoundingClientRect();
668
+ const pillCenterX = rect.left + rect.width / 2;
669
+ const pillCenterY = rect.top + rect.height / 2;
670
+ const pillWidth = glassSize.width;
671
+ const pillHeight = glassSize.height;
672
+
673
+ const edgeDistanceX = Math.max(0, Math.abs(globalMousePos.x - pillCenterX) - pillWidth / 2);
674
+ const edgeDistanceY = Math.max(0, Math.abs(globalMousePos.y - pillCenterY) - pillHeight / 2);
675
+ const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
676
+
677
+ const activationZone = 200;
678
+ return edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone;
679
+ }, [globalMousePos, glassSize]);
680
+
681
+ const calculateElasticTranslation = useCallback(() => {
682
+ if (!glassRef.current) {
683
+ return { x: 0, y: 0 };
684
+ }
685
+
686
+ const fadeInFactor = calculateFadeInFactor();
687
+ const rect = glassRef.current.getBoundingClientRect();
688
+ const pillCenterX = rect.left + rect.width / 2;
689
+ const pillCenterY = rect.top + rect.height / 2;
690
+
691
+ return {
692
+ x: (globalMousePos.x - pillCenterX) * elasticity * 0.1 * fadeInFactor,
693
+ y: (globalMousePos.y - pillCenterY) * elasticity * 0.1 * fadeInFactor,
694
+ };
695
+ }, [globalMousePos, elasticity, calculateFadeInFactor]);
696
+
697
+ useEffect(() => {
698
+ const isValidElement = (element: HTMLElement | null): element is HTMLElement => {
699
+ return element !== null && element instanceof HTMLElement && element.isConnected;
700
+ };
701
+
702
+ let rafId: number | null = null;
703
+ let lastSize = { width: 0, height: 0 };
704
+ let lastCornerRadius = cornerRadius;
705
+
706
+ const updateGlassSize = (forceUpdate = false): void => {
707
+ try {
708
+ if (rafId !== null) {
709
+ cancelAnimationFrame(rafId);
710
+ }
711
+
712
+ rafId = requestAnimationFrame(() => {
713
+ try {
714
+ if (!isValidElement(glassRef.current)) {
715
+ console.warn('AtomixGlass: Element not available for size calculation');
716
+ return;
717
+ }
718
+
719
+ const rect = glassRef.current.getBoundingClientRect();
720
+
721
+ if (rect.width <= 0 || rect.height <= 0) {
722
+ console.warn('AtomixGlass: Invalid dimensions detected', {
723
+ width: rect.width,
724
+ height: rect.height,
725
+ });
726
+ return;
727
+ }
728
+
729
+ const cornerRadiusOffset = Math.max(0, cornerRadius * 0.1);
730
+ const newSize = {
731
+ width: Math.round(rect.width + cornerRadiusOffset),
732
+ height: Math.round(rect.height + cornerRadiusOffset),
733
+ };
734
+
735
+ const cornerRadiusChanged = lastCornerRadius !== cornerRadius;
736
+ const dimensionsChanged =
737
+ newSize.width !== lastSize.width || newSize.height !== lastSize.height;
738
+
739
+ if (forceUpdate || cornerRadiusChanged || dimensionsChanged) {
740
+ lastSize = newSize;
741
+ lastCornerRadius = cornerRadius;
742
+ setGlassSize(newSize);
743
+
744
+ if (enablePerformanceMonitoring && (cornerRadiusChanged || dimensionsChanged)) {
745
+ console.log('AtomixGlass: Size updated', {
746
+ newSize,
747
+ cornerRadius,
748
+ cornerRadiusChanged,
749
+ dimensionsChanged,
750
+ });
751
+ }
752
+ }
753
+ } catch (error) {
754
+ console.error('AtomixGlass: Error updating glass size:', error);
755
+ } finally {
756
+ rafId = null;
757
+ }
758
+ });
759
+ } catch (error) {
760
+ console.error('AtomixGlass: Error in updateGlassSize:', error);
761
+ }
762
+ };
763
+
764
+ let resizeTimeoutId: NodeJS.Timeout | null = null;
765
+ const debouncedResizeHandler = (): void => {
766
+ if (resizeTimeoutId) {
767
+ clearTimeout(resizeTimeoutId);
768
+ }
769
+ resizeTimeoutId = setTimeout(updateGlassSize, 16);
770
+ };
771
+
772
+ try {
773
+ updateGlassSize(true);
774
+ } catch (error) {
775
+ console.error('AtomixGlass: Error in initial size update:', error);
776
+ }
777
+
778
+ let resizeObserver: ResizeObserver | null = null;
779
+ let fallbackInterval: NodeJS.Timeout | null = null;
780
+
781
+ try {
782
+ const hasResizeObserver =
783
+ typeof ResizeObserver !== 'undefined' &&
784
+ typeof ResizeObserver.prototype.observe === 'function';
785
+
786
+ if (hasResizeObserver && isValidElement(glassRef.current)) {
787
+ try {
788
+ resizeObserver = new ResizeObserver(entries => {
789
+ try {
790
+ for (const entry of entries) {
791
+ if (entry.target === glassRef.current) {
792
+ updateGlassSize();
793
+ break;
794
+ }
795
+ }
796
+ } catch (error) {
797
+ console.error('AtomixGlass: Error in ResizeObserver callback:', error);
798
+ }
799
+ });
800
+
801
+ resizeObserver.observe(glassRef.current);
802
+ } catch (resizeObserverError) {
803
+ console.warn(
804
+ 'AtomixGlass: ResizeObserver creation failed, using fallback:',
805
+ resizeObserverError
806
+ );
807
+ fallbackInterval = setInterval(() => {
808
+ if (isValidElement(glassRef.current)) {
809
+ updateGlassSize();
810
+ }
811
+ }, 100);
812
+ }
813
+ } else {
814
+ console.warn('AtomixGlass: ResizeObserver not supported, using fallback polling');
815
+ fallbackInterval = setInterval(() => {
816
+ if (isValidElement(glassRef.current)) {
817
+ updateGlassSize();
818
+ }
819
+ }, 100);
820
+ }
821
+ } catch (error) {
822
+ console.error('AtomixGlass: Error setting up ResizeObserver:', error);
823
+ fallbackInterval = setInterval(() => {
824
+ if (isValidElement(glassRef.current)) {
825
+ updateGlassSize();
826
+ }
827
+ }, 100);
828
+ }
829
+
830
+ window.addEventListener('resize', debouncedResizeHandler, { passive: true });
831
+
832
+ return () => {
833
+ try {
834
+ if (rafId !== null) {
835
+ cancelAnimationFrame(rafId);
836
+ rafId = null;
837
+ }
838
+
839
+ if (resizeTimeoutId) {
840
+ clearTimeout(resizeTimeoutId);
841
+ resizeTimeoutId = null;
842
+ }
843
+
844
+ window.removeEventListener('resize', debouncedResizeHandler);
845
+
846
+ if (resizeObserver) {
847
+ try {
848
+ if (isValidElement(glassRef.current)) {
849
+ resizeObserver.unobserve(glassRef.current);
850
+ }
851
+ resizeObserver.disconnect();
852
+ } catch (error) {
853
+ console.error('AtomixGlass: Error cleaning up ResizeObserver:', error);
854
+ }
855
+ resizeObserver = null;
856
+ }
857
+
858
+ if (fallbackInterval) {
859
+ clearInterval(fallbackInterval);
860
+ fallbackInterval = null;
861
+ }
862
+ } catch (error) {
863
+ console.error('AtomixGlass: Error in cleanup:', error);
864
+ }
865
+ };
866
+ }, [cornerRadius, enablePerformanceMonitoring]);
867
+
868
+ useEffect(() => {
869
+ if (!glassRef.current) return;
870
+
871
+ const timeoutId = setTimeout(() => {
872
+ try {
873
+ const rect = glassRef.current?.getBoundingClientRect();
874
+ if (rect && rect.width > 0 && rect.height > 0) {
875
+ const cornerRadiusOffset = Math.max(0, cornerRadius * 0.1);
876
+ const newSize = {
877
+ width: Math.round(rect.width + cornerRadiusOffset),
878
+ height: Math.round(rect.height + cornerRadiusOffset),
879
+ };
880
+ setGlassSize(newSize);
881
+
882
+ if (enablePerformanceMonitoring) {
883
+ console.log('AtomixGlass: Corner radius change triggered size update', {
884
+ cornerRadius,
885
+ newSize,
886
+ });
887
+ }
888
+ }
889
+ } catch (error) {
890
+ console.warn('AtomixGlass: Error in corner radius size update:', error);
891
+ }
892
+ }, 0);
893
+
894
+ return () => clearTimeout(timeoutId);
895
+ }, [cornerRadius, enablePerformanceMonitoring]);
896
+
897
+ const elasticTranslation = useMemo(() => {
898
+ if (effectiveDisableEffects) {
899
+ return { x: 0, y: 0 };
900
+ }
901
+ return calculateElasticTranslation();
902
+ }, [calculateElasticTranslation, effectiveDisableEffects]);
903
+
904
+ const directionalScale = useMemo(() => {
905
+ if (effectiveDisableEffects) {
906
+ return 'scale(1)';
907
+ }
908
+ return calculateDirectionalScale();
909
+ }, [calculateDirectionalScale, effectiveDisableEffects]);
910
+
911
+ const transformStyle = useMemo(() => {
912
+ if (effectiveDisableEffects) {
913
+ return isActive && Boolean(onClick) ? 'scale(0.98)' : 'scale(1)';
914
+ }
915
+ return `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? 'scale(0.96)' : directionalScale}`;
916
+ }, [elasticTranslation, isActive, onClick, directionalScale, effectiveDisableEffects]);
917
+
918
+ const baseStyle = useMemo(
919
+ () => ({
920
+ ...style,
921
+ transform: transformStyle,
922
+ transition: effectiveReducedMotion ? 'none' : 'all ease-out 0.2s',
923
+ willChange: effectiveDisableEffects ? 'auto' : 'transform',
924
+ ...(effectiveHighContrast && {
925
+ border: '2px solid currentColor',
926
+ outline: '2px solid transparent',
927
+ outlineOffset: '2px',
928
+ }),
929
+ }),
930
+ [style, transformStyle, effectiveReducedMotion, effectiveDisableEffects, effectiveHighContrast]
931
+ );
932
+
933
+ const positionStyles = useMemo(
934
+ () => ({
935
+ position: (baseStyle.position || 'absolute') as React.CSSProperties['position'],
936
+ top: baseStyle.top || 0,
937
+ left: baseStyle.left || 0,
938
+ }),
939
+ [baseStyle]
940
+ );
941
+
942
+ const getCurrentElementSize = useCallback(() => {
943
+ if (!glassRef.current) {
944
+ return { width: 0, height: 0 };
945
+ }
946
+
947
+ try {
948
+ const rect = glassRef.current.getBoundingClientRect();
949
+ return {
950
+ width: Math.max(rect.width, 0),
951
+ height: Math.max(rect.height, 0),
952
+ };
953
+ } catch (error) {
954
+ console.warn('AtomixGlass: Error getting current element size:', error);
955
+ return { width: 0, height: 0 };
956
+ }
957
+ }, []);
958
+
959
+ const getTransformedSize = useCallback(() => {
960
+ const currentSize = getCurrentElementSize();
961
+
962
+ if (effectiveDisableEffects || currentSize.width === 0 || currentSize.height === 0) {
963
+ return currentSize;
964
+ }
965
+
966
+ let scaleX = 1;
967
+ let scaleY = 1;
968
+
969
+ const simpleScaleMatch = directionalScale.match(/scale\(([^)]+)\)/);
970
+ if (simpleScaleMatch && simpleScaleMatch[1]) {
971
+ const scaleValue = parseFloat(simpleScaleMatch[1]);
972
+ scaleX = scaleValue;
973
+ scaleY = scaleValue;
974
+ } else {
975
+ const scaleXMatch = directionalScale.match(/scaleX\(([^)]+)\)/);
976
+ if (scaleXMatch && scaleXMatch[1]) {
977
+ scaleX = parseFloat(scaleXMatch[1]);
978
+ }
979
+
980
+ const scaleYMatch = directionalScale.match(/scaleY\(([^)]+)\)/);
981
+ if (scaleYMatch && scaleYMatch[1]) {
982
+ scaleY = parseFloat(scaleYMatch[1]);
983
+ }
984
+ }
985
+
986
+ const transformedSize = {
987
+ width: currentSize.width * scaleX,
988
+ height: currentSize.height * scaleY,
989
+ };
990
+
991
+ if (enablePerformanceMonitoring && (scaleX !== 1 || scaleY !== 1)) {
992
+ console.log('AtomixGlass: Scale transformation detected', {
993
+ directionalScale,
994
+ scaleX,
995
+ scaleY,
996
+ originalSize: currentSize,
997
+ transformedSize,
998
+ });
999
+ }
1000
+
1001
+ return transformedSize;
1002
+ }, [
1003
+ getCurrentElementSize,
1004
+ directionalScale,
1005
+ effectiveDisableEffects,
1006
+ enablePerformanceMonitoring,
1007
+ ]);
1008
+
1009
+ const borderLayer1Style = useMemo(() => {
1010
+ const borderWidth = 1.5;
1011
+
1012
+ const adjustedSize = {
1013
+ width: baseStyle.position !== 'fixed' ? '100%' : baseStyle.width ? baseStyle.width : Math.max(glassSize.width, 0),
1014
+ height: baseStyle.position !== 'fixed' ? '100%' : baseStyle.height ? baseStyle.height : Math.max(glassSize.height, 0),
1015
+ };
1016
+
1017
+ return {
1018
+ ...positionStyles,
1019
+
1020
+ width: adjustedSize.width,
1021
+ height: adjustedSize.height,
1022
+ borderRadius: `${Math.max(0, cornerRadius)}px`,
1023
+ transform: baseStyle.transform,
1024
+ transition: effectiveReducedMotion ? 'none' : baseStyle.transition,
1025
+ overflow: 'hidden',
1026
+ pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
1027
+ mixBlendMode: 'screen' as React.CSSProperties['mixBlendMode'],
1028
+ opacity: 0.2,
1029
+ padding: `${borderWidth}px`,
1030
+ boxSizing: 'border-box' as React.CSSProperties['boxSizing'],
1031
+ zIndex: 5,
1032
+ WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
1033
+ WebkitMaskComposite: 'xor',
1034
+ maskComposite: 'exclude',
1035
+ boxShadow:
1036
+ '0 0 0 0.5px rgba(255, 255, 255, 0.5) inset, 0 1px 3px rgba(255, 255, 255, 0.25) inset, 0 1px 4px rgba(0, 0, 0, 0.35)',
1037
+ background: `linear-gradient(
1038
+ ${135 + mouseOffset.x * 1.2}deg,
1039
+ rgba(255, 255, 255, 0.0) 0%,
1040
+ rgba(255, 255, 255, ${0.12 + Math.abs(mouseOffset.x) * 0.008}) ${Math.max(10, 33 + mouseOffset.y * 0.3)}%,
1041
+ rgba(255, 255, 255, ${0.4 + Math.abs(mouseOffset.x) * 0.012}) ${Math.min(90, 66 + mouseOffset.y * 0.4)}%,
1042
+ rgba(255, 255, 255, 0.0) 100%
1043
+ )`,
1044
+ };
1045
+ }, [
1046
+ positionStyles,
1047
+ glassSize,
1048
+ cornerRadius,
1049
+ baseStyle,
1050
+ mouseOffset,
1051
+ effectiveReducedMotion,
1052
+ ]);
1053
+
1054
+ const borderLayer2Style = useMemo(() => {
1055
+ const borderWidth = 1.5;
1056
+
1057
+ const adjustedSize = {
1058
+ width: baseStyle.position !== 'fixed' ? '100%' : baseStyle.width ? baseStyle.width : Math.max(glassSize.width, 0),
1059
+ height: baseStyle.position !== 'fixed' ? '100%' : baseStyle.height ? baseStyle.height : Math.max(glassSize.height, 0),
1060
+ };
1061
+
1062
+ return {
1063
+ ...positionStyles,
1064
+ width: adjustedSize.width,
1065
+ height: adjustedSize.height,
1066
+ borderRadius: `${Math.max(0, cornerRadius)}px`,
1067
+ transform: baseStyle.transform,
1068
+ transition: effectiveReducedMotion ? 'none' : baseStyle.transition,
1069
+ overflow: 'hidden',
1070
+ pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
1071
+ zIndex: 6,
1072
+ mixBlendMode: 'overlay' as React.CSSProperties['mixBlendMode'],
1073
+ padding: `${borderWidth}px`,
1074
+ boxSizing: 'border-box' as React.CSSProperties['boxSizing'],
1075
+ WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
1076
+ WebkitMaskComposite: 'xor',
1077
+ maskComposite: 'exclude',
1078
+ boxShadow:
1079
+ '0 0 0 0.5px rgba(255, 255, 255, 0.5) inset, 0 1px 3px rgba(255, 255, 255, 0.25) inset, 0 1px 4px rgba(0, 0, 0, 0.35)',
1080
+ background: `linear-gradient(
1081
+ ${135 + mouseOffset.x * 1.2}deg,
1082
+ rgba(255, 255, 255, 0.0) 0%,
1083
+ rgba(255, 255, 255, ${0.32 + Math.abs(mouseOffset.x) * 0.008}) ${Math.max(10, 33 + mouseOffset.y * 0.3)}%,
1084
+ rgba(255, 255, 255, ${0.6 + Math.abs(mouseOffset.x) * 0.012}) ${Math.min(90, 66 + mouseOffset.y * 0.4)}%,
1085
+ rgba(255, 255, 255, 0.0) 100%
1086
+ )`,
1087
+ };
1088
+ }, [
1089
+ positionStyles,
1090
+ glassSize,
1091
+ cornerRadius,
1092
+ baseStyle,
1093
+ mouseOffset,
1094
+ effectiveReducedMotion,
1095
+ ]);
1096
+
1097
+ const hoverEffect1Style = useMemo(() => {
1098
+ return {
1099
+ ...positionStyles,
1100
+ position: 'absolute' as React.CSSProperties['position'],
1101
+ inset: '0',
1102
+ borderRadius: `${Math.max(0, cornerRadius)}px`,
1103
+ transform: baseStyle.transform,
1104
+ pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
1105
+ transition: effectiveReducedMotion ? 'none' : 'all 0.2s ease-out',
1106
+ opacity: isHovered || isActive ? 0.5 : 0,
1107
+ background: `radial-gradient(
1108
+ circle at ${50 + mouseOffset.x / 2}% ${50 + mouseOffset.y / 2}%,
1109
+ rgba(255, 255, 255, 0.5) 0%,
1110
+ rgba(255, 255, 255, 0) 50%
1111
+ )`,
1112
+ mixBlendMode: 'overlay' as React.CSSProperties['mixBlendMode'],
1113
+ };
1114
+ }, [
1115
+ positionStyles,
1116
+ cornerRadius,
1117
+ baseStyle,
1118
+ isHovered,
1119
+ isActive,
1120
+ mouseOffset,
1121
+ effectiveReducedMotion,
1122
+ ]);
1123
+
1124
+ const hoverEffect2Style = useMemo(() => {
1125
+ return {
1126
+ ...positionStyles,
1127
+ position: 'absolute' as React.CSSProperties['position'],
1128
+ inset: '0',
1129
+ borderRadius: `${Math.max(0, cornerRadius)}px`,
1130
+ overflow: 'hidden',
1131
+ transform: baseStyle.transform,
1132
+ pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
1133
+ transition: effectiveReducedMotion ? 'none' : 'all 0.2s ease-out',
1134
+ opacity: isActive ? 0.5 : 0,
1135
+ background: `radial-gradient(
1136
+ circle at ${50 + mouseOffset.x / 1.5}% ${50 + mouseOffset.y / 1.5}%,
1137
+ rgba(255, 255, 255, 1) 0%,
1138
+ rgba(255, 255, 255, 0) 80%
1139
+ )`,
1140
+ mixBlendMode: 'overlay' as React.CSSProperties['mixBlendMode'],
1141
+ };
1142
+ }, [positionStyles, cornerRadius, baseStyle, isActive, mouseOffset, effectiveReducedMotion]);
1143
+
1144
+ const hoverEffect3Style = useMemo(() => {
1145
+ return {
1146
+ ...positionStyles,
1147
+ position: 'absolute' as React.CSSProperties['position'],
1148
+ inset: '0',
1149
+ transform: baseStyle.transform,
1150
+ borderRadius: `${Math.max(0, cornerRadius)}px`,
1151
+ pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
1152
+ transition: effectiveReducedMotion ? 'none' : 'all 0.2s ease-out',
1153
+ opacity: isHovered ? 0.4 : isActive ? 0.8 : 0,
1154
+ background: `radial-gradient(
1155
+ circle at ${50 + mouseOffset.x}% ${50 + mouseOffset.y}%,
1156
+ rgba(255, 255, 255, 1) 0%,
1157
+ rgba(255, 255, 255, 0) 100%
1158
+ )`,
1159
+ mixBlendMode: 'overlay' as React.CSSProperties['mixBlendMode'],
1160
+ };
1161
+ }, [
1162
+ positionStyles,
1163
+ cornerRadius,
1164
+ baseStyle,
1165
+ isHovered,
1166
+ isActive,
1167
+ mouseOffset,
1168
+ effectiveReducedMotion,
1169
+ ]);
1170
+
1171
+ return (
1172
+ <div
1173
+ style={{ ...positionStyles, position: 'relative' }}
1174
+ role={role || (onClick ? 'button' : undefined)}
1175
+ tabIndex={onClick ? (tabIndex ?? 0) : tabIndex}
1176
+ aria-label={ariaLabel}
1177
+ aria-describedby={ariaDescribedBy}
1178
+ aria-disabled={onClick ? false : undefined}
1179
+ onKeyDown={
1180
+ onClick
1181
+ ? e => {
1182
+ if (e.key === 'Enter' || e.key === ' ') {
1183
+ e.preventDefault();
1184
+ onClick();
1185
+ }
1186
+ }
1187
+ : undefined
1188
+ }
1189
+ >
1190
+ <div
1191
+ className={`u-bg-dark ${overLight ? 'u-opacity-50' : 'u-opacity-0'}`}
1192
+ style={{
1193
+ ...positionStyles,
1194
+ height: glassSize.height,
1195
+ width: glassSize.width,
1196
+ borderRadius: `${cornerRadius}px`,
1197
+ transform: baseStyle.transform,
1198
+ transition: baseStyle.transition,
1199
+ willChange: 'transform',
1200
+ }}
1201
+ />
1202
+ <div
1203
+ className={`u-bg-black ${overLight ? 'u-opacity-25' : 'u-opacity-0'}`}
1204
+ style={{
1205
+ ...positionStyles,
1206
+ height: glassSize.height,
1207
+ width: glassSize.width,
1208
+ borderRadius: `${cornerRadius}px`,
1209
+ transform: baseStyle.transform,
1210
+ transition: baseStyle.transition,
1211
+ mixBlendMode: 'overlay',
1212
+ pointerEvents: 'none',
1213
+ willChange: 'transform',
1214
+ }}
1215
+ />
1216
+
1217
+ <GlassContainer
1218
+ ref={glassRef}
1219
+ className={className}
1220
+ style={{
1221
+ ...baseStyle,
1222
+ transform: baseStyle.transform,
1223
+ }}
1224
+ cornerRadius={cornerRadius}
1225
+ displacementScale={
1226
+ effectiveDisableEffects ? 0 : overLight ? displacementScale * 0.5 : displacementScale
1227
+ }
1228
+ blurAmount={effectiveDisableEffects ? 0 : blurAmount}
1229
+ saturation={effectiveHighContrast ? 200 : saturation}
1230
+ aberrationIntensity={effectiveDisableEffects ? 0 : aberrationIntensity}
1231
+ glassSize={glassSize}
1232
+ padding={padding}
1233
+ mouseOffset={effectiveDisableEffects ? { x: 0, y: 0 } : mouseOffset}
1234
+ globalMousePos={effectiveDisableEffects ? { x: 0, y: 0 } : globalMousePos}
1235
+ onMouseEnter={() => setIsHovered(true)}
1236
+ onMouseLeave={() => setIsHovered(false)}
1237
+ onMouseDown={() => setIsActive(true)}
1238
+ onMouseUp={() => setIsActive(false)}
1239
+ active={isActive}
1240
+ isHovered={isHovered}
1241
+ isActive={isActive}
1242
+ overLight={overLight}
1243
+ onClick={onClick}
1244
+ mode={effectiveDisableEffects ? 'standard' : mode}
1245
+ transform={baseStyle.transform}
1246
+ >
1247
+ {children}
1248
+ </GlassContainer>
1249
+
1250
+ <span style={borderLayer1Style} />
1251
+
1252
+ <span style={borderLayer2Style} />
1253
+
1254
+ {Boolean(onClick) && (
1255
+ <>
1256
+ <div style={hoverEffect1Style} />
1257
+ <div style={hoverEffect2Style} />
1258
+ <div style={hoverEffect3Style} />
1259
+ </>
1260
+ )}
1261
+ </div>
1262
+ );
1263
+ }
1264
+
1265
+ export default AtomixGlass;
1266
+