@shohojdhara/atomix 0.2.3 → 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 (225) hide show
  1. package/README.md +19 -0
  2. package/dist/atomix.css +1703 -1544
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/index.d.ts +1465 -963
  5. package/dist/index.esm.js +16289 -25908
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +15650 -21780
  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 +1608 -1450
  14. package/dist/themes/boomdevs.min.css +5 -5
  15. package/dist/themes/esrar.css +1702 -1543
  16. package/dist/themes/esrar.min.css +4 -4
  17. package/dist/themes/flashtrade.css +15159 -0
  18. package/dist/themes/flashtrade.min.css +86 -0
  19. package/dist/themes/mashroom.css +1699 -1540
  20. package/dist/themes/mashroom.min.css +7 -7
  21. package/dist/themes/shaj-default.css +1693 -1534
  22. package/dist/themes/shaj-default.min.css +4 -4
  23. package/package.json +6 -17
  24. package/src/components/Accordion/Accordion.stories.tsx +662 -21
  25. package/src/components/Accordion/Accordion.tsx +21 -14
  26. package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
  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 +792 -68
  34. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
  35. package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
  36. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
  37. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
  38. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
  39. package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
  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 +76 -55
  44. package/src/components/Badge/Badge.tsx +12 -14
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
  46. package/src/components/Button/Button.stories.tsx +501 -20
  47. package/src/components/Button/Button.tsx +5 -8
  48. package/src/components/Callout/Callout.stories.tsx +86 -35
  49. package/src/components/Callout/Callout.tsx +31 -9
  50. package/src/components/Card/Card.stories.tsx +565 -2
  51. package/src/components/Card/Card.tsx +15 -4
  52. package/src/components/Card/ElevationCard.tsx +2 -0
  53. package/src/components/Chart/AnimatedChart.tsx +179 -156
  54. package/src/components/Chart/AreaChart.tsx +123 -12
  55. package/src/components/Chart/BarChart.tsx +91 -100
  56. package/src/components/Chart/BaseChart.tsx +80 -0
  57. package/src/components/Chart/BubbleChart.tsx +114 -290
  58. package/src/components/Chart/CandlestickChart.tsx +282 -622
  59. package/src/components/Chart/Chart.stories.tsx +576 -179
  60. package/src/components/Chart/Chart.tsx +374 -75
  61. package/src/components/Chart/ChartRenderer.tsx +371 -220
  62. package/src/components/Chart/ChartToolbar.tsx +372 -61
  63. package/src/components/Chart/ChartTooltip.tsx +33 -18
  64. package/src/components/Chart/DonutChart.tsx +172 -254
  65. package/src/components/Chart/FunnelChart.tsx +169 -240
  66. package/src/components/Chart/GaugeChart.tsx +224 -392
  67. package/src/components/Chart/HeatmapChart.tsx +302 -440
  68. package/src/components/Chart/LineChart.tsx +148 -103
  69. package/src/components/Chart/MultiAxisChart.tsx +267 -395
  70. package/src/components/Chart/PieChart.tsx +114 -64
  71. package/src/components/Chart/RadarChart.tsx +202 -218
  72. package/src/components/Chart/ScatterChart.tsx +111 -97
  73. package/src/components/Chart/TreemapChart.tsx +147 -222
  74. package/src/components/Chart/WaterfallChart.tsx +253 -291
  75. package/src/components/Chart/index.ts +11 -9
  76. package/src/components/Chart/types.ts +85 -9
  77. package/src/components/Chart/utils.ts +66 -0
  78. package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
  79. package/src/components/Countdown/Countdown.tsx +4 -0
  80. package/src/components/DataTable/DataTable.tsx +2 -1
  81. package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
  82. package/src/components/DatePicker/DatePicker.tsx +3 -9
  83. package/src/components/DatePicker/types.ts +5 -0
  84. package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
  85. package/src/components/Dropdown/Dropdown.tsx +26 -28
  86. package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
  87. package/src/components/EdgePanel/EdgePanel.tsx +101 -13
  88. package/src/components/Footer/Footer.stories.tsx +187 -60
  89. package/src/components/Footer/Footer.test.tsx +134 -0
  90. package/src/components/Footer/Footer.tsx +133 -34
  91. package/src/components/Footer/FooterLink.tsx +1 -1
  92. package/src/components/Footer/FooterSection.tsx +53 -36
  93. package/src/components/Footer/FooterSocialLink.tsx +32 -29
  94. package/src/components/Footer/README.md +82 -3
  95. package/src/components/Footer/index.ts +1 -1
  96. package/src/components/Form/Checkbox.stories.tsx +13 -5
  97. package/src/components/Form/Checkbox.tsx +3 -6
  98. package/src/components/Form/Form.stories.tsx +10 -3
  99. package/src/components/Form/Form.tsx +2 -0
  100. package/src/components/Form/FormGroup.tsx +2 -1
  101. package/src/components/Form/Input.stories.tsx +12 -11
  102. package/src/components/Form/Input.tsx +97 -95
  103. package/src/components/Form/Radio.stories.tsx +22 -7
  104. package/src/components/Form/Radio.tsx +3 -6
  105. package/src/components/Form/Select.stories.tsx +21 -6
  106. package/src/components/Form/Select.tsx +3 -5
  107. package/src/components/Form/Textarea.stories.tsx +13 -11
  108. package/src/components/Form/Textarea.tsx +88 -86
  109. package/src/components/Hero/Hero.stories.tsx +2 -3
  110. package/src/components/Hero/Hero.tsx +5 -6
  111. package/src/components/Icon/Icon.tsx +12 -1
  112. package/src/components/List/List.tsx +2 -1
  113. package/src/components/List/ListGroup.tsx +2 -1
  114. package/src/components/Messages/Messages.stories.tsx +113 -0
  115. package/src/components/Messages/Messages.tsx +52 -9
  116. package/src/components/Modal/Modal.stories.tsx +48 -32
  117. package/src/components/Modal/Modal.tsx +19 -24
  118. package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
  119. package/src/components/Navigation/Menu/Menu.tsx +2 -2
  120. package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
  121. package/src/components/Navigation/Nav/Nav.tsx +22 -4
  122. package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
  123. package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
  124. package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
  125. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
  126. package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
  127. package/src/components/Pagination/Pagination.stories.tsx +13 -6
  128. package/src/components/Pagination/Pagination.tsx +7 -6
  129. package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
  130. package/src/components/Popover/Popover.stories.tsx +32 -24
  131. package/src/components/Popover/Popover.tsx +4 -1
  132. package/src/components/ProductReview/ProductReview.tsx +8 -2
  133. package/src/components/Progress/Progress.tsx +19 -3
  134. package/src/components/Rating/Rating.stories.tsx +11 -6
  135. package/src/components/Rating/Rating.tsx +3 -5
  136. package/src/components/River/River.tsx +5 -5
  137. package/src/components/SectionIntro/SectionIntro.tsx +8 -2
  138. package/src/components/Slider/Slider.stories.tsx +4 -4
  139. package/src/components/Slider/Slider.tsx +4 -3
  140. package/src/components/Spinner/Spinner.tsx +19 -3
  141. package/src/components/Steps/Steps.stories.tsx +5 -4
  142. package/src/components/Steps/Steps.tsx +8 -5
  143. package/src/components/Tab/Tab.stories.tsx +4 -3
  144. package/src/components/Tab/Tab.tsx +8 -6
  145. package/src/components/Testimonial/Testimonial.tsx +8 -2
  146. package/src/components/Todo/Todo.tsx +2 -1
  147. package/src/components/Toggle/Toggle.stories.tsx +5 -4
  148. package/src/components/Toggle/Toggle.tsx +8 -5
  149. package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
  150. package/src/components/Tooltip/Tooltip.tsx +9 -2
  151. package/src/components/Upload/Upload.stories.tsx +252 -0
  152. package/src/components/Upload/Upload.tsx +92 -53
  153. package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
  154. package/src/components/index.ts +0 -4
  155. package/src/layouts/Grid/Grid.stories.tsx +10 -23
  156. package/src/layouts/Grid/Grid.tsx +20 -1
  157. package/src/layouts/Grid/GridCol.tsx +76 -48
  158. package/src/lib/composables/useAtomixGlass.ts +861 -44
  159. package/src/lib/composables/useBarChart.ts +21 -4
  160. package/src/lib/composables/useChart.ts +227 -370
  161. package/src/lib/composables/useChartExport.ts +19 -78
  162. package/src/lib/composables/useChartToolbar.ts +11 -21
  163. package/src/lib/composables/useEdgePanel.ts +125 -71
  164. package/src/lib/composables/useFooter.ts +3 -3
  165. package/src/lib/composables/useGlassContainer.ts +16 -7
  166. package/src/lib/composables/useLineChart.ts +11 -2
  167. package/src/lib/composables/usePieChart.ts +4 -14
  168. package/src/lib/composables/useRiver.ts +5 -0
  169. package/src/lib/composables/useSlider.ts +62 -24
  170. package/src/lib/composables/useVideoPlayer.ts +60 -63
  171. package/src/lib/constants/components.ts +147 -32
  172. package/src/lib/types/components.ts +355 -25
  173. package/src/lib/utils/displacement-generator.ts +55 -49
  174. package/src/lib/utils/icons.ts +1 -1
  175. package/src/lib/utils/index.ts +16 -10
  176. package/src/styles/01-settings/_settings.accordion.scss +19 -19
  177. package/src/styles/01-settings/_settings.animations.scss +5 -5
  178. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  179. package/src/styles/01-settings/_settings.avatar.scss +17 -17
  180. package/src/styles/01-settings/_settings.background.scss +0 -3
  181. package/src/styles/01-settings/_settings.badge.scss +1 -1
  182. package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
  183. package/src/styles/01-settings/_settings.card.scss +1 -1
  184. package/src/styles/01-settings/_settings.chart.scss +65 -2
  185. package/src/styles/01-settings/_settings.dropdown.scss +1 -1
  186. package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
  187. package/src/styles/01-settings/_settings.footer.scss +35 -42
  188. package/src/styles/01-settings/_settings.input.scss +1 -1
  189. package/src/styles/01-settings/_settings.list.scss +1 -1
  190. package/src/styles/01-settings/_settings.rating.scss +1 -1
  191. package/src/styles/01-settings/_settings.tabs.scss +1 -1
  192. package/src/styles/01-settings/_settings.upload.scss +6 -5
  193. package/src/styles/02-tools/_tools.animations.scss +4 -5
  194. package/src/styles/02-tools/_tools.background.scss +1 -13
  195. package/src/styles/02-tools/_tools.glass.scss +0 -1
  196. package/src/styles/02-tools/_tools.utility-api.scss +91 -48
  197. package/src/styles/03-generic/_generic.root.scss +1 -4
  198. package/src/styles/04-elements/_elements.body.scss +0 -1
  199. package/src/styles/06-components/_components.atomix-glass.scss +249 -0
  200. package/src/styles/06-components/_components.badge.scss +8 -23
  201. package/src/styles/06-components/_components.button.scss +8 -3
  202. package/src/styles/06-components/_components.callout.scss +10 -5
  203. package/src/styles/06-components/_components.card.scss +2 -14
  204. package/src/styles/06-components/_components.chart.scss +969 -1449
  205. package/src/styles/06-components/_components.dropdown.scss +19 -7
  206. package/src/styles/06-components/_components.edge-panel.scss +103 -0
  207. package/src/styles/06-components/_components.footer.scss +166 -85
  208. package/src/styles/06-components/_components.input.scss +8 -9
  209. package/src/styles/06-components/_components.list.scss +1 -0
  210. package/src/styles/06-components/_components.messages.scss +176 -0
  211. package/src/styles/06-components/_components.modal.scss +16 -4
  212. package/src/styles/06-components/_components.navbar.scss +12 -1
  213. package/src/styles/06-components/_components.side-menu.scss +5 -0
  214. package/src/styles/06-components/_components.skeleton.scss +8 -6
  215. package/src/styles/06-components/_components.upload.scss +219 -4
  216. package/src/styles/06-components/old.chart.styles.scss +1 -30
  217. package/src/styles/99-utilities/_index.scss +1 -0
  218. package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
  219. package/src/styles/99-utilities/_utilities.scss +1 -1
  220. package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
  221. package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
  222. package/src/components/Chart/AdvancedChart.tsx +0 -624
  223. package/src/components/Chart/LineChartNew.tsx +0 -167
  224. package/src/components/Chart/RealTimeChart.tsx +0 -436
  225. package/src/components/DatePicker/DatePicker copy.tsx +0 -551
