@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.
- package/README.md +19 -0
- package/dist/atomix.css +1703 -1544
- package/dist/atomix.min.css +4 -4
- package/dist/index.d.ts +1465 -963
- package/dist/index.esm.js +16289 -25908
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +15650 -21780
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +15008 -0
- package/dist/themes/applemix.min.css +72 -0
- package/dist/themes/boomdevs.css +1608 -1450
- package/dist/themes/boomdevs.min.css +5 -5
- package/dist/themes/esrar.css +1702 -1543
- package/dist/themes/esrar.min.css +4 -4
- package/dist/themes/flashtrade.css +15159 -0
- package/dist/themes/flashtrade.min.css +86 -0
- package/dist/themes/mashroom.css +1699 -1540
- package/dist/themes/mashroom.min.css +7 -7
- package/dist/themes/shaj-default.css +1693 -1534
- package/dist/themes/shaj-default.min.css +4 -4
- package/package.json +6 -17
- package/src/components/Accordion/Accordion.stories.tsx +662 -21
- package/src/components/Accordion/Accordion.tsx +21 -14
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
- package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
- package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
- package/src/components/AtomixGlass/README.md +124 -2
- package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
- package/src/components/AtomixGlass/glass-utils.ts +263 -0
- package/src/components/AtomixGlass/shader-utils.ts +792 -68
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
- package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
- package/src/components/AtomixGlass/utils.ts +3 -3
- package/src/components/Avatar/Avatar.tsx +3 -0
- package/src/components/Avatar/AvatarGroup.tsx +2 -1
- package/src/components/Badge/Badge.stories.tsx +76 -55
- package/src/components/Badge/Badge.tsx +12 -14
- package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
- package/src/components/Button/Button.stories.tsx +501 -20
- package/src/components/Button/Button.tsx +5 -8
- package/src/components/Callout/Callout.stories.tsx +86 -35
- package/src/components/Callout/Callout.tsx +31 -9
- package/src/components/Card/Card.stories.tsx +565 -2
- package/src/components/Card/Card.tsx +15 -4
- package/src/components/Card/ElevationCard.tsx +2 -0
- package/src/components/Chart/AnimatedChart.tsx +179 -156
- package/src/components/Chart/AreaChart.tsx +123 -12
- package/src/components/Chart/BarChart.tsx +91 -100
- package/src/components/Chart/BaseChart.tsx +80 -0
- package/src/components/Chart/BubbleChart.tsx +114 -290
- package/src/components/Chart/CandlestickChart.tsx +282 -622
- package/src/components/Chart/Chart.stories.tsx +576 -179
- package/src/components/Chart/Chart.tsx +374 -75
- package/src/components/Chart/ChartRenderer.tsx +371 -220
- package/src/components/Chart/ChartToolbar.tsx +372 -61
- package/src/components/Chart/ChartTooltip.tsx +33 -18
- package/src/components/Chart/DonutChart.tsx +172 -254
- package/src/components/Chart/FunnelChart.tsx +169 -240
- package/src/components/Chart/GaugeChart.tsx +224 -392
- package/src/components/Chart/HeatmapChart.tsx +302 -440
- package/src/components/Chart/LineChart.tsx +148 -103
- package/src/components/Chart/MultiAxisChart.tsx +267 -395
- package/src/components/Chart/PieChart.tsx +114 -64
- package/src/components/Chart/RadarChart.tsx +202 -218
- package/src/components/Chart/ScatterChart.tsx +111 -97
- package/src/components/Chart/TreemapChart.tsx +147 -222
- package/src/components/Chart/WaterfallChart.tsx +253 -291
- package/src/components/Chart/index.ts +11 -9
- package/src/components/Chart/types.ts +85 -9
- package/src/components/Chart/utils.ts +66 -0
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
- package/src/components/Countdown/Countdown.tsx +4 -0
- package/src/components/DataTable/DataTable.tsx +2 -1
- package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
- package/src/components/DatePicker/DatePicker.tsx +3 -9
- package/src/components/DatePicker/types.ts +5 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
- package/src/components/Dropdown/Dropdown.tsx +26 -28
- package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
- package/src/components/EdgePanel/EdgePanel.tsx +101 -13
- package/src/components/Footer/Footer.stories.tsx +187 -60
- package/src/components/Footer/Footer.test.tsx +134 -0
- package/src/components/Footer/Footer.tsx +133 -34
- package/src/components/Footer/FooterLink.tsx +1 -1
- package/src/components/Footer/FooterSection.tsx +53 -36
- package/src/components/Footer/FooterSocialLink.tsx +32 -29
- package/src/components/Footer/README.md +82 -3
- package/src/components/Footer/index.ts +1 -1
- package/src/components/Form/Checkbox.stories.tsx +13 -5
- package/src/components/Form/Checkbox.tsx +3 -6
- package/src/components/Form/Form.stories.tsx +10 -3
- package/src/components/Form/Form.tsx +2 -0
- package/src/components/Form/FormGroup.tsx +2 -1
- package/src/components/Form/Input.stories.tsx +12 -11
- package/src/components/Form/Input.tsx +97 -95
- package/src/components/Form/Radio.stories.tsx +22 -7
- package/src/components/Form/Radio.tsx +3 -6
- package/src/components/Form/Select.stories.tsx +21 -6
- package/src/components/Form/Select.tsx +3 -5
- package/src/components/Form/Textarea.stories.tsx +13 -11
- package/src/components/Form/Textarea.tsx +88 -86
- package/src/components/Hero/Hero.stories.tsx +2 -3
- package/src/components/Hero/Hero.tsx +5 -6
- package/src/components/Icon/Icon.tsx +12 -1
- package/src/components/List/List.tsx +2 -1
- package/src/components/List/ListGroup.tsx +2 -1
- package/src/components/Messages/Messages.stories.tsx +113 -0
- package/src/components/Messages/Messages.tsx +52 -9
- package/src/components/Modal/Modal.stories.tsx +48 -32
- package/src/components/Modal/Modal.tsx +19 -24
- package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
- package/src/components/Navigation/Menu/Menu.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
- package/src/components/Navigation/Nav/Nav.tsx +22 -4
- package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
- package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
- package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
- package/src/components/Pagination/Pagination.stories.tsx +13 -6
- package/src/components/Pagination/Pagination.tsx +7 -6
- package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
- package/src/components/Popover/Popover.stories.tsx +32 -24
- package/src/components/Popover/Popover.tsx +4 -1
- package/src/components/ProductReview/ProductReview.tsx +8 -2
- package/src/components/Progress/Progress.tsx +19 -3
- package/src/components/Rating/Rating.stories.tsx +11 -6
- package/src/components/Rating/Rating.tsx +3 -5
- package/src/components/River/River.tsx +5 -5
- package/src/components/SectionIntro/SectionIntro.tsx +8 -2
- package/src/components/Slider/Slider.stories.tsx +4 -4
- package/src/components/Slider/Slider.tsx +4 -3
- package/src/components/Spinner/Spinner.tsx +19 -3
- package/src/components/Steps/Steps.stories.tsx +5 -4
- package/src/components/Steps/Steps.tsx +8 -5
- package/src/components/Tab/Tab.stories.tsx +4 -3
- package/src/components/Tab/Tab.tsx +8 -6
- package/src/components/Testimonial/Testimonial.tsx +8 -2
- package/src/components/Todo/Todo.tsx +2 -1
- package/src/components/Toggle/Toggle.stories.tsx +5 -4
- package/src/components/Toggle/Toggle.tsx +8 -5
- package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
- package/src/components/Tooltip/Tooltip.tsx +9 -2
- package/src/components/Upload/Upload.stories.tsx +252 -0
- package/src/components/Upload/Upload.tsx +92 -53
- package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
- package/src/components/index.ts +0 -4
- package/src/layouts/Grid/Grid.stories.tsx +10 -23
- package/src/layouts/Grid/Grid.tsx +20 -1
- package/src/layouts/Grid/GridCol.tsx +76 -48
- package/src/lib/composables/useAtomixGlass.ts +861 -44
- package/src/lib/composables/useBarChart.ts +21 -4
- package/src/lib/composables/useChart.ts +227 -370
- package/src/lib/composables/useChartExport.ts +19 -78
- package/src/lib/composables/useChartToolbar.ts +11 -21
- package/src/lib/composables/useEdgePanel.ts +125 -71
- package/src/lib/composables/useFooter.ts +3 -3
- package/src/lib/composables/useGlassContainer.ts +16 -7
- package/src/lib/composables/useLineChart.ts +11 -2
- package/src/lib/composables/usePieChart.ts +4 -14
- package/src/lib/composables/useRiver.ts +5 -0
- package/src/lib/composables/useSlider.ts +62 -24
- package/src/lib/composables/useVideoPlayer.ts +60 -63
- package/src/lib/constants/components.ts +147 -32
- package/src/lib/types/components.ts +355 -25
- package/src/lib/utils/displacement-generator.ts +55 -49
- package/src/lib/utils/icons.ts +1 -1
- package/src/lib/utils/index.ts +16 -10
- package/src/styles/01-settings/_settings.accordion.scss +19 -19
- package/src/styles/01-settings/_settings.animations.scss +5 -5
- package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
- package/src/styles/01-settings/_settings.avatar.scss +17 -17
- package/src/styles/01-settings/_settings.background.scss +0 -3
- package/src/styles/01-settings/_settings.badge.scss +1 -1
- package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
- package/src/styles/01-settings/_settings.card.scss +1 -1
- package/src/styles/01-settings/_settings.chart.scss +65 -2
- package/src/styles/01-settings/_settings.dropdown.scss +1 -1
- package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
- package/src/styles/01-settings/_settings.footer.scss +35 -42
- package/src/styles/01-settings/_settings.input.scss +1 -1
- package/src/styles/01-settings/_settings.list.scss +1 -1
- package/src/styles/01-settings/_settings.rating.scss +1 -1
- package/src/styles/01-settings/_settings.tabs.scss +1 -1
- package/src/styles/01-settings/_settings.upload.scss +6 -5
- package/src/styles/02-tools/_tools.animations.scss +4 -5
- package/src/styles/02-tools/_tools.background.scss +1 -13
- package/src/styles/02-tools/_tools.glass.scss +0 -1
- package/src/styles/02-tools/_tools.utility-api.scss +91 -48
- package/src/styles/03-generic/_generic.root.scss +1 -4
- package/src/styles/04-elements/_elements.body.scss +0 -1
- package/src/styles/06-components/_components.atomix-glass.scss +249 -0
- package/src/styles/06-components/_components.badge.scss +8 -23
- package/src/styles/06-components/_components.button.scss +8 -3
- package/src/styles/06-components/_components.callout.scss +10 -5
- package/src/styles/06-components/_components.card.scss +2 -14
- package/src/styles/06-components/_components.chart.scss +969 -1449
- package/src/styles/06-components/_components.dropdown.scss +19 -7
- package/src/styles/06-components/_components.edge-panel.scss +103 -0
- package/src/styles/06-components/_components.footer.scss +166 -85
- package/src/styles/06-components/_components.input.scss +8 -9
- package/src/styles/06-components/_components.list.scss +1 -0
- package/src/styles/06-components/_components.messages.scss +176 -0
- package/src/styles/06-components/_components.modal.scss +16 -4
- package/src/styles/06-components/_components.navbar.scss +12 -1
- package/src/styles/06-components/_components.side-menu.scss +5 -0
- package/src/styles/06-components/_components.skeleton.scss +8 -6
- package/src/styles/06-components/_components.upload.scss +219 -4
- package/src/styles/06-components/old.chart.styles.scss +1 -30
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
- package/src/styles/99-utilities/_utilities.scss +1 -1
- package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
- package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
- package/src/components/Chart/AdvancedChart.tsx +0 -624
- package/src/components/Chart/LineChartNew.tsx +0 -167
- package/src/components/Chart/RealTimeChart.tsx +0 -436
- package/src/components/DatePicker/DatePicker copy.tsx +0 -551
|
@@ -1,1277 +1,611 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
type="matrix"
|
|
79
|
-
values="0.3 0.3 0.3 0 0
|
|
80
|
-
0.3 0.3 0.3 0 0
|
|
81
|
-
0.3 0.3 0.3 0 0
|
|
82
|
-
0 0 0 1 0"
|
|
83
|
-
result="EDGE_INTENSITY"
|
|
84
|
-
/>
|
|
85
|
-
<feComponentTransfer in="EDGE_INTENSITY" result="EDGE_MASK">
|
|
86
|
-
<feFuncA type="discrete" tableValues={`0 ${aberrationIntensity * 0.05} 1`} />
|
|
87
|
-
</feComponentTransfer>
|
|
88
|
-
|
|
89
|
-
<feOffset in="SourceGraphic" dx="0" dy="0" result="CENTER_ORIGINAL" />
|
|
90
|
-
|
|
91
|
-
<feDisplacementMap
|
|
92
|
-
in="SourceGraphic"
|
|
93
|
-
in2="DISPLACEMENT_MAP"
|
|
94
|
-
scale={displacementScale * (mode === 'shader' ? 1 : -1)}
|
|
95
|
-
xChannelSelector="R"
|
|
96
|
-
yChannelSelector="B"
|
|
97
|
-
result="RED_DISPLACED"
|
|
98
|
-
/>
|
|
99
|
-
<feColorMatrix
|
|
100
|
-
in="RED_DISPLACED"
|
|
101
|
-
type="matrix"
|
|
102
|
-
values="1 0 0 0 0
|
|
103
|
-
0 0 0 0 0
|
|
104
|
-
0 0 0 0 0
|
|
105
|
-
0 0 0 1 0"
|
|
106
|
-
result="RED_CHANNEL"
|
|
107
|
-
/>
|
|
108
|
-
|
|
109
|
-
<feDisplacementMap
|
|
110
|
-
in="SourceGraphic"
|
|
111
|
-
in2="DISPLACEMENT_MAP"
|
|
112
|
-
scale={displacementScale * ((mode === 'shader' ? 1 : -1) - aberrationIntensity * 0.02)}
|
|
113
|
-
xChannelSelector="R"
|
|
114
|
-
yChannelSelector="B"
|
|
115
|
-
result="GREEN_DISPLACED"
|
|
116
|
-
/>
|
|
117
|
-
<feColorMatrix
|
|
118
|
-
in="GREEN_DISPLACED"
|
|
119
|
-
type="matrix"
|
|
120
|
-
values="0 0 0 0 0
|
|
121
|
-
0 1 0 0 0
|
|
122
|
-
0 0 0 0 0
|
|
123
|
-
0 0 0 1 0"
|
|
124
|
-
result="GREEN_CHANNEL"
|
|
125
|
-
/>
|
|
126
|
-
|
|
127
|
-
<feDisplacementMap
|
|
128
|
-
in="SourceGraphic"
|
|
129
|
-
in2="DISPLACEMENT_MAP"
|
|
130
|
-
scale={displacementScale * ((mode === 'shader' ? 1 : -1) - aberrationIntensity * 0.03)}
|
|
131
|
-
xChannelSelector="R"
|
|
132
|
-
yChannelSelector="B"
|
|
133
|
-
result="BLUE_DISPLACED"
|
|
134
|
-
/>
|
|
135
|
-
<feColorMatrix
|
|
136
|
-
in="BLUE_DISPLACED"
|
|
137
|
-
type="matrix"
|
|
138
|
-
values="0 0 0 0 0
|
|
139
|
-
0 0 0 0 0
|
|
140
|
-
0 0 1 0 0
|
|
141
|
-
0 0 0 1 0"
|
|
142
|
-
result="BLUE_CHANNEL"
|
|
143
|
-
/>
|
|
144
|
-
|
|
145
|
-
<feBlend in="GREEN_CHANNEL" in2="BLUE_CHANNEL" mode="screen" result="GB_COMBINED" />
|
|
146
|
-
<feBlend in="RED_CHANNEL" in2="GB_COMBINED" mode="screen" result="RGB_COMBINED" />
|
|
147
|
-
|
|
148
|
-
<feGaussianBlur
|
|
149
|
-
in="RGB_COMBINED"
|
|
150
|
-
stdDeviation={Math.max(0.1, 0.5 - aberrationIntensity * 0.1)}
|
|
151
|
-
result="ABERRATED_BLURRED"
|
|
152
|
-
/>
|
|
153
|
-
|
|
154
|
-
<feComposite
|
|
155
|
-
in="ABERRATED_BLURRED"
|
|
156
|
-
in2="EDGE_MASK"
|
|
157
|
-
operator="in"
|
|
158
|
-
result="EDGE_ABERRATION"
|
|
159
|
-
/>
|
|
160
|
-
|
|
161
|
-
<feComponentTransfer in="EDGE_MASK" result="INVERTED_MASK">
|
|
162
|
-
<feFuncA type="table" tableValues="1 0" />
|
|
163
|
-
</feComponentTransfer>
|
|
164
|
-
<feComposite in="CENTER_ORIGINAL" in2="INVERTED_MASK" operator="in" result="CENTER_CLEAN" />
|
|
165
|
-
|
|
166
|
-
<feComposite in="EDGE_ABERRATION" in2="CENTER_CLEAN" operator="over" />
|
|
167
|
-
</filter>
|
|
168
|
-
</defs>
|
|
169
|
-
</svg>
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const GlassContainer = forwardRef<
|
|
173
|
-
HTMLDivElement,
|
|
174
|
-
React.PropsWithChildren<{
|
|
175
|
-
className?: string;
|
|
176
|
-
style?: React.CSSProperties;
|
|
177
|
-
displacementScale?: number;
|
|
178
|
-
blurAmount?: number;
|
|
179
|
-
saturation?: number;
|
|
180
|
-
aberrationIntensity?: number;
|
|
181
|
-
mouseOffset?: { x: number; y: number };
|
|
182
|
-
globalMousePos?: { x: number; y: number };
|
|
183
|
-
onMouseLeave?: () => void;
|
|
184
|
-
onMouseEnter?: () => void;
|
|
185
|
-
onMouseDown?: () => void;
|
|
186
|
-
onMouseUp?: () => void;
|
|
187
|
-
active?: boolean;
|
|
188
|
-
isHovered?: boolean;
|
|
189
|
-
isActive?: boolean;
|
|
190
|
-
overLight?: boolean;
|
|
191
|
-
cornerRadius?: number;
|
|
192
|
-
padding?: string;
|
|
193
|
-
glassSize?: { width: number; height: number };
|
|
194
|
-
|
|
195
|
-
onClick?: () => void;
|
|
196
|
-
mode?: 'standard' | 'polar' | 'prominent' | 'shader';
|
|
197
|
-
transform?: string;
|
|
198
|
-
effectiveDisableEffects?: boolean;
|
|
199
|
-
effectiveReducedMotion?: boolean;
|
|
200
|
-
}>
|
|
201
|
-
>(
|
|
202
|
-
(
|
|
203
|
-
{
|
|
204
|
-
children,
|
|
205
|
-
className = '',
|
|
206
|
-
style,
|
|
207
|
-
displacementScale = 25,
|
|
208
|
-
blurAmount = 0.0625,
|
|
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
|
-
effectiveDisableEffects = false,
|
|
228
|
-
effectiveReducedMotion = false,
|
|
229
|
-
},
|
|
230
|
-
ref
|
|
231
|
-
) => {
|
|
232
|
-
const filterId = useId();
|
|
233
|
-
const [shaderMapUrl, setShaderMapUrl] = useState<string>('');
|
|
234
|
-
|
|
235
|
-
const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
|
|
236
|
-
|
|
237
|
-
useEffect(() => {
|
|
238
|
-
if (mode === 'shader' && glassSize.width > 0 && glassSize.height > 0) {
|
|
239
|
-
try {
|
|
240
|
-
const url = generateShaderDisplacementMap(glassSize.width, glassSize.height);
|
|
241
|
-
setShaderMapUrl(url);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
console.warn('Failed to generate shader displacement map:', error);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}, [mode, glassSize.width, glassSize.height]);
|
|
247
|
-
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
if (!ref || typeof ref === 'function') return;
|
|
250
|
-
|
|
251
|
-
const element = (ref as React.RefObject<HTMLDivElement>).current;
|
|
252
|
-
if (!element) return;
|
|
253
|
-
|
|
254
|
-
const timeoutId = setTimeout(() => {
|
|
255
|
-
try {
|
|
256
|
-
element.offsetHeight;
|
|
257
|
-
} catch (error) {
|
|
258
|
-
console.warn('AtomixGlass: Error in GlassContainer size sync:', error);
|
|
259
|
-
}
|
|
260
|
-
}, 0);
|
|
261
|
-
|
|
262
|
-
return () => clearTimeout(timeoutId);
|
|
263
|
-
}, [cornerRadius, glassSize.width, glassSize.height]);
|
|
264
|
-
|
|
265
|
-
const liquidBlur = useMemo(() => {
|
|
266
|
-
if (!ref || !globalMousePos.x || !globalMousePos.y) {
|
|
267
|
-
return {
|
|
268
|
-
baseBlur: blurAmount,
|
|
269
|
-
edgeBlur: blurAmount * 0.5,
|
|
270
|
-
centerBlur: blurAmount * 0.2,
|
|
271
|
-
flowBlur: blurAmount * 0.3,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const rect = (ref as React.RefObject<HTMLDivElement>).current?.getBoundingClientRect();
|
|
276
|
-
if (!rect) {
|
|
277
|
-
return {
|
|
278
|
-
baseBlur: blurAmount,
|
|
279
|
-
edgeBlur: blurAmount * 0.5,
|
|
280
|
-
centerBlur: blurAmount * 0.2,
|
|
281
|
-
flowBlur: blurAmount * 0.3,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const centerX = rect.left + rect.width / 2;
|
|
285
|
-
const centerY = rect.top + rect.height / 2;
|
|
286
|
-
|
|
287
|
-
const deltaX = globalMousePos.x - centerX;
|
|
288
|
-
const deltaY = globalMousePos.y - centerY;
|
|
289
|
-
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
290
|
-
|
|
291
|
-
const maxDistance = Math.sqrt(rect.width * rect.width + rect.height * rect.height) / 2;
|
|
292
|
-
const normalizedDistance = Math.min(distance / maxDistance, 1);
|
|
293
|
-
|
|
294
|
-
const mouseInfluence =
|
|
295
|
-
Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / 100;
|
|
296
|
-
|
|
297
|
-
const baseBlur = blurAmount + mouseInfluence * blurAmount * 0.4;
|
|
298
|
-
|
|
299
|
-
const edgeIntensity = normalizedDistance * 1.5 + mouseInfluence * 0.3;
|
|
300
|
-
const edgeBlur = baseBlur * (0.8 + edgeIntensity * 0.6);
|
|
301
|
-
|
|
302
|
-
const centerIntensity = (1 - normalizedDistance) * 0.3 + mouseInfluence * 0.2;
|
|
303
|
-
const centerBlur = baseBlur * (0.3 + centerIntensity * 0.4);
|
|
304
|
-
|
|
305
|
-
const flowDirection = Math.atan2(deltaY, deltaX);
|
|
306
|
-
const flowIntensity = Math.sin(flowDirection + mouseInfluence * Math.PI) * 0.5 + 0.5;
|
|
307
|
-
const flowBlur = baseBlur * (0.4 + flowIntensity * 0.6);
|
|
308
|
-
|
|
309
|
-
const hoverMultiplier = isHovered ? 1.2 : 1;
|
|
310
|
-
const activeMultiplier = isActive ? 1.4 : 1;
|
|
311
|
-
const stateMultiplier = hoverMultiplier * activeMultiplier;
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
baseBlur: Math.max(0.1, baseBlur * stateMultiplier),
|
|
315
|
-
edgeBlur: Math.max(0.1, edgeBlur * stateMultiplier),
|
|
316
|
-
centerBlur: Math.max(0.1, centerBlur * stateMultiplier),
|
|
317
|
-
flowBlur: Math.max(0.1, flowBlur * stateMultiplier),
|
|
318
|
-
};
|
|
319
|
-
}, [blurAmount, globalMousePos, mouseOffset, isHovered, isActive, ref]);
|
|
320
|
-
|
|
321
|
-
const backdropStyle = useMemo(() => {
|
|
322
|
-
const dynamicSaturation = saturation + liquidBlur.baseBlur * 20;
|
|
323
|
-
|
|
324
|
-
const blurLayers = [
|
|
325
|
-
`blur(${liquidBlur.baseBlur}px)`,
|
|
326
|
-
// `blur(${liquidBlur.edgeBlur}px)`,
|
|
327
|
-
// `blur(${liquidBlur.centerBlur}px)`,
|
|
328
|
-
`blur(${liquidBlur.flowBlur}px)`,
|
|
329
|
-
];
|
|
330
|
-
|
|
331
|
-
return {
|
|
332
|
-
filter: `url(#${filterId})`,
|
|
333
|
-
backdropFilter: `${blurLayers.join(' ')} saturate(${Math.min(dynamicSaturation, 200)}%)`,
|
|
334
|
-
};
|
|
335
|
-
}, [filterId, liquidBlur, saturation]);
|
|
336
|
-
|
|
337
|
-
return (
|
|
338
|
-
<div
|
|
339
|
-
ref={ref}
|
|
340
|
-
className={` ${className} ${active ? 'active' : ''}`}
|
|
341
|
-
style={style}
|
|
342
|
-
onClick={onClick}
|
|
343
|
-
>
|
|
344
|
-
<div
|
|
345
|
-
className="glass"
|
|
346
|
-
style={{
|
|
347
|
-
position: 'relative',
|
|
348
|
-
padding,
|
|
349
|
-
borderRadius: `${cornerRadius}px`,
|
|
350
|
-
transition: 'all 0.2s ease-out',
|
|
351
|
-
|
|
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
|
-
shaderMapUrl={shaderMapUrl}
|
|
364
|
-
/>
|
|
365
|
-
<span
|
|
366
|
-
className="glass__warp"
|
|
367
|
-
style={
|
|
368
|
-
{
|
|
369
|
-
...backdropStyle,
|
|
370
|
-
borderRadius: `${cornerRadius}px`,
|
|
371
|
-
position: 'absolute',
|
|
372
|
-
inset: '0',
|
|
373
|
-
} as CSSProperties
|
|
374
|
-
}
|
|
375
|
-
/>
|
|
376
|
-
|
|
377
|
-
{/* Apple Liquid Glass Inner Shadow Layer */}
|
|
378
|
-
<div
|
|
379
|
-
style={{
|
|
380
|
-
position: 'absolute',
|
|
381
|
-
inset: '1px',
|
|
382
|
-
borderRadius: `${cornerRadius}px`,
|
|
383
|
-
pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
|
|
384
|
-
zIndex: 20,
|
|
385
|
-
boxShadow: [
|
|
386
|
-
'0 0 20px rgba(0, 0, 0, 0.15) inset',
|
|
387
|
-
'0 4px 8px rgba(0, 0, 0, 0.08) inset',
|
|
388
|
-
].join(', '),
|
|
389
|
-
opacity: effectiveDisableEffects ? 0 : 1,
|
|
390
|
-
transition: effectiveReducedMotion ? 'none' : 'opacity 0.2s ease-out',
|
|
391
|
-
}}
|
|
392
|
-
/>
|
|
393
|
-
|
|
394
|
-
<div
|
|
395
|
-
style={{
|
|
396
|
-
position: 'relative',
|
|
397
|
-
zIndex: 1,
|
|
398
|
-
textShadow: overLight
|
|
399
|
-
? '0px 2px 12px rgba(0, 0, 0, 0)'
|
|
400
|
-
: '0px 2px 12px rgba(0, 0, 0, 0.4)',
|
|
401
|
-
}}
|
|
402
|
-
>
|
|
403
|
-
{children}
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
</div>
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
GlassContainer.displayName = 'GlassContainer';
|
|
412
|
-
|
|
413
|
-
interface AtomixGlassProps {
|
|
414
|
-
children: React.ReactNode;
|
|
415
|
-
displacementScale?: number;
|
|
416
|
-
blurAmount?: number;
|
|
417
|
-
saturation?: number;
|
|
418
|
-
aberrationIntensity?: number;
|
|
419
|
-
elasticity?: number;
|
|
420
|
-
cornerRadius?: number;
|
|
421
|
-
globalMousePos?: { x: number; y: number };
|
|
422
|
-
mouseOffset?: { x: number; y: number };
|
|
423
|
-
mouseContainer?: React.RefObject<HTMLElement | null> | null;
|
|
424
|
-
className?: string;
|
|
425
|
-
padding?: string;
|
|
426
|
-
style?: React.CSSProperties;
|
|
427
|
-
overLight?: boolean;
|
|
428
|
-
mode?: 'standard' | 'polar' | 'prominent' | 'shader';
|
|
429
|
-
onClick?: () => void;
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Accessibility props
|
|
433
|
-
*/
|
|
434
|
-
'aria-label'?: string;
|
|
435
|
-
'aria-describedby'?: string;
|
|
436
|
-
role?: string;
|
|
437
|
-
tabIndex?: number;
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Performance and accessibility options
|
|
441
|
-
*/
|
|
442
|
-
reducedMotion?: boolean;
|
|
443
|
-
highContrast?: boolean;
|
|
444
|
-
disableEffects?: boolean;
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Performance monitoring
|
|
448
|
-
*/
|
|
449
|
-
enablePerformanceMonitoring?: boolean;
|
|
450
|
-
}
|
|
451
|
-
|
|
1
|
+
import React, { useMemo, useRef } from 'react';
|
|
2
|
+
import type { AtomixGlassProps, GlassSize } from '../../lib/types/components';
|
|
3
|
+
import { ATOMIX_GLASS } from '../../lib/constants/components';
|
|
4
|
+
import { AtomixGlassContainer } from './AtomixGlassContainer';
|
|
5
|
+
import { useAtomixGlass } from '../../lib/composables/useAtomixGlass';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* AtomixGlass - A high-performance glass morphism component with liquid distortion effects
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Hardware-accelerated glass effects with SVG filters
|
|
12
|
+
* - Mouse-responsive liquid distortion
|
|
13
|
+
* - Dynamic border-radius extraction from children CSS properties
|
|
14
|
+
* - Automatic light/dark theme detection via overLight prop
|
|
15
|
+
* - Accessibility and performance optimizations
|
|
16
|
+
* - Multiple displacement modes (standard, polar, prominent, shader)
|
|
17
|
+
* - Design token integration for consistent theming
|
|
18
|
+
* - Focus ring support for keyboard navigation
|
|
19
|
+
* - Responsive breakpoints for mobile optimization
|
|
20
|
+
* - Enhanced ARIA attributes for screen readers
|
|
21
|
+
*
|
|
22
|
+
* Design System Compliance:
|
|
23
|
+
* - Uses design tokens for opacity, spacing, and colors
|
|
24
|
+
* - Follows BEM methodology for class naming
|
|
25
|
+
* - Implements focus-ring mixin for accessibility
|
|
26
|
+
* - Supports reduced motion and high contrast preferences
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Basic usage with dynamic border-radius extraction
|
|
30
|
+
* <AtomixGlass>
|
|
31
|
+
* <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
|
|
32
|
+
* </AtomixGlass>
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Manual border-radius override
|
|
36
|
+
* <AtomixGlass cornerRadius={20}>
|
|
37
|
+
* <div>Content with 20px glass radius</div>
|
|
38
|
+
* </AtomixGlass>
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Interactive glass with click handler
|
|
42
|
+
* <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
|
|
43
|
+
* <div>Clickable content</div>
|
|
44
|
+
* </AtomixGlass>
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // OverLight - Boolean mode (explicit control)
|
|
48
|
+
* <AtomixGlass overLight={true}>
|
|
49
|
+
* <div>Content on light background</div>
|
|
50
|
+
* </AtomixGlass>
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // OverLight - Auto-detection mode
|
|
54
|
+
* <AtomixGlass overLight="auto">
|
|
55
|
+
* <div>Content with auto-detected background</div>
|
|
56
|
+
* </AtomixGlass>
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // OverLight - Object config with custom settings
|
|
60
|
+
* <AtomixGlass
|
|
61
|
+
* overLight={{
|
|
62
|
+
* threshold: 0.8,
|
|
63
|
+
* opacity: 0.6,
|
|
64
|
+
* contrast: 1.8,
|
|
65
|
+
* brightness: 1.0,
|
|
66
|
+
* saturationBoost: 1.5
|
|
67
|
+
* }}
|
|
68
|
+
* >
|
|
69
|
+
* <div>Content with custom overLight config</div>
|
|
70
|
+
* </AtomixGlass>
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Debug mode for overLight detection
|
|
74
|
+
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
75
|
+
* <div>Content with debug logging enabled</div>
|
|
76
|
+
* </AtomixGlass>
|
|
77
|
+
*/
|
|
452
78
|
export function AtomixGlass({
|
|
453
79
|
children,
|
|
454
|
-
displacementScale =
|
|
455
|
-
blurAmount =
|
|
456
|
-
saturation =
|
|
457
|
-
aberrationIntensity =
|
|
458
|
-
elasticity =
|
|
459
|
-
cornerRadius
|
|
460
|
-
|
|
80
|
+
displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
|
|
81
|
+
blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
|
|
82
|
+
saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
|
|
83
|
+
aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
|
|
84
|
+
elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY,
|
|
85
|
+
cornerRadius,
|
|
86
|
+
globalMousePosition: externalGlobalMousePosition,
|
|
461
87
|
mouseOffset: externalMouseOffset,
|
|
462
88
|
mouseContainer = null,
|
|
463
89
|
className = '',
|
|
464
|
-
padding =
|
|
465
|
-
overLight =
|
|
90
|
+
padding = ATOMIX_GLASS.DEFAULTS.PADDING,
|
|
91
|
+
overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT,
|
|
466
92
|
style = {},
|
|
467
|
-
mode =
|
|
93
|
+
mode = ATOMIX_GLASS.DEFAULTS.MODE,
|
|
468
94
|
onClick,
|
|
469
|
-
|
|
95
|
+
shaderVariant = 'liquidGlass',
|
|
470
96
|
'aria-label': ariaLabel,
|
|
471
97
|
'aria-describedby': ariaDescribedBy,
|
|
472
98
|
role,
|
|
473
99
|
tabIndex,
|
|
474
|
-
|
|
475
100
|
reducedMotion = false,
|
|
476
101
|
highContrast = false,
|
|
477
102
|
disableEffects = false,
|
|
478
|
-
|
|
103
|
+
enableLiquidBlur = false,
|
|
104
|
+
enableBorderEffect = true,
|
|
105
|
+
enableOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS,
|
|
479
106
|
enablePerformanceMonitoring = false,
|
|
107
|
+
debugCornerRadius = false,
|
|
108
|
+
debugOverLight = false,
|
|
480
109
|
}: AtomixGlassProps) {
|
|
481
110
|
const glassRef = useRef<HTMLDivElement>(null);
|
|
482
|
-
const
|
|
483
|
-
const [isActive, setIsActive] = useState(false);
|
|
484
|
-
const [glassSize, setGlassSize] = useState({ width: 270, height: 69 });
|
|
485
|
-
const [internalGlobalMousePos, setInternalGlobalMousePos] = useState({ x: 0, y: 0 });
|
|
486
|
-
const [internalMouseOffset, setInternalMouseOffset] = useState({ x: 0, y: 0 });
|
|
487
|
-
|
|
488
|
-
const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
|
|
489
|
-
const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
|
|
490
|
-
|
|
491
|
-
useEffect(() => {
|
|
492
|
-
if (typeof window.matchMedia !== 'function') {
|
|
493
|
-
console.warn('AtomixGlass: matchMedia not supported, using default preferences');
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
try {
|
|
498
|
-
const mediaQueryReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
499
|
-
const mediaQueryHighContrast = window.matchMedia('(prefers-contrast: high)');
|
|
500
|
-
|
|
501
|
-
setUserPrefersReducedMotion(mediaQueryReducedMotion.matches);
|
|
502
|
-
setUserPrefersHighContrast(mediaQueryHighContrast.matches);
|
|
503
|
-
|
|
504
|
-
const handleReducedMotionChange = (e: MediaQueryListEvent) => {
|
|
505
|
-
setUserPrefersReducedMotion(e.matches);
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
const handleHighContrastChange = (e: MediaQueryListEvent) => {
|
|
509
|
-
setUserPrefersHighContrast(e.matches);
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
if (mediaQueryReducedMotion.addEventListener) {
|
|
513
|
-
mediaQueryReducedMotion.addEventListener('change', handleReducedMotionChange);
|
|
514
|
-
mediaQueryHighContrast.addEventListener('change', handleHighContrastChange);
|
|
515
|
-
} else if (mediaQueryReducedMotion.addListener) {
|
|
516
|
-
mediaQueryReducedMotion.addListener(handleReducedMotionChange);
|
|
517
|
-
mediaQueryHighContrast.addListener(handleHighContrastChange);
|
|
518
|
-
}
|
|
111
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
519
112
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
};
|
|
529
|
-
} catch (error) {
|
|
530
|
-
console.warn('AtomixGlass: Error setting up media queries:', error);
|
|
531
|
-
}
|
|
532
|
-
}, []);
|
|
533
|
-
|
|
534
|
-
const effectiveReducedMotion = reducedMotion || userPrefersReducedMotion;
|
|
535
|
-
const effectiveHighContrast = highContrast || userPrefersHighContrast;
|
|
536
|
-
const effectiveDisableEffects = disableEffects || effectiveReducedMotion;
|
|
537
|
-
|
|
538
|
-
const globalMousePos = externalGlobalMousePos || internalGlobalMousePos;
|
|
539
|
-
const mouseOffset = externalMouseOffset || internalMouseOffset;
|
|
540
|
-
|
|
541
|
-
const mouseMoveThrottleRef = useRef<number | null>(null);
|
|
542
|
-
const lastMouseEventRef = useRef<MouseEvent | null>(null);
|
|
543
|
-
|
|
544
|
-
const handleMouseMove = useCallback(
|
|
545
|
-
(e: MouseEvent) => {
|
|
546
|
-
lastMouseEventRef.current = e;
|
|
547
|
-
|
|
548
|
-
if (mouseMoveThrottleRef.current === null) {
|
|
549
|
-
mouseMoveThrottleRef.current = requestAnimationFrame(() => {
|
|
550
|
-
const event = lastMouseEventRef.current;
|
|
551
|
-
if (!event) {
|
|
552
|
-
mouseMoveThrottleRef.current = null;
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
557
|
-
if (!container) {
|
|
558
|
-
mouseMoveThrottleRef.current = null;
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
try {
|
|
563
|
-
const startTime = enablePerformanceMonitoring ? performance.now() : 0;
|
|
564
|
-
|
|
565
|
-
const rect = container.getBoundingClientRect();
|
|
566
|
-
const centerX = rect.left + rect.width / 2;
|
|
567
|
-
const centerY = rect.top + rect.height / 2;
|
|
568
|
-
|
|
569
|
-
setInternalMouseOffset({
|
|
570
|
-
x: ((event.clientX - centerX) / rect.width) * 100,
|
|
571
|
-
y: ((event.clientY - centerY) / rect.height) * 100,
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
setInternalGlobalMousePos({
|
|
575
|
-
x: event.clientX,
|
|
576
|
-
y: event.clientY,
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
if (enablePerformanceMonitoring) {
|
|
580
|
-
const endTime = performance.now();
|
|
581
|
-
const duration = endTime - startTime;
|
|
582
|
-
if (duration > 5) {
|
|
583
|
-
console.warn(`AtomixGlass: Mouse tracking took ${duration.toFixed(2)}ms`);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
} catch (error) {
|
|
587
|
-
console.warn('AtomixGlass: Error in mouse tracking:', error);
|
|
588
|
-
} finally {
|
|
589
|
-
mouseMoveThrottleRef.current = null;
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
},
|
|
594
|
-
[mouseContainer]
|
|
595
|
-
);
|
|
596
|
-
|
|
597
|
-
useEffect(() => {
|
|
598
|
-
if (externalGlobalMousePos && externalMouseOffset) {
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (effectiveDisableEffects) {
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
607
|
-
if (!container) {
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
container.addEventListener('mousemove', handleMouseMove, { passive: true });
|
|
612
|
-
|
|
613
|
-
return () => {
|
|
614
|
-
container.removeEventListener('mousemove', handleMouseMove);
|
|
615
|
-
if (mouseMoveThrottleRef.current) {
|
|
616
|
-
cancelAnimationFrame(mouseMoveThrottleRef.current);
|
|
617
|
-
mouseMoveThrottleRef.current = null;
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
}, [
|
|
621
|
-
handleMouseMove,
|
|
622
|
-
mouseContainer,
|
|
623
|
-
externalGlobalMousePos,
|
|
624
|
-
externalMouseOffset,
|
|
113
|
+
// Use composable hook for all state and logic
|
|
114
|
+
const {
|
|
115
|
+
isHovered,
|
|
116
|
+
isActive,
|
|
117
|
+
glassSize,
|
|
118
|
+
effectiveCornerRadius,
|
|
119
|
+
effectiveReducedMotion,
|
|
120
|
+
effectiveHighContrast,
|
|
625
121
|
effectiveDisableEffects,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
655
|
-
if (centerDistance === 0) {
|
|
656
|
-
return 'scale(1)';
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const normalizedX = deltaX / centerDistance;
|
|
660
|
-
const normalizedY = deltaY / centerDistance;
|
|
661
|
-
|
|
662
|
-
const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
|
|
663
|
-
|
|
664
|
-
const scaleX =
|
|
665
|
-
1 +
|
|
666
|
-
Math.abs(normalizedX) * stretchIntensity * 0.3 -
|
|
667
|
-
Math.abs(normalizedY) * stretchIntensity * 0.15;
|
|
668
|
-
|
|
669
|
-
const scaleY =
|
|
670
|
-
1 +
|
|
671
|
-
Math.abs(normalizedY) * stretchIntensity * 0.3 -
|
|
672
|
-
Math.abs(normalizedX) * stretchIntensity * 0.15;
|
|
673
|
-
|
|
674
|
-
return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
|
|
675
|
-
}, [globalMousePos, elasticity, glassSize]);
|
|
676
|
-
|
|
677
|
-
const calculateFadeInFactor = useCallback(() => {
|
|
678
|
-
if (!globalMousePos.x || !globalMousePos.y || !glassRef.current) {
|
|
679
|
-
return 0;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
683
|
-
const pillCenterX = rect.left + rect.width / 2;
|
|
684
|
-
const pillCenterY = rect.top + rect.height / 2;
|
|
685
|
-
const pillWidth = glassSize.width;
|
|
686
|
-
const pillHeight = glassSize.height;
|
|
687
|
-
|
|
688
|
-
const edgeDistanceX = Math.max(0, Math.abs(globalMousePos.x - pillCenterX) - pillWidth / 2);
|
|
689
|
-
const edgeDistanceY = Math.max(0, Math.abs(globalMousePos.y - pillCenterY) - pillHeight / 2);
|
|
690
|
-
const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
|
|
691
|
-
|
|
692
|
-
const activationZone = 200;
|
|
693
|
-
return edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone;
|
|
694
|
-
}, [globalMousePos, glassSize]);
|
|
695
|
-
|
|
696
|
-
const calculateElasticTranslation = useCallback(() => {
|
|
697
|
-
if (!glassRef.current) {
|
|
698
|
-
return { x: 0, y: 0 };
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
const fadeInFactor = calculateFadeInFactor();
|
|
702
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
703
|
-
const pillCenterX = rect.left + rect.width / 2;
|
|
704
|
-
const pillCenterY = rect.top + rect.height / 2;
|
|
705
|
-
|
|
706
|
-
return {
|
|
707
|
-
x: (globalMousePos.x - pillCenterX) * elasticity * 0.1 * fadeInFactor,
|
|
708
|
-
y: (globalMousePos.y - pillCenterY) * elasticity * 0.1 * fadeInFactor,
|
|
709
|
-
};
|
|
710
|
-
}, [globalMousePos, elasticity, calculateFadeInFactor]);
|
|
711
|
-
|
|
712
|
-
useEffect(() => {
|
|
713
|
-
const isValidElement = (element: HTMLElement | null): element is HTMLElement => {
|
|
714
|
-
return element !== null && element instanceof HTMLElement && element.isConnected;
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
let rafId: number | null = null;
|
|
718
|
-
let lastSize = { width: 0, height: 0 };
|
|
719
|
-
let lastCornerRadius = cornerRadius;
|
|
720
|
-
|
|
721
|
-
const updateGlassSize = (forceUpdate = false): void => {
|
|
722
|
-
try {
|
|
723
|
-
if (rafId !== null) {
|
|
724
|
-
cancelAnimationFrame(rafId);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
rafId = requestAnimationFrame(() => {
|
|
728
|
-
try {
|
|
729
|
-
if (!isValidElement(glassRef.current)) {
|
|
730
|
-
console.warn('AtomixGlass: Element not available for size calculation');
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
735
|
-
|
|
736
|
-
if (rect.width <= 0 || rect.height <= 0) {
|
|
737
|
-
console.warn('AtomixGlass: Invalid dimensions detected', {
|
|
738
|
-
width: rect.width,
|
|
739
|
-
height: rect.height,
|
|
740
|
-
});
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
const cornerRadiusOffset = Math.max(0, cornerRadius * 0.1);
|
|
745
|
-
const newSize = {
|
|
746
|
-
width: Math.round(rect.width + cornerRadiusOffset),
|
|
747
|
-
height: Math.round(rect.height + cornerRadiusOffset),
|
|
748
|
-
};
|
|
749
|
-
|
|
750
|
-
const cornerRadiusChanged = lastCornerRadius !== cornerRadius;
|
|
751
|
-
const dimensionsChanged =
|
|
752
|
-
newSize.width !== lastSize.width || newSize.height !== lastSize.height;
|
|
753
|
-
|
|
754
|
-
if (forceUpdate || cornerRadiusChanged || dimensionsChanged) {
|
|
755
|
-
lastSize = newSize;
|
|
756
|
-
lastCornerRadius = cornerRadius;
|
|
757
|
-
setGlassSize(newSize);
|
|
758
|
-
|
|
759
|
-
if (enablePerformanceMonitoring && (cornerRadiusChanged || dimensionsChanged)) {
|
|
760
|
-
console.log('AtomixGlass: Size updated', {
|
|
761
|
-
newSize,
|
|
762
|
-
cornerRadius,
|
|
763
|
-
cornerRadiusChanged,
|
|
764
|
-
dimensionsChanged,
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
} catch (error) {
|
|
769
|
-
console.error('AtomixGlass: Error updating glass size:', error);
|
|
770
|
-
} finally {
|
|
771
|
-
rafId = null;
|
|
772
|
-
}
|
|
773
|
-
});
|
|
774
|
-
} catch (error) {
|
|
775
|
-
console.error('AtomixGlass: Error in updateGlassSize:', error);
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
let resizeTimeoutId: NodeJS.Timeout | null = null;
|
|
780
|
-
const debouncedResizeHandler = (): void => {
|
|
781
|
-
if (resizeTimeoutId) {
|
|
782
|
-
clearTimeout(resizeTimeoutId);
|
|
783
|
-
}
|
|
784
|
-
resizeTimeoutId = setTimeout(updateGlassSize, 16);
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
try {
|
|
788
|
-
updateGlassSize(true);
|
|
789
|
-
} catch (error) {
|
|
790
|
-
console.error('AtomixGlass: Error in initial size update:', error);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
let resizeObserver: ResizeObserver | null = null;
|
|
794
|
-
let fallbackInterval: NodeJS.Timeout | null = null;
|
|
795
|
-
|
|
796
|
-
try {
|
|
797
|
-
const hasResizeObserver =
|
|
798
|
-
typeof ResizeObserver !== 'undefined' &&
|
|
799
|
-
typeof ResizeObserver.prototype.observe === 'function';
|
|
800
|
-
|
|
801
|
-
if (hasResizeObserver && isValidElement(glassRef.current)) {
|
|
802
|
-
try {
|
|
803
|
-
resizeObserver = new ResizeObserver(entries => {
|
|
804
|
-
try {
|
|
805
|
-
for (const entry of entries) {
|
|
806
|
-
if (entry.target === glassRef.current) {
|
|
807
|
-
updateGlassSize();
|
|
808
|
-
break;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
} catch (error) {
|
|
812
|
-
console.error('AtomixGlass: Error in ResizeObserver callback:', error);
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
resizeObserver.observe(glassRef.current);
|
|
817
|
-
} catch (resizeObserverError) {
|
|
818
|
-
console.warn(
|
|
819
|
-
'AtomixGlass: ResizeObserver creation failed, using fallback:',
|
|
820
|
-
resizeObserverError
|
|
821
|
-
);
|
|
822
|
-
fallbackInterval = setInterval(() => {
|
|
823
|
-
if (isValidElement(glassRef.current)) {
|
|
824
|
-
updateGlassSize();
|
|
825
|
-
}
|
|
826
|
-
}, 100);
|
|
827
|
-
}
|
|
828
|
-
} else {
|
|
829
|
-
console.warn('AtomixGlass: ResizeObserver not supported, using fallback polling');
|
|
830
|
-
fallbackInterval = setInterval(() => {
|
|
831
|
-
if (isValidElement(glassRef.current)) {
|
|
832
|
-
updateGlassSize();
|
|
833
|
-
}
|
|
834
|
-
}, 100);
|
|
835
|
-
}
|
|
836
|
-
} catch (error) {
|
|
837
|
-
console.error('AtomixGlass: Error setting up ResizeObserver:', error);
|
|
838
|
-
fallbackInterval = setInterval(() => {
|
|
839
|
-
if (isValidElement(glassRef.current)) {
|
|
840
|
-
updateGlassSize();
|
|
841
|
-
}
|
|
842
|
-
}, 100);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
window.addEventListener('resize', debouncedResizeHandler, { passive: true });
|
|
846
|
-
|
|
847
|
-
return () => {
|
|
848
|
-
try {
|
|
849
|
-
if (rafId !== null) {
|
|
850
|
-
cancelAnimationFrame(rafId);
|
|
851
|
-
rafId = null;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
if (resizeTimeoutId) {
|
|
855
|
-
clearTimeout(resizeTimeoutId);
|
|
856
|
-
resizeTimeoutId = null;
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
window.removeEventListener('resize', debouncedResizeHandler);
|
|
860
|
-
|
|
861
|
-
if (resizeObserver) {
|
|
862
|
-
try {
|
|
863
|
-
if (isValidElement(glassRef.current)) {
|
|
864
|
-
resizeObserver.unobserve(glassRef.current);
|
|
865
|
-
}
|
|
866
|
-
resizeObserver.disconnect();
|
|
867
|
-
} catch (error) {
|
|
868
|
-
console.error('AtomixGlass: Error cleaning up ResizeObserver:', error);
|
|
869
|
-
}
|
|
870
|
-
resizeObserver = null;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
if (fallbackInterval) {
|
|
874
|
-
clearInterval(fallbackInterval);
|
|
875
|
-
fallbackInterval = null;
|
|
876
|
-
}
|
|
877
|
-
} catch (error) {
|
|
878
|
-
console.error('AtomixGlass: Error in cleanup:', error);
|
|
879
|
-
}
|
|
880
|
-
};
|
|
881
|
-
}, [cornerRadius, enablePerformanceMonitoring]);
|
|
882
|
-
|
|
883
|
-
useEffect(() => {
|
|
884
|
-
if (!glassRef.current) return;
|
|
885
|
-
|
|
886
|
-
const timeoutId = setTimeout(() => {
|
|
887
|
-
try {
|
|
888
|
-
const rect = glassRef.current?.getBoundingClientRect();
|
|
889
|
-
if (rect && rect.width > 0 && rect.height > 0) {
|
|
890
|
-
const cornerRadiusOffset = Math.max(0, cornerRadius * 0.1);
|
|
891
|
-
const newSize = {
|
|
892
|
-
width: Math.round(rect.width + cornerRadiusOffset),
|
|
893
|
-
height: Math.round(rect.height + cornerRadiusOffset),
|
|
894
|
-
};
|
|
895
|
-
setGlassSize(newSize);
|
|
896
|
-
|
|
897
|
-
if (enablePerformanceMonitoring) {
|
|
898
|
-
console.log('AtomixGlass: Corner radius change triggered size update', {
|
|
899
|
-
cornerRadius,
|
|
900
|
-
newSize,
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
} catch (error) {
|
|
905
|
-
console.warn('AtomixGlass: Error in corner radius size update:', error);
|
|
906
|
-
}
|
|
907
|
-
}, 0);
|
|
908
|
-
|
|
909
|
-
return () => clearTimeout(timeoutId);
|
|
910
|
-
}, [cornerRadius, enablePerformanceMonitoring]);
|
|
911
|
-
|
|
912
|
-
const elasticTranslation = useMemo(() => {
|
|
913
|
-
if (effectiveDisableEffects) {
|
|
914
|
-
return { x: 0, y: 0 };
|
|
915
|
-
}
|
|
916
|
-
return calculateElasticTranslation();
|
|
917
|
-
}, [calculateElasticTranslation, effectiveDisableEffects]);
|
|
122
|
+
overLightConfig,
|
|
123
|
+
globalMousePosition,
|
|
124
|
+
mouseOffset,
|
|
125
|
+
transformStyle,
|
|
126
|
+
handleMouseEnter,
|
|
127
|
+
handleMouseLeave,
|
|
128
|
+
handleMouseDown,
|
|
129
|
+
handleMouseUp,
|
|
130
|
+
handleKeyDown,
|
|
131
|
+
} = useAtomixGlass({
|
|
132
|
+
glassRef,
|
|
133
|
+
contentRef,
|
|
134
|
+
cornerRadius,
|
|
135
|
+
globalMousePosition: externalGlobalMousePosition,
|
|
136
|
+
mouseOffset: externalMouseOffset,
|
|
137
|
+
mouseContainer,
|
|
138
|
+
overLight,
|
|
139
|
+
reducedMotion,
|
|
140
|
+
highContrast,
|
|
141
|
+
disableEffects,
|
|
142
|
+
elasticity,
|
|
143
|
+
onClick,
|
|
144
|
+
debugCornerRadius,
|
|
145
|
+
debugOverLight,
|
|
146
|
+
enablePerformanceMonitoring,
|
|
147
|
+
children,
|
|
148
|
+
});
|
|
918
149
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
return calculateDirectionalScale();
|
|
924
|
-
}, [calculateDirectionalScale, effectiveDisableEffects]);
|
|
150
|
+
// Use consistent overLight state from hook
|
|
151
|
+
const isOverLight = overLightConfig.isOverLight;
|
|
152
|
+
const shouldRenderOverLightLayers = enableOverLightLayers && isOverLight;
|
|
925
153
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}, [elasticTranslation, isActive, onClick, directionalScale, effectiveDisableEffects]);
|
|
154
|
+
// Memoize transition duration using design token pattern
|
|
155
|
+
const transitionDuration = useMemo(
|
|
156
|
+
() => (effectiveReducedMotion ? 'none' : 'var(--atomix-transition-duration, 0.2s) ease-out'),
|
|
157
|
+
[effectiveReducedMotion]
|
|
158
|
+
);
|
|
932
159
|
|
|
160
|
+
// Calculate base style with transforms (only dynamic values)
|
|
161
|
+
// Performance: willChange is set only when transforms are active and effects are enabled
|
|
933
162
|
const baseStyle = useMemo(
|
|
934
163
|
() => ({
|
|
935
164
|
...style,
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
165
|
+
...(elasticity !== 0 && !effectiveDisableEffects && {
|
|
166
|
+
transform: transformStyle,
|
|
167
|
+
willChange: 'transform',
|
|
168
|
+
}),
|
|
169
|
+
// Reset willChange when effects are disabled to allow browser optimization
|
|
170
|
+
...(effectiveDisableEffects && {
|
|
171
|
+
willChange: 'auto',
|
|
943
172
|
}),
|
|
944
173
|
}),
|
|
945
|
-
[style, transformStyle,
|
|
174
|
+
[style, transformStyle, effectiveDisableEffects, elasticity]
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Build className with state modifiers
|
|
178
|
+
const componentClassName = useMemo(
|
|
179
|
+
() =>
|
|
180
|
+
[
|
|
181
|
+
ATOMIX_GLASS.BASE_CLASS,
|
|
182
|
+
effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`,
|
|
183
|
+
effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`,
|
|
184
|
+
effectiveDisableEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`,
|
|
185
|
+
className,
|
|
186
|
+
]
|
|
187
|
+
.filter(Boolean)
|
|
188
|
+
.join(' '),
|
|
189
|
+
[effectiveReducedMotion, effectiveHighContrast, effectiveDisableEffects, className]
|
|
946
190
|
);
|
|
947
191
|
|
|
192
|
+
// Calculate position and size styles
|
|
193
|
+
// Optimize: only depend on specific baseStyle properties used
|
|
194
|
+
const baseStylePosition = baseStyle.position;
|
|
195
|
+
const baseStyleTop = baseStyle.top;
|
|
196
|
+
const baseStyleLeft = baseStyle.left;
|
|
197
|
+
|
|
948
198
|
const positionStyles = useMemo(
|
|
949
199
|
() => ({
|
|
950
|
-
position: (
|
|
951
|
-
top:
|
|
952
|
-
left:
|
|
200
|
+
position: (baseStylePosition || 'absolute') as React.CSSProperties['position'],
|
|
201
|
+
top: baseStyleTop || 0,
|
|
202
|
+
left: baseStyleLeft || 0,
|
|
953
203
|
}),
|
|
954
|
-
[
|
|
204
|
+
[baseStylePosition, baseStyleTop, baseStyleLeft]
|
|
955
205
|
);
|
|
956
206
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
try {
|
|
963
|
-
const rect = glassRef.current.getBoundingClientRect();
|
|
964
|
-
return {
|
|
965
|
-
width: Math.max(rect.width, 0),
|
|
966
|
-
height: Math.max(rect.height, 0),
|
|
967
|
-
};
|
|
968
|
-
} catch (error) {
|
|
969
|
-
console.warn('AtomixGlass: Error getting current element size:', error);
|
|
970
|
-
return { width: 0, height: 0 };
|
|
971
|
-
}
|
|
972
|
-
}, []);
|
|
973
|
-
|
|
974
|
-
const getTransformedSize = useCallback(() => {
|
|
975
|
-
const currentSize = getCurrentElementSize();
|
|
976
|
-
|
|
977
|
-
if (effectiveDisableEffects || currentSize.width === 0 || currentSize.height === 0) {
|
|
978
|
-
return currentSize;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
let scaleX = 1;
|
|
982
|
-
let scaleY = 1;
|
|
983
|
-
|
|
984
|
-
const simpleScaleMatch = directionalScale.match(/scale\(([^)]+)\)/);
|
|
985
|
-
if (simpleScaleMatch && simpleScaleMatch[1]) {
|
|
986
|
-
const scaleValue = parseFloat(simpleScaleMatch[1]);
|
|
987
|
-
scaleX = scaleValue;
|
|
988
|
-
scaleY = scaleValue;
|
|
989
|
-
} else {
|
|
990
|
-
const scaleXMatch = directionalScale.match(/scaleX\(([^)]+)\)/);
|
|
991
|
-
if (scaleXMatch && scaleXMatch[1]) {
|
|
992
|
-
scaleX = parseFloat(scaleXMatch[1]);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
const scaleYMatch = directionalScale.match(/scaleY\(([^)]+)\)/);
|
|
996
|
-
if (scaleYMatch && scaleYMatch[1]) {
|
|
997
|
-
scaleY = parseFloat(scaleYMatch[1]);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
const transformedSize = {
|
|
1002
|
-
width: currentSize.width * scaleX,
|
|
1003
|
-
height: currentSize.height * scaleY,
|
|
1004
|
-
};
|
|
207
|
+
// Optimize: only depend on specific baseStyle properties used
|
|
208
|
+
const baseStyleWidth = baseStyle.width;
|
|
209
|
+
const baseStyleHeight = baseStyle.height;
|
|
210
|
+
const glassSizeWidth = glassSize.width;
|
|
211
|
+
const glassSizeHeight = glassSize.height;
|
|
1005
212
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
213
|
+
const adjustedSize = useMemo(
|
|
214
|
+
() => ({
|
|
215
|
+
width:
|
|
216
|
+
baseStylePosition !== 'fixed'
|
|
217
|
+
? '100%'
|
|
218
|
+
: baseStyleWidth
|
|
219
|
+
? baseStyleWidth
|
|
220
|
+
: Math.max(glassSizeWidth, 0),
|
|
221
|
+
height:
|
|
222
|
+
baseStylePosition !== 'fixed'
|
|
223
|
+
? '100%'
|
|
224
|
+
: baseStyleHeight
|
|
225
|
+
? baseStyleHeight
|
|
226
|
+
: Math.max(glassSizeHeight, 0),
|
|
227
|
+
}),
|
|
228
|
+
[baseStylePosition, baseStyleWidth, baseStyleHeight, glassSizeWidth, glassSizeHeight]
|
|
229
|
+
);
|
|
1023
230
|
|
|
1024
|
-
const borderLayer1Style = useMemo(() => {
|
|
1025
|
-
const borderWidth = 1.5;
|
|
1026
231
|
|
|
1027
|
-
const adjustedSize = {
|
|
1028
|
-
width: baseStyle.position !== 'fixed' ? '100%' : baseStyle.width ? baseStyle.width : Math.max(glassSize.width, 0),
|
|
1029
|
-
height: baseStyle.position !== 'fixed' ? '100%' : baseStyle.height ? baseStyle.height : Math.max(glassSize.height, 0),
|
|
1030
|
-
};
|
|
1031
232
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
height: adjustedSize.height,
|
|
1037
|
-
borderRadius: `${Math.max(0, cornerRadius)}px`,
|
|
1038
|
-
transform: baseStyle.transform,
|
|
1039
|
-
transition: effectiveReducedMotion ? 'none' : baseStyle.transition,
|
|
1040
|
-
pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
|
|
1041
|
-
mixBlendMode: 'screen' as React.CSSProperties['mixBlendMode'],
|
|
1042
|
-
opacity: 0.2,
|
|
1043
|
-
padding: `${borderWidth}px`,
|
|
1044
|
-
boxSizing: 'border-box' as React.CSSProperties['boxSizing'],
|
|
1045
|
-
zIndex: 5,
|
|
1046
|
-
WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
|
|
1047
|
-
WebkitMaskComposite: 'xor',
|
|
1048
|
-
maskComposite: 'exclude',
|
|
1049
|
-
boxShadow:
|
|
1050
|
-
'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)',
|
|
1051
|
-
background: `linear-gradient(
|
|
1052
|
-
${135 + mouseOffset.x * 1.2}deg,
|
|
1053
|
-
rgba(255, 255, 255, 0.0) 0%,
|
|
1054
|
-
rgba(255, 255, 255, ${0.12 + Math.abs(mouseOffset.x) * 0.008}) ${Math.max(10, 33 + mouseOffset.y * 0.3)}%,
|
|
1055
|
-
rgba(255, 255, 255, ${0.4 + Math.abs(mouseOffset.x) * 0.012}) ${Math.min(90, 66 + mouseOffset.y * 0.4)}%,
|
|
1056
|
-
rgba(255, 255, 255, 0.0) 100%
|
|
1057
|
-
)`,
|
|
1058
|
-
};
|
|
1059
|
-
}, [
|
|
1060
|
-
positionStyles,
|
|
1061
|
-
glassSize,
|
|
1062
|
-
cornerRadius,
|
|
1063
|
-
baseStyle,
|
|
1064
|
-
mouseOffset,
|
|
1065
|
-
effectiveReducedMotion,
|
|
1066
|
-
]);
|
|
233
|
+
// Memoize gradient calculations separately for better performance
|
|
234
|
+
// Extract mouse position values for dependency optimization
|
|
235
|
+
const mouseOffsetX = mouseOffset.x;
|
|
236
|
+
const mouseOffsetY = mouseOffset.y;
|
|
1067
237
|
|
|
1068
|
-
const
|
|
1069
|
-
const
|
|
238
|
+
const gradientCalculations = useMemo(() => {
|
|
239
|
+
const mx = mouseOffsetX;
|
|
240
|
+
const my = mouseOffsetY;
|
|
241
|
+
const { GRADIENT } = ATOMIX_GLASS.CONSTANTS;
|
|
1070
242
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
baseStyle,
|
|
1107
|
-
mouseOffset,
|
|
1108
|
-
effectiveReducedMotion,
|
|
1109
|
-
]);
|
|
243
|
+
// Calculate gradient angles and stops (optimized)
|
|
244
|
+
const borderGradientAngle =
|
|
245
|
+
GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER;
|
|
246
|
+
const borderStop1 = Math.max(
|
|
247
|
+
GRADIENT.BORDER_STOP_1.MIN,
|
|
248
|
+
GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
|
|
249
|
+
);
|
|
250
|
+
const borderStop2 = Math.min(
|
|
251
|
+
GRADIENT.BORDER_STOP_2.MAX,
|
|
252
|
+
GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
|
|
253
|
+
);
|
|
254
|
+
const borderOpacity1 =
|
|
255
|
+
GRADIENT.BORDER_OPACITY.BASE_1 +
|
|
256
|
+
Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW;
|
|
257
|
+
const borderOpacity2 =
|
|
258
|
+
GRADIENT.BORDER_OPACITY.BASE_2 +
|
|
259
|
+
Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH;
|
|
260
|
+
const borderOpacity3 =
|
|
261
|
+
GRADIENT.BORDER_OPACITY.BASE_3 +
|
|
262
|
+
Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW;
|
|
263
|
+
const borderOpacity4 =
|
|
264
|
+
GRADIENT.BORDER_OPACITY.BASE_4 +
|
|
265
|
+
Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH;
|
|
266
|
+
|
|
267
|
+
// Hover gradient positions
|
|
268
|
+
const hover1X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1;
|
|
269
|
+
const hover1Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1;
|
|
270
|
+
const hover2X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2;
|
|
271
|
+
const hover2Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2;
|
|
272
|
+
const hover3X = GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3;
|
|
273
|
+
const hover3Y = GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3;
|
|
274
|
+
|
|
275
|
+
// Base layer positions
|
|
276
|
+
const baseX = GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER;
|
|
277
|
+
const baseY = GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER;
|
|
1110
278
|
|
|
1111
|
-
const hoverEffect1Style = useMemo(() => {
|
|
1112
279
|
return {
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
280
|
+
isOverLight,
|
|
281
|
+
mx,
|
|
282
|
+
my,
|
|
283
|
+
borderGradientAngle,
|
|
284
|
+
borderStop1,
|
|
285
|
+
borderStop2,
|
|
286
|
+
borderOpacity1,
|
|
287
|
+
borderOpacity2,
|
|
288
|
+
borderOpacity3,
|
|
289
|
+
borderOpacity4,
|
|
290
|
+
hover1X,
|
|
291
|
+
hover1Y,
|
|
292
|
+
hover2X,
|
|
293
|
+
hover2Y,
|
|
294
|
+
hover3X,
|
|
295
|
+
hover3Y,
|
|
296
|
+
baseX,
|
|
297
|
+
baseY,
|
|
1127
298
|
};
|
|
1128
|
-
}, [
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
299
|
+
}, [mouseOffsetX, mouseOffsetY, isOverLight]);
|
|
300
|
+
|
|
301
|
+
// Memoize opacity values separately - using design token values where applicable
|
|
302
|
+
// Optimize: extract overLightConfig.opacity to avoid depending on whole object
|
|
303
|
+
const overLightOpacity = overLightConfig.opacity;
|
|
304
|
+
|
|
305
|
+
// Read opacity design tokens from CSS custom properties
|
|
306
|
+
const opacityValues = useMemo(() => {
|
|
307
|
+
// Get opacity values from CSS custom properties with fallbacks
|
|
308
|
+
// These align with design tokens: --atomix-opacity-50, --atomix-opacity-40, etc.
|
|
309
|
+
let opacity50 = 0.5;
|
|
310
|
+
let opacity40 = 0.4;
|
|
311
|
+
let opacity80 = 0.8;
|
|
312
|
+
let opacity0 = 0;
|
|
313
|
+
|
|
314
|
+
// Try to read from CSS custom properties if available (SSR-safe)
|
|
315
|
+
if (typeof window !== 'undefined' && glassRef.current) {
|
|
316
|
+
try {
|
|
317
|
+
const computedStyle = window.getComputedStyle(glassRef.current);
|
|
318
|
+
const opacity50Value = computedStyle.getPropertyValue('--atomix-opacity-50').trim();
|
|
319
|
+
const opacity40Value = computedStyle.getPropertyValue('--atomix-opacity-40').trim();
|
|
320
|
+
const opacity80Value = computedStyle.getPropertyValue('--atomix-opacity-80').trim();
|
|
321
|
+
const opacity0Value = computedStyle.getPropertyValue('--atomix-opacity-0').trim();
|
|
322
|
+
|
|
323
|
+
if (opacity50Value) opacity50 = parseFloat(opacity50Value) || 0.5;
|
|
324
|
+
if (opacity40Value) opacity40 = parseFloat(opacity40Value) || 0.4;
|
|
325
|
+
if (opacity80Value) opacity80 = parseFloat(opacity80Value) || 0.8;
|
|
326
|
+
if (opacity0Value) opacity0 = parseFloat(opacity0Value) || 0;
|
|
327
|
+
} catch (error) {
|
|
328
|
+
// Fallback to defaults if reading fails
|
|
329
|
+
}
|
|
330
|
+
}
|
|
1137
331
|
|
|
1138
|
-
|
|
332
|
+
const BASE_OVER_LIGHT_OPACITY = opacity40; // Uses design token
|
|
333
|
+
const OVER_OPACITY_MULTIPLIER = 1.1; // Dynamic multiplier for overlay
|
|
334
|
+
|
|
1139
335
|
return {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
transform: baseStyle.transform,
|
|
1146
|
-
pointerEvents: 'none' as React.CSSProperties['pointerEvents'],
|
|
1147
|
-
transition: effectiveReducedMotion ? 'none' : 'all 0.2s ease-out',
|
|
1148
|
-
opacity: isActive ? 0.5 : 0,
|
|
1149
|
-
background: `radial-gradient(
|
|
1150
|
-
circle at ${50 + mouseOffset.x / 1.5}% ${50 + mouseOffset.y / 1.5}%,
|
|
1151
|
-
rgba(255, 255, 255, 1) 0%,
|
|
1152
|
-
rgba(255, 255, 255, 0) 80%
|
|
1153
|
-
)`,
|
|
1154
|
-
mixBlendMode: 'overlay' as React.CSSProperties['mixBlendMode'],
|
|
336
|
+
hover1: isHovered || isActive ? opacity50 : opacity0,
|
|
337
|
+
hover2: isActive ? opacity50 : opacity0,
|
|
338
|
+
hover3: isHovered ? opacity40 : isActive ? opacity80 : opacity0,
|
|
339
|
+
base: isOverLight ? overLightOpacity || BASE_OVER_LIGHT_OPACITY : opacity0,
|
|
340
|
+
over: isOverLight ? (overLightOpacity || BASE_OVER_LIGHT_OPACITY) * OVER_OPACITY_MULTIPLIER : opacity0,
|
|
1155
341
|
};
|
|
1156
|
-
}, [
|
|
342
|
+
}, [isHovered, isActive, isOverLight, overLightOpacity, glassRef]);
|
|
343
|
+
|
|
344
|
+
// Generate CSS variables for layers (only dynamic values)
|
|
345
|
+
// Optimize: extract specific properties from objects to minimize dependencies
|
|
346
|
+
const gradientIsOverLight = gradientCalculations.isOverLight;
|
|
347
|
+
const gradientMx = gradientCalculations.mx;
|
|
348
|
+
const gradientMy = gradientCalculations.my;
|
|
349
|
+
const gradientBorderGradientAngle = gradientCalculations.borderGradientAngle;
|
|
350
|
+
const gradientBorderStop1 = gradientCalculations.borderStop1;
|
|
351
|
+
const gradientBorderStop2 = gradientCalculations.borderStop2;
|
|
352
|
+
const gradientBorderOpacity1 = gradientCalculations.borderOpacity1;
|
|
353
|
+
const gradientBorderOpacity2 = gradientCalculations.borderOpacity2;
|
|
354
|
+
const gradientBorderOpacity3 = gradientCalculations.borderOpacity3;
|
|
355
|
+
const gradientBorderOpacity4 = gradientCalculations.borderOpacity4;
|
|
356
|
+
const gradientHover1X = gradientCalculations.hover1X;
|
|
357
|
+
const gradientHover1Y = gradientCalculations.hover1Y;
|
|
358
|
+
const gradientHover2X = gradientCalculations.hover2X;
|
|
359
|
+
const gradientHover2Y = gradientCalculations.hover2Y;
|
|
360
|
+
const gradientHover3X = gradientCalculations.hover3X;
|
|
361
|
+
const gradientHover3Y = gradientCalculations.hover3Y;
|
|
362
|
+
const gradientBaseX = gradientCalculations.baseX;
|
|
363
|
+
const gradientBaseY = gradientCalculations.baseY;
|
|
364
|
+
|
|
365
|
+
const positionStylesPosition = positionStyles.position;
|
|
366
|
+
const positionStylesTop = positionStyles.top;
|
|
367
|
+
const positionStylesLeft = positionStyles.left;
|
|
368
|
+
|
|
369
|
+
const adjustedSizeWidth = adjustedSize.width;
|
|
370
|
+
const adjustedSizeHeight = adjustedSize.height;
|
|
371
|
+
|
|
372
|
+
const baseStyleTransform = baseStyle.transform;
|
|
373
|
+
const opacityValuesHover1 = opacityValues.hover1;
|
|
374
|
+
const opacityValuesHover2 = opacityValues.hover2;
|
|
375
|
+
const opacityValuesHover3 = opacityValues.hover3;
|
|
376
|
+
const opacityValuesBase = opacityValues.base;
|
|
377
|
+
const opacityValuesOver = opacityValues.over;
|
|
378
|
+
|
|
379
|
+
const glassVars = useMemo(() => {
|
|
380
|
+
// RGB color values for rgba() functions
|
|
381
|
+
// Note: CSS doesn't support rgba(var(--rgb), opacity) syntax, so we use direct values
|
|
382
|
+
// These values align with design tokens: --atomix-white-rgb and --atomix-black-rgb
|
|
383
|
+
// The actual RGB values are defined in SCSS and should match these fallbacks
|
|
384
|
+
// TODO: Consider reading from CSS custom properties if browser support improves
|
|
385
|
+
const whiteColor = '255, 255, 255'; // Matches --atomix-white-rgb design token
|
|
386
|
+
const blackColor = '0, 0, 0'; // Matches --atomix-black-rgb design token
|
|
1157
387
|
|
|
1158
|
-
const hoverEffect3Style = useMemo(() => {
|
|
1159
388
|
return {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
)
|
|
1173
|
-
|
|
1174
|
-
|
|
389
|
+
// Standard CSS custom properties for dynamic values
|
|
390
|
+
'--atomix-glass-radius': `${effectiveCornerRadius}px`,
|
|
391
|
+
'--atomix-glass-transform': baseStyleTransform || 'none',
|
|
392
|
+
'--atomix-glass-transition': effectiveReducedMotion ? 'none' : transitionDuration,
|
|
393
|
+
'--atomix-glass-position': positionStylesPosition,
|
|
394
|
+
'--atomix-glass-top': positionStylesTop !== 'fixed' ? `${positionStylesTop}px` : '0',
|
|
395
|
+
'--atomix-glass-left': positionStylesLeft !== 'fixed' ? `${positionStylesLeft}px` : '0',
|
|
396
|
+
'--atomix-glass-width':
|
|
397
|
+
baseStylePosition !== 'fixed' ? adjustedSizeWidth : `${adjustedSizeWidth}px`,
|
|
398
|
+
'--atomix-glass-height':
|
|
399
|
+
baseStylePosition !== 'fixed' ? adjustedSizeHeight : `${adjustedSizeHeight}px`,
|
|
400
|
+
// Border width: Use spacing token for consistency
|
|
401
|
+
'--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
|
|
402
|
+
'--atomix-glass-blend-mode': gradientIsOverLight ? 'multiply' : 'overlay',
|
|
403
|
+
// Dynamic gradients and backgrounds
|
|
404
|
+
// Note: RGB values use design token-aligned constants (white: 255,255,255; black: 0,0,0)
|
|
405
|
+
'--atomix-glass-border-gradient-1': `linear-gradient(${gradientBorderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${gradientBorderOpacity1}) ${gradientBorderStop1}%, rgba(${whiteColor}, ${gradientBorderOpacity2}) ${gradientBorderStop2}%, rgba(${whiteColor}, 0) 100%)`,
|
|
406
|
+
'--atomix-glass-border-gradient-2': `linear-gradient(${gradientBorderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${gradientBorderOpacity3}) ${gradientBorderStop1}%, rgba(${whiteColor}, ${gradientBorderOpacity4}) ${gradientBorderStop2}%, rgba(${whiteColor}, 0) 100%)`,
|
|
407
|
+
'--atomix-glass-hover-1-opacity': opacityValuesHover1,
|
|
408
|
+
'--atomix-glass-hover-1-gradient': gradientIsOverLight
|
|
409
|
+
? `radial-gradient(circle at ${gradientHover1X}% ${gradientHover1Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)`
|
|
410
|
+
: `radial-gradient(circle at ${gradientHover1X}% ${gradientHover1Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`,
|
|
411
|
+
'--atomix-glass-hover-2-opacity': opacityValuesHover2,
|
|
412
|
+
'--atomix-glass-hover-2-gradient': gradientIsOverLight
|
|
413
|
+
? `radial-gradient(circle at ${gradientHover2X}% ${gradientHover2Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)`
|
|
414
|
+
: `radial-gradient(circle at ${gradientHover2X}% ${gradientHover2Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`,
|
|
415
|
+
'--atomix-glass-hover-3-opacity': opacityValuesHover3,
|
|
416
|
+
'--atomix-glass-hover-3-gradient': gradientIsOverLight
|
|
417
|
+
? `radial-gradient(circle at ${gradientHover3X}% ${gradientHover3Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)`
|
|
418
|
+
: `radial-gradient(circle at ${gradientHover3X}% ${gradientHover3Y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`,
|
|
419
|
+
'--atomix-glass-base-opacity': opacityValuesBase,
|
|
420
|
+
'--atomix-glass-base-gradient': gradientIsOverLight
|
|
421
|
+
? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + gradientMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + gradientMy * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
|
|
422
|
+
: `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
|
|
423
|
+
'--atomix-glass-overlay-opacity': opacityValuesOver,
|
|
424
|
+
'--atomix-glass-overlay-gradient': gradientIsOverLight
|
|
425
|
+
? `radial-gradient(circle at ${gradientBaseX}% ${gradientBaseY}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + Math.abs(gradientMy) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
|
|
426
|
+
: `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
|
|
427
|
+
} as React.CSSProperties;
|
|
1175
428
|
}, [
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
429
|
+
// Position styles - only specific properties
|
|
430
|
+
positionStylesPosition,
|
|
431
|
+
positionStylesTop,
|
|
432
|
+
positionStylesLeft,
|
|
433
|
+
// Adjusted size - only specific properties
|
|
434
|
+
adjustedSizeWidth,
|
|
435
|
+
adjustedSizeHeight,
|
|
436
|
+
// Base style - only transform property
|
|
437
|
+
baseStyleTransform,
|
|
438
|
+
baseStylePosition,
|
|
439
|
+
// Other values
|
|
440
|
+
effectiveCornerRadius,
|
|
1182
441
|
effectiveReducedMotion,
|
|
442
|
+
transitionDuration,
|
|
443
|
+
// Gradient calculations - extracted properties
|
|
444
|
+
gradientIsOverLight,
|
|
445
|
+
gradientMx,
|
|
446
|
+
gradientMy,
|
|
447
|
+
gradientBorderGradientAngle,
|
|
448
|
+
gradientBorderStop1,
|
|
449
|
+
gradientBorderStop2,
|
|
450
|
+
gradientBorderOpacity1,
|
|
451
|
+
gradientBorderOpacity2,
|
|
452
|
+
gradientBorderOpacity3,
|
|
453
|
+
gradientBorderOpacity4,
|
|
454
|
+
gradientHover1X,
|
|
455
|
+
gradientHover1Y,
|
|
456
|
+
gradientHover2X,
|
|
457
|
+
gradientHover2Y,
|
|
458
|
+
gradientHover3X,
|
|
459
|
+
gradientHover3Y,
|
|
460
|
+
gradientBaseX,
|
|
461
|
+
gradientBaseY,
|
|
462
|
+
// Opacity values - extracted properties
|
|
463
|
+
opacityValuesHover1,
|
|
464
|
+
opacityValuesHover2,
|
|
465
|
+
opacityValuesHover3,
|
|
466
|
+
opacityValuesBase,
|
|
467
|
+
opacityValuesOver,
|
|
1183
468
|
]);
|
|
1184
469
|
|
|
1185
470
|
return (
|
|
1186
471
|
<div
|
|
1187
|
-
|
|
472
|
+
className={componentClassName}
|
|
473
|
+
style={glassVars}
|
|
1188
474
|
role={role || (onClick ? 'button' : undefined)}
|
|
1189
475
|
tabIndex={onClick ? (tabIndex ?? 0) : tabIndex}
|
|
1190
476
|
aria-label={ariaLabel}
|
|
1191
477
|
aria-describedby={ariaDescribedBy}
|
|
1192
|
-
aria-disabled={onClick ? false : undefined}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
? e => {
|
|
1196
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
1197
|
-
e.preventDefault();
|
|
1198
|
-
onClick();
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
: undefined
|
|
1202
|
-
}
|
|
478
|
+
aria-disabled={onClick && effectiveDisableEffects ? true : onClick ? false : undefined}
|
|
479
|
+
aria-pressed={onClick && isActive ? true : onClick ? false : undefined}
|
|
480
|
+
onKeyDown={onClick ? handleKeyDown : undefined}
|
|
1203
481
|
>
|
|
1204
|
-
|
|
1205
|
-
className={`u-bg-dark ${overLight ? 'u-opacity-50' : 'u-opacity-0'}`}
|
|
1206
|
-
style={{
|
|
1207
|
-
...positionStyles,
|
|
1208
|
-
height: glassSize.height,
|
|
1209
|
-
width: glassSize.width,
|
|
1210
|
-
borderRadius: `${cornerRadius}px`,
|
|
1211
|
-
transform: baseStyle.transform,
|
|
1212
|
-
transition: baseStyle.transition,
|
|
1213
|
-
willChange: 'transform',
|
|
1214
|
-
}}
|
|
1215
|
-
/>
|
|
1216
|
-
<div
|
|
1217
|
-
className={`u-bg-black ${overLight ? 'u-opacity-25' : 'u-opacity-0'}`}
|
|
1218
|
-
style={{
|
|
1219
|
-
...positionStyles,
|
|
1220
|
-
height: glassSize.height,
|
|
1221
|
-
width: glassSize.width,
|
|
1222
|
-
borderRadius: `${cornerRadius}px`,
|
|
1223
|
-
transform: baseStyle.transform,
|
|
1224
|
-
transition: baseStyle.transition,
|
|
1225
|
-
mixBlendMode: 'overlay',
|
|
1226
|
-
pointerEvents: 'none',
|
|
1227
|
-
willChange: 'transform',
|
|
1228
|
-
}}
|
|
1229
|
-
/>
|
|
482
|
+
|
|
1230
483
|
|
|
1231
|
-
<
|
|
484
|
+
<AtomixGlassContainer
|
|
1232
485
|
ref={glassRef}
|
|
486
|
+
contentRef={contentRef}
|
|
1233
487
|
className={className}
|
|
1234
|
-
style={
|
|
1235
|
-
|
|
1236
|
-
transform: baseStyle.transform,
|
|
1237
|
-
}}
|
|
1238
|
-
cornerRadius={cornerRadius}
|
|
488
|
+
style={baseStyle}
|
|
489
|
+
cornerRadius={effectiveCornerRadius}
|
|
1239
490
|
displacementScale={
|
|
1240
|
-
effectiveDisableEffects
|
|
491
|
+
effectiveDisableEffects
|
|
492
|
+
? 0
|
|
493
|
+
: mode === 'shader'
|
|
494
|
+
? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT
|
|
495
|
+
: overLightConfig.isOverLight
|
|
496
|
+
? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT
|
|
497
|
+
: displacementScale
|
|
1241
498
|
}
|
|
1242
499
|
blurAmount={effectiveDisableEffects ? 0 : blurAmount}
|
|
1243
|
-
saturation={
|
|
1244
|
-
|
|
500
|
+
saturation={
|
|
501
|
+
effectiveHighContrast
|
|
502
|
+
? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST
|
|
503
|
+
: overLightConfig.isOverLight
|
|
504
|
+
? saturation * overLightConfig.saturationBoost
|
|
505
|
+
: saturation
|
|
506
|
+
}
|
|
507
|
+
aberrationIntensity={
|
|
508
|
+
effectiveDisableEffects
|
|
509
|
+
? 0
|
|
510
|
+
: mode === 'shader'
|
|
511
|
+
? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION
|
|
512
|
+
: aberrationIntensity
|
|
513
|
+
}
|
|
1245
514
|
glassSize={glassSize}
|
|
1246
515
|
padding={padding}
|
|
1247
516
|
mouseOffset={effectiveDisableEffects ? { x: 0, y: 0 } : mouseOffset}
|
|
1248
|
-
|
|
1249
|
-
onMouseEnter={
|
|
1250
|
-
onMouseLeave={
|
|
1251
|
-
onMouseDown={
|
|
1252
|
-
onMouseUp={
|
|
517
|
+
globalMousePosition={effectiveDisableEffects ? { x: 0, y: 0 } : globalMousePosition}
|
|
518
|
+
onMouseEnter={handleMouseEnter}
|
|
519
|
+
onMouseLeave={handleMouseLeave}
|
|
520
|
+
onMouseDown={handleMouseDown}
|
|
521
|
+
onMouseUp={handleMouseUp}
|
|
1253
522
|
active={isActive}
|
|
1254
523
|
isHovered={isHovered}
|
|
1255
524
|
isActive={isActive}
|
|
1256
|
-
overLight={
|
|
525
|
+
overLight={overLightConfig.isOverLight}
|
|
1257
526
|
onClick={onClick}
|
|
1258
|
-
mode={
|
|
527
|
+
mode={mode}
|
|
1259
528
|
transform={baseStyle.transform}
|
|
1260
529
|
effectiveDisableEffects={effectiveDisableEffects}
|
|
1261
530
|
effectiveReducedMotion={effectiveReducedMotion}
|
|
531
|
+
shaderVariant={shaderVariant}
|
|
532
|
+
elasticity={elasticity}
|
|
533
|
+
enableLiquidBlur={enableLiquidBlur}
|
|
1262
534
|
>
|
|
1263
535
|
{children}
|
|
1264
|
-
</
|
|
1265
|
-
|
|
1266
|
-
<span style={borderLayer1Style} />
|
|
1267
|
-
|
|
1268
|
-
<span style={borderLayer2Style} />
|
|
1269
|
-
|
|
536
|
+
</AtomixGlassContainer>
|
|
1270
537
|
{Boolean(onClick) && (
|
|
1271
538
|
<>
|
|
1272
|
-
|
|
1273
|
-
<div
|
|
1274
|
-
<div
|
|
539
|
+
{/* Hover layers - opacity and background set via CSS variables in SCSS */}
|
|
540
|
+
<div className={ATOMIX_GLASS.HOVER_1_CLASS} />
|
|
541
|
+
<div className={ATOMIX_GLASS.HOVER_2_CLASS} />
|
|
542
|
+
<div className={ATOMIX_GLASS.HOVER_3_CLASS} />
|
|
543
|
+
</>
|
|
544
|
+
)}
|
|
545
|
+
|
|
546
|
+
{/* Background layers for over-light mode */}
|
|
547
|
+
{/* Static styles (pointer-events, will-change) are in SCSS */}
|
|
548
|
+
<div
|
|
549
|
+
className={[
|
|
550
|
+
ATOMIX_GLASS.BACKGROUND_LAYER_CLASS,
|
|
551
|
+
ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS,
|
|
552
|
+
isOverLight
|
|
553
|
+
? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS
|
|
554
|
+
: ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS,
|
|
555
|
+
]
|
|
556
|
+
.filter(Boolean)
|
|
557
|
+
.join(' ')}
|
|
558
|
+
style={{
|
|
559
|
+
...positionStyles,
|
|
560
|
+
height: adjustedSize.height,
|
|
561
|
+
width: adjustedSize.width,
|
|
562
|
+
borderRadius: `${effectiveCornerRadius}px`,
|
|
563
|
+
transform: baseStyle.transform,
|
|
564
|
+
transition: baseStyle.transition,
|
|
565
|
+
}}
|
|
566
|
+
/>
|
|
567
|
+
<div
|
|
568
|
+
className={[
|
|
569
|
+
ATOMIX_GLASS.BACKGROUND_LAYER_CLASS,
|
|
570
|
+
ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS,
|
|
571
|
+
isOverLight
|
|
572
|
+
? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS
|
|
573
|
+
: ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS,
|
|
574
|
+
]
|
|
575
|
+
.filter(Boolean)
|
|
576
|
+
.join(' ')}
|
|
577
|
+
style={{
|
|
578
|
+
...positionStyles,
|
|
579
|
+
height: adjustedSize.height,
|
|
580
|
+
width: adjustedSize.width,
|
|
581
|
+
borderRadius: `${effectiveCornerRadius}px`,
|
|
582
|
+
transform: baseStyle.transform,
|
|
583
|
+
transition: baseStyle.transition,
|
|
584
|
+
}}
|
|
585
|
+
/>
|
|
586
|
+
{shouldRenderOverLightLayers && (
|
|
587
|
+
<>
|
|
588
|
+
{/* Base and overlay layers - opacity and background set via CSS variables in SCSS */}
|
|
589
|
+
<div className={ATOMIX_GLASS.BASE_LAYER_CLASS} />
|
|
590
|
+
<div className={ATOMIX_GLASS.OVERLAY_LAYER_CLASS} />
|
|
591
|
+
{/* Overlay highlight - opacity and background are dynamic, calculated inline */}
|
|
592
|
+
<div
|
|
593
|
+
className={ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS}
|
|
594
|
+
style={{
|
|
595
|
+
opacity:
|
|
596
|
+
opacityValues.over *
|
|
597
|
+
ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
|
|
598
|
+
background: `radial-gradient(circle at ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_X}% ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_Y}%, rgba(255, 255, 255, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.WHITE_OPACITY}) 0%, transparent ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.STOP}%)`,
|
|
599
|
+
}}
|
|
600
|
+
/>
|
|
601
|
+
</>
|
|
602
|
+
)}
|
|
603
|
+
{enableBorderEffect && (
|
|
604
|
+
<>
|
|
605
|
+
{/* Border elements - all styles (static and dynamic via CSS variables) are in SCSS */}
|
|
606
|
+
{/* Position, size, transform, transition, border-radius all use CSS variables set in glassVars */}
|
|
607
|
+
<span className={ATOMIX_GLASS.BORDER_1_CLASS} />
|
|
608
|
+
<span className={ATOMIX_GLASS.BORDER_2_CLASS} />
|
|
1275
609
|
</>
|
|
1276
610
|
)}
|
|
1277
611
|
</div>
|