@@ -1,71 +1,888 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
-
3
- export interface UseAtomixGlassOptions {
4
- size?: 'sm' | 'md' | 'lg';
5
- mode?: 'standard' | 'polar' | 'prominent' | 'shader';
6
- overLight?: boolean;
7
- clickable?: boolean;
8
- active?: boolean;
9
- className?: string;
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import type {
9
+ AtomixGlassProps,
10
+ DisplacementMode,
11
+ GlassSize,
12
+ MousePosition,
13
+ OverLightConfig,
14
+ OverLightObjectConfig,
15
+ } from '../types/components';
16
+ import { ATOMIX_GLASS } from '../constants/components';
17
+ import {
18
+ calculateDistance,
19
+ calculateElementCenter,
20
+ calculateMouseInfluence,
21
+ extractBorderRadiusFromChildren,
22
+ extractBorderRadiusFromDOMElement,
23
+ validateGlassSize,
24
+ } from '../../components/AtomixGlass/glass-utils';
25
+
26
+ const { CONSTANTS } = ATOMIX_GLASS;
27
+
28
+ interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
29
+ glassRef: React.RefObject<HTMLDivElement>;
30
+ contentRef: React.RefObject<HTMLDivElement>;
31
+ children?: React.ReactNode;
10
32
  }
11
33
 
12
- export interface UseAtomixGlassReturn {
13
- generateGlassClass: (options: UseAtomixGlassOptions) => string;
14
- generateElementClass: (element: string, modifier?: string) => string;
34
+ interface UseAtomixGlassReturn {
35
+ // State
36
+ isHovered: boolean;
37
+ isActive: boolean;
38
+ glassSize: GlassSize;
39
+ dynamicCornerRadius: number;
40
+ effectiveCornerRadius: number;
41
+ effectiveReducedMotion: boolean;
42
+ effectiveHighContrast: boolean;
43
+ effectiveDisableEffects: boolean;
44
+ detectedOverLight: boolean;
45
+ globalMousePosition: MousePosition;
46
+ mouseOffset: MousePosition;
47
+
48
+ // OverLight config
49
+ overLightConfig: {
50
+ isOverLight: boolean;
51
+ threshold: number;
52
+ opacity: number;
53
+ contrast: number;
54
+ brightness: number;
55
+ saturationBoost: number;
56
+ shadowIntensity: number;
57
+ borderOpacity: number;
58
+ };
59
+
60
+ // Transform calculations
61
+ elasticTranslation: { x: number; y: number };
62
+ directionalScale: string;
63
+ transformStyle: string;
64
+
65
+ // Event handlers
66
+ handleMouseEnter: () => void;
67
+ handleMouseLeave: () => void;
68
+ handleMouseDown: () => void;
69
+ handleMouseUp: () => void;
70
+ handleMouseMove: (e: MouseEvent) => void;
71
+ handleKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
15
72
  }
16
73
 
17
74
  /**
18
- * Hook for generating AtomixGlass CSS classes following BEM methodology
75
+ * Composable hook for AtomixGlass component logic
76
+ * Manages all state, calculations, and event handlers
19
77
  */
20
- export function useAtomixGlass(): UseAtomixGlassReturn {
21
- // Generate CSS class for the main glass component
22
- const generateGlassClass = useCallback((options: UseAtomixGlassOptions) => {
23
- const classes = ['c-atomix-glass'];
78
+ export function useAtomixGlass({
79
+ glassRef,
80
+ contentRef,
81
+ cornerRadius,
82
+ globalMousePosition: externalGlobalMousePosition,
83
+ mouseOffset: externalMouseOffset,
84
+ mouseContainer,
85
+ overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT,
86
+ reducedMotion = false,
87
+ highContrast = false,
88
+ disableEffects = false,
89
+ elasticity = 0.05,
90
+ onClick,
91
+ debugCornerRadius = false,
92
+ debugOverLight = false,
93
+ enablePerformanceMonitoring = false,
94
+ children,
95
+ }: UseAtomixGlassOptions): UseAtomixGlassReturn {
96
+ // State
97
+ const [isHovered, setIsHovered] = useState(false);
98
+ const [isActive, setIsActive] = useState(false);
99
+ const [glassSize, setGlassSize] = useState<GlassSize>({ width: 270, height: 69 });
100
+ const [internalGlobalMousePosition, setInternalGlobalMousePosition] = useState<MousePosition>({
101
+ x: 0,
102
+ y: 0,
103
+ });
104
+ const [internalMouseOffset, setInternalMouseOffset] = useState<MousePosition>({ x: 0, y: 0 });
105
+ const [dynamicCornerRadius, setDynamicCornerRadius] = useState<number>(
106
+ CONSTANTS.DEFAULT_CORNER_RADIUS
107
+ );
108
+ const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
109
+ const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
110
+ const [detectedOverLight, setDetectedOverLight] = useState(false);
24
111
 
25
- // Add size modifier
26
- if (options.size && options.size !== 'md') {
27
- classes.push(`c-atomix-glass--${options.size}`);
112
+ // Memoized derived values
113
+ const effectiveCornerRadius = useMemo(() => {
114
+ if (cornerRadius !== undefined) {
115
+ const result = Math.max(0, cornerRadius);
116
+ if (debugCornerRadius) {
117
+ console.log('[AtomixGlass] Using manual cornerRadius prop:', result);
118
+ }
119
+ return result;
28
120
  }
29
121
 
30
- // Add mode modifier
31
- if (options.mode && options.mode !== 'standard') {
32
- classes.push(`c-atomix-glass--${options.mode}`);
122
+ const result = Math.max(0, dynamicCornerRadius);
123
+ if (debugCornerRadius) {
124
+ console.log('[AtomixGlass] Using dynamic cornerRadius:', result);
33
125
  }
126
+ return result;
127
+ }, [cornerRadius, dynamicCornerRadius, debugCornerRadius]);
128
+
129
+ const effectiveReducedMotion = useMemo(
130
+ () => reducedMotion || userPrefersReducedMotion,
131
+ [reducedMotion, userPrefersReducedMotion]
132
+ );
133
+
134
+ const effectiveHighContrast = useMemo(
135
+ () => highContrast || userPrefersHighContrast,
136
+ [highContrast, userPrefersHighContrast]
137
+ );
138
+
139
+ const effectiveDisableEffects = useMemo(
140
+ () => disableEffects || effectiveReducedMotion,
141
+ [disableEffects, effectiveReducedMotion]
142
+ );
143
+
144
+ const globalMousePosition = useMemo(
145
+ () => externalGlobalMousePosition || internalGlobalMousePosition,
146
+ [externalGlobalMousePosition, internalGlobalMousePosition]
147
+ );
148
+
149
+ const mouseOffset = useMemo(
150
+ () => externalMouseOffset || internalMouseOffset,
151
+ [externalMouseOffset, internalMouseOffset]
152
+ );
153
+
154
+ // Extract border-radius from children
155
+ useEffect(() => {
156
+ const extractRadius = () => {
157
+ try {
158
+ let extractedRadius: number | null = null;
159
+ let extractionSource = 'default';
160
+
161
+ if (contentRef.current) {
162
+ const firstChild = contentRef.current.firstElementChild as HTMLElement;
163
+ if (firstChild) {
164
+ const domRadius = extractBorderRadiusFromDOMElement(firstChild);
165
+ if (domRadius !== null && domRadius > 0) {
166
+ extractedRadius = domRadius;
167
+ extractionSource = 'DOM element';
168
+ }
169
+ }
170
+ }
171
+
172
+ if (extractedRadius === null) {
173
+ const childRadius = extractBorderRadiusFromChildren(children);
174
+ if (childRadius > 0 && childRadius !== CONSTANTS.DEFAULT_CORNER_RADIUS) {
175
+ extractedRadius = childRadius;
176
+ extractionSource = 'React children';
177
+ }
178
+ }
179
+
180
+ if (extractedRadius !== null && extractedRadius > 0) {
181
+ setDynamicCornerRadius(extractedRadius);
182
+
183
+ if (debugCornerRadius) {
184
+ console.log('[AtomixGlass] Corner radius extracted:', {
185
+ value: extractedRadius,
186
+ source: extractionSource,
187
+ timestamp: new Date().toISOString(),
188
+ });
189
+ }
190
+ } else if (debugCornerRadius) {
191
+ console.log(
192
+ '[AtomixGlass] No corner radius found, using default:',
193
+ CONSTANTS.DEFAULT_CORNER_RADIUS
194
+ );
195
+ }
196
+ } catch (error) {
197
+ if (debugCornerRadius) {
198
+ console.error('[AtomixGlass] Error extracting corner radius:', error);
199
+ }
200
+ }
201
+ };
34
202
 
35
- // Add state modifiers
36
- if (options.clickable) {
37
- classes.push('c-atomix-glass--clickable');
203
+ extractRadius();
204
+ const timeoutId = setTimeout(extractRadius, 100);
205
+ return () => clearTimeout(timeoutId);
206
+ }, [children, debugCornerRadius, contentRef]);
207
+
208
+ // Media query handlers and background detection
209
+ useEffect(() => {
210
+ // Only run auto-detection for 'auto' mode or object config (which uses auto-detection)
211
+ const shouldDetect = (overLight === 'auto' || (typeof overLight === 'object' && overLight !== null));
212
+
213
+ if (shouldDetect && glassRef.current) {
214
+ const timeoutId = setTimeout(() => {
215
+ try {
216
+ const element = glassRef.current;
217
+ if (!element) {
218
+ setDetectedOverLight(false);
219
+ return;
220
+ }
221
+
222
+ // Validate window context
223
+ if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
224
+ setDetectedOverLight(false);
225
+ return;
226
+ }
227
+
228
+ let totalLuminance = 0;
229
+ let validSamples = 0;
230
+ let hasValidBackground = false;
231
+
232
+ let currentElement = element.parentElement;
233
+ let depth = 0;
234
+ const maxDepth = 20;
235
+ const maxSamples = 10;
236
+
237
+ // Limit traversal depth to prevent infinite loops and performance issues
238
+ while (currentElement && validSamples < maxSamples && depth < maxDepth) {
239
+ try {
240
+ const computedStyle = window.getComputedStyle(currentElement);
241
+ if (!computedStyle) {
242
+ currentElement = currentElement.parentElement;
243
+ depth++;
244
+ continue;
245
+ }
246
+
247
+ const bgColor = computedStyle.backgroundColor;
248
+ const bgImage = computedStyle.backgroundImage;
249
+
250
+ // Check for solid color backgrounds
251
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' && bgColor !== 'initial' && bgColor !== 'none') {
252
+ const rgb = bgColor.match(/\d+/g);
253
+ if (rgb && rgb.length >= 3) {
254
+ const r = Number(rgb[0]);
255
+ const g = Number(rgb[1]);
256
+ const b = Number(rgb[2]);
257
+
258
+ // Validate RGB values are valid numbers
259
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b) &&
260
+ isFinite(r) && isFinite(g) && isFinite(b) &&
261
+ r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
262
+ // Only consider if it's not pure black or very dark
263
+ if (r > 10 || g > 10 || b > 10) {
264
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
265
+ if (!isNaN(luminance) && isFinite(luminance)) {
266
+ totalLuminance += luminance;
267
+ validSamples++;
268
+ hasValidBackground = true;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ // Check for image backgrounds
276
+ if (bgImage && bgImage !== 'none' && bgImage !== 'initial') {
277
+ // For image backgrounds, assume medium luminance
278
+ totalLuminance += 0.5;
279
+ validSamples++;
280
+ hasValidBackground = true;
281
+ }
282
+ } catch (styleError) {
283
+ // Silently continue if getting computed style fails for this element
284
+ if (process.env.NODE_ENV === 'development') {
285
+ console.debug('AtomixGlass: Error getting computed style for element:', styleError);
286
+ }
287
+ }
288
+
289
+ // Move to parent element for next iteration
290
+ if (currentElement) {
291
+ currentElement = currentElement.parentElement;
292
+ depth++;
293
+ } else {
294
+ break; // Exit loop if currentElement becomes null
295
+ }
296
+ }
297
+
298
+ // More conservative detection with better error handling
299
+ if (hasValidBackground && validSamples > 0) {
300
+ const avgLuminance = totalLuminance / validSamples;
301
+ if (!isNaN(avgLuminance) && isFinite(avgLuminance)) {
302
+ let threshold = 0.7; // Conservative threshold for overlight
303
+
304
+ // If overLight is an object, use its threshold property with validation
305
+ if (typeof overLight === 'object' && overLight !== null) {
306
+ const objConfig = overLight as OverLightObjectConfig;
307
+ if (objConfig.threshold !== undefined) {
308
+ const configThreshold = typeof objConfig.threshold === 'number' &&
309
+ !isNaN(objConfig.threshold) &&
310
+ isFinite(objConfig.threshold)
311
+ ? objConfig.threshold
312
+ : 0.7;
313
+ threshold = Math.min(0.9, Math.max(0.1, configThreshold));
314
+ }
315
+ }
316
+
317
+ const isOverLightDetected = avgLuminance > threshold;
318
+ setDetectedOverLight(isOverLightDetected);
319
+
320
+ // Debug logging
321
+ if (debugOverLight) {
322
+ console.log('[AtomixGlass] OverLight Detection:', {
323
+ avgLuminance: avgLuminance.toFixed(3),
324
+ threshold: threshold.toFixed(3),
325
+ detected: isOverLightDetected,
326
+ validSamples,
327
+ totalLuminance: totalLuminance.toFixed(3),
328
+ configType: typeof overLight === 'object' ? 'object' : typeof overLight,
329
+ timestamp: new Date().toISOString(),
330
+ });
331
+ }
332
+ } else {
333
+ // Invalid luminance calculation, default to false
334
+ setDetectedOverLight(false);
335
+ }
336
+ } else {
337
+ // Default to false if no valid background found
338
+ setDetectedOverLight(false);
339
+ }
340
+ } catch (error) {
341
+ // Enhanced error logging with context
342
+ if (process.env.NODE_ENV === 'development') {
343
+ console.warn('AtomixGlass: Error detecting background brightness:', error);
344
+ }
345
+ setDetectedOverLight(false);
346
+ }
347
+ }, 150);
348
+
349
+ return () => clearTimeout(timeoutId);
350
+ } else if (typeof overLight === 'boolean') {
351
+ // For boolean values, disable auto-detection
352
+ setDetectedOverLight(false);
353
+
354
+ // Debug logging for boolean mode
355
+ if (debugOverLight) {
356
+ console.log('[AtomixGlass] OverLight Mode: boolean', {
357
+ value: overLight,
358
+ autoDetection: false,
359
+ timestamp: new Date().toISOString(),
360
+ });
361
+ }
38
362
  }
39
363
 
40
- if (options.overLight) {
41
- classes.push('c-atomix-glass--over-light');
364
+ if (typeof window.matchMedia !== 'function') {
365
+ return undefined;
42
366
  }
43
367
 
44
- if (options.active) {
45
- classes.push('c-atomix-glass--active');
368
+ try {
369
+ const mediaQueryReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
370
+ const mediaQueryHighContrast = window.matchMedia('(prefers-contrast: high)');
371
+
372
+ setUserPrefersReducedMotion(mediaQueryReducedMotion.matches);
373
+ setUserPrefersHighContrast(mediaQueryHighContrast.matches);
374
+
375
+ const handleReducedMotionChange = (e: MediaQueryListEvent) => {
376
+ setUserPrefersReducedMotion(e.matches);
377
+ };
378
+
379
+ const handleHighContrastChange = (e: MediaQueryListEvent) => {
380
+ setUserPrefersHighContrast(e.matches);
381
+ };
382
+
383
+ if (mediaQueryReducedMotion.addEventListener) {
384
+ mediaQueryReducedMotion.addEventListener('change', handleReducedMotionChange);
385
+ mediaQueryHighContrast.addEventListener('change', handleHighContrastChange);
386
+ } else if (mediaQueryReducedMotion.addListener) {
387
+ mediaQueryReducedMotion.addListener(handleReducedMotionChange);
388
+ mediaQueryHighContrast.addListener(handleHighContrastChange);
389
+ }
390
+
391
+ return () => {
392
+ try {
393
+ if (mediaQueryReducedMotion.removeEventListener) {
394
+ mediaQueryReducedMotion.removeEventListener('change', handleReducedMotionChange);
395
+ mediaQueryHighContrast.removeEventListener('change', handleHighContrastChange);
396
+ } else if (mediaQueryReducedMotion.removeListener) {
397
+ mediaQueryReducedMotion.removeListener(handleReducedMotionChange);
398
+ mediaQueryHighContrast.removeListener(handleHighContrastChange);
399
+ }
400
+ } catch (cleanupError) {
401
+ console.error('AtomixGlass: Error cleaning up media query listeners:', cleanupError);
402
+ }
403
+ };
404
+ } catch (error) {
405
+ console.error('AtomixGlass: Error setting up media queries:', error);
406
+ return undefined;
46
407
  }
408
+ }, [overLight, glassRef, debugOverLight]);
409
+
410
+ // Mouse tracking
411
+ const mouseMoveThrottleRef = useRef<number | null>(null);
412
+ const lastMouseEventRef = useRef<MouseEvent | null>(null);
413
+
414
+ const handleMouseMove = useCallback(
415
+ (e: MouseEvent) => {
416
+ lastMouseEventRef.current = e;
417
+
418
+ if (mouseMoveThrottleRef.current === null) {
419
+ mouseMoveThrottleRef.current = requestAnimationFrame(() => {
420
+ const event = lastMouseEventRef.current;
421
+ if (!event) {
422
+ mouseMoveThrottleRef.current = null;
423
+ return;
424
+ }
47
425
 
48
- // Add custom className
49
- if (options.className) {
50
- classes.push(options.className);
426
+ const container = mouseContainer?.current || glassRef.current;
427
+ if (!container) {
428
+ mouseMoveThrottleRef.current = null;
429
+ return;
430
+ }
431
+
432
+ const startTime = enablePerformanceMonitoring ? performance.now() : 0;
433
+
434
+ const rect = container.getBoundingClientRect();
435
+ if (rect.width === 0 || rect.height === 0) {
436
+ mouseMoveThrottleRef.current = null;
437
+ return;
438
+ }
439
+
440
+ const center = calculateElementCenter(rect);
441
+
442
+ setInternalMouseOffset({
443
+ x: ((event.clientX - center.x) / rect.width) * 100,
444
+ y: ((event.clientY - center.y) / rect.height) * 100,
445
+ });
446
+
447
+ setInternalGlobalMousePosition({
448
+ x: event.clientX,
449
+ y: event.clientY,
450
+ });
451
+
452
+ if (enablePerformanceMonitoring) {
453
+ const endTime = performance.now();
454
+ const duration = endTime - startTime;
455
+ if (duration > 5) {
456
+ console.warn(`AtomixGlass: Mouse tracking took ${duration.toFixed(2)}ms`);
457
+ }
458
+ }
459
+
460
+ mouseMoveThrottleRef.current = null;
461
+ });
462
+ }
463
+ },
464
+ [mouseContainer, glassRef, enablePerformanceMonitoring]
465
+ );
466
+
467
+ useEffect(() => {
468
+ if (externalGlobalMousePosition && externalMouseOffset) {
469
+ return undefined;
470
+ }
471
+
472
+ if (effectiveDisableEffects) {
473
+ return undefined;
474
+ }
475
+
476
+ const container = mouseContainer?.current || glassRef.current;
477
+ if (!container) {
478
+ return undefined;
51
479
  }
52
480
 
53
- return classes.filter(Boolean).join(' ');
54
- }, []);
481
+ container.addEventListener('mousemove', handleMouseMove, { passive: true });
482
+
483
+ return () => {
484
+ container.removeEventListener('mousemove', handleMouseMove);
485
+ if (mouseMoveThrottleRef.current) {
486
+ cancelAnimationFrame(mouseMoveThrottleRef.current);
487
+ mouseMoveThrottleRef.current = null;
488
+ }
489
+ };
490
+ }, [
491
+ handleMouseMove,
492
+ mouseContainer,
493
+ glassRef,
494
+ externalGlobalMousePosition,
495
+ externalMouseOffset,
496
+ effectiveDisableEffects,
497
+ ]);
498
+
499
+ // Transform calculations
500
+ const calculateDirectionalScale = useCallback(() => {
501
+ if (
502
+ !globalMousePosition.x ||
503
+ !globalMousePosition.y ||
504
+ !glassRef.current ||
505
+ !validateGlassSize(glassSize)
506
+ ) {
507
+ return 'scale(1)';
508
+ }
509
+
510
+ const rect = glassRef.current.getBoundingClientRect();
511
+ const center = calculateElementCenter(rect);
512
+ const deltaX = globalMousePosition.x - center.x;
513
+ const deltaY = globalMousePosition.y - center.y;
514
+
515
+ const edgeDistanceX = Math.max(0, Math.abs(deltaX) - glassSize.width / 2);
516
+ const edgeDistanceY = Math.max(0, Math.abs(deltaY) - glassSize.height / 2);
517
+ const edgeDistance = calculateDistance({ x: edgeDistanceX, y: edgeDistanceY }, { x: 0, y: 0 });
518
+
519
+ if (edgeDistance > CONSTANTS.ACTIVATION_ZONE) {
520
+ return 'scale(1)';
521
+ }
522
+
523
+ const fadeInFactor = 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
524
+ const centerDistance = calculateDistance(globalMousePosition, center);
525
+
526
+ if (centerDistance === 0) {
527
+ return 'scale(1)';
528
+ }
529
+
530
+ const normalizedX = deltaX / centerDistance;
531
+ const normalizedY = deltaY / centerDistance;
532
+ const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
533
+
534
+ const scaleX =
535
+ 1 +
536
+ Math.abs(normalizedX) * stretchIntensity * 0.3 -
537
+ Math.abs(normalizedY) * stretchIntensity * 0.15;
538
+ const scaleY =
539
+ 1 +
540
+ Math.abs(normalizedY) * stretchIntensity * 0.3 -
541
+ Math.abs(normalizedX) * stretchIntensity * 0.15;
55
542
 
56
- // Generate CSS class for elements
57
- const generateElementClass = useCallback((element: string, modifier?: string) => {
58
- const baseClass = `c-atomix-glass__${element}`;
543
+ return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
544
+ }, [globalMousePosition, elasticity, glassSize, glassRef]);
59
545
 
60
- if (modifier) {
61
- return `${baseClass}--${modifier}`;
546
+ const calculateFadeInFactor = useCallback(() => {
547
+ if (
548
+ !globalMousePosition.x ||
549
+ !globalMousePosition.y ||
550
+ !glassRef.current ||
551
+ !validateGlassSize(glassSize)
552
+ ) {
553
+ return 0;
62
554
  }
63
555
 
64
- return baseClass;
65
- }, []);
556
+ const rect = glassRef.current.getBoundingClientRect();
557
+ const center = calculateElementCenter(rect);
558
+
559
+ const edgeDistanceX = Math.max(
560
+ 0,
561
+ Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2
562
+ );
563
+ const edgeDistanceY = Math.max(
564
+ 0,
565
+ Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2
566
+ );
567
+ const edgeDistance = calculateDistance({ x: edgeDistanceX, y: edgeDistanceY }, { x: 0, y: 0 });
568
+
569
+ return edgeDistance > CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
570
+ }, [globalMousePosition, glassSize, glassRef]);
571
+
572
+ const calculateElasticTranslation = useCallback(() => {
573
+ if (!glassRef.current) {
574
+ return { x: 0, y: 0 };
575
+ }
576
+
577
+ const fadeInFactor = calculateFadeInFactor();
578
+ const rect = glassRef.current.getBoundingClientRect();
579
+ const center = calculateElementCenter(rect);
580
+
581
+ return {
582
+ x: (globalMousePosition.x - center.x) * elasticity * 0.1 * fadeInFactor,
583
+ y: (globalMousePosition.y - center.y) * elasticity * 0.1 * fadeInFactor,
584
+ };
585
+ }, [globalMousePosition, elasticity, calculateFadeInFactor, glassRef]);
586
+
587
+ const elasticTranslation = useMemo(() => {
588
+ if (effectiveDisableEffects) {
589
+ return { x: 0, y: 0 };
590
+ }
591
+ return calculateElasticTranslation();
592
+ }, [calculateElasticTranslation, effectiveDisableEffects]);
593
+
594
+ const directionalScale = useMemo(() => {
595
+ if (effectiveDisableEffects) {
596
+ return 'scale(1)';
597
+ }
598
+ return calculateDirectionalScale();
599
+ }, [calculateDirectionalScale, effectiveDisableEffects]);
600
+
601
+ const transformStyle = useMemo(() => {
602
+ if (effectiveDisableEffects) {
603
+ return isActive && Boolean(onClick) ? 'scale(0.98)' : 'scale(1)';
604
+ }
605
+ return `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? 'scale(0.96)' : directionalScale}`;
606
+ }, [elasticTranslation, isActive, onClick, directionalScale, effectiveDisableEffects]);
607
+
608
+ // Size management
609
+ useEffect(() => {
610
+ const isValidElement = (element: HTMLElement | null): element is HTMLElement =>
611
+ element !== null && element instanceof HTMLElement && element.isConnected;
612
+
613
+ const validateSize = (size: GlassSize): boolean =>
614
+ validateGlassSize(size) && size.width <= CONSTANTS.MAX_SIZE && size.height <= CONSTANTS.MAX_SIZE;
615
+
616
+ let rafId: number | null = null;
617
+ let lastSize = { width: 0, height: 0 };
618
+ let lastCornerRadius = effectiveCornerRadius;
619
+
620
+ const updateGlassSize = (forceUpdate = false): void => {
621
+ if (rafId !== null) cancelAnimationFrame(rafId);
622
+
623
+ rafId = requestAnimationFrame(() => {
624
+ if (!isValidElement(glassRef.current)) {
625
+ rafId = null;
626
+ return;
627
+ }
628
+
629
+ const rect = glassRef.current.getBoundingClientRect();
630
+ if (rect.width <= 0 || rect.height <= 0) {
631
+ rafId = null;
632
+ return;
633
+ }
634
+
635
+ const cornerRadiusOffset = Math.max(0, Math.min(effectiveCornerRadius * 0.1, 10));
636
+ const newSize: GlassSize = {
637
+ width: Math.round(rect.width + cornerRadiusOffset),
638
+ height: Math.round(rect.height + cornerRadiusOffset),
639
+ };
640
+
641
+ const cornerRadiusChanged = lastCornerRadius !== effectiveCornerRadius;
642
+ const dimensionsChanged =
643
+ Math.abs(newSize.width - lastSize.width) > 1 ||
644
+ Math.abs(newSize.height - lastSize.height) > 1;
645
+
646
+ if ((forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateSize(newSize)) {
647
+ lastSize = newSize;
648
+ lastCornerRadius = effectiveCornerRadius;
649
+ setGlassSize(newSize);
650
+ }
651
+
652
+ rafId = null;
653
+ });
654
+ };
655
+
656
+ let resizeTimeoutId: NodeJS.Timeout | null = null;
657
+ const debouncedResizeHandler = (): void => {
658
+ if (resizeTimeoutId) clearTimeout(resizeTimeoutId);
659
+ resizeTimeoutId = setTimeout(() => updateGlassSize(false), 16);
660
+ };
661
+
662
+ const initialTimeoutId = setTimeout(() => updateGlassSize(true), 0);
663
+
664
+ let resizeObserver: ResizeObserver | null = null;
665
+ let fallbackInterval: NodeJS.Timeout | null = null;
666
+
667
+ const hasResizeObserver = typeof ResizeObserver !== 'undefined';
668
+
669
+ if (hasResizeObserver && isValidElement(glassRef.current)) {
670
+ try {
671
+ resizeObserver = new ResizeObserver(entries => {
672
+ for (const entry of entries) {
673
+ if (entry.target === glassRef.current) {
674
+ updateGlassSize(false);
675
+ break;
676
+ }
677
+ }
678
+ });
679
+ resizeObserver.observe(glassRef.current);
680
+ } catch {
681
+ fallbackInterval = setInterval(
682
+ () => isValidElement(glassRef.current) && updateGlassSize(false),
683
+ 100
684
+ );
685
+ }
686
+ } else {
687
+ fallbackInterval = setInterval(
688
+ () => isValidElement(glassRef.current) && updateGlassSize(false),
689
+ 100
690
+ );
691
+ }
692
+
693
+ window.addEventListener('resize', debouncedResizeHandler, { passive: true });
694
+
695
+ return () => {
696
+ clearTimeout(initialTimeoutId);
697
+ if (rafId !== null) cancelAnimationFrame(rafId);
698
+ if (resizeTimeoutId) clearTimeout(resizeTimeoutId);
699
+ if (fallbackInterval) clearInterval(fallbackInterval);
700
+ window.removeEventListener('resize', debouncedResizeHandler);
701
+ resizeObserver?.disconnect();
702
+ };
703
+ }, [effectiveCornerRadius, glassRef]);
704
+
705
+ // OverLight config
706
+ /**
707
+ * Get effective overLight value based on configuration
708
+ * - boolean: returns the boolean value directly
709
+ * - 'auto': returns detectedOverLight (auto-detected from background)
710
+ * - object: returns detectedOverLight (auto-detected, but config object provides customization)
711
+ */
712
+ const getEffectiveOverLight = useCallback(() => {
713
+ if (typeof overLight === 'boolean') {
714
+ return overLight;
715
+ }
716
+ if (overLight === 'auto') {
717
+ return detectedOverLight;
718
+ }
719
+ if (typeof overLight === 'object' && overLight !== null) {
720
+ return detectedOverLight;
721
+ }
722
+ // Default to false for safety when overLight is undefined or invalid
723
+ return false;
724
+ }, [overLight, detectedOverLight]);
725
+
726
+ /**
727
+ * Validate and clamp a numeric config value
728
+ * @param value - The value to validate
729
+ * @param min - Minimum allowed value
730
+ * @param max - Maximum allowed value
731
+ * @param defaultValue - Default value if validation fails
732
+ * @returns Validated and clamped value
733
+ */
734
+ const validateConfigValue = useCallback(
735
+ (value: unknown, min: number, max: number, defaultValue: number): number => {
736
+ if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
737
+ return defaultValue;
738
+ }
739
+ return Math.min(max, Math.max(min, value));
740
+ },
741
+ []
742
+ );
743
+
744
+ const overLightConfig = useMemo(() => {
745
+ const isOverLight = getEffectiveOverLight();
746
+ const mouseInfluence = calculateMouseInfluence(mouseOffset);
747
+ const hoverIntensity = isHovered ? 1.4 : 1;
748
+ const activeIntensity = isActive ? 1.6 : 1;
749
+
750
+ // More robust overlight configuration with better defaults and clamping
751
+ const baseOpacity = isOverLight ? Math.min(0.6, Math.max(0.2, 0.5 * hoverIntensity * activeIntensity)) : 0;
752
+
753
+ const baseConfig = {
754
+ isOverLight,
755
+ threshold: 0.7,
756
+ opacity: baseOpacity,
757
+ contrast: Math.min(1.8, Math.max(1.0, 1.4 + mouseInfluence * 0.3)),
758
+ brightness: Math.min(1.2, Math.max(0.7, 0.85 + mouseInfluence * 0.15)),
759
+ saturationBoost: Math.min(2.0, Math.max(1.0, 1.3 + mouseInfluence * 0.4)),
760
+ shadowIntensity: Math.min(1.5, Math.max(0.5, 0.9 + mouseInfluence * 0.5)),
761
+ borderOpacity: Math.min(1.0, Math.max(0.3, 0.7 + mouseInfluence * 0.3)),
762
+ };
763
+
764
+ if (typeof overLight === 'object' && overLight !== null) {
765
+ const objConfig = overLight as OverLightObjectConfig;
766
+
767
+ // Validate and apply object config values with proper clamping
768
+ const validatedThreshold = validateConfigValue(objConfig.threshold, 0.1, 1.0, baseConfig.threshold);
769
+ const validatedOpacity = validateConfigValue(objConfig.opacity, 0.1, 1.0, baseConfig.opacity);
770
+ const validatedContrast = validateConfigValue(objConfig.contrast, 0.5, 2.5, baseConfig.contrast);
771
+ const validatedBrightness = validateConfigValue(objConfig.brightness, 0.5, 2.0, baseConfig.brightness);
772
+ const validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, 0.5, 3.0, baseConfig.saturationBoost);
773
+
774
+ const finalConfig = {
775
+ ...baseConfig,
776
+ threshold: validatedThreshold,
777
+ opacity: validatedOpacity * hoverIntensity * activeIntensity,
778
+ contrast: validatedContrast + mouseInfluence * 0.3,
779
+ brightness: validatedBrightness + mouseInfluence * 0.15,
780
+ saturationBoost: validatedSaturationBoost + mouseInfluence * 0.4,
781
+ };
782
+
783
+ // Debug logging
784
+ if (debugOverLight) {
785
+ console.log('[AtomixGlass] OverLight Config:', {
786
+ isOverLight,
787
+ config: {
788
+ threshold: finalConfig.threshold.toFixed(3),
789
+ opacity: finalConfig.opacity.toFixed(3),
790
+ contrast: finalConfig.contrast.toFixed(3),
791
+ brightness: finalConfig.brightness.toFixed(3),
792
+ saturationBoost: finalConfig.saturationBoost.toFixed(3),
793
+ shadowIntensity: finalConfig.shadowIntensity.toFixed(3),
794
+ borderOpacity: finalConfig.borderOpacity.toFixed(3),
795
+ },
796
+ input: {
797
+ threshold: objConfig.threshold,
798
+ opacity: objConfig.opacity,
799
+ contrast: objConfig.contrast,
800
+ brightness: objConfig.brightness,
801
+ saturationBoost: objConfig.saturationBoost,
802
+ },
803
+ dynamic: {
804
+ mouseInfluence: mouseInfluence.toFixed(3),
805
+ hoverIntensity: hoverIntensity.toFixed(3),
806
+ activeIntensity: activeIntensity.toFixed(3),
807
+ },
808
+ timestamp: new Date().toISOString(),
809
+ });
810
+ }
811
+
812
+ return finalConfig;
813
+ }
814
+
815
+ // Debug logging for non-object configs
816
+ if (debugOverLight) {
817
+ console.log('[AtomixGlass] OverLight Config:', {
818
+ isOverLight,
819
+ configType: typeof overLight === 'boolean' ? (overLight ? 'true' : 'false') : overLight,
820
+ config: {
821
+ threshold: baseConfig.threshold.toFixed(3),
822
+ opacity: baseConfig.opacity.toFixed(3),
823
+ contrast: baseConfig.contrast.toFixed(3),
824
+ brightness: baseConfig.brightness.toFixed(3),
825
+ saturationBoost: baseConfig.saturationBoost.toFixed(3),
826
+ shadowIntensity: baseConfig.shadowIntensity.toFixed(3),
827
+ borderOpacity: baseConfig.borderOpacity.toFixed(3),
828
+ },
829
+ dynamic: {
830
+ mouseInfluence: mouseInfluence.toFixed(3),
831
+ hoverIntensity: hoverIntensity.toFixed(3),
832
+ activeIntensity: activeIntensity.toFixed(3),
833
+ },
834
+ timestamp: new Date().toISOString(),
835
+ });
836
+ }
837
+
838
+ return baseConfig;
839
+ }, [overLight, getEffectiveOverLight, mouseOffset, isHovered, isActive, validateConfigValue, debugOverLight]);
840
+
841
+ // Event handlers
842
+ const handleMouseEnter = useCallback(() => setIsHovered(true), []);
843
+ const handleMouseLeave = useCallback(() => setIsHovered(false), []);
844
+ const handleMouseDown = useCallback(() => setIsActive(true), []);
845
+ const handleMouseUp = useCallback(() => setIsActive(false), []);
846
+
847
+ const handleKeyDown = useCallback(
848
+ (e: React.KeyboardEvent<HTMLDivElement>) => {
849
+ if (onClick && (e.key === 'Enter' || e.key === ' ')) {
850
+ e.preventDefault();
851
+ onClick();
852
+ }
853
+ },
854
+ [onClick]
855
+ );
66
856
 
67
857
  return {
68
- generateGlassClass,
69
- generateElementClass,
858
+ // State
859
+ isHovered,
860
+ isActive,
861
+ glassSize,
862
+ dynamicCornerRadius,
863
+ effectiveCornerRadius,
864
+ effectiveReducedMotion,
865
+ effectiveHighContrast,
866
+ effectiveDisableEffects,
867
+ detectedOverLight,
868
+ globalMousePosition,
869
+ mouseOffset,
870
+
871
+ // OverLight config
872
+ overLightConfig,
873
+
874
+ // Transform calculations
875
+ elasticTranslation,
876
+ directionalScale,
877
+ transformStyle,
878
+
879
+ // Event handlers
880
+ handleMouseEnter,
881
+ handleMouseLeave,
882
+ handleMouseDown,
883
+ handleMouseUp,
884
+ handleMouseMove,
885
+ handleKeyDown,
70
886
  };
71
887
  }
888
+