@shohojdhara/atomix 0.3.4 → 0.3.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/dist/atomix.css +9 -10
- package/dist/atomix.css.map +1 -0
- package/dist/atomix.min.css +15108 -11
- package/dist/atomix.min.css.map +1 -0
- package/dist/charts.d.ts +1929 -0
- package/dist/charts.js +6482 -0
- package/dist/charts.js.map +1 -0
- package/dist/core.d.ts +1289 -0
- package/dist/core.js +3357 -0
- package/dist/core.js.map +1 -0
- package/dist/forms.d.ts +1085 -0
- package/dist/forms.js +2450 -0
- package/dist/forms.js.map +1 -0
- package/dist/heavy.d.ts +636 -0
- package/dist/heavy.js +4550 -0
- package/dist/heavy.js.map +1 -0
- package/dist/index.d.ts +5161 -4990
- package/dist/index.esm.js +1457 -784
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1473 -790
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.d.ts +300 -0
- package/dist/layout.js +336 -0
- package/dist/layout.js.map +1 -0
- package/dist/theme.d.ts +1992 -0
- package/dist/theme.js +5348 -0
- package/dist/theme.js.map +1 -0
- package/package.json +66 -20
- package/scripts/atomix-cli.js +544 -16
- package/scripts/cli/__tests__/cli-commands.test.js +204 -0
- package/scripts/cli/__tests__/utils.test.js +201 -0
- package/scripts/cli/__tests__/vitest.config.js +26 -0
- package/scripts/cli/interactive-init.js +1 -1
- package/scripts/cli/token-manager.js +32 -7
- package/scripts/cli/utils.js +347 -0
- package/src/components/Accordion/Accordion.tsx +5 -54
- package/src/components/Accordion/index.ts +1 -1
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Badge/Badge.tsx +3 -3
- package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
- package/src/components/Card/ElevationCard.tsx +1 -1
- package/src/components/Chart/AnimatedChart.tsx +19 -17
- package/src/components/Chart/AreaChart.tsx +5 -1
- package/src/components/Chart/BarChart.tsx +1 -0
- package/src/components/Chart/BubbleChart.tsx +6 -5
- package/src/components/Chart/ChartToolbar.tsx +1 -0
- package/src/components/Chart/FunnelChart.tsx +1 -1
- package/src/components/Chart/RadarChart.tsx +19 -12
- package/src/components/Chart/ScatterChart.tsx +3 -3
- package/src/components/Chart/TreemapChart.tsx +2 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -1
- package/src/components/Chart/types.ts +12 -2
- package/src/components/Chart/utils.ts +4 -3
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/Dropdown/Dropdown.tsx +12 -9
- package/src/components/Footer/FooterSection.tsx +3 -3
- package/src/components/Form/Checkbox.tsx +3 -3
- package/src/components/Form/Input.tsx +4 -2
- package/src/components/Form/Radio.tsx +3 -3
- package/src/components/Form/Select.tsx +3 -3
- package/src/components/Form/Textarea.tsx +4 -2
- package/src/components/List/List.stories.tsx +3 -3
- package/src/components/List/List.tsx +3 -3
- package/src/components/List/ListGroup.tsx +3 -1
- package/src/components/Modal/Modal.tsx +3 -3
- package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
- package/src/components/Navigation/Menu/Menu.tsx +9 -3
- package/src/components/Pagination/Pagination.tsx +6 -5
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
- package/src/components/Popover/Popover.tsx +4 -4
- package/src/components/Progress/Progress.tsx +6 -2
- package/src/components/Rating/Rating.tsx +5 -2
- package/src/components/Slider/Slider.tsx +10 -9
- package/src/components/Spinner/Spinner.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/index.ts +5 -2
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
- package/src/lib/composables/useChartPerformance.ts +102 -78
- package/src/lib/composables/useChartScale.ts +10 -0
- package/src/lib/composables/useHero.ts +9 -2
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
- package/src/lib/composables/useSideMenu.ts +1 -0
- package/src/lib/composables/useVideoPlayer.ts +3 -2
- package/src/lib/config/loader.ts +55 -13
- package/src/lib/hooks/index.ts +0 -1
- package/src/lib/hooks/useComponentCustomization.ts +10 -14
- package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
- package/src/lib/patterns/index.ts +2 -2
- package/src/lib/patterns/slots.tsx +2 -2
- package/src/lib/theme/composeTheme.ts +1 -1
- package/src/lib/theme/core/ThemeEngine.ts +8 -0
- package/src/lib/theme/core/ThemeValidator.ts +5 -2
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +11 -5
- package/src/lib/theme/generateCSSVariables.ts +1 -1
- package/src/lib/theme/i18n/rtl.ts +2 -1
- package/src/lib/theme/runtime/ThemeApplicator.ts +28 -11
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
- package/src/lib/theme/runtime/ThemeManager.ts +4 -0
- package/src/lib/theme-tools.ts +1 -1
- package/src/lib/types/components.ts +183 -34
- package/src/lib/types/partProps.ts +0 -16
- package/src/lib/utils/fontPreloader.ts +148 -0
- package/src/lib/utils/index.ts +11 -0
- package/src/lib/utils/memoryMonitor.ts +189 -0
- package/src/styles/01-settings/_settings.fonts.scss +2 -5
- package/src/styles/03-generic/_generated-root.css +22 -1
- package/src/styles/06-components/_components.navbar.scss +0 -6
- package/src/themes/themes.config.js +37 -4
- package/scripts/build-themes.js +0 -208
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
package/dist/forms.js
ADDED
|
@@ -0,0 +1,2450 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
import React, { useRef, useEffect, memo, forwardRef, useId, useState, useMemo, useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
// Define checkbox class constants
|
|
6
|
+
const FORM_CLASSES_BASE = "c-form", FORM_CLASSES_DISABLED = "c-form--disabled", FORM_GROUP_CLASSES_BASE = "c-form-group", FORM_GROUP_CLASSES_SMALL = "c-form-group--sm", FORM_GROUP_CLASSES_LARGE = "c-form-group--lg", FORM_GROUP_CLASSES_INVALID = "c-form-group--invalid", FORM_GROUP_CLASSES_VALID = "c-form-group--valid", FORM_GROUP_CLASSES_DISABLED = "c-form-group--disabled", INPUT_CLASSES_BASE = "c-input", INPUT_CLASSES_SMALL = "c-input--sm", INPUT_CLASSES_LARGE = "c-input--lg", INPUT_CLASSES_INVALID = "is-invalid", INPUT_CLASSES_VALID = "is-valid", INPUT_CLASSES_DISABLED = "is-disabled", INPUT_CLASSES_FULL_WIDTH = "c-input--full-width", INPUT_CLASSES_PREFIX_ICON = "c-input--prefix-icon", INPUT_CLASSES_SUFFIX_ICON = "c-input--suffix-icon", INPUT_CLASSES_CLEARABLE = "c-input--clearable", INPUT_CLASSES_WITH_COUNTER = "c-input--with-counter", INPUT_CLASSES_PASSWORD_TOGGLE = "c-input--password-toggle", INPUT_ELEMENTS_WRAPPER = "c-input-wrapper", RADIO_CLASSES_BASE = "c-radio", RADIO_CLASSES_INVALID = "is-error", RADIO_CLASSES_VALID = "is-valid", RADIO_CLASSES_DISABLED = "is-disabled", SELECT_CLASSES_BASE = "c-select", SELECT_CLASSES_SELECTED = "c-select__selected", SELECT_CLASSES_SELECT_BODY = "c-select__body", SELECT_CLASSES_SELECT_PANEL = "c-select__panel", SELECT_CLASSES_SELECT_ITEMS = "c-select__items", SELECT_CLASSES_SELECT_ITEM = "c-select__item", SELECT_CLASSES_TOGGLE_ICON = "c-select__toggle-icon", SELECT_CLASSES_ICON_CARET = "icon-atomix-caret-down", SELECT_CLASSES_SMALL = "c-select--sm", SELECT_CLASSES_LARGE = "c-select--lg", SELECT_CLASSES_INVALID = "is-invalid", SELECT_CLASSES_VALID = "is-valid", SELECT_CLASSES_DISABLED = "is-disabled", SELECT_CLASSES_IS_OPEN = "is-open", TEXTAREA_CLASSES_BASE = "c-input c-input--textarea", TEXTAREA_CLASSES_SMALL = "c-input--sm", TEXTAREA_CLASSES_LARGE = "c-input--lg", TEXTAREA_CLASSES_INVALID = "is-invalid", TEXTAREA_CLASSES_VALID = "is-valid", TEXTAREA_CLASSES_DISABLED = "is-disabled", ATOMIX_GLASS = {
|
|
7
|
+
BASE_CLASS: "c-atomix-glass",
|
|
8
|
+
CONTAINER_CLASS: "c-atomix-glass__container",
|
|
9
|
+
INNER_CLASS: "c-atomix-glass__inner",
|
|
10
|
+
FILTER_CLASS: "c-atomix-glass__filter",
|
|
11
|
+
FILTER_OVERLAY_CLASS: "c-atomix-glass__filter-overlay",
|
|
12
|
+
FILTER_SHADOW_CLASS: "c-atomix-glass__filter-shadow",
|
|
13
|
+
CONTENT_CLASS: "c-atomix-glass__content",
|
|
14
|
+
BORDER_1_CLASS: "c-atomix-glass__border-1",
|
|
15
|
+
BORDER_2_CLASS: "c-atomix-glass__border-2",
|
|
16
|
+
HOVER_1_CLASS: "c-atomix-glass__hover-1",
|
|
17
|
+
HOVER_2_CLASS: "c-atomix-glass__hover-2",
|
|
18
|
+
HOVER_3_CLASS: "c-atomix-glass__hover-3",
|
|
19
|
+
BASE_LAYER_CLASS: "c-atomix-glass__base",
|
|
20
|
+
OVERLAY_LAYER_CLASS: "c-atomix-glass__overlay",
|
|
21
|
+
OVERLAY_HIGHLIGHT_CLASS: "c-atomix-glass__overlay-highlight",
|
|
22
|
+
BACKGROUND_LAYER_CLASS: "c-atomix-glass__background-layer",
|
|
23
|
+
BACKGROUND_LAYER_DARK_CLASS: "c-atomix-glass__background-layer--dark",
|
|
24
|
+
BACKGROUND_LAYER_BLACK_CLASS: "c-atomix-glass__background-layer--black",
|
|
25
|
+
BACKGROUND_LAYER_OVER_LIGHT_CLASS: "c-atomix-glass__background-layer--over-light",
|
|
26
|
+
BACKGROUND_LAYER_HIDDEN_CLASS: "c-atomix-glass__background-layer--hidden",
|
|
27
|
+
VARIANT_PREFIX: "c-atomix-glass--",
|
|
28
|
+
MODE_PREFIX: "c-atomix-glass--",
|
|
29
|
+
CLASSES: {
|
|
30
|
+
BASE: "c-atomix-glass",
|
|
31
|
+
CONTAINER: "c-atomix-glass__container",
|
|
32
|
+
INNER: "c-atomix-glass__inner",
|
|
33
|
+
FILTER: "c-atomix-glass__filter",
|
|
34
|
+
CONTENT: "c-atomix-glass__content",
|
|
35
|
+
ACTIVE: "active",
|
|
36
|
+
OVER_LIGHT: "c-atomix-glass__container--over-light",
|
|
37
|
+
// Mode variants
|
|
38
|
+
STANDARD: "c-atomix-glass--standard",
|
|
39
|
+
POLAR: "c-atomix-glass--polar",
|
|
40
|
+
PROMINENT: "c-atomix-glass--prominent",
|
|
41
|
+
SHADER: "c-atomix-glass--shader"
|
|
42
|
+
},
|
|
43
|
+
DEFAULTS: {
|
|
44
|
+
DISPLACEMENT_SCALE: 20,
|
|
45
|
+
BLUR_AMOUNT: 1,
|
|
46
|
+
SATURATION: 140,
|
|
47
|
+
ABERRATION_INTENSITY: 2.5,
|
|
48
|
+
ELASTICITY: .05,
|
|
49
|
+
CORNER_RADIUS: 16,
|
|
50
|
+
// Default border-radius matching design system
|
|
51
|
+
PADDING: "0 0",
|
|
52
|
+
MODE: "standard",
|
|
53
|
+
OVER_LIGHT: !1,
|
|
54
|
+
ENABLE_OVER_LIGHT_LAYERS: !0
|
|
55
|
+
},
|
|
56
|
+
CONSTANTS: {
|
|
57
|
+
ACTIVATION_ZONE: 200,
|
|
58
|
+
MIN_BLUR: .1,
|
|
59
|
+
MOUSE_INFLUENCE_DIVISOR: 100,
|
|
60
|
+
EDGE_FADE_PIXELS: 2,
|
|
61
|
+
DEFAULT_CORNER_RADIUS: 16,
|
|
62
|
+
// Fallback value matching design system
|
|
63
|
+
MAX_SIZE: 4096,
|
|
64
|
+
// Maximum width/height for glass size
|
|
65
|
+
// Gradient calculation constants
|
|
66
|
+
GRADIENT: {
|
|
67
|
+
BASE_ANGLE: 135,
|
|
68
|
+
// Base angle for border gradients (degrees)
|
|
69
|
+
ANGLE_MULTIPLIER: 1.2,
|
|
70
|
+
// Multiplier for mouse influence on angle
|
|
71
|
+
BORDER_STOP_1: {
|
|
72
|
+
MIN: 10,
|
|
73
|
+
// Minimum percentage for border stop 1
|
|
74
|
+
BASE: 33,
|
|
75
|
+
// Base percentage for border stop 1
|
|
76
|
+
MULTIPLIER: .3
|
|
77
|
+
},
|
|
78
|
+
BORDER_STOP_2: {
|
|
79
|
+
MAX: 90,
|
|
80
|
+
// Maximum percentage for border stop 2
|
|
81
|
+
BASE: 66,
|
|
82
|
+
// Base percentage for border stop 2
|
|
83
|
+
MULTIPLIER: .4
|
|
84
|
+
},
|
|
85
|
+
BORDER_OPACITY: {
|
|
86
|
+
BASE_1: .12,
|
|
87
|
+
// Base opacity for border gradient 1
|
|
88
|
+
BASE_2: .4,
|
|
89
|
+
// Base opacity for border gradient 2
|
|
90
|
+
BASE_3: .32,
|
|
91
|
+
// Base opacity for border gradient 3
|
|
92
|
+
BASE_4: .6,
|
|
93
|
+
// Base opacity for border gradient 4
|
|
94
|
+
MULTIPLIER_LOW: .008,
|
|
95
|
+
// Low multiplier for mouse influence on opacity
|
|
96
|
+
MULTIPLIER_HIGH: .012
|
|
97
|
+
},
|
|
98
|
+
CENTER_POSITION: 50,
|
|
99
|
+
// Center position percentage (50%)
|
|
100
|
+
HOVER_POSITION: {
|
|
101
|
+
DIVISOR_1: 2,
|
|
102
|
+
// Divisor for hover 1 position calculation
|
|
103
|
+
DIVISOR_2: 1.5,
|
|
104
|
+
// Divisor for hover 2 position calculation
|
|
105
|
+
MULTIPLIER_3: 1
|
|
106
|
+
},
|
|
107
|
+
BASE_LAYER_MULTIPLIER: .5
|
|
108
|
+
},
|
|
109
|
+
// Gradient opacity values for hover effects
|
|
110
|
+
GRADIENT_OPACITY: {
|
|
111
|
+
HOVER_1: {
|
|
112
|
+
BLACK_START: .3,
|
|
113
|
+
// Start opacity for black hover 1
|
|
114
|
+
BLACK_MID: .1,
|
|
115
|
+
// Mid opacity for black hover 1
|
|
116
|
+
BLACK_STOP: 30,
|
|
117
|
+
// Stop percentage for black hover 1
|
|
118
|
+
BLACK_END: 60,
|
|
119
|
+
// End percentage for black hover 1
|
|
120
|
+
WHITE_START: .5,
|
|
121
|
+
// Start opacity for white hover 1
|
|
122
|
+
WHITE_STOP: 50
|
|
123
|
+
},
|
|
124
|
+
HOVER_2: {
|
|
125
|
+
BLACK_START: .4,
|
|
126
|
+
// Start opacity for black hover 2
|
|
127
|
+
BLACK_MID: .15,
|
|
128
|
+
// Mid opacity for black hover 2
|
|
129
|
+
BLACK_STOP: 40,
|
|
130
|
+
// Stop percentage for black hover 2
|
|
131
|
+
BLACK_END: 80,
|
|
132
|
+
// End percentage for black hover 2
|
|
133
|
+
WHITE_START: 1,
|
|
134
|
+
// Start opacity for white hover 2
|
|
135
|
+
WHITE_STOP: 80
|
|
136
|
+
},
|
|
137
|
+
HOVER_3: {
|
|
138
|
+
BLACK_START: .5,
|
|
139
|
+
// Start opacity for black hover 3
|
|
140
|
+
BLACK_MID: .2,
|
|
141
|
+
// Mid opacity for black hover 3
|
|
142
|
+
BLACK_STOP: 50,
|
|
143
|
+
// Stop percentage for black hover 3
|
|
144
|
+
BLACK_END: 100,
|
|
145
|
+
// End percentage for black hover 3
|
|
146
|
+
WHITE_START: 1,
|
|
147
|
+
// Start opacity for white hover 3
|
|
148
|
+
WHITE_STOP: 100
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
// Base and overlay gradient constants
|
|
152
|
+
BASE_GRADIENT: {
|
|
153
|
+
ANGLE: 135,
|
|
154
|
+
// Gradient angle in degrees
|
|
155
|
+
BLACK_START_BASE: .15,
|
|
156
|
+
// Base start opacity for black
|
|
157
|
+
BLACK_START_MULTIPLIER: .003,
|
|
158
|
+
// Multiplier for mouse X influence on start
|
|
159
|
+
BLACK_MID_BASE: .1,
|
|
160
|
+
// Base mid opacity for black
|
|
161
|
+
BLACK_MID_MULTIPLIER: .002,
|
|
162
|
+
// Multiplier for mouse Y influence on mid
|
|
163
|
+
BLACK_MID_STOP: 50,
|
|
164
|
+
// Mid stop percentage
|
|
165
|
+
BLACK_END_BASE: .18,
|
|
166
|
+
// Base end opacity for black
|
|
167
|
+
BLACK_END_MULTIPLIER: .004,
|
|
168
|
+
// Multiplier for mouse X influence on end
|
|
169
|
+
WHITE_OPACITY: .1
|
|
170
|
+
},
|
|
171
|
+
OVERLAY_GRADIENT: {
|
|
172
|
+
BLACK_START_BASE: .12,
|
|
173
|
+
// Base start opacity for black overlay
|
|
174
|
+
BLACK_START_MULTIPLIER: .003,
|
|
175
|
+
// Multiplier for mouse X influence on start
|
|
176
|
+
BLACK_MID: .06,
|
|
177
|
+
// Mid opacity for black overlay
|
|
178
|
+
BLACK_MID_STOP: 40,
|
|
179
|
+
// Mid stop percentage
|
|
180
|
+
BLACK_END_BASE: .15,
|
|
181
|
+
// Base end opacity for black overlay
|
|
182
|
+
BLACK_END_MULTIPLIER: .003,
|
|
183
|
+
// Multiplier for mouse Y influence on end
|
|
184
|
+
WHITE_OPACITY: .05
|
|
185
|
+
},
|
|
186
|
+
// Overlay highlight constants
|
|
187
|
+
OVERLAY_HIGHLIGHT: {
|
|
188
|
+
POSITION_X: 20,
|
|
189
|
+
// X position percentage
|
|
190
|
+
POSITION_Y: 20,
|
|
191
|
+
// Y position percentage
|
|
192
|
+
WHITE_OPACITY: .4,
|
|
193
|
+
// White opacity in gradient
|
|
194
|
+
STOP: 60,
|
|
195
|
+
// Stop percentage
|
|
196
|
+
OPACITY_MULTIPLIER: .7
|
|
197
|
+
},
|
|
198
|
+
// Displacement and aberration multipliers
|
|
199
|
+
MULTIPLIERS: {
|
|
200
|
+
SHADER_DISPLACEMENT: .8,
|
|
201
|
+
// Displacement scale multiplier for shader mode
|
|
202
|
+
OVER_LIGHT_DISPLACEMENT: .6,
|
|
203
|
+
// Displacement scale multiplier for over-light mode
|
|
204
|
+
SHADER_ABERRATION: .7
|
|
205
|
+
},
|
|
206
|
+
// Saturation constants
|
|
207
|
+
SATURATION: {
|
|
208
|
+
HIGH_CONTRAST: 200
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
|
|
212
|
+
if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
|
|
213
|
+
const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
|
|
214
|
+
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
215
|
+
}, calculateElementCenter = rect => rect ? {
|
|
216
|
+
x: rect.left + rect.width / 2,
|
|
217
|
+
y: rect.top + rect.height / 2
|
|
218
|
+
} : {
|
|
219
|
+
x: 0,
|
|
220
|
+
y: 0
|
|
221
|
+
}, calculateMouseInfluence = mouseOffset => {
|
|
222
|
+
if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
|
|
223
|
+
// More responsive calculation for overlight effects
|
|
224
|
+
const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$1.MOUSE_INFLUENCE_DIVISOR;
|
|
225
|
+
return Math.min(1.5, influence);
|
|
226
|
+
// Cap influence for better control
|
|
227
|
+
}, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$1.MIN_BLUR : Math.max(CONSTANTS$1.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$1.MAX_SIZE && size.height <= CONSTANTS$1.MAX_SIZE, parseBorderRadiusValue = value => {
|
|
228
|
+
if ("number" == typeof value) return Math.max(0, value);
|
|
229
|
+
if ("string" != typeof value || !value.trim()) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
|
|
230
|
+
const trimmedValue = value.trim();
|
|
231
|
+
// Handle px values
|
|
232
|
+
if (trimmedValue.endsWith("px")) {
|
|
233
|
+
const parsed = parseFloat(trimmedValue);
|
|
234
|
+
return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
|
|
235
|
+
}
|
|
236
|
+
// Handle rem values (assume 16px = 1rem)
|
|
237
|
+
if (trimmedValue.endsWith("rem")) {
|
|
238
|
+
const parsed = parseFloat(trimmedValue);
|
|
239
|
+
return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
|
|
240
|
+
}
|
|
241
|
+
// Handle em values (assume 16px = 1em for simplicity)
|
|
242
|
+
if (trimmedValue.endsWith("em")) {
|
|
243
|
+
const parsed = parseFloat(trimmedValue);
|
|
244
|
+
return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
|
|
245
|
+
}
|
|
246
|
+
// Handle percentage (convert to approximate px value, assuming 200px container)
|
|
247
|
+
if (trimmedValue.endsWith("%")) {
|
|
248
|
+
const parsed = parseFloat(trimmedValue);
|
|
249
|
+
return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
|
|
250
|
+
}
|
|
251
|
+
// Handle unitless numbers
|
|
252
|
+
const numValue = parseFloat(trimmedValue);
|
|
253
|
+
return isNaN(numValue) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
|
|
254
|
+
}, extractBorderRadiusFromElement = element => {
|
|
255
|
+
if (!element || !element.props) return null;
|
|
256
|
+
// Check inline styles first (highest priority)
|
|
257
|
+
if (element.props.style) {
|
|
258
|
+
const radiusFromStyle = (style => {
|
|
259
|
+
if (!style) return null;
|
|
260
|
+
// Check various border-radius properties
|
|
261
|
+
const borderRadius = style.borderRadius || style.borderTopLeftRadius || style.borderTopRightRadius || style.borderBottomLeftRadius || style.borderBottomRightRadius;
|
|
262
|
+
return void 0 !== borderRadius ? parseBorderRadiusValue(borderRadius) : null;
|
|
263
|
+
})(element.props.style);
|
|
264
|
+
if (null !== radiusFromStyle && radiusFromStyle > 0) return radiusFromStyle;
|
|
265
|
+
}
|
|
266
|
+
// If element has children, recursively check them
|
|
267
|
+
if (element.props.children) {
|
|
268
|
+
const childRadius = extractBorderRadiusFromChildren(element.props.children);
|
|
269
|
+
if (childRadius > 0 && childRadius !== CONSTANTS$1.DEFAULT_CORNER_RADIUS) return childRadius;
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}, extractBorderRadiusFromChildren = children => {
|
|
273
|
+
if (!children) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
|
|
274
|
+
try {
|
|
275
|
+
const childArray = React.Children.toArray(children);
|
|
276
|
+
for (let i = 0; i < childArray.length; i++) {
|
|
277
|
+
const child = childArray[i];
|
|
278
|
+
if ( React.isValidElement(child)) {
|
|
279
|
+
const radius = extractBorderRadiusFromElement(child);
|
|
280
|
+
if (null !== radius) return radius;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
// Silently handle errors
|
|
285
|
+
}
|
|
286
|
+
return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
|
|
287
|
+
}, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
|
|
288
|
+
switch (mode) {
|
|
289
|
+
case "standard":
|
|
290
|
+
return displacementMap;
|
|
291
|
+
|
|
292
|
+
case "polar":
|
|
293
|
+
return polarDisplacementMap;
|
|
294
|
+
|
|
295
|
+
case "prominent":
|
|
296
|
+
return prominentDisplacementMap;
|
|
297
|
+
|
|
298
|
+
case "shader":
|
|
299
|
+
return shaderMapUrl || displacementMap;
|
|
300
|
+
|
|
301
|
+
default:
|
|
302
|
+
return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
|
|
303
|
+
}
|
|
304
|
+
}, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
|
|
305
|
+
style: {
|
|
306
|
+
position: "absolute",
|
|
307
|
+
width: "100%",
|
|
308
|
+
height: "100%",
|
|
309
|
+
inset: 0
|
|
310
|
+
},
|
|
311
|
+
"aria-hidden": "true",
|
|
312
|
+
suppressHydrationWarning: !0,
|
|
313
|
+
children: jsxs("defs", {
|
|
314
|
+
children: [ jsxs("radialGradient", {
|
|
315
|
+
id: `${id}-edge-mask`,
|
|
316
|
+
cx: "50%",
|
|
317
|
+
cy: "50%",
|
|
318
|
+
r: "50%",
|
|
319
|
+
children: [ jsx("stop", {
|
|
320
|
+
offset: "0%",
|
|
321
|
+
stopColor: "black",
|
|
322
|
+
stopOpacity: "0"
|
|
323
|
+
}), jsx("stop", {
|
|
324
|
+
offset: `${Math.max(30, 80 - 2 * aberrationIntensity)}%`,
|
|
325
|
+
stopColor: "black",
|
|
326
|
+
stopOpacity: "0"
|
|
327
|
+
}), jsx("stop", {
|
|
328
|
+
offset: "100%",
|
|
329
|
+
stopColor: "white",
|
|
330
|
+
stopOpacity: "1"
|
|
331
|
+
}) ]
|
|
332
|
+
}), jsxs("filter", {
|
|
333
|
+
id: id,
|
|
334
|
+
x: "-35%",
|
|
335
|
+
y: "-35%",
|
|
336
|
+
width: "170%",
|
|
337
|
+
height: "170%",
|
|
338
|
+
colorInterpolationFilters: "sRGB",
|
|
339
|
+
children: [ jsx("feImage", {
|
|
340
|
+
id: "feimage",
|
|
341
|
+
x: "0",
|
|
342
|
+
y: "0",
|
|
343
|
+
width: "100%",
|
|
344
|
+
height: "100%",
|
|
345
|
+
result: "DISPLACEMENT_MAP",
|
|
346
|
+
href: getDisplacementMap(mode, "data:image/jpeg;base64,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cUAUAkEkziomgGgkoxZGgxZFQFQlYnQHRdPfj/10KCSCKESCNiVkViPSLpD0h6I0Q0I0A2IoBWBIJIBKBIJoJIJ2R2J0JWBUJ0JUB0XTv479dFZDYiglYigkhEgjZFQjRFQjRFQjQigFYigHYigmgEgmglYlYnQlQlYlQHQlQnQ9P/kf1yVkNiNCNkNiVENiNiViNEViNkVCVgKCViViViSCViSCVgdCViVCViVCdgVCVCdD1D+U/XBWQ2I0I2Q2JUQ2I0JWQ0I2JUQ2JUI2JUI2J0JWJWJWA2R0BWJ0I2JUJ2BUJUJ0P//EABkQAQEBAQEBAAAAAAAAAAAAAAECABEDEP/aAAgBAQABAgB1atWrVq1atWrVq1atWrVq1atWrVq1atWrVq+OrVq1atWrVq1atWrVq1atWrVq1atWrVq1atXxVppppppdWrVq1atWrVq1NNNNNNNNNNNPVWmmmmms6tWrVq1atWpppppppppppppp6q0000uc51atWrVq1ammmmmmmmmmmmmt1Vpppc5znVq1atWrVqaaaaaaaaaaaaaeqtNLnOc51atWrVq1ammmmmmmmmmmmmnqrS5znOc6tWrVq16222mmmmmmlVppp6tKuc5znOrVq1a9TbbbbTTTTTSq000qtLnOc5zq1atWrW0222200000qqqtKqrnOc5zq1atTbbbbbbbbTTTSqqqqqq5znOc6tTTTbbbbbbbbTTTSqqqqrlVznOctNNNtttttttttNNNNKqqqrqznKqrTTTTbbbbbbbbbTTTSqqqqrqznOc5aaaabbbbbbbbbaaaaVVVVVdWc5znVq1NNttttttttttNNKqqqqudWc5znVq16tbbbbbbbbbbTTSqqqq5XVnOc6tWrVrb1tttttttttNNKqqqqrWrK5VWmmm2230bbbbbbaaaXOc5zlVa1KuVVppptttt9G22222mmlzlVznK6tWVVWmmmm2222222222mlznOc5znLWppVVWmmm22222229bTWrOc5znOcq1qaaVpWmm222222229erVqznOc5znKtatStK0rTbTTbbbberXr1as5znOc5aVpppppWlabaabbbb1ta9WrVnOc5znU0rTTTTTTTTTbTTbbbTWvVq1as5znOdTTStNNNNNNNNNtNNtttN6tWvVq1ZznOrU00rTTTTTTTTTTTTTbTWvVq1atWrOc6tTTTStNNNNNNNNNNtNNtNa9WrVq1Z1Z1NNNNNK1q1NNNNNNNNNNNtNatWrVq1atWrU00000rWrVq1atWrVq1alaaa1atWrVq1NNNammmmla1atWrVq1aterVq16tWrVnVqa1NK1qaaaVX/xAAWEAADAAAAAAAAAAAAAAAAAAAhgJD/2gAIAQEAAz8AaExf/8QAGhEBAQEBAQEBAAAAAAAAAAAAAQISEQADEP/aAAgBAgEBAgDx48ePHjx48ePHjx48ePHjx48ePHjx48ePHj86IiIiIiInjx48ePHjx48IiIiIj0oooooooooRERER73ve60UUUUUUVrWiiiiiihERERER73ve97ooooorRWiiiiihKERERER73ve973RRRRWtFFFFFFCIiIiIiPe973ve60UUVrRRRRRRQiIlCIiI973ve973pRRWiiiiiiiiiiiiiiihEe973ve973RRWtFFFFFFFFFFFFFFFFFFa13ve973WitaKKKKKKKKKKKKKKKKKK1rWtd1rutFa1oooooooooooosssooorWta1rWta1rRRRRRRRRRRZZZZZZZZZWta1rWta1rRRRRRRRRZZZZZZZZZZZZe9a1rWta1rWitaKLLLLLLLLLLLLLLLLL3rWta1rWtFbLLLLLLLLLLLLLLLLLLLL3vWta1rWita1ssssssss+hZZZZZZZZe961rWta0Vre97LLLLLLLLLLLPoWWWWWXrWta1oorWta3ssss+hZZZZ9Cyyyyyyyyiita1orWta1ve9llllllllllllllllFFa0VorWta1ve9llllllllllllllllllFFFaK1rWta1rWiyyyyyyyyyyyyiiiiiiitFFa1rWta1oosoosssssoooosoooorRRRWta1rWta0UUUUUWUUUUUUUUUUUVoooorWta1rWtaKKKKKKmiiiiiiiiiiiiiiitd73ve61oSiiipoqaKKKKKKKKKK0UUUVrve973vREREZoSihEooooorRRRRWtd73ve9EREREREoSiiiiitFllllla73ve9ERERERESiiiiiitH0PoWWWWVrXe96IiIiMoiJRRRRRRWjwlFFllllFFd6IiIiIlCUUUUUUUUUePHjx48ePCIiIiIiIiUUUUUUUUUUUePHjx48ePHjx48ePHjx48IiUUUUUUJRRRX//xAAWEQADAAAAAAAAAAAAAAAAAAABYJD/2gAIAQIBAz8AtEV7/8QAFxEBAQEBAAAAAAAAAAAAAAAAAAECEP/aAAgBAwEBAgCtNNNNNNNNNNNNNNNNNNNNNNNNNNNNNcrTTTTTTTTTTTTTTTTTTTTTTTTTTTTTXKrTTTTTTTU000000000000000000001FVpppppqampqaaaaaaaaaaaaaaaaaaaa5Vaaaaampqampqammmmmmmmmmmlaaaaaaiq0001NTU1NTU1NTTTTTTTTTTSqqtNNNcqtNNSyzU1LNTU1NTTTTTTTTTSqqq001ytNLLLLNTU1NTU1NTbbbTTTTTSqqq001ytNLLLLLNTU1NTU3NttttNNNNNKqq001KrSyyyyyzU1NTU3Nzc02220000qqqqrSqqyyyyyzU1NTU3Nzc3NttttNNNKqqqqqqssssss1NTU3Nzc3NzbbbbTTTSqqqqqqrLLLLLNTU1Nzc3Nzc22220000qqqqqqqqssss1NTU3Nzc3NzbbbbbTTSqqqqqqqqqqzU1NTc3Nzc3Nzbc22000qqqqqqqqqqqtTU3Nzc3Nzc3NtzbTTSqqqqrKqqqqqtNNzc23Nzc3Nzc3NTU1KqqqrKqqqqqtNNNNttzc3Nzc3NzU1NLLLLLKqqqqqqqq0022223Nzc3NzU1NSyyyyyyqqqqqqqrTTbbbbc3Nzc3NTU1LLLLLLKsqqqqqqrTTTTbbbc3Nzc1NTUsssssssqqqqqqrTTTTTbbbTc3NTU1NTUsssssqqqqqqqq0000222023NTU1NTUsssssqqqqqqqq000000003NTU1NTU1LLLLLNKrTSqqqqtNNNNNNtNNTU1NSzUssss00qq0qqqqrTTTTTTTTTU1NTUs1LLLNNNKrTTTSqqq00000000001NTU1LNTU0000qtNNNKqqqtNNNNNNNNTU1NTUs1NNNNNKss1NNNK00qtK0000001NNTU0s000000qq000001NKrStNNNNK1NNNNStNNNNNKqtNNNNNNNK0000000rU0000rTTTTTSq00000rTTTTTTTTTTTTTTTTStNNNNKr/xAAUEQEAAAAAAAAAAAAAAAAAAACg/9oACAEDAQM/AAAf/9k=", "data:image/jpeg;base64,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", "data:image/png;base64,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", shaderMapUrl),
|
|
347
|
+
preserveAspectRatio: "xMidYMid slice"
|
|
348
|
+
}), jsx("feColorMatrix", {
|
|
349
|
+
in: "DISPLACEMENT_MAP",
|
|
350
|
+
type: "matrix",
|
|
351
|
+
values: "0.3 0.3 0.3 0 0\n 0.3 0.3 0.3 0 0\n 0.3 0.3 0.3 0 0\n 0 0 0 1 0",
|
|
352
|
+
result: "EDGE_INTENSITY"
|
|
353
|
+
}), jsx("feComponentTransfer", {
|
|
354
|
+
in: "EDGE_INTENSITY",
|
|
355
|
+
result: "EDGE_MASK",
|
|
356
|
+
children: jsx("feFuncA", {
|
|
357
|
+
type: "discrete",
|
|
358
|
+
tableValues: `0 ${.05 * aberrationIntensity} 1`
|
|
359
|
+
})
|
|
360
|
+
}), jsx("feOffset", {
|
|
361
|
+
in: "SourceGraphic",
|
|
362
|
+
dx: "0",
|
|
363
|
+
dy: "0",
|
|
364
|
+
result: "CENTER_ORIGINAL"
|
|
365
|
+
}), jsx("feDisplacementMap", {
|
|
366
|
+
in: "SourceGraphic",
|
|
367
|
+
in2: "DISPLACEMENT_MAP",
|
|
368
|
+
scale: displacementScale * ("shader" === mode ? 1 : -1),
|
|
369
|
+
xChannelSelector: "R",
|
|
370
|
+
yChannelSelector: "B",
|
|
371
|
+
result: "RED_DISPLACED"
|
|
372
|
+
}), jsx("feColorMatrix", {
|
|
373
|
+
in: "RED_DISPLACED",
|
|
374
|
+
type: "matrix",
|
|
375
|
+
values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
|
|
376
|
+
result: "RED_CHANNEL"
|
|
377
|
+
}), jsx("feDisplacementMap", {
|
|
378
|
+
in: "SourceGraphic",
|
|
379
|
+
in2: "DISPLACEMENT_MAP",
|
|
380
|
+
scale: displacementScale * (("shader" === mode ? 1 : -1) - .02 * aberrationIntensity),
|
|
381
|
+
xChannelSelector: "R",
|
|
382
|
+
yChannelSelector: "B",
|
|
383
|
+
result: "GREEN_DISPLACED"
|
|
384
|
+
}), jsx("feColorMatrix", {
|
|
385
|
+
in: "GREEN_DISPLACED",
|
|
386
|
+
type: "matrix",
|
|
387
|
+
values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
|
|
388
|
+
result: "GREEN_CHANNEL"
|
|
389
|
+
}), jsx("feDisplacementMap", {
|
|
390
|
+
in: "SourceGraphic",
|
|
391
|
+
in2: "DISPLACEMENT_MAP",
|
|
392
|
+
scale: displacementScale * (("shader" === mode ? 1 : -1) - .03 * aberrationIntensity),
|
|
393
|
+
xChannelSelector: "R",
|
|
394
|
+
yChannelSelector: "B",
|
|
395
|
+
result: "BLUE_DISPLACED"
|
|
396
|
+
}), jsx("feColorMatrix", {
|
|
397
|
+
in: "BLUE_DISPLACED",
|
|
398
|
+
type: "matrix",
|
|
399
|
+
values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0",
|
|
400
|
+
result: "BLUE_CHANNEL"
|
|
401
|
+
}), jsx("feBlend", {
|
|
402
|
+
in: "GREEN_CHANNEL",
|
|
403
|
+
in2: "BLUE_CHANNEL",
|
|
404
|
+
mode: "screen",
|
|
405
|
+
result: "GB_COMBINED"
|
|
406
|
+
}), jsx("feBlend", {
|
|
407
|
+
in: "RED_CHANNEL",
|
|
408
|
+
in2: "GB_COMBINED",
|
|
409
|
+
mode: "screen",
|
|
410
|
+
result: "RGB_COMBINED"
|
|
411
|
+
}), jsx("feGaussianBlur", {
|
|
412
|
+
in: "RGB_COMBINED",
|
|
413
|
+
result: "ABERRATED_BLURRED",
|
|
414
|
+
stdDeviation: blurAmount * aberrationIntensity * .05
|
|
415
|
+
}), jsx("feComposite", {
|
|
416
|
+
in: "ABERRATED_BLURRED",
|
|
417
|
+
in2: "EDGE_MASK",
|
|
418
|
+
operator: "in",
|
|
419
|
+
result: "EDGE_ABERRATION"
|
|
420
|
+
}), jsx("feComponentTransfer", {
|
|
421
|
+
in: "EDGE_MASK",
|
|
422
|
+
result: "INVERTED_MASK",
|
|
423
|
+
children: jsx("feFuncA", {
|
|
424
|
+
type: "table",
|
|
425
|
+
tableValues: "1 0"
|
|
426
|
+
})
|
|
427
|
+
}), jsx("feComposite", {
|
|
428
|
+
in: "CENTER_ORIGINAL",
|
|
429
|
+
in2: "INVERTED_MASK",
|
|
430
|
+
operator: "in",
|
|
431
|
+
result: "CENTER_CLEAN"
|
|
432
|
+
}), jsx("feComposite", {
|
|
433
|
+
in: "EDGE_ABERRATION",
|
|
434
|
+
in2: "CENTER_CLEAN",
|
|
435
|
+
operator: "over"
|
|
436
|
+
}) ]
|
|
437
|
+
}) ]
|
|
438
|
+
})
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Checkbox state and functionality
|
|
443
|
+
* @param initialProps - Initial checkbox properties
|
|
444
|
+
* @returns Checkbox state and methods
|
|
445
|
+
*/ GlassFilterComponent.displayName = "GlassFilter";
|
|
446
|
+
|
|
447
|
+
// Memoize component to prevent unnecessary re-renders
|
|
448
|
+
const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount)), sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
|
|
449
|
+
x: 0,
|
|
450
|
+
y: 0
|
|
451
|
+
}, globalMousePosition: globalMousePosition = {
|
|
452
|
+
x: 0,
|
|
453
|
+
y: 0
|
|
454
|
+
}, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, active: active = !1, isHovered: isHovered = !1, isActive: isActive = !1, overLight: overLight = !1, cornerRadius: cornerRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
|
|
455
|
+
width: 0,
|
|
456
|
+
height: 0
|
|
457
|
+
}, onClick: onClick, mode: mode = "standard", effectiveDisableEffects: effectiveDisableEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", enableLiquidBlur: enableLiquidBlur = !1, elasticity: elasticity = 0, contentRef: contentRef}, ref) => {
|
|
458
|
+
// Use React's useId() for SSR compatibility
|
|
459
|
+
// Note: In Next.js, IDs may differ between server and client
|
|
460
|
+
// We'll suppress hydration warnings on elements that use this ID
|
|
461
|
+
const filterId = useId(), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null);
|
|
462
|
+
// Lazy load shader utilities only when shader mode is needed
|
|
463
|
+
useEffect((() => {
|
|
464
|
+
"shader" === mode ?
|
|
465
|
+
// Dynamic import shader utilities
|
|
466
|
+
Promise.resolve().then((() => shaderUtils)).then((shaderUtils => {
|
|
467
|
+
shaderUtilsRef.current = {
|
|
468
|
+
ShaderDisplacementGenerator: shaderUtils.ShaderDisplacementGenerator,
|
|
469
|
+
fragmentShaders: shaderUtils.fragmentShaders
|
|
470
|
+
};
|
|
471
|
+
})).catch((error => {
|
|
472
|
+
console.warn("AtomixGlassContainer: Error loading shader utilities", error);
|
|
473
|
+
})) :
|
|
474
|
+
// Clear shader utils when not in shader mode to free memory
|
|
475
|
+
shaderUtilsRef.current = null;
|
|
476
|
+
}), [ mode ]),
|
|
477
|
+
// Generate shader map with debouncing and caching
|
|
478
|
+
useEffect((() => {
|
|
479
|
+
// Enhanced validation for shader mode
|
|
480
|
+
if ("shader" === mode && glassSize && validateGlassSize(glassSize) && shaderUtilsRef.current) {
|
|
481
|
+
// Create cache key from size and variant
|
|
482
|
+
const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
|
|
483
|
+
const entry = sharedShaderCache.get(key);
|
|
484
|
+
return entry ? (
|
|
485
|
+
// Update access timestamp for LRU
|
|
486
|
+
entry.timestamp = Date.now(), entry.url) : null;
|
|
487
|
+
})(cacheKey);
|
|
488
|
+
// Check shared cache first
|
|
489
|
+
if (cachedUrl) return void setShaderMapUrl(cachedUrl);
|
|
490
|
+
// Clear any pending debounce
|
|
491
|
+
shaderDebounceTimeoutRef.current && clearTimeout(shaderDebounceTimeoutRef.current);
|
|
492
|
+
// Debounce shader generation to avoid blocking on rapid size changes
|
|
493
|
+
const generateShader = () => {
|
|
494
|
+
if (shaderUtilsRef.current) try {
|
|
495
|
+
const {ShaderDisplacementGenerator: ShaderDisplacementGenerator, fragmentShaders: fragmentShaders} = shaderUtilsRef.current;
|
|
496
|
+
shaderGeneratorRef.current?.destroy();
|
|
497
|
+
const selectedShader = fragmentShaders[shaderVariant] || fragmentShaders.liquidGlass;
|
|
498
|
+
shaderGeneratorRef.current = new ShaderDisplacementGenerator({
|
|
499
|
+
width: glassSize.width,
|
|
500
|
+
height: glassSize.height,
|
|
501
|
+
fragment: selectedShader
|
|
502
|
+
});
|
|
503
|
+
// Use requestIdleCallback if available for non-blocking generation
|
|
504
|
+
const generate = () => {
|
|
505
|
+
const url = shaderGeneratorRef.current?.updateShader() || "";
|
|
506
|
+
((key, url) => {
|
|
507
|
+
// Evict oldest entries if at capacity
|
|
508
|
+
if (sharedShaderCache.size >= 15) {
|
|
509
|
+
const entries = Array.from(sharedShaderCache.entries());
|
|
510
|
+
// Sort by timestamp (oldest first)
|
|
511
|
+
entries.sort(((a, b) => a[1].timestamp - b[1].timestamp));
|
|
512
|
+
// Remove oldest entry
|
|
513
|
+
const oldestEntry = entries[0];
|
|
514
|
+
oldestEntry && sharedShaderCache.delete(oldestEntry[0]);
|
|
515
|
+
}
|
|
516
|
+
sharedShaderCache.set(key, {
|
|
517
|
+
url: url,
|
|
518
|
+
timestamp: Date.now()
|
|
519
|
+
}),
|
|
520
|
+
// Development mode: log cache size
|
|
521
|
+
"production" !== process.env.NODE_ENV && sharedShaderCache.size;
|
|
522
|
+
})(cacheKey, url), setShaderMapUrl(url);
|
|
523
|
+
};
|
|
524
|
+
"undefined" != typeof requestIdleCallback ? requestIdleCallback(generate, {
|
|
525
|
+
timeout: 1e3
|
|
526
|
+
}) :
|
|
527
|
+
// Fallback to setTimeout for browsers without requestIdleCallback
|
|
528
|
+
setTimeout(generate, 0);
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.warn("AtomixGlassContainer: Error generating shader map", error), setShaderMapUrl("");
|
|
531
|
+
} else
|
|
532
|
+
// Shader utils not loaded yet, retry after a short delay
|
|
533
|
+
shaderDebounceTimeoutRef.current = setTimeout(generateShader, 100);
|
|
534
|
+
};
|
|
535
|
+
// Debounce with 300ms delay
|
|
536
|
+
shaderDebounceTimeoutRef.current = setTimeout(generateShader, 300);
|
|
537
|
+
} else
|
|
538
|
+
// Not in shader mode, clear URL
|
|
539
|
+
setShaderMapUrl("");
|
|
540
|
+
// Cleanup function with error handling
|
|
541
|
+
return () => {
|
|
542
|
+
shaderDebounceTimeoutRef.current && (clearTimeout(shaderDebounceTimeoutRef.current),
|
|
543
|
+
shaderDebounceTimeoutRef.current = null);
|
|
544
|
+
try {
|
|
545
|
+
shaderGeneratorRef.current?.destroy();
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.warn("AtomixGlassContainer: Error during shader cleanup", error);
|
|
548
|
+
} finally {
|
|
549
|
+
shaderGeneratorRef.current = null;
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}), [ mode, glassSize, shaderVariant ]);
|
|
553
|
+
// Removed forced reflow to avoid layout thrash and potential feedback sizing loops
|
|
554
|
+
const [rectCache, setRectCache] = useState(null);
|
|
555
|
+
useEffect((() => {
|
|
556
|
+
if (!ref || "function" == typeof ref) return;
|
|
557
|
+
const element = ref.current;
|
|
558
|
+
if (element) try {
|
|
559
|
+
setRectCache(element.getBoundingClientRect());
|
|
560
|
+
} catch (error) {
|
|
561
|
+
console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
|
|
562
|
+
}
|
|
563
|
+
}), [ ref, glassSize ]);
|
|
564
|
+
// Pre-calculate static multipliers outside useMemo
|
|
565
|
+
const liquidBlur = useMemo((() => {
|
|
566
|
+
const defaultBlur = {
|
|
567
|
+
baseBlur: blurAmount,
|
|
568
|
+
edgeBlur: 1.25 * blurAmount,
|
|
569
|
+
centerBlur: 1.1 * blurAmount,
|
|
570
|
+
flowBlur: 1.2 * blurAmount
|
|
571
|
+
};
|
|
572
|
+
// Enhanced validation for liquid blur
|
|
573
|
+
if (!enableLiquidBlur || !rectCache || !globalMousePosition || "number" != typeof globalMousePosition.x || "number" != typeof globalMousePosition.y || isNaN(globalMousePosition.x) || isNaN(globalMousePosition.y)) return defaultBlur;
|
|
574
|
+
try {
|
|
575
|
+
// Cache center and distance calculations
|
|
576
|
+
const center = calculateElementCenter(rectCache), distance = calculateDistance(globalMousePosition, center), maxDistance = Math.sqrt(rectCache.width * rectCache.width + rectCache.height * rectCache.height) / 2, normalizedDistance = Math.min(distance / maxDistance, 1), mouseInfluence = calculateMouseInfluence(mouseOffset), baseBlur = blurAmount + mouseInfluence * blurAmount * .4, edgeBlur = baseBlur * (.8 + .6 * (1.5 * normalizedDistance + .3 * mouseInfluence)), centerBlur = baseBlur * (.3 + .4 * (.3 * (1 - normalizedDistance) + .2 * mouseInfluence)), deltaX = globalMousePosition.x - center.x, deltaY = globalMousePosition.y - center.y, flowDirection = Math.atan2(deltaY, deltaX), flowBlur = baseBlur * (.4 + .6 * (.5 * Math.sin(flowDirection + mouseInfluence * Math.PI) + .5)), stateMultiplier = (isHovered ? 1.2 : 1) * (isActive ? 1.4 : 1);
|
|
577
|
+
return {
|
|
578
|
+
baseBlur: clampBlur(baseBlur * stateMultiplier),
|
|
579
|
+
edgeBlur: clampBlur(edgeBlur * stateMultiplier),
|
|
580
|
+
centerBlur: clampBlur(centerBlur * stateMultiplier),
|
|
581
|
+
flowBlur: clampBlur(flowBlur * stateMultiplier)
|
|
582
|
+
};
|
|
583
|
+
} catch (error) {
|
|
584
|
+
return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
|
|
585
|
+
defaultBlur;
|
|
586
|
+
}
|
|
587
|
+
}), [ enableLiquidBlur, blurAmount, globalMousePosition, mouseOffset, isHovered, isActive, rectCache, style, glassSize ]), backdropStyle = useMemo((() => {
|
|
588
|
+
try {
|
|
589
|
+
const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), validatedBaseBlur = "number" != typeof liquidBlur.baseBlur || isNaN(liquidBlur.baseBlur) ? 0 : liquidBlur.baseBlur, validatedEdgeBlur = "number" != typeof liquidBlur.edgeBlur || isNaN(liquidBlur.edgeBlur) ? 0 : liquidBlur.edgeBlur, validatedCenterBlur = "number" != typeof liquidBlur.centerBlur || isNaN(liquidBlur.centerBlur) ? 0 : liquidBlur.centerBlur, validatedFlowBlur = "number" != typeof liquidBlur.flowBlur || isNaN(liquidBlur.flowBlur) ? 0 : liquidBlur.flowBlur, area = rectCache ? rectCache.width * rectCache.height : 0;
|
|
590
|
+
// Validate blur values before using them
|
|
591
|
+
return !enableLiquidBlur || effectiveReducedMotion || effectiveDisableEffects || area > 18e4 ? {
|
|
592
|
+
backdropFilter: `blur(${clampBlur(Math.max(validatedBaseBlur, .8 * validatedEdgeBlur, 1.1 * validatedCenterBlur, .9 * validatedFlowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(1.05) brightness(1.05)`
|
|
593
|
+
} : {
|
|
594
|
+
backdropFilter: `${[ `blur(${validatedBaseBlur}px)`, `blur(${validatedEdgeBlur}px)`, `blur(${validatedCenterBlur}px)`, `blur(${validatedFlowBlur}px)` ].join(" ")} saturate(${Math.min(dynamicSaturation, 200)}%) contrast(1.05) brightness(1.05)`
|
|
595
|
+
};
|
|
596
|
+
// Single-pass fallback: stronger radius to match perceived blur of multi-pass
|
|
597
|
+
} catch (error) {
|
|
598
|
+
return console.warn("AtomixGlassContainer: Error calculating backdrop style", error),
|
|
599
|
+
{
|
|
600
|
+
backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}), [ filterId, liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveDisableEffects, enableLiquidBlur ]), containerVars = useMemo((() => {
|
|
604
|
+
try {
|
|
605
|
+
// Safe extraction of mouse offset values
|
|
606
|
+
const mx = mouseOffset && "number" == typeof mouseOffset.x && !isNaN(mouseOffset.x) ? mouseOffset.x : 0, my = mouseOffset && "number" == typeof mouseOffset.y && !isNaN(mouseOffset.y) ? mouseOffset.y : 0;
|
|
607
|
+
return {
|
|
608
|
+
"--atomix-glass-container-width": `${glassSize?.width}`,
|
|
609
|
+
"--atomix-glass-container-height": `${glassSize?.height}`,
|
|
610
|
+
"--atomix-glass-container-padding": padding || "0 0",
|
|
611
|
+
"--atomix-glass-container-radius": `${"number" != typeof cornerRadius || isNaN(cornerRadius) ? 0 : cornerRadius}px`,
|
|
612
|
+
"--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
|
|
613
|
+
"--atomix-glass-container-shadow": overLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${.4 + .002 * mx})`, `inset 0 -1px 0 rgba(0, 0, 0, ${.2 + .001 * Math.abs(my)})`, `inset 0 0 20px rgba(0, 0, 0, ${.08 + .001 * Math.abs(mx + my)})`, `0 2px 12px rgba(0, 0, 0, ${.12 + .002 * Math.abs(my)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset",
|
|
614
|
+
"--atomix-glass-container-shadow-opacity": effectiveDisableEffects ? 0 : 1,
|
|
615
|
+
// Background and shadow values use design token-aligned RGB values
|
|
616
|
+
"--atomix-glass-container-bg": overLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none",
|
|
617
|
+
"--atomix-glass-container-text-shadow": overLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
|
|
618
|
+
"--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
|
|
619
|
+
};
|
|
620
|
+
} catch (error) {
|
|
621
|
+
return console.warn("AtomixGlassContainer: Error generating container variables", error),
|
|
622
|
+
{
|
|
623
|
+
"--atomix-glass-container-padding": "0 0",
|
|
624
|
+
"--atomix-glass-container-radius": "0px",
|
|
625
|
+
"--atomix-glass-container-backdrop": "none",
|
|
626
|
+
"--atomix-glass-container-shadow": "none",
|
|
627
|
+
"--atomix-glass-container-shadow-opacity": 1,
|
|
628
|
+
"--atomix-glass-container-bg": "none",
|
|
629
|
+
"--atomix-glass-container-text-shadow": "none"
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}), [ glassSize, padding, cornerRadius, backdropStyle, mouseOffset, overLight, effectiveDisableEffects ]);
|
|
633
|
+
return jsx("div", {
|
|
634
|
+
ref: ref,
|
|
635
|
+
className: `${ATOMIX_GLASS.CONTAINER_CLASS} ${className} ${active ? ATOMIX_GLASS.CLASSES.ACTIVE : ""} ${overLight ? ATOMIX_GLASS.CLASSES.OVER_LIGHT : ""}`,
|
|
636
|
+
style: {
|
|
637
|
+
...style,
|
|
638
|
+
...containerVars
|
|
639
|
+
},
|
|
640
|
+
onClick: onClick,
|
|
641
|
+
children: jsxs("div", {
|
|
642
|
+
className: ATOMIX_GLASS.INNER_CLASS,
|
|
643
|
+
style: {
|
|
644
|
+
padding: "var(--atomix-glass-container-padding)",
|
|
645
|
+
boxShadow: "var(--atomix-glass-container-box-shadow)"
|
|
646
|
+
},
|
|
647
|
+
onMouseEnter: onMouseEnter,
|
|
648
|
+
onMouseLeave: onMouseLeave,
|
|
649
|
+
onMouseDown: onMouseDown,
|
|
650
|
+
onMouseUp: onMouseUp,
|
|
651
|
+
children: [ jsxs("div", {
|
|
652
|
+
className: ATOMIX_GLASS.FILTER_CLASS,
|
|
653
|
+
children: [ jsx(GlassFilter, {
|
|
654
|
+
blurAmount: blurAmount,
|
|
655
|
+
mode: mode,
|
|
656
|
+
id: filterId,
|
|
657
|
+
displacementScale: "number" != typeof displacementScale || isNaN(displacementScale) ? 0 : displacementScale,
|
|
658
|
+
aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
|
|
659
|
+
shaderMapUrl: shaderMapUrl
|
|
660
|
+
}), jsx("div", {
|
|
661
|
+
className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
|
|
662
|
+
suppressHydrationWarning: !0,
|
|
663
|
+
style: {
|
|
664
|
+
filter: `url(#${filterId})`,
|
|
665
|
+
backdropFilter: "var(--atomix-glass-container-backdrop)",
|
|
666
|
+
borderRadius: "var(--atomix-glass-container-radius)"
|
|
667
|
+
}
|
|
668
|
+
}), jsx("div", {
|
|
669
|
+
className: ATOMIX_GLASS.FILTER_SHADOW_CLASS,
|
|
670
|
+
style: {
|
|
671
|
+
boxShadow: "var(--atomix-glass-container-shadow)",
|
|
672
|
+
opacity: "var(--atomix-glass-container-shadow-opacity)",
|
|
673
|
+
background: "var(--atomix-glass-container-bg)",
|
|
674
|
+
borderRadius: "var(--atomix-glass-container-radius)"
|
|
675
|
+
}
|
|
676
|
+
}) ]
|
|
677
|
+
}), jsx("div", {
|
|
678
|
+
ref: contentRef,
|
|
679
|
+
className: ATOMIX_GLASS.CONTENT_CLASS,
|
|
680
|
+
style: {
|
|
681
|
+
position: "relative",
|
|
682
|
+
textShadow: "var(--atomix-glass-container-text-shadow)",
|
|
683
|
+
...elasticity > 0 ? {
|
|
684
|
+
zIndex: 100
|
|
685
|
+
} : {}
|
|
686
|
+
},
|
|
687
|
+
children: children
|
|
688
|
+
}) ]
|
|
689
|
+
})
|
|
690
|
+
});
|
|
691
|
+
}));
|
|
692
|
+
|
|
693
|
+
// Module-level shared shader cache with LRU eviction
|
|
694
|
+
AtomixGlassContainer.displayName = "AtomixGlassContainer";
|
|
695
|
+
|
|
696
|
+
// Singleton instance
|
|
697
|
+
const globalMouseTracker = new
|
|
698
|
+
/**
|
|
699
|
+
* Global mouse tracker singleton
|
|
700
|
+
* Tracks mouse position at document level and distributes to subscribers
|
|
701
|
+
* Reduces event processing overhead when multiple AtomixGlass instances are present
|
|
702
|
+
*/
|
|
703
|
+
class {
|
|
704
|
+
constructor() {
|
|
705
|
+
this.listeners = new Set, this.position = {
|
|
706
|
+
x: 0,
|
|
707
|
+
y: 0
|
|
708
|
+
}, this.rafId = null, this.lastEvent = null, this.isTracking = !1,
|
|
709
|
+
/**
|
|
710
|
+
* Handle mouse move event
|
|
711
|
+
*/
|
|
712
|
+
this.handleMouseMove = e => {
|
|
713
|
+
this.lastEvent = e,
|
|
714
|
+
// Use requestAnimationFrame to throttle updates
|
|
715
|
+
null === this.rafId && (this.rafId = requestAnimationFrame((() => {
|
|
716
|
+
this.lastEvent && (this.position = {
|
|
717
|
+
x: this.lastEvent.clientX,
|
|
718
|
+
y: this.lastEvent.clientY
|
|
719
|
+
},
|
|
720
|
+
// Notify all subscribers
|
|
721
|
+
this.listeners.forEach((callback => {
|
|
722
|
+
try {
|
|
723
|
+
callback(this.position);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
console.error("GlobalMouseTracker: Error in subscriber callback", error);
|
|
726
|
+
}
|
|
727
|
+
}))), this.rafId = null;
|
|
728
|
+
})));
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Subscribe to mouse position updates
|
|
733
|
+
* @param callback Function to call when mouse position changes
|
|
734
|
+
* @returns Unsubscribe function
|
|
735
|
+
*/ subscribe(callback) {
|
|
736
|
+
// Return unsubscribe function
|
|
737
|
+
return this.listeners.add(callback),
|
|
738
|
+
// Start tracking if this is the first subscriber
|
|
739
|
+
1 === this.listeners.size && this.startTracking(),
|
|
740
|
+
// Immediately notify with current position
|
|
741
|
+
callback(this.position), () => {
|
|
742
|
+
this.unsubscribe(callback);
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Unsubscribe from mouse position updates
|
|
747
|
+
*/ unsubscribe(callback) {
|
|
748
|
+
this.listeners.delete(callback),
|
|
749
|
+
// Stop tracking if no more subscribers
|
|
750
|
+
0 === this.listeners.size && this.stopTracking();
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Start tracking mouse movement
|
|
754
|
+
*/ startTracking() {
|
|
755
|
+
this.isTracking || (this.isTracking = !0,
|
|
756
|
+
// Use document-level listener for global tracking
|
|
757
|
+
document.addEventListener("mousemove", this.handleMouseMove, {
|
|
758
|
+
passive: !0
|
|
759
|
+
}));
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Stop tracking mouse movement
|
|
763
|
+
*/ stopTracking() {
|
|
764
|
+
this.isTracking && (this.isTracking = !1, document.removeEventListener("mousemove", this.handleMouseMove),
|
|
765
|
+
// Cancel any pending RAF
|
|
766
|
+
null !== this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null), this.lastEvent = null);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Get current mouse position (synchronous)
|
|
770
|
+
*/ getPosition() {
|
|
771
|
+
return {
|
|
772
|
+
...this.position
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Get number of active subscribers (for debugging)
|
|
777
|
+
*/ getSubscriberCount() {
|
|
778
|
+
return this.listeners.size;
|
|
779
|
+
}
|
|
780
|
+
}, {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
|
|
781
|
+
parentElement && backgroundDetectionCache.set(parentElement, {
|
|
782
|
+
result: result,
|
|
783
|
+
timestamp: Date.now(),
|
|
784
|
+
config: overLightConfig,
|
|
785
|
+
threshold: threshold
|
|
786
|
+
});
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Composable hook for AtomixGlass component logic
|
|
791
|
+
* Manages all state, calculations, and event handlers
|
|
792
|
+
*/
|
|
793
|
+
function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadius: cornerRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, disableEffects: disableEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugCornerRadius: debugCornerRadius = !1, debugOverLight: debugOverLight = !1, enablePerformanceMonitoring: enablePerformanceMonitoring = !1, children: children}) {
|
|
794
|
+
// State
|
|
795
|
+
const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), [glassSize, setGlassSize] = useState({
|
|
796
|
+
width: 270,
|
|
797
|
+
height: 69
|
|
798
|
+
}), [internalGlobalMousePosition, setInternalGlobalMousePosition] = useState({
|
|
799
|
+
x: 0,
|
|
800
|
+
y: 0
|
|
801
|
+
}), [internalMouseOffset, setInternalMouseOffset] = useState({
|
|
802
|
+
x: 0,
|
|
803
|
+
y: 0
|
|
804
|
+
}), [dynamicCornerRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), effectiveCornerRadius = useMemo((() => void 0 !== cornerRadius ? Math.max(0, cornerRadius) : Math.max(0, dynamicCornerRadius)), [ cornerRadius, dynamicCornerRadius, debugCornerRadius ]), effectiveReducedMotion = useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveHighContrast = useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveDisableEffects = useMemo((() => disableEffects || effectiveReducedMotion), [ disableEffects, effectiveReducedMotion ]), globalMousePosition = useMemo((() => externalGlobalMousePosition || internalGlobalMousePosition), [ externalGlobalMousePosition, internalGlobalMousePosition ]), mouseOffset = useMemo((() => externalMouseOffset || internalMouseOffset), [ externalMouseOffset, internalMouseOffset ]);
|
|
805
|
+
// Extract border-radius from children
|
|
806
|
+
useEffect((() => {
|
|
807
|
+
const extractRadius = () => {
|
|
808
|
+
try {
|
|
809
|
+
let extractedRadius = null, extractionSource = "default";
|
|
810
|
+
if (contentRef.current) {
|
|
811
|
+
const firstChild = contentRef.current.firstElementChild;
|
|
812
|
+
if (firstChild) {
|
|
813
|
+
const domRadius = (element => {
|
|
814
|
+
if (!element || "undefined" == typeof window) return null;
|
|
815
|
+
try {
|
|
816
|
+
const computedStyles = window.getComputedStyle(element), borderRadius = computedStyles.borderRadius || computedStyles.borderTopLeftRadius || computedStyles.borderTopRightRadius || computedStyles.borderBottomLeftRadius || computedStyles.borderBottomRightRadius;
|
|
817
|
+
if (borderRadius && "0px" !== borderRadius && "auto" !== borderRadius) {
|
|
818
|
+
const parsed = parseBorderRadiusValue(borderRadius);
|
|
819
|
+
return parsed > 0 ? parsed : null;
|
|
820
|
+
}
|
|
821
|
+
return null;
|
|
822
|
+
} catch (error) {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
})(firstChild);
|
|
826
|
+
null !== domRadius && domRadius > 0 && (extractedRadius = domRadius, extractionSource = "DOM element");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (null === extractedRadius) {
|
|
830
|
+
const childRadius = extractBorderRadiusFromChildren(children);
|
|
831
|
+
childRadius > 0 && childRadius !== CONSTANTS.DEFAULT_CORNER_RADIUS && (extractedRadius = childRadius,
|
|
832
|
+
extractionSource = "React children");
|
|
833
|
+
}
|
|
834
|
+
null !== extractedRadius && extractedRadius > 0 ? setDynamicCornerRadius(extractedRadius) : process.env.NODE_ENV;
|
|
835
|
+
} catch (error) {
|
|
836
|
+
"production" !== process.env.NODE_ENV && debugCornerRadius && console.error("[AtomixGlass] Error extracting corner radius:", error);
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
extractRadius();
|
|
840
|
+
const timeoutId = setTimeout(extractRadius, 100);
|
|
841
|
+
return () => clearTimeout(timeoutId);
|
|
842
|
+
}), [ children, debugCornerRadius, contentRef ]),
|
|
843
|
+
// Media query handlers and background detection
|
|
844
|
+
useEffect((() => {
|
|
845
|
+
if (("auto" === overLight || "object" == typeof overLight && null !== overLight) && glassRef.current) {
|
|
846
|
+
const element = glassRef.current, cachedResult = ((parentElement, overLightConfig) => {
|
|
847
|
+
if (!parentElement) return null;
|
|
848
|
+
const cached = backgroundDetectionCache.get(parentElement);
|
|
849
|
+
return cached && ((config1, config2) => {
|
|
850
|
+
// Primitive comparison for boolean and 'auto'
|
|
851
|
+
if ("object" != typeof config1 || null === config1) return config1 === config2;
|
|
852
|
+
// Both must be objects at this point
|
|
853
|
+
if ("object" != typeof config2 || null === config2) return !1;
|
|
854
|
+
const obj1 = config1, obj2 = config2, props = [ "threshold", "opacity", "contrast", "brightness", "saturationBoost" ];
|
|
855
|
+
for (const prop of props) {
|
|
856
|
+
const val1 = obj1[prop], val2 = obj2[prop];
|
|
857
|
+
// If both are undefined, they're equal for this property
|
|
858
|
+
if (void 0 !== val1 || void 0 !== val2) {
|
|
859
|
+
// If one is undefined and the other isn't, they're different
|
|
860
|
+
if (void 0 === val1 || void 0 === val2) return !1;
|
|
861
|
+
// Compare numeric values (handle NaN and floating point precision)
|
|
862
|
+
if ("number" == typeof val1 && "number" == typeof val2) {
|
|
863
|
+
// Use Number.isNaN for proper NaN comparison
|
|
864
|
+
if (Number.isNaN(val1) && Number.isNaN(val2)) continue;
|
|
865
|
+
if (Number.isNaN(val1) || Number.isNaN(val2)) return !1;
|
|
866
|
+
// Compare with small epsilon for floating point numbers
|
|
867
|
+
if (Math.abs(val1 - val2) > Number.EPSILON) return !1;
|
|
868
|
+
} else if (val1 !== val2) return !1;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return !0;
|
|
872
|
+
})(cached.config, overLightConfig) ? (
|
|
873
|
+
// Update timestamp for LRU-like behavior (though WeakMap doesn't support LRU)
|
|
874
|
+
cached.timestamp = Date.now(), cached.result) : null;
|
|
875
|
+
})(element.parentElement, overLight);
|
|
876
|
+
if (null !== cachedResult) return void setDetectedOverLight(cachedResult);
|
|
877
|
+
const timeoutId = setTimeout((() => {
|
|
878
|
+
try {
|
|
879
|
+
if (!element) return void setDetectedOverLight(!1);
|
|
880
|
+
// Validate window context
|
|
881
|
+
if ("undefined" == typeof window || "function" != typeof window.getComputedStyle) return void setDetectedOverLight(!1);
|
|
882
|
+
let totalLuminance = 0, validSamples = 0, hasValidBackground = !1, currentElement = element.parentElement, depth = 0;
|
|
883
|
+
const maxDepth = 20, maxSamples = 10;
|
|
884
|
+
// Limit traversal depth to prevent infinite loops and performance issues
|
|
885
|
+
for (;currentElement && validSamples < maxSamples && depth < maxDepth; ) {
|
|
886
|
+
try {
|
|
887
|
+
const computedStyle = window.getComputedStyle(currentElement);
|
|
888
|
+
if (!computedStyle) {
|
|
889
|
+
currentElement = currentElement.parentElement, depth++;
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
const bgColor = computedStyle.backgroundColor, bgImage = computedStyle.backgroundImage;
|
|
893
|
+
// Check for solid color backgrounds
|
|
894
|
+
if (bgColor && "rgba(0, 0, 0, 0)" !== bgColor && "transparent" !== bgColor && "initial" !== bgColor && "none" !== bgColor) {
|
|
895
|
+
const rgb = bgColor.match(/\d+/g);
|
|
896
|
+
if (rgb && rgb.length >= 3) {
|
|
897
|
+
const r = Number(rgb[0]), g = Number(rgb[1]), b = Number(rgb[2]);
|
|
898
|
+
// Validate RGB values are valid numbers
|
|
899
|
+
if (!isNaN(r) && !isNaN(g) && !isNaN(b) && isFinite(r) && isFinite(g) && isFinite(b) && r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && (r > 10 || g > 10 || b > 10)) {
|
|
900
|
+
const luminance = (.299 * r + .587 * g + .114 * b) / 255;
|
|
901
|
+
!isNaN(luminance) && isFinite(luminance) && (totalLuminance += luminance, validSamples++,
|
|
902
|
+
hasValidBackground = !0);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
// Check for image backgrounds
|
|
907
|
+
bgImage && "none" !== bgImage && "initial" !== bgImage && (
|
|
908
|
+
// For image backgrounds, assume medium luminance
|
|
909
|
+
totalLuminance += .5, validSamples++, hasValidBackground = !0);
|
|
910
|
+
} catch (styleError) {
|
|
911
|
+
process.env.NODE_ENV;
|
|
912
|
+
}
|
|
913
|
+
// Move to parent element for next iteration
|
|
914
|
+
if (!currentElement) break;
|
|
915
|
+
// Exit loop if currentElement becomes null
|
|
916
|
+
currentElement = currentElement.parentElement, depth++;
|
|
917
|
+
}
|
|
918
|
+
// More conservative detection with better error handling
|
|
919
|
+
if (hasValidBackground && validSamples > 0) {
|
|
920
|
+
const avgLuminance = totalLuminance / validSamples;
|
|
921
|
+
if (!isNaN(avgLuminance) && isFinite(avgLuminance)) {
|
|
922
|
+
let threshold = .7;
|
|
923
|
+
// Conservative threshold for overlight
|
|
924
|
+
// If overLight is an object, use its threshold property with validation
|
|
925
|
+
if ("object" == typeof overLight && null !== overLight) {
|
|
926
|
+
const objConfig = overLight;
|
|
927
|
+
if (void 0 !== objConfig.threshold) {
|
|
928
|
+
const configThreshold = "number" == typeof objConfig.threshold && !isNaN(objConfig.threshold) && isFinite(objConfig.threshold) ? objConfig.threshold : .7;
|
|
929
|
+
threshold = Math.min(.9, Math.max(.1, configThreshold));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
const isOverLightDetected = avgLuminance > threshold;
|
|
933
|
+
// Cache the result in shared cache
|
|
934
|
+
setCachedBackgroundDetection(element.parentElement, overLight, isOverLightDetected, threshold),
|
|
935
|
+
setDetectedOverLight(isOverLightDetected);
|
|
936
|
+
} else {
|
|
937
|
+
// Invalid luminance calculation, default to false
|
|
938
|
+
const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
|
|
939
|
+
setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
|
|
940
|
+
setDetectedOverLight(result);
|
|
941
|
+
}
|
|
942
|
+
} else {
|
|
943
|
+
// Default to false if no valid background found
|
|
944
|
+
const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
|
|
945
|
+
setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
|
|
946
|
+
setDetectedOverLight(result);
|
|
947
|
+
}
|
|
948
|
+
} catch (error) {
|
|
949
|
+
// Enhanced error logging with context
|
|
950
|
+
"development" === process.env.NODE_ENV && console.warn("AtomixGlass: Error detecting background brightness:", error);
|
|
951
|
+
const result = !1;
|
|
952
|
+
if (element && element.parentElement) {
|
|
953
|
+
const threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
|
|
954
|
+
setCachedBackgroundDetection(element.parentElement, overLight, result, threshold);
|
|
955
|
+
}
|
|
956
|
+
setDetectedOverLight(result);
|
|
957
|
+
}
|
|
958
|
+
}), 150);
|
|
959
|
+
return () => clearTimeout(timeoutId);
|
|
960
|
+
}
|
|
961
|
+
if ("boolean" == typeof overLight &&
|
|
962
|
+
// For boolean values, disable auto-detection
|
|
963
|
+
// Cache is automatically managed by WeakMap (no manual clearing needed)
|
|
964
|
+
setDetectedOverLight(!1), "function" == typeof window.matchMedia) try {
|
|
965
|
+
const mediaQueryReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)"), mediaQueryHighContrast = window.matchMedia("(prefers-contrast: high)");
|
|
966
|
+
setUserPrefersReducedMotion(mediaQueryReducedMotion.matches), setUserPrefersHighContrast(mediaQueryHighContrast.matches);
|
|
967
|
+
const handleReducedMotionChange = e => {
|
|
968
|
+
setUserPrefersReducedMotion(e.matches);
|
|
969
|
+
}, handleHighContrastChange = e => {
|
|
970
|
+
setUserPrefersHighContrast(e.matches);
|
|
971
|
+
};
|
|
972
|
+
return mediaQueryReducedMotion.addEventListener ? (mediaQueryReducedMotion.addEventListener("change", handleReducedMotionChange),
|
|
973
|
+
mediaQueryHighContrast.addEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.addListener && (mediaQueryReducedMotion.addListener(handleReducedMotionChange),
|
|
974
|
+
mediaQueryHighContrast.addListener(handleHighContrastChange)), () => {
|
|
975
|
+
try {
|
|
976
|
+
mediaQueryReducedMotion.removeEventListener ? (mediaQueryReducedMotion.removeEventListener("change", handleReducedMotionChange),
|
|
977
|
+
mediaQueryHighContrast.removeEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.removeListener && (mediaQueryReducedMotion.removeListener(handleReducedMotionChange),
|
|
978
|
+
mediaQueryHighContrast.removeListener(handleHighContrastChange));
|
|
979
|
+
} catch (cleanupError) {
|
|
980
|
+
console.error("AtomixGlass: Error cleaning up media query listeners:", cleanupError);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
} catch (error) {
|
|
984
|
+
return void console.error("AtomixGlass: Error setting up media queries:", error);
|
|
985
|
+
}
|
|
986
|
+
}), [ overLight, glassRef, debugOverLight ]);
|
|
987
|
+
// Mouse tracking using shared global tracker
|
|
988
|
+
// Cache bounding rect to avoid repeated getBoundingClientRect calls
|
|
989
|
+
const cachedRectRef = useRef(null), updateRectRef = useRef(null), handleGlobalMousePosition = useCallback((globalPos => {
|
|
990
|
+
if (externalGlobalMousePosition && externalMouseOffset)
|
|
991
|
+
// External mouse position provided, skip internal tracking
|
|
992
|
+
return;
|
|
993
|
+
if (effectiveDisableEffects) return;
|
|
994
|
+
const container = mouseContainer?.current || glassRef.current;
|
|
995
|
+
if (!container) return;
|
|
996
|
+
enablePerformanceMonitoring && performance.now();
|
|
997
|
+
// Use cached rect if available, otherwise get new one
|
|
998
|
+
let rect = cachedRectRef.current;
|
|
999
|
+
if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
|
|
1000
|
+
cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
|
|
1001
|
+
const center = calculateElementCenter(rect), newOffset = {
|
|
1002
|
+
x: (globalPos.x - center.x) / rect.width * 100,
|
|
1003
|
+
y: (globalPos.y - center.y) / rect.height * 100
|
|
1004
|
+
};
|
|
1005
|
+
// Calculate offset relative to this container
|
|
1006
|
+
// React 18 automatically batches these updates
|
|
1007
|
+
setInternalMouseOffset(newOffset), setInternalGlobalMousePosition(globalPos), "production" !== process.env.NODE_ENV && enablePerformanceMonitoring && performance.now();
|
|
1008
|
+
}), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects, enablePerformanceMonitoring ]);
|
|
1009
|
+
// Subscribe to shared mouse tracker
|
|
1010
|
+
useEffect((() => {
|
|
1011
|
+
if (externalGlobalMousePosition && externalMouseOffset)
|
|
1012
|
+
// External mouse position provided, don't subscribe
|
|
1013
|
+
return;
|
|
1014
|
+
if (effectiveDisableEffects)
|
|
1015
|
+
// Effects disabled, don't subscribe
|
|
1016
|
+
return;
|
|
1017
|
+
// Subscribe to shared tracker
|
|
1018
|
+
const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition), container = mouseContainer?.current || glassRef.current;
|
|
1019
|
+
// Update cached rect when container size changes
|
|
1020
|
+
let resizeObserver = null;
|
|
1021
|
+
return container && "undefined" != typeof ResizeObserver && (resizeObserver = new ResizeObserver((() => {
|
|
1022
|
+
null !== updateRectRef.current && cancelAnimationFrame(updateRectRef.current), updateRectRef.current = requestAnimationFrame((() => {
|
|
1023
|
+
const container = mouseContainer?.current || glassRef.current;
|
|
1024
|
+
container && (cachedRectRef.current = container.getBoundingClientRect()), updateRectRef.current = null;
|
|
1025
|
+
}));
|
|
1026
|
+
})), resizeObserver.observe(container)), () => {
|
|
1027
|
+
unsubscribe(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
|
|
1028
|
+
updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
|
|
1029
|
+
};
|
|
1030
|
+
}), [ handleGlobalMousePosition, mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects ]);
|
|
1031
|
+
// Transform calculations
|
|
1032
|
+
const calculateDirectionalScale = useCallback((() => {
|
|
1033
|
+
if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return "scale(1)";
|
|
1034
|
+
const rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), deltaX = globalMousePosition.x - center.x, deltaY = globalMousePosition.y - center.y, edgeDistanceX = Math.max(0, Math.abs(deltaX) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(deltaY) - glassSize.height / 2), edgeDistance = calculateDistance({
|
|
1035
|
+
x: edgeDistanceX,
|
|
1036
|
+
y: edgeDistanceY
|
|
1037
|
+
}, {
|
|
1038
|
+
x: 0,
|
|
1039
|
+
y: 0
|
|
1040
|
+
});
|
|
1041
|
+
if (edgeDistance > CONSTANTS.ACTIVATION_ZONE) return "scale(1)";
|
|
1042
|
+
const fadeInFactor = 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE, centerDistance = calculateDistance(globalMousePosition, center);
|
|
1043
|
+
if (0 === centerDistance) return "scale(1)";
|
|
1044
|
+
const normalizedX = deltaX / centerDistance, normalizedY = deltaY / centerDistance, stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor, scaleX = 1 + Math.abs(normalizedX) * stretchIntensity * .3 - Math.abs(normalizedY) * stretchIntensity * .15, scaleY = 1 + Math.abs(normalizedY) * stretchIntensity * .3 - Math.abs(normalizedX) * stretchIntensity * .15;
|
|
1045
|
+
return `scaleX(${Math.max(.8, scaleX)}) scaleY(${Math.max(.8, scaleY)})`;
|
|
1046
|
+
}), [ globalMousePosition, elasticity, glassSize, glassRef ]), calculateFadeInFactor = useCallback((() => {
|
|
1047
|
+
if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return 0;
|
|
1048
|
+
const rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), edgeDistanceX = Math.max(0, Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2), edgeDistance = calculateDistance({
|
|
1049
|
+
x: edgeDistanceX,
|
|
1050
|
+
y: edgeDistanceY
|
|
1051
|
+
}, {
|
|
1052
|
+
x: 0,
|
|
1053
|
+
y: 0
|
|
1054
|
+
});
|
|
1055
|
+
return edgeDistance > CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
|
|
1056
|
+
}), [ globalMousePosition, glassSize, glassRef ]), calculateElasticTranslation = useCallback((() => {
|
|
1057
|
+
if (!glassRef.current) return {
|
|
1058
|
+
x: 0,
|
|
1059
|
+
y: 0
|
|
1060
|
+
};
|
|
1061
|
+
const fadeInFactor = calculateFadeInFactor(), rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect);
|
|
1062
|
+
return {
|
|
1063
|
+
x: (globalMousePosition.x - center.x) * elasticity * .1 * fadeInFactor,
|
|
1064
|
+
y: (globalMousePosition.y - center.y) * elasticity * .1 * fadeInFactor
|
|
1065
|
+
};
|
|
1066
|
+
}), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveDisableEffects ? {
|
|
1067
|
+
x: 0,
|
|
1068
|
+
y: 0
|
|
1069
|
+
} : calculateElasticTranslation()), [ calculateElasticTranslation, effectiveDisableEffects ]), directionalScale = useMemo((() => effectiveDisableEffects ? "scale(1)" : calculateDirectionalScale()), [ calculateDirectionalScale, effectiveDisableEffects ]), transformStyle = useMemo((() => effectiveDisableEffects ? isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)" : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? "scale(0.96)" : directionalScale}`), [ elasticTranslation, isActive, onClick, directionalScale, effectiveDisableEffects ]);
|
|
1070
|
+
// Size management
|
|
1071
|
+
useEffect((() => {
|
|
1072
|
+
const isValidElement = element => null !== element && element instanceof HTMLElement && element.isConnected;
|
|
1073
|
+
let rafId = null, lastSize = {
|
|
1074
|
+
width: 0,
|
|
1075
|
+
height: 0
|
|
1076
|
+
}, lastCornerRadius = effectiveCornerRadius;
|
|
1077
|
+
const updateGlassSize = (forceUpdate = !1) => {
|
|
1078
|
+
null !== rafId && cancelAnimationFrame(rafId), rafId = requestAnimationFrame((() => {
|
|
1079
|
+
if (!isValidElement(glassRef.current)) return void (rafId = null);
|
|
1080
|
+
const rect = glassRef.current.getBoundingClientRect();
|
|
1081
|
+
if (rect.width <= 0 || rect.height <= 0) return void (rafId = null);
|
|
1082
|
+
// Measure actual rendered size without artificial offsets to avoid feedback loops
|
|
1083
|
+
const newSize = {
|
|
1084
|
+
width: Math.round(rect.width),
|
|
1085
|
+
height: Math.round(rect.height)
|
|
1086
|
+
}, cornerRadiusChanged = lastCornerRadius !== effectiveCornerRadius, dimensionsChanged = Math.abs(newSize.width - lastSize.width) > 1 || Math.abs(newSize.height - lastSize.height) > 1;
|
|
1087
|
+
var size;
|
|
1088
|
+
(forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateGlassSize(size = newSize) && size.width <= CONSTANTS.MAX_SIZE && size.height <= CONSTANTS.MAX_SIZE && (lastSize = newSize,
|
|
1089
|
+
lastCornerRadius = effectiveCornerRadius, setGlassSize(newSize)), rafId = null;
|
|
1090
|
+
}));
|
|
1091
|
+
};
|
|
1092
|
+
let resizeTimeoutId = null;
|
|
1093
|
+
const debouncedResizeHandler = () => {
|
|
1094
|
+
resizeTimeoutId && clearTimeout(resizeTimeoutId), resizeTimeoutId = setTimeout((() => updateGlassSize(!1)), 16);
|
|
1095
|
+
}, initialTimeoutId = setTimeout((() => updateGlassSize(!0)), 0);
|
|
1096
|
+
let resizeObserver = null, resizeDebounceTimeout = null;
|
|
1097
|
+
// ResizeObserver has 98%+ browser support, no need for fallback
|
|
1098
|
+
if ("undefined" != typeof ResizeObserver && isValidElement(glassRef.current)) try {
|
|
1099
|
+
resizeObserver = new ResizeObserver((entries => {
|
|
1100
|
+
for (const entry of entries) if (entry.target === glassRef.current) {
|
|
1101
|
+
// Update cached rect when size changes
|
|
1102
|
+
glassRef.current && (cachedRectRef.current = glassRef.current.getBoundingClientRect()),
|
|
1103
|
+
// Debounce resize updates to match RAF timing (16ms)
|
|
1104
|
+
resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), resizeDebounceTimeout = setTimeout((() => updateGlassSize(!1)), 16);
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
})), resizeObserver.observe(glassRef.current);
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
console.warn("AtomixGlass: ResizeObserver not available, using window resize only", error);
|
|
1110
|
+
}
|
|
1111
|
+
return window.addEventListener("resize", debouncedResizeHandler, {
|
|
1112
|
+
passive: !0
|
|
1113
|
+
}), () => {
|
|
1114
|
+
clearTimeout(initialTimeoutId), null !== rafId && cancelAnimationFrame(rafId), resizeTimeoutId && clearTimeout(resizeTimeoutId),
|
|
1115
|
+
resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), window.removeEventListener("resize", debouncedResizeHandler),
|
|
1116
|
+
resizeObserver?.disconnect();
|
|
1117
|
+
};
|
|
1118
|
+
}), [ effectiveCornerRadius, glassRef ]);
|
|
1119
|
+
// OverLight config
|
|
1120
|
+
/**
|
|
1121
|
+
* Get effective overLight value based on configuration
|
|
1122
|
+
* - boolean: returns the boolean value directly
|
|
1123
|
+
* - 'auto': returns detectedOverLight (auto-detected from background)
|
|
1124
|
+
* - object: returns detectedOverLight (auto-detected, but config object provides customization)
|
|
1125
|
+
*/
|
|
1126
|
+
const getEffectiveOverLight = useCallback((() => "boolean" == typeof overLight ? overLight : ("auto" === overLight || "object" == typeof overLight && null !== overLight) && detectedOverLight), [ overLight, detectedOverLight ]), validateConfigValue = useCallback(((value, min, max, defaultValue) => "number" != typeof value || isNaN(value) || !isFinite(value) ? defaultValue : Math.min(max, Math.max(min, value))), []), overLightConfig = useMemo((() => {
|
|
1127
|
+
const isOverLight = getEffectiveOverLight(), mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, baseConfig = {
|
|
1128
|
+
isOverLight: isOverLight,
|
|
1129
|
+
threshold: .7,
|
|
1130
|
+
opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
|
|
1131
|
+
contrast: Math.min(1.8, Math.max(1, 1.4 + .3 * mouseInfluence)),
|
|
1132
|
+
brightness: Math.min(1.2, Math.max(.7, .85 + .15 * mouseInfluence)),
|
|
1133
|
+
saturationBoost: Math.min(2, Math.max(1, 1.3 + .4 * mouseInfluence)),
|
|
1134
|
+
shadowIntensity: Math.min(1.5, Math.max(.5, .9 + .5 * mouseInfluence)),
|
|
1135
|
+
borderOpacity: Math.min(1, Math.max(.3, .7 + .3 * mouseInfluence))
|
|
1136
|
+
};
|
|
1137
|
+
if ("object" == typeof overLight && null !== overLight) {
|
|
1138
|
+
const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), finalConfig = {
|
|
1139
|
+
...baseConfig,
|
|
1140
|
+
threshold: validatedThreshold,
|
|
1141
|
+
opacity: validatedOpacity * hoverIntensity * activeIntensity,
|
|
1142
|
+
contrast: validatedContrast + .3 * mouseInfluence,
|
|
1143
|
+
brightness: validatedBrightness + .15 * mouseInfluence,
|
|
1144
|
+
saturationBoost: validatedSaturationBoost + .4 * mouseInfluence
|
|
1145
|
+
};
|
|
1146
|
+
// Validate and apply object config values with proper clamping
|
|
1147
|
+
return process.env.NODE_ENV, finalConfig;
|
|
1148
|
+
}
|
|
1149
|
+
// Debug logging for non-object configs
|
|
1150
|
+
return process.env.NODE_ENV, baseConfig;
|
|
1151
|
+
}), [ overLight, getEffectiveOverLight, mouseOffset, isHovered, isActive, validateConfigValue, debugOverLight ]), handleMouseEnter = useCallback((() => setIsHovered(!0)), []), handleMouseLeave = useCallback((() => setIsHovered(!1)), []), handleMouseDown = useCallback((() => setIsActive(!0)), []), handleMouseUp = useCallback((() => setIsActive(!1)), []), handleKeyDown = useCallback((e => {
|
|
1152
|
+
!onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
|
|
1153
|
+
}), [ onClick ]), handleMouseMove = useCallback((_e => {}), []);
|
|
1154
|
+
/**
|
|
1155
|
+
* Validate and clamp a numeric config value
|
|
1156
|
+
* @param value - The value to validate
|
|
1157
|
+
* @param min - Minimum allowed value
|
|
1158
|
+
* @param max - Maximum allowed value
|
|
1159
|
+
* @param defaultValue - Default value if validation fails
|
|
1160
|
+
* @returns Validated and clamped value
|
|
1161
|
+
*/ return {
|
|
1162
|
+
// State
|
|
1163
|
+
isHovered: isHovered,
|
|
1164
|
+
isActive: isActive,
|
|
1165
|
+
glassSize: glassSize,
|
|
1166
|
+
dynamicCornerRadius: dynamicCornerRadius,
|
|
1167
|
+
effectiveCornerRadius: effectiveCornerRadius,
|
|
1168
|
+
effectiveReducedMotion: effectiveReducedMotion,
|
|
1169
|
+
effectiveHighContrast: effectiveHighContrast,
|
|
1170
|
+
effectiveDisableEffects: effectiveDisableEffects,
|
|
1171
|
+
detectedOverLight: detectedOverLight,
|
|
1172
|
+
globalMousePosition: globalMousePosition,
|
|
1173
|
+
mouseOffset: mouseOffset,
|
|
1174
|
+
// OverLight config
|
|
1175
|
+
overLightConfig: overLightConfig,
|
|
1176
|
+
// Transform calculations
|
|
1177
|
+
elasticTranslation: elasticTranslation,
|
|
1178
|
+
directionalScale: directionalScale,
|
|
1179
|
+
transformStyle: transformStyle,
|
|
1180
|
+
// Event handlers
|
|
1181
|
+
handleMouseEnter: handleMouseEnter,
|
|
1182
|
+
handleMouseLeave: handleMouseLeave,
|
|
1183
|
+
handleMouseDown: handleMouseDown,
|
|
1184
|
+
handleMouseUp: handleMouseUp,
|
|
1185
|
+
handleMouseMove: handleMouseMove,
|
|
1186
|
+
handleKeyDown: handleKeyDown
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* AtomixGlass - A high-performance glass morphism component with liquid distortion effects
|
|
1192
|
+
*
|
|
1193
|
+
* Features:
|
|
1194
|
+
* - Hardware-accelerated glass effects with SVG filters
|
|
1195
|
+
* - Mouse-responsive liquid distortion
|
|
1196
|
+
* - Dynamic border-radius extraction from children CSS properties
|
|
1197
|
+
* - Automatic light/dark theme detection via overLight prop
|
|
1198
|
+
* - Accessibility and performance optimizations
|
|
1199
|
+
* - Multiple displacement modes (standard, polar, prominent, shader)
|
|
1200
|
+
* - Design token integration for consistent theming
|
|
1201
|
+
* - Focus ring support for keyboard navigation
|
|
1202
|
+
* - Responsive breakpoints for mobile optimization
|
|
1203
|
+
* - Enhanced ARIA attributes for screen readers
|
|
1204
|
+
*
|
|
1205
|
+
* Design System Compliance:
|
|
1206
|
+
* - Uses design tokens for opacity, spacing, and colors
|
|
1207
|
+
* - Follows BEM methodology for class naming
|
|
1208
|
+
* - Implements focus-ring mixin for accessibility
|
|
1209
|
+
* - Supports reduced motion and high contrast preferences
|
|
1210
|
+
*
|
|
1211
|
+
* @example
|
|
1212
|
+
* // Basic usage with dynamic border-radius extraction
|
|
1213
|
+
* <AtomixGlass>
|
|
1214
|
+
* <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
|
|
1215
|
+
* </AtomixGlass>
|
|
1216
|
+
*
|
|
1217
|
+
* @example
|
|
1218
|
+
* // Manual border-radius override
|
|
1219
|
+
* <AtomixGlass cornerRadius={20}>
|
|
1220
|
+
* <div>Content with 20px glass radius</div>
|
|
1221
|
+
* </AtomixGlass>
|
|
1222
|
+
*
|
|
1223
|
+
* @example
|
|
1224
|
+
* // Interactive glass with click handler
|
|
1225
|
+
* <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
|
|
1226
|
+
* <div>Clickable content</div>
|
|
1227
|
+
* </AtomixGlass>
|
|
1228
|
+
*
|
|
1229
|
+
* @example
|
|
1230
|
+
* // OverLight - Boolean mode (explicit control)
|
|
1231
|
+
* <AtomixGlass overLight={true}>
|
|
1232
|
+
* <div>Content on light background</div>
|
|
1233
|
+
* </AtomixGlass>
|
|
1234
|
+
*
|
|
1235
|
+
* @example
|
|
1236
|
+
* // OverLight - Auto-detection mode
|
|
1237
|
+
* <AtomixGlass overLight="auto">
|
|
1238
|
+
* <div>Content with auto-detected background</div>
|
|
1239
|
+
* </AtomixGlass>
|
|
1240
|
+
*
|
|
1241
|
+
* @example
|
|
1242
|
+
* // OverLight - Object config with custom settings
|
|
1243
|
+
* <AtomixGlass
|
|
1244
|
+
* overLight={{
|
|
1245
|
+
* threshold: 0.8,
|
|
1246
|
+
* opacity: 0.6,
|
|
1247
|
+
* contrast: 1.8,
|
|
1248
|
+
* brightness: 1.0,
|
|
1249
|
+
* saturationBoost: 1.5
|
|
1250
|
+
* }}
|
|
1251
|
+
* >
|
|
1252
|
+
* <div>Content with custom overLight config</div>
|
|
1253
|
+
* </AtomixGlass>
|
|
1254
|
+
*
|
|
1255
|
+
* @example
|
|
1256
|
+
* // Debug mode for overLight detection
|
|
1257
|
+
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
1258
|
+
* <div>Content with debug logging enabled</div>
|
|
1259
|
+
* </AtomixGlass>
|
|
1260
|
+
*/ function AtomixGlass({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, cornerRadius: cornerRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, disableEffects: disableEffects = !1, enableLiquidBlur: enableLiquidBlur = !1, enableBorderEffect: enableBorderEffect = !0, enableOverLightLayers: enableOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, enablePerformanceMonitoring: enablePerformanceMonitoring = !1, debugCornerRadius: debugCornerRadius = !1, debugOverLight: debugOverLight = !1}) {
|
|
1261
|
+
const glassRef = useRef(null), contentRef = useRef(null), opacityCacheRef = useRef(null), {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveCornerRadius: effectiveCornerRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveDisableEffects: effectiveDisableEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
|
|
1262
|
+
glassRef: glassRef,
|
|
1263
|
+
contentRef: contentRef,
|
|
1264
|
+
cornerRadius: cornerRadius,
|
|
1265
|
+
globalMousePosition: externalGlobalMousePosition,
|
|
1266
|
+
mouseOffset: externalMouseOffset,
|
|
1267
|
+
mouseContainer: mouseContainer,
|
|
1268
|
+
overLight: overLight,
|
|
1269
|
+
reducedMotion: reducedMotion,
|
|
1270
|
+
highContrast: highContrast,
|
|
1271
|
+
disableEffects: disableEffects,
|
|
1272
|
+
elasticity: elasticity,
|
|
1273
|
+
onClick: onClick,
|
|
1274
|
+
debugCornerRadius: debugCornerRadius,
|
|
1275
|
+
debugOverLight: debugOverLight,
|
|
1276
|
+
enablePerformanceMonitoring: enablePerformanceMonitoring,
|
|
1277
|
+
children: children
|
|
1278
|
+
}), isOverLight = overLightConfig.isOverLight, shouldRenderOverLightLayers = enableOverLightLayers && isOverLight;
|
|
1279
|
+
// Read CSS custom properties once on mount and cache them
|
|
1280
|
+
useEffect((() => {
|
|
1281
|
+
if ("undefined" != typeof window && glassRef.current && !opacityCacheRef.current) try {
|
|
1282
|
+
const computedStyle = window.getComputedStyle(glassRef.current), opacity50Value = computedStyle.getPropertyValue("--atomix-opacity-50").trim(), opacity40Value = computedStyle.getPropertyValue("--atomix-opacity-40").trim(), opacity80Value = computedStyle.getPropertyValue("--atomix-opacity-80").trim(), opacity0Value = computedStyle.getPropertyValue("--atomix-opacity-0").trim(), parseOpacity = (value, defaultValue) => {
|
|
1283
|
+
if (!value) return defaultValue;
|
|
1284
|
+
const parsed = parseFloat(value);
|
|
1285
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
1286
|
+
};
|
|
1287
|
+
opacityCacheRef.current = {
|
|
1288
|
+
opacity50: parseOpacity(opacity50Value, .5),
|
|
1289
|
+
opacity40: parseOpacity(opacity40Value, .4),
|
|
1290
|
+
opacity80: parseOpacity(opacity80Value, .8),
|
|
1291
|
+
opacity0: parseOpacity(opacity0Value, 0)
|
|
1292
|
+
};
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
// Fallback to defaults if reading fails
|
|
1295
|
+
opacityCacheRef.current = {
|
|
1296
|
+
opacity50: .5,
|
|
1297
|
+
opacity40: .4,
|
|
1298
|
+
opacity80: .8,
|
|
1299
|
+
opacity0: 0
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
}), []);
|
|
1303
|
+
// Calculate base style with transforms (only dynamic values)
|
|
1304
|
+
const baseStyle = useMemo((() => ({
|
|
1305
|
+
...style,
|
|
1306
|
+
...0 !== elasticity && !effectiveDisableEffects && {
|
|
1307
|
+
transform: transformStyle
|
|
1308
|
+
}
|
|
1309
|
+
})), [ style, transformStyle, effectiveDisableEffects, elasticity ]), componentClassName = useMemo((() => [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveDisableEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" ")), [ effectiveReducedMotion, effectiveHighContrast, effectiveDisableEffects, className ]), baseStylePosition = baseStyle.position, baseStyleTop = baseStyle.top, baseStyleLeft = baseStyle.left, positionStyles = useMemo((() => ({
|
|
1310
|
+
position: baseStylePosition || "absolute",
|
|
1311
|
+
top: baseStyleTop || 0,
|
|
1312
|
+
left: baseStyleLeft || 0
|
|
1313
|
+
})), [ baseStylePosition, baseStyleTop, baseStyleLeft ]), baseStyleWidth = baseStyle.width, baseStyleHeight = baseStyle.height, glassSizeWidth = glassSize.width, glassSizeHeight = glassSize.height, adjustedSize = useMemo((() => ({
|
|
1314
|
+
width: "fixed" !== baseStylePosition ? "100%" : baseStyleWidth || Math.max(glassSizeWidth, 0),
|
|
1315
|
+
height: "fixed" !== baseStylePosition ? "100%" : baseStyleHeight || Math.max(glassSizeHeight, 0)
|
|
1316
|
+
})), [ baseStylePosition, baseStyleWidth, baseStyleHeight, glassSizeWidth, glassSizeHeight ]), mouseOffsetX = mouseOffset.x, mouseOffsetY = mouseOffset.y, GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, gradientCalculations = useMemo((() => {
|
|
1317
|
+
const mx = mouseOffsetX, my = mouseOffsetY, borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), borderOpacity1 = GRADIENT.BORDER_OPACITY.BASE_1 + Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, borderOpacity2 = GRADIENT.BORDER_OPACITY.BASE_2 + Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, borderOpacity3 = GRADIENT.BORDER_OPACITY.BASE_3 + Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, borderOpacity4 = GRADIENT.BORDER_OPACITY.BASE_4 + Math.abs(mx) * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, hover1X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1, hover1Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1, hover2X = GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2, hover2Y = GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2, hover3X = GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3, hover3Y = GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3, baseX = GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER, baseY = GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER;
|
|
1318
|
+
return {
|
|
1319
|
+
isOverLight: isOverLight,
|
|
1320
|
+
mx: mx,
|
|
1321
|
+
my: my,
|
|
1322
|
+
borderGradientAngle: borderGradientAngle,
|
|
1323
|
+
borderStop1: borderStop1,
|
|
1324
|
+
borderStop2: borderStop2,
|
|
1325
|
+
borderOpacity1: borderOpacity1,
|
|
1326
|
+
borderOpacity2: borderOpacity2,
|
|
1327
|
+
borderOpacity3: borderOpacity3,
|
|
1328
|
+
borderOpacity4: borderOpacity4,
|
|
1329
|
+
hover1X: hover1X,
|
|
1330
|
+
hover1Y: hover1Y,
|
|
1331
|
+
hover2X: hover2X,
|
|
1332
|
+
hover2Y: hover2Y,
|
|
1333
|
+
hover3X: hover3X,
|
|
1334
|
+
hover3Y: hover3Y,
|
|
1335
|
+
baseX: baseX,
|
|
1336
|
+
baseY: baseY
|
|
1337
|
+
};
|
|
1338
|
+
}), [ mouseOffsetX, mouseOffsetY, isOverLight ]), overLightOpacity = overLightConfig.opacity, opacityValues = useMemo((() => {
|
|
1339
|
+
// Use cached values if available, otherwise fallback to defaults
|
|
1340
|
+
const opacity50 = opacityCacheRef.current?.opacity50 ?? .5, opacity40 = opacityCacheRef.current?.opacity40 ?? .4, opacity80 = opacityCacheRef.current?.opacity80 ?? .8, opacity0 = opacityCacheRef.current?.opacity0 ?? 0;
|
|
1341
|
+
// Dynamic multiplier for overlay
|
|
1342
|
+
return {
|
|
1343
|
+
hover1: isHovered || isActive ? opacity50 : opacity0,
|
|
1344
|
+
hover2: isActive ? opacity50 : opacity0,
|
|
1345
|
+
hover3: isHovered ? opacity40 : isActive ? opacity80 : opacity0,
|
|
1346
|
+
base: isOverLight ? overLightOpacity || opacity40 : opacity0,
|
|
1347
|
+
over: isOverLight ? 1.1 * (overLightOpacity || opacity40) : opacity0
|
|
1348
|
+
};
|
|
1349
|
+
}), [ isHovered, isActive, isOverLight, overLightOpacity ]), gradientIsOverLight = gradientCalculations.isOverLight, gradientMx = gradientCalculations.mx, gradientMy = gradientCalculations.my, gradientBorderGradientAngle = gradientCalculations.borderGradientAngle, gradientBorderStop1 = gradientCalculations.borderStop1, gradientBorderStop2 = gradientCalculations.borderStop2, gradientBorderOpacity1 = gradientCalculations.borderOpacity1, gradientBorderOpacity2 = gradientCalculations.borderOpacity2, gradientBorderOpacity3 = gradientCalculations.borderOpacity3, gradientBorderOpacity4 = gradientCalculations.borderOpacity4, gradientHover1X = gradientCalculations.hover1X, gradientHover1Y = gradientCalculations.hover1Y, gradientHover2X = gradientCalculations.hover2X, gradientHover2Y = gradientCalculations.hover2Y, gradientHover3X = gradientCalculations.hover3X, gradientHover3Y = gradientCalculations.hover3Y, gradientBaseX = gradientCalculations.baseX, gradientBaseY = gradientCalculations.baseY, positionStylesPosition = positionStyles.position, positionStylesTop = positionStyles.top, positionStylesLeft = positionStyles.left, adjustedSizeWidth = adjustedSize.width, adjustedSizeHeight = adjustedSize.height, baseStyleTransform = baseStyle.transform, opacityValuesHover1 = opacityValues.hover1, opacityValuesHover2 = opacityValues.hover2, opacityValuesHover3 = opacityValues.hover3, opacityValuesBase = opacityValues.base, opacityValuesOver = opacityValues.over, glassVars = useMemo((() => {
|
|
1350
|
+
// RGB color values for rgba() functions
|
|
1351
|
+
// Note: CSS doesn't support rgba(var(--rgb), opacity) syntax, so we use direct values
|
|
1352
|
+
// These values align with design tokens: --atomix-white-rgb and --atomix-black-rgb
|
|
1353
|
+
// The actual RGB values are defined in SCSS and should match these fallbacks
|
|
1354
|
+
// TODO: Consider reading from CSS custom properties if browser support improves
|
|
1355
|
+
const whiteColor = "255, 255, 255";
|
|
1356
|
+
// Matches --atomix-white-rgb design token
|
|
1357
|
+
// Matches --atomix-black-rgb design token
|
|
1358
|
+
return {
|
|
1359
|
+
// Standard CSS custom properties for dynamic values
|
|
1360
|
+
"--atomix-glass-radius": `${effectiveCornerRadius}px`,
|
|
1361
|
+
"--atomix-glass-transform": baseStyleTransform || "none",
|
|
1362
|
+
"--atomix-glass-position": positionStylesPosition,
|
|
1363
|
+
"--atomix-glass-top": "fixed" !== positionStylesTop ? `${positionStylesTop}px` : "0",
|
|
1364
|
+
"--atomix-glass-left": "fixed" !== positionStylesLeft ? `${positionStylesLeft}px` : "0",
|
|
1365
|
+
"--atomix-glass-width": "fixed" !== baseStylePosition ? adjustedSizeWidth : `${adjustedSizeWidth}px`,
|
|
1366
|
+
"--atomix-glass-height": "fixed" !== baseStylePosition ? adjustedSizeHeight : `${adjustedSizeHeight}px`,
|
|
1367
|
+
// Border width: Use spacing token for consistency
|
|
1368
|
+
"--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
|
|
1369
|
+
"--atomix-glass-blend-mode": gradientIsOverLight ? "multiply" : "overlay",
|
|
1370
|
+
// Dynamic gradients and backgrounds
|
|
1371
|
+
// Note: RGB values use design token-aligned constants (white: 255,255,255; black: 0,0,0)
|
|
1372
|
+
"--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%)`,
|
|
1373
|
+
"--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%)`,
|
|
1374
|
+
"--atomix-glass-hover-1-opacity": opacityValuesHover1,
|
|
1375
|
+
"--atomix-glass-hover-1-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover1X}% ${gradientHover1Y}%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(0, 0, 0, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `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}%)`,
|
|
1376
|
+
"--atomix-glass-hover-2-opacity": opacityValuesHover2,
|
|
1377
|
+
"--atomix-glass-hover-2-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover2X}% ${gradientHover2Y}%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(0, 0, 0, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `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}%)`,
|
|
1378
|
+
"--atomix-glass-hover-3-opacity": opacityValuesHover3,
|
|
1379
|
+
"--atomix-glass-hover-3-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover3X}% ${gradientHover3Y}%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(0, 0, 0, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `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}%)`,
|
|
1380
|
+
"--atomix-glass-base-opacity": opacityValuesBase,
|
|
1381
|
+
"--atomix-glass-base-gradient": gradientIsOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + gradientMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(0, 0, 0, ${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(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
|
|
1382
|
+
"--atomix-glass-overlay-opacity": opacityValuesOver,
|
|
1383
|
+
"--atomix-glass-overlay-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientBaseX}% ${gradientBaseY}%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(0, 0, 0, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + Math.abs(gradientMy) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`
|
|
1384
|
+
};
|
|
1385
|
+
}), [
|
|
1386
|
+
// Position styles - only specific properties
|
|
1387
|
+
positionStylesPosition, positionStylesTop, positionStylesLeft,
|
|
1388
|
+
// Adjusted size - only specific properties
|
|
1389
|
+
adjustedSizeWidth, adjustedSizeHeight,
|
|
1390
|
+
// Base style - only transform property
|
|
1391
|
+
baseStyleTransform, baseStylePosition,
|
|
1392
|
+
// Other values
|
|
1393
|
+
effectiveCornerRadius, effectiveReducedMotion,
|
|
1394
|
+
// Gradient calculations - extracted properties
|
|
1395
|
+
gradientIsOverLight, gradientMx, gradientMy, gradientBorderGradientAngle, gradientBorderStop1, gradientBorderStop2, gradientBorderOpacity1, gradientBorderOpacity2, gradientBorderOpacity3, gradientBorderOpacity4, gradientHover1X, gradientHover1Y, gradientHover2X, gradientHover2Y, gradientHover3X, gradientHover3Y, gradientBaseX, gradientBaseY,
|
|
1396
|
+
// Opacity values - extracted properties
|
|
1397
|
+
opacityValuesHover1, opacityValuesHover2, opacityValuesHover3, opacityValuesBase, opacityValuesOver ]);
|
|
1398
|
+
// Build className with state modifiers
|
|
1399
|
+
return jsxs("div", {
|
|
1400
|
+
className: componentClassName,
|
|
1401
|
+
style: glassVars,
|
|
1402
|
+
role: role || (onClick ? "button" : void 0),
|
|
1403
|
+
tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
|
|
1404
|
+
"aria-label": ariaLabel,
|
|
1405
|
+
"aria-describedby": ariaDescribedBy,
|
|
1406
|
+
"aria-disabled": !(!onClick || !effectiveDisableEffects) || !onClick && void 0,
|
|
1407
|
+
"aria-pressed": !(!onClick || !isActive) || !onClick && void 0,
|
|
1408
|
+
onKeyDown: onClick ? handleKeyDown : void 0,
|
|
1409
|
+
children: [ jsx(AtomixGlassContainer, {
|
|
1410
|
+
ref: glassRef,
|
|
1411
|
+
contentRef: contentRef,
|
|
1412
|
+
className: className,
|
|
1413
|
+
style: baseStyle,
|
|
1414
|
+
cornerRadius: effectiveCornerRadius,
|
|
1415
|
+
displacementScale: effectiveDisableEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : overLightConfig.isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
|
|
1416
|
+
blurAmount: effectiveDisableEffects ? 0 : blurAmount,
|
|
1417
|
+
saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : overLightConfig.isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
|
|
1418
|
+
aberrationIntensity: effectiveDisableEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
|
|
1419
|
+
glassSize: glassSize,
|
|
1420
|
+
padding: padding,
|
|
1421
|
+
mouseOffset: effectiveDisableEffects ? {
|
|
1422
|
+
x: 0,
|
|
1423
|
+
y: 0
|
|
1424
|
+
} : mouseOffset,
|
|
1425
|
+
globalMousePosition: effectiveDisableEffects ? {
|
|
1426
|
+
x: 0,
|
|
1427
|
+
y: 0
|
|
1428
|
+
} : globalMousePosition,
|
|
1429
|
+
onMouseEnter: handleMouseEnter,
|
|
1430
|
+
onMouseLeave: handleMouseLeave,
|
|
1431
|
+
onMouseDown: handleMouseDown,
|
|
1432
|
+
onMouseUp: handleMouseUp,
|
|
1433
|
+
active: isActive,
|
|
1434
|
+
isHovered: isHovered,
|
|
1435
|
+
isActive: isActive,
|
|
1436
|
+
overLight: overLightConfig.isOverLight,
|
|
1437
|
+
onClick: onClick,
|
|
1438
|
+
mode: mode,
|
|
1439
|
+
transform: baseStyle.transform,
|
|
1440
|
+
effectiveDisableEffects: effectiveDisableEffects,
|
|
1441
|
+
effectiveReducedMotion: effectiveReducedMotion,
|
|
1442
|
+
shaderVariant: shaderVariant,
|
|
1443
|
+
elasticity: elasticity,
|
|
1444
|
+
enableLiquidBlur: enableLiquidBlur,
|
|
1445
|
+
children: children
|
|
1446
|
+
}), Boolean(onClick) && jsxs(Fragment, {
|
|
1447
|
+
children: [ jsx("div", {
|
|
1448
|
+
className: ATOMIX_GLASS.HOVER_1_CLASS
|
|
1449
|
+
}), jsx("div", {
|
|
1450
|
+
className: ATOMIX_GLASS.HOVER_2_CLASS
|
|
1451
|
+
}), jsx("div", {
|
|
1452
|
+
className: ATOMIX_GLASS.HOVER_3_CLASS
|
|
1453
|
+
}) ]
|
|
1454
|
+
}), jsx("div", {
|
|
1455
|
+
className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" "),
|
|
1456
|
+
style: {
|
|
1457
|
+
...positionStyles,
|
|
1458
|
+
height: adjustedSize.height,
|
|
1459
|
+
width: adjustedSize.width,
|
|
1460
|
+
borderRadius: `${effectiveCornerRadius}px`,
|
|
1461
|
+
transform: baseStyle.transform
|
|
1462
|
+
}
|
|
1463
|
+
}), jsx("div", {
|
|
1464
|
+
className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" "),
|
|
1465
|
+
style: {
|
|
1466
|
+
...positionStyles,
|
|
1467
|
+
height: adjustedSize.height,
|
|
1468
|
+
width: adjustedSize.width,
|
|
1469
|
+
borderRadius: `${effectiveCornerRadius}px`,
|
|
1470
|
+
transform: baseStyle.transform
|
|
1471
|
+
}
|
|
1472
|
+
}), shouldRenderOverLightLayers && jsxs(Fragment, {
|
|
1473
|
+
children: [ jsx("div", {
|
|
1474
|
+
className: ATOMIX_GLASS.BASE_LAYER_CLASS
|
|
1475
|
+
}), jsx("div", {
|
|
1476
|
+
className: ATOMIX_GLASS.OVERLAY_LAYER_CLASS
|
|
1477
|
+
}), jsx("div", {
|
|
1478
|
+
className: ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS,
|
|
1479
|
+
style: {
|
|
1480
|
+
opacity: opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
|
|
1481
|
+
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}%)`
|
|
1482
|
+
}
|
|
1483
|
+
}) ]
|
|
1484
|
+
}), enableBorderEffect && jsxs(Fragment, {
|
|
1485
|
+
children: [ jsx("span", {
|
|
1486
|
+
className: ATOMIX_GLASS.BORDER_1_CLASS
|
|
1487
|
+
}), jsx("span", {
|
|
1488
|
+
className: ATOMIX_GLASS.BORDER_2_CLASS
|
|
1489
|
+
}) ]
|
|
1490
|
+
}) ]
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* Checkbox - A component for checkbox inputs
|
|
1496
|
+
*/ const Checkbox = memo((({label: label, checked: checked = !1, onChange: onChange, className: className = "", style: style, disabled: disabled = !1, required: required = !1, id: id, name: name, value: value, invalid: invalid = !1, valid: valid = !1, indeterminate: indeterminate = !1, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, glass: glass}) => {
|
|
1497
|
+
const {generateCheckboxClass: generateCheckboxClass, checkboxRef: checkboxRef} = function(initialProps) {
|
|
1498
|
+
// Default checkbox properties
|
|
1499
|
+
const defaultProps = {
|
|
1500
|
+
disabled: !1,
|
|
1501
|
+
invalid: !1,
|
|
1502
|
+
valid: !1,
|
|
1503
|
+
indeterminate: !1,
|
|
1504
|
+
...initialProps
|
|
1505
|
+
}, checkboxRef = useRef(null);
|
|
1506
|
+
// Ref for the checkbox input element
|
|
1507
|
+
// Handle indeterminate state
|
|
1508
|
+
return useEffect((() => {
|
|
1509
|
+
checkboxRef.current && (checkboxRef.current.indeterminate = Boolean(defaultProps.indeterminate));
|
|
1510
|
+
}), [ defaultProps.indeterminate ]), {
|
|
1511
|
+
defaultProps: defaultProps,
|
|
1512
|
+
generateCheckboxClass: props => {
|
|
1513
|
+
const {disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, indeterminate: indeterminate = defaultProps.indeterminate, className: className = ""} = props;
|
|
1514
|
+
let validationClass = "";
|
|
1515
|
+
return invalid ? validationClass = "is-error" : valid && (validationClass = "is-valid"),
|
|
1516
|
+
`c-checkbox ${validationClass} ${disabled ? "is-disabled" : ""} ${indeterminate ? "c-checkbox--mixed" : ""} ${className}`.trim();
|
|
1517
|
+
},
|
|
1518
|
+
checkboxRef: checkboxRef
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Default theme colors for components
|
|
1523
|
+
*/
|
|
1524
|
+
/**
|
|
1525
|
+
* Form-specific constants
|
|
1526
|
+
*/ ({
|
|
1527
|
+
indeterminate: indeterminate,
|
|
1528
|
+
disabled: disabled,
|
|
1529
|
+
invalid: invalid,
|
|
1530
|
+
valid: valid
|
|
1531
|
+
}), checkboxClass = generateCheckboxClass({
|
|
1532
|
+
className: `${className} ${glass ? "c-checkbox--glass" : ""}`.trim(),
|
|
1533
|
+
disabled: disabled,
|
|
1534
|
+
invalid: invalid,
|
|
1535
|
+
valid: valid,
|
|
1536
|
+
indeterminate: indeterminate
|
|
1537
|
+
}), checkboxContent = jsxs("div", {
|
|
1538
|
+
className: checkboxClass,
|
|
1539
|
+
style: style,
|
|
1540
|
+
children: [ jsx("input", {
|
|
1541
|
+
ref: checkboxRef,
|
|
1542
|
+
type: "checkbox",
|
|
1543
|
+
className: "c-checkbox__input",
|
|
1544
|
+
checked: checked,
|
|
1545
|
+
onChange: onChange,
|
|
1546
|
+
disabled: disabled,
|
|
1547
|
+
required: required,
|
|
1548
|
+
id: id,
|
|
1549
|
+
name: name,
|
|
1550
|
+
value: value,
|
|
1551
|
+
"aria-label": label ? void 0 : ariaLabel,
|
|
1552
|
+
"aria-describedby": ariaDescribedBy,
|
|
1553
|
+
"aria-invalid": invalid
|
|
1554
|
+
}), label && jsx("label", {
|
|
1555
|
+
className: "c-checkbox__label",
|
|
1556
|
+
htmlFor: id,
|
|
1557
|
+
children: label
|
|
1558
|
+
}) ]
|
|
1559
|
+
});
|
|
1560
|
+
if (glass) {
|
|
1561
|
+
// Default glass settings for checkboxes
|
|
1562
|
+
const defaultGlassProps = {
|
|
1563
|
+
displacementScale: 40,
|
|
1564
|
+
blurAmount: 1,
|
|
1565
|
+
saturation: 160,
|
|
1566
|
+
aberrationIntensity: .3,
|
|
1567
|
+
cornerRadius: 6,
|
|
1568
|
+
mode: "shader"
|
|
1569
|
+
}, glassProps = !0 === glass ? defaultGlassProps : {
|
|
1570
|
+
...defaultGlassProps,
|
|
1571
|
+
...glass
|
|
1572
|
+
};
|
|
1573
|
+
return jsx(AtomixGlass, {
|
|
1574
|
+
...glassProps,
|
|
1575
|
+
children: checkboxContent
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
return checkboxContent;
|
|
1579
|
+
}));
|
|
1580
|
+
|
|
1581
|
+
Checkbox.displayName = "Checkbox";
|
|
1582
|
+
|
|
1583
|
+
/**
|
|
1584
|
+
* Form - A component for creating form layouts
|
|
1585
|
+
*/
|
|
1586
|
+
const Form = ({children: children, onSubmit: onSubmit, onReset: onReset, className: className = "", style: style, disabled: disabled = !1, id: id, method: method = "post", encType: encType, noValidate: noValidate = !1, autoComplete: autoComplete = "on"}) => {
|
|
1587
|
+
const {generateFormClass: generateFormClass, handleSubmit: handleSubmit, handleReset: handleReset} =
|
|
1588
|
+
/**
|
|
1589
|
+
* Form state and functionality
|
|
1590
|
+
* @param initialProps - Initial form properties
|
|
1591
|
+
* @returns Form state and methods
|
|
1592
|
+
*/
|
|
1593
|
+
function(initialProps) {
|
|
1594
|
+
// Default form properties
|
|
1595
|
+
const defaultProps = {
|
|
1596
|
+
disabled: !1,
|
|
1597
|
+
...initialProps
|
|
1598
|
+
};
|
|
1599
|
+
/**
|
|
1600
|
+
* Generate form class based on properties
|
|
1601
|
+
* @param props - Form properties
|
|
1602
|
+
* @returns Class string
|
|
1603
|
+
*/ return {
|
|
1604
|
+
defaultProps: defaultProps,
|
|
1605
|
+
generateFormClass: props => {
|
|
1606
|
+
const {disabled: disabled = defaultProps.disabled, className: className = ""} = props;
|
|
1607
|
+
return `${FORM_CLASSES_BASE} ${disabled ? FORM_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
1608
|
+
},
|
|
1609
|
+
handleSubmit: handler => event => {
|
|
1610
|
+
event.preventDefault(), !defaultProps.disabled && handler && handler(event);
|
|
1611
|
+
},
|
|
1612
|
+
handleReset: handler => event => {
|
|
1613
|
+
!defaultProps.disabled && handler && handler(event);
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
}({
|
|
1617
|
+
disabled: disabled
|
|
1618
|
+
}), formClass = generateFormClass({
|
|
1619
|
+
className: className,
|
|
1620
|
+
disabled: disabled
|
|
1621
|
+
});
|
|
1622
|
+
return jsx("form", {
|
|
1623
|
+
id: id,
|
|
1624
|
+
className: formClass,
|
|
1625
|
+
style: style,
|
|
1626
|
+
onSubmit: handleSubmit(onSubmit),
|
|
1627
|
+
onReset: handleReset(onReset),
|
|
1628
|
+
method: method,
|
|
1629
|
+
encType: encType,
|
|
1630
|
+
noValidate: noValidate,
|
|
1631
|
+
autoComplete: autoComplete,
|
|
1632
|
+
children: children
|
|
1633
|
+
});
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
Form.displayName = "Form";
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* FormGroup - A component for grouping form controls with labels and help text
|
|
1640
|
+
*/
|
|
1641
|
+
const FormGroup = ({children: children, label: label, helperText: helperText, htmlFor: htmlFor, className: className = "", style: style, disabled: disabled = !1, required: required = !1, invalid: invalid = !1, valid: valid = !1, size: size = "md"}) => {
|
|
1642
|
+
const {generateFormGroupClass: generateFormGroupClass} =
|
|
1643
|
+
/**
|
|
1644
|
+
* Form Group state and functionality
|
|
1645
|
+
* @param initialProps - Initial form group properties
|
|
1646
|
+
* @returns Form Group state and methods
|
|
1647
|
+
*/
|
|
1648
|
+
function(initialProps) {
|
|
1649
|
+
// Default form group properties
|
|
1650
|
+
const defaultProps = {
|
|
1651
|
+
size: "md",
|
|
1652
|
+
disabled: !1,
|
|
1653
|
+
invalid: !1,
|
|
1654
|
+
valid: !1,
|
|
1655
|
+
...initialProps
|
|
1656
|
+
};
|
|
1657
|
+
/**
|
|
1658
|
+
* Generate form group class based on properties
|
|
1659
|
+
* @param props - Form group properties
|
|
1660
|
+
* @returns Class string
|
|
1661
|
+
*/ return {
|
|
1662
|
+
defaultProps: defaultProps,
|
|
1663
|
+
generateFormGroupClass: props => {
|
|
1664
|
+
const {size: size = defaultProps.size, disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, className: className = ""} = props;
|
|
1665
|
+
return `${FORM_GROUP_CLASSES_BASE} ${"md" === size ? "" : "sm" === size ? FORM_GROUP_CLASSES_SMALL : FORM_GROUP_CLASSES_LARGE} ${invalid ? FORM_GROUP_CLASSES_INVALID : valid ? FORM_GROUP_CLASSES_VALID : ""} ${disabled ? FORM_GROUP_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
}({
|
|
1669
|
+
size: size,
|
|
1670
|
+
disabled: disabled,
|
|
1671
|
+
invalid: invalid,
|
|
1672
|
+
valid: valid
|
|
1673
|
+
}), formGroupClass = generateFormGroupClass({
|
|
1674
|
+
className: className,
|
|
1675
|
+
disabled: disabled,
|
|
1676
|
+
invalid: invalid,
|
|
1677
|
+
valid: valid,
|
|
1678
|
+
size: size
|
|
1679
|
+
});
|
|
1680
|
+
return jsxs("div", {
|
|
1681
|
+
className: formGroupClass,
|
|
1682
|
+
style: style,
|
|
1683
|
+
children: [ label && jsxs("label", {
|
|
1684
|
+
className: "c-form-group__label",
|
|
1685
|
+
htmlFor: htmlFor,
|
|
1686
|
+
children: [ label, required && jsx("span", {
|
|
1687
|
+
className: "c-form-group__required",
|
|
1688
|
+
children: "*"
|
|
1689
|
+
}) ]
|
|
1690
|
+
}), jsx("div", {
|
|
1691
|
+
className: "c-form-group__field",
|
|
1692
|
+
children: children
|
|
1693
|
+
}), helperText && jsx("div", {
|
|
1694
|
+
className: "c-form-group__helper",
|
|
1695
|
+
children: helperText
|
|
1696
|
+
}) ]
|
|
1697
|
+
});
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
FormGroup.displayName = "FormGroup";
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* Input - A component for text input fields
|
|
1704
|
+
*/
|
|
1705
|
+
const Input = memo( forwardRef((({type: type = "text", value: value, onChange: onChange, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder, className: className = "", style: style, disabled: disabled = !1, required: required = !1, readOnly: readOnly = !1, id: id, name: name, autoComplete: autoComplete, autoFocus: autoFocus = !1, size: size = "md", variant: variant, invalid: invalid = !1, valid: valid = !1, maxLength: maxLength, minLength: minLength, pattern: pattern, min: min, max: max, step: step, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, glass: glass}, ref) => {
|
|
1706
|
+
const {generateInputClass: generateInputClass} =
|
|
1707
|
+
/**
|
|
1708
|
+
* Input state and functionality
|
|
1709
|
+
* @param initialProps - Initial input properties
|
|
1710
|
+
* @returns Input state and methods
|
|
1711
|
+
*/
|
|
1712
|
+
function(initialProps) {
|
|
1713
|
+
// Default input properties
|
|
1714
|
+
const defaultProps = {
|
|
1715
|
+
size: "md",
|
|
1716
|
+
disabled: !1,
|
|
1717
|
+
invalid: !1,
|
|
1718
|
+
valid: !1,
|
|
1719
|
+
...initialProps
|
|
1720
|
+
};
|
|
1721
|
+
/**
|
|
1722
|
+
* Generate input class based on properties
|
|
1723
|
+
* @param props - Input properties
|
|
1724
|
+
* @returns Class string
|
|
1725
|
+
*/ return {
|
|
1726
|
+
defaultProps: defaultProps,
|
|
1727
|
+
generateInputClass: props => {
|
|
1728
|
+
const {size: size = defaultProps.size, variant: variant = defaultProps.variant, disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, className: className = "", type: type} = props;
|
|
1729
|
+
let validationClass = "";
|
|
1730
|
+
invalid ? validationClass = INPUT_CLASSES_INVALID : valid && (validationClass = INPUT_CLASSES_VALID);
|
|
1731
|
+
return `${INPUT_CLASSES_BASE} ${"md" === size ? "" : "sm" === size ? INPUT_CLASSES_SMALL : INPUT_CLASSES_LARGE} ${variant ? `c-input--${variant}` : ""} ${"textarea" === type ? "c-input--textarea" : ""} ${validationClass} ${disabled ? INPUT_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
1732
|
+
},
|
|
1733
|
+
generateWrapperClass: props => {
|
|
1734
|
+
const {className: className = ""} = props, {prefixIcon: prefixIcon = !1, suffixIcon: suffixIcon = !1, clearable: clearable = !1, showCounter: showCounter = !1, showPasswordToggle: showPasswordToggle = !1, fullWidth: fullWidth = !1} = initialProps || {}, classes = [ INPUT_ELEMENTS_WRAPPER ];
|
|
1735
|
+
return prefixIcon && classes.push(INPUT_CLASSES_PREFIX_ICON), (suffixIcon || clearable || showPasswordToggle) && classes.push(INPUT_CLASSES_SUFFIX_ICON),
|
|
1736
|
+
clearable && classes.push(INPUT_CLASSES_CLEARABLE), showCounter && classes.push(INPUT_CLASSES_WITH_COUNTER),
|
|
1737
|
+
showPasswordToggle && classes.push(INPUT_CLASSES_PASSWORD_TOGGLE), fullWidth && classes.push(INPUT_CLASSES_FULL_WIDTH),
|
|
1738
|
+
className && classes.push(className), classes.filter(Boolean).join(" ");
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
}({
|
|
1742
|
+
size: size,
|
|
1743
|
+
variant: variant,
|
|
1744
|
+
disabled: disabled,
|
|
1745
|
+
invalid: invalid,
|
|
1746
|
+
valid: valid
|
|
1747
|
+
}), inputClass = generateInputClass({
|
|
1748
|
+
className: `${className} ${glass ? "c-input--glass" : ""}`.trim(),
|
|
1749
|
+
size: size,
|
|
1750
|
+
variant: variant,
|
|
1751
|
+
disabled: disabled,
|
|
1752
|
+
invalid: invalid,
|
|
1753
|
+
valid: valid,
|
|
1754
|
+
type: type
|
|
1755
|
+
}), inputElement = jsx("input", {
|
|
1756
|
+
ref: ref,
|
|
1757
|
+
type: type,
|
|
1758
|
+
className: inputClass,
|
|
1759
|
+
value: value,
|
|
1760
|
+
onChange: onChange,
|
|
1761
|
+
onBlur: onBlur,
|
|
1762
|
+
onFocus: onFocus,
|
|
1763
|
+
placeholder: placeholder,
|
|
1764
|
+
disabled: disabled,
|
|
1765
|
+
required: required,
|
|
1766
|
+
readOnly: readOnly,
|
|
1767
|
+
id: id,
|
|
1768
|
+
name: name,
|
|
1769
|
+
autoComplete: autoComplete,
|
|
1770
|
+
autoFocus: autoFocus,
|
|
1771
|
+
maxLength: maxLength,
|
|
1772
|
+
minLength: minLength,
|
|
1773
|
+
pattern: pattern,
|
|
1774
|
+
min: min,
|
|
1775
|
+
max: max,
|
|
1776
|
+
step: step,
|
|
1777
|
+
"aria-label": ariaLabel,
|
|
1778
|
+
"aria-describedby": ariaDescribedBy,
|
|
1779
|
+
"aria-invalid": invalid,
|
|
1780
|
+
style: glass ? {
|
|
1781
|
+
...style
|
|
1782
|
+
} : style
|
|
1783
|
+
});
|
|
1784
|
+
if (glass) {
|
|
1785
|
+
// Default glass settings for inputs
|
|
1786
|
+
const defaultGlassProps = {
|
|
1787
|
+
displacementScale: 60,
|
|
1788
|
+
blurAmount: 1,
|
|
1789
|
+
saturation: 180,
|
|
1790
|
+
aberrationIntensity: .2,
|
|
1791
|
+
cornerRadius: 12,
|
|
1792
|
+
mode: "shader"
|
|
1793
|
+
}, glassProps = !0 === glass ? defaultGlassProps : {
|
|
1794
|
+
...defaultGlassProps,
|
|
1795
|
+
...glass
|
|
1796
|
+
};
|
|
1797
|
+
return jsx(AtomixGlass, {
|
|
1798
|
+
...glassProps,
|
|
1799
|
+
children: inputElement
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
return inputElement;
|
|
1803
|
+
})));
|
|
1804
|
+
|
|
1805
|
+
Input.displayName = "Input";
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* Radio - A component for radio button inputs
|
|
1809
|
+
*/
|
|
1810
|
+
const Radio = memo((({label: label, checked: checked = !1, onChange: onChange, className: className = "", style: style, disabled: disabled = !1, required: required = !1, id: id, name: name, value: value, invalid: invalid = !1, valid: valid = !1, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, glass: glass}) => {
|
|
1811
|
+
const {generateRadioClass: generateRadioClass} =
|
|
1812
|
+
/**
|
|
1813
|
+
* Radio state and functionality
|
|
1814
|
+
* @param initialProps - Initial radio properties
|
|
1815
|
+
* @returns Radio state and methods
|
|
1816
|
+
*/
|
|
1817
|
+
function(initialProps) {
|
|
1818
|
+
// Default radio properties
|
|
1819
|
+
const defaultProps = {
|
|
1820
|
+
disabled: !1,
|
|
1821
|
+
invalid: !1,
|
|
1822
|
+
valid: !1,
|
|
1823
|
+
...initialProps
|
|
1824
|
+
};
|
|
1825
|
+
/**
|
|
1826
|
+
* Generate radio class based on properties
|
|
1827
|
+
* @param props - Radio properties
|
|
1828
|
+
* @returns Class string
|
|
1829
|
+
*/ return {
|
|
1830
|
+
defaultProps: defaultProps,
|
|
1831
|
+
generateRadioClass: props => {
|
|
1832
|
+
const {disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, className: className = ""} = props;
|
|
1833
|
+
let validationClass = "";
|
|
1834
|
+
invalid ? validationClass = RADIO_CLASSES_INVALID : valid && (validationClass = RADIO_CLASSES_VALID);
|
|
1835
|
+
return `${RADIO_CLASSES_BASE} ${validationClass} ${disabled ? RADIO_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
}({
|
|
1839
|
+
disabled: disabled,
|
|
1840
|
+
invalid: invalid,
|
|
1841
|
+
valid: valid
|
|
1842
|
+
}), radioClass = generateRadioClass({
|
|
1843
|
+
className: `${className} ${glass ? "c-radio--glass" : ""}`.trim(),
|
|
1844
|
+
disabled: disabled,
|
|
1845
|
+
invalid: invalid,
|
|
1846
|
+
valid: valid
|
|
1847
|
+
}), radioContent = jsxs("div", {
|
|
1848
|
+
className: radioClass,
|
|
1849
|
+
style: style,
|
|
1850
|
+
children: [ jsx("input", {
|
|
1851
|
+
type: "radio",
|
|
1852
|
+
className: "c-radio__input",
|
|
1853
|
+
checked: checked,
|
|
1854
|
+
onChange: onChange,
|
|
1855
|
+
disabled: disabled,
|
|
1856
|
+
required: required,
|
|
1857
|
+
id: id,
|
|
1858
|
+
name: name,
|
|
1859
|
+
value: value,
|
|
1860
|
+
"aria-label": label ? void 0 : ariaLabel,
|
|
1861
|
+
"aria-describedby": ariaDescribedBy,
|
|
1862
|
+
"aria-invalid": invalid
|
|
1863
|
+
}), label && jsx("label", {
|
|
1864
|
+
className: "c-radio__label",
|
|
1865
|
+
htmlFor: id,
|
|
1866
|
+
children: label
|
|
1867
|
+
}) ]
|
|
1868
|
+
});
|
|
1869
|
+
if (glass) {
|
|
1870
|
+
// Default glass settings for radio buttons
|
|
1871
|
+
const defaultGlassProps = {
|
|
1872
|
+
displacementScale: 40,
|
|
1873
|
+
blurAmount: 1,
|
|
1874
|
+
saturation: 160,
|
|
1875
|
+
aberrationIntensity: .3,
|
|
1876
|
+
cornerRadius: 6,
|
|
1877
|
+
mode: "shader"
|
|
1878
|
+
}, glassProps = !0 === glass ? defaultGlassProps : {
|
|
1879
|
+
...defaultGlassProps,
|
|
1880
|
+
...glass
|
|
1881
|
+
};
|
|
1882
|
+
return jsx(AtomixGlass, {
|
|
1883
|
+
...glassProps,
|
|
1884
|
+
children: radioContent
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
return radioContent;
|
|
1888
|
+
}));
|
|
1889
|
+
|
|
1890
|
+
Radio.displayName = "Radio";
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* Select - A component for dropdown selection
|
|
1894
|
+
*/
|
|
1895
|
+
const Select = memo((({options: options = [], value: value, onChange: onChange, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder = "Select an option", className: className = "", style: style, disabled: disabled = !1, required: required = !1, id: id, name: name, size: size = "md", invalid: invalid = !1, valid: valid = !1, multiple: multiple = !1, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, glass: glass}) => {
|
|
1896
|
+
const {generateSelectClass: generateSelectClass} =
|
|
1897
|
+
/**
|
|
1898
|
+
* Select state and functionality
|
|
1899
|
+
* @param initialProps - Initial select properties
|
|
1900
|
+
* @returns Select state and methods
|
|
1901
|
+
*/
|
|
1902
|
+
function(initialProps) {
|
|
1903
|
+
// Default select properties
|
|
1904
|
+
const defaultProps = {
|
|
1905
|
+
size: "md",
|
|
1906
|
+
disabled: !1,
|
|
1907
|
+
invalid: !1,
|
|
1908
|
+
valid: !1,
|
|
1909
|
+
...initialProps
|
|
1910
|
+
};
|
|
1911
|
+
/**
|
|
1912
|
+
* Generate select class based on properties
|
|
1913
|
+
* @param props - Select properties
|
|
1914
|
+
* @returns Class string
|
|
1915
|
+
*/ return {
|
|
1916
|
+
defaultProps: defaultProps,
|
|
1917
|
+
generateSelectClass: props => {
|
|
1918
|
+
const {size: size = defaultProps.size, disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, className: className = ""} = props;
|
|
1919
|
+
let validationClass = "";
|
|
1920
|
+
invalid ? validationClass = SELECT_CLASSES_INVALID : valid && (validationClass = SELECT_CLASSES_VALID);
|
|
1921
|
+
return `${SELECT_CLASSES_BASE} ${"md" === size ? "" : "sm" === size ? SELECT_CLASSES_SMALL : SELECT_CLASSES_LARGE} ${validationClass} ${disabled ? SELECT_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Textarea state and functionality
|
|
1927
|
+
* @param initialProps - Initial textarea properties
|
|
1928
|
+
* @returns Textarea state and methods
|
|
1929
|
+
*/ ({
|
|
1930
|
+
size: size,
|
|
1931
|
+
disabled: disabled,
|
|
1932
|
+
invalid: invalid,
|
|
1933
|
+
valid: valid
|
|
1934
|
+
}), selectClass = generateSelectClass({
|
|
1935
|
+
className: `${className} ${glass ? "c-select--glass" : ""}`.trim(),
|
|
1936
|
+
size: size,
|
|
1937
|
+
disabled: disabled,
|
|
1938
|
+
invalid: invalid,
|
|
1939
|
+
valid: valid
|
|
1940
|
+
}), [isOpen, setIsOpen] = useState(!1), [selectedLabel, setSelectedLabel] = useState(placeholder), dropdownRef = useRef(null), panelRef = useRef(null), bodyRef = useRef(null), nativeSelectRef = useRef(null);
|
|
1941
|
+
// Update selected label when value changes
|
|
1942
|
+
useEffect((() => {
|
|
1943
|
+
if (value) {
|
|
1944
|
+
const selectedOption = options.find((opt => opt.value === value));
|
|
1945
|
+
selectedOption && setSelectedLabel(selectedOption.label);
|
|
1946
|
+
} else setSelectedLabel(placeholder);
|
|
1947
|
+
}), [ value, options, placeholder ]),
|
|
1948
|
+
// Handle click outside to close dropdown
|
|
1949
|
+
useEffect((() => {
|
|
1950
|
+
const handleClickOutside = event => {
|
|
1951
|
+
dropdownRef.current && !dropdownRef.current.contains(event.target) && (setIsOpen(!1),
|
|
1952
|
+
bodyRef.current && (bodyRef.current.style.height = "0px"));
|
|
1953
|
+
};
|
|
1954
|
+
return document.addEventListener("mousedown", handleClickOutside), () => {
|
|
1955
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1956
|
+
};
|
|
1957
|
+
}), []);
|
|
1958
|
+
// Toggle dropdown
|
|
1959
|
+
const selectContent = jsxs("div", {
|
|
1960
|
+
className: `${selectClass} ${isOpen ? SELECT_CLASSES_IS_OPEN : ""}`,
|
|
1961
|
+
ref: dropdownRef,
|
|
1962
|
+
style: style,
|
|
1963
|
+
"aria-expanded": isOpen,
|
|
1964
|
+
children: [ jsxs("select", {
|
|
1965
|
+
ref: nativeSelectRef,
|
|
1966
|
+
value: value,
|
|
1967
|
+
onChange: onChange,
|
|
1968
|
+
onBlur: onBlur,
|
|
1969
|
+
onFocus: onFocus,
|
|
1970
|
+
disabled: disabled,
|
|
1971
|
+
required: required,
|
|
1972
|
+
id: id,
|
|
1973
|
+
name: name,
|
|
1974
|
+
multiple: multiple,
|
|
1975
|
+
"aria-label": ariaLabel,
|
|
1976
|
+
"aria-describedby": ariaDescribedBy,
|
|
1977
|
+
"aria-invalid": invalid,
|
|
1978
|
+
style: {
|
|
1979
|
+
display: "none"
|
|
1980
|
+
},
|
|
1981
|
+
children: [ placeholder && jsx("option", {
|
|
1982
|
+
value: "",
|
|
1983
|
+
disabled: !0,
|
|
1984
|
+
children: placeholder
|
|
1985
|
+
}), options.map((option => jsx("option", {
|
|
1986
|
+
value: option.value,
|
|
1987
|
+
disabled: option.disabled,
|
|
1988
|
+
children: option.label
|
|
1989
|
+
}, option.value))) ]
|
|
1990
|
+
}), jsx("div", {
|
|
1991
|
+
className: SELECT_CLASSES_SELECTED,
|
|
1992
|
+
onClick: () => {
|
|
1993
|
+
disabled || (!isOpen && bodyRef.current && panelRef.current ? bodyRef.current.style.height = `${panelRef.current.clientHeight}px` : bodyRef.current && (bodyRef.current.style.height = "0px"),
|
|
1994
|
+
setIsOpen(!isOpen));
|
|
1995
|
+
},
|
|
1996
|
+
"aria-disabled": disabled,
|
|
1997
|
+
children: selectedLabel
|
|
1998
|
+
}), jsx("i", {
|
|
1999
|
+
className: `${SELECT_CLASSES_ICON_CARET} ${SELECT_CLASSES_TOGGLE_ICON}`
|
|
2000
|
+
}), jsx("div", {
|
|
2001
|
+
className: SELECT_CLASSES_SELECT_BODY,
|
|
2002
|
+
ref: bodyRef,
|
|
2003
|
+
style: {
|
|
2004
|
+
height: 0
|
|
2005
|
+
},
|
|
2006
|
+
children: jsx("div", {
|
|
2007
|
+
className: SELECT_CLASSES_SELECT_PANEL,
|
|
2008
|
+
ref: panelRef,
|
|
2009
|
+
children: jsx("ul", {
|
|
2010
|
+
className: SELECT_CLASSES_SELECT_ITEMS,
|
|
2011
|
+
children: options.map(((option, index) => jsx("li", {
|
|
2012
|
+
className: SELECT_CLASSES_SELECT_ITEM,
|
|
2013
|
+
"data-value": option.value,
|
|
2014
|
+
onClick: () => !option.disabled && (option => {
|
|
2015
|
+
if (setSelectedLabel(option.label), setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"),
|
|
2016
|
+
nativeSelectRef.current && (nativeSelectRef.current.value = option.value), onChange) {
|
|
2017
|
+
// Create a synthetic event
|
|
2018
|
+
const event = {
|
|
2019
|
+
target: {
|
|
2020
|
+
name: name,
|
|
2021
|
+
value: option.value
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
onChange(event);
|
|
2025
|
+
}
|
|
2026
|
+
})(option),
|
|
2027
|
+
children: jsxs("label", {
|
|
2028
|
+
htmlFor: `SelectItem${index}`,
|
|
2029
|
+
className: "c-checkbox",
|
|
2030
|
+
children: [ jsx("input", {
|
|
2031
|
+
type: "checkbox",
|
|
2032
|
+
id: `SelectItem${index}`,
|
|
2033
|
+
className: "c-checkbox__input c-select__item-input",
|
|
2034
|
+
checked: value === option.value,
|
|
2035
|
+
readOnly: !0,
|
|
2036
|
+
disabled: option.disabled
|
|
2037
|
+
}), jsx("div", {
|
|
2038
|
+
className: "c-select__item-label",
|
|
2039
|
+
children: option.label
|
|
2040
|
+
}) ]
|
|
2041
|
+
})
|
|
2042
|
+
}, option.value)))
|
|
2043
|
+
})
|
|
2044
|
+
})
|
|
2045
|
+
}) ]
|
|
2046
|
+
});
|
|
2047
|
+
// Handle item selection
|
|
2048
|
+
if (glass) {
|
|
2049
|
+
// Default glass settings for select components
|
|
2050
|
+
const defaultGlassProps = {
|
|
2051
|
+
displacementScale: 60,
|
|
2052
|
+
blurAmount: 1,
|
|
2053
|
+
saturation: 180,
|
|
2054
|
+
aberrationIntensity: .2,
|
|
2055
|
+
cornerRadius: 12,
|
|
2056
|
+
mode: "shader"
|
|
2057
|
+
}, glassProps = !0 === glass ? defaultGlassProps : {
|
|
2058
|
+
...defaultGlassProps,
|
|
2059
|
+
...glass
|
|
2060
|
+
};
|
|
2061
|
+
return jsx(AtomixGlass, {
|
|
2062
|
+
...glassProps,
|
|
2063
|
+
children: selectContent
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
return selectContent;
|
|
2067
|
+
}));
|
|
2068
|
+
|
|
2069
|
+
Select.displayName = "Select";
|
|
2070
|
+
|
|
2071
|
+
/**
|
|
2072
|
+
* Textarea - A component for multiline text input
|
|
2073
|
+
*/
|
|
2074
|
+
const Textarea = memo( forwardRef((({value: value, onChange: onChange, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder, className: className = "", style: style, disabled: disabled = !1, required: required = !1, readOnly: readOnly = !1, id: id, name: name, rows: rows = 4, cols: cols, maxLength: maxLength, minLength: minLength, size: size = "md", variant: variant, invalid: invalid = !1, valid: valid = !1, autoFocus: autoFocus = !1, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, glass: glass}, ref) => {
|
|
2075
|
+
const {generateTextareaClass: generateTextareaClass} = function(initialProps) {
|
|
2076
|
+
// Default textarea properties
|
|
2077
|
+
const defaultProps = {
|
|
2078
|
+
size: "md",
|
|
2079
|
+
disabled: !1,
|
|
2080
|
+
invalid: !1,
|
|
2081
|
+
valid: !1,
|
|
2082
|
+
...initialProps
|
|
2083
|
+
};
|
|
2084
|
+
/**
|
|
2085
|
+
* Generate textarea class based on properties
|
|
2086
|
+
* @param props - Textarea properties
|
|
2087
|
+
* @returns Class string
|
|
2088
|
+
*/ return {
|
|
2089
|
+
defaultProps: defaultProps,
|
|
2090
|
+
generateTextareaClass: props => {
|
|
2091
|
+
const {size: size = defaultProps.size, variant: variant = defaultProps.variant, disabled: disabled = defaultProps.disabled, invalid: invalid = defaultProps.invalid, valid: valid = defaultProps.valid, className: className = ""} = props;
|
|
2092
|
+
let validationClass = "";
|
|
2093
|
+
invalid ? validationClass = TEXTAREA_CLASSES_INVALID : valid && (validationClass = TEXTAREA_CLASSES_VALID);
|
|
2094
|
+
return `${TEXTAREA_CLASSES_BASE} ${"md" === size ? "" : "sm" === size ? TEXTAREA_CLASSES_SMALL : TEXTAREA_CLASSES_LARGE} ${variant ? `c-input--${variant}` : ""} ${validationClass} ${disabled ? TEXTAREA_CLASSES_DISABLED : ""} ${className}`.trim();
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
}({
|
|
2098
|
+
size: size,
|
|
2099
|
+
variant: variant,
|
|
2100
|
+
disabled: disabled,
|
|
2101
|
+
invalid: invalid,
|
|
2102
|
+
valid: valid
|
|
2103
|
+
}), textareaClass = generateTextareaClass({
|
|
2104
|
+
className: `${className} ${glass ? "c-input--glass" : ""}`.trim(),
|
|
2105
|
+
size: size,
|
|
2106
|
+
variant: variant,
|
|
2107
|
+
disabled: disabled,
|
|
2108
|
+
invalid: invalid,
|
|
2109
|
+
valid: valid
|
|
2110
|
+
}), textareaElement = jsx("textarea", {
|
|
2111
|
+
ref: ref,
|
|
2112
|
+
className: textareaClass,
|
|
2113
|
+
value: value,
|
|
2114
|
+
onChange: onChange,
|
|
2115
|
+
onBlur: onBlur,
|
|
2116
|
+
onFocus: onFocus,
|
|
2117
|
+
placeholder: placeholder,
|
|
2118
|
+
disabled: disabled,
|
|
2119
|
+
required: required,
|
|
2120
|
+
readOnly: readOnly,
|
|
2121
|
+
id: id,
|
|
2122
|
+
name: name,
|
|
2123
|
+
rows: rows,
|
|
2124
|
+
cols: cols,
|
|
2125
|
+
maxLength: maxLength,
|
|
2126
|
+
minLength: minLength,
|
|
2127
|
+
autoFocus: autoFocus,
|
|
2128
|
+
"aria-label": ariaLabel,
|
|
2129
|
+
"aria-describedby": ariaDescribedBy,
|
|
2130
|
+
"aria-invalid": invalid,
|
|
2131
|
+
style: glass ? {
|
|
2132
|
+
...style
|
|
2133
|
+
} : style
|
|
2134
|
+
});
|
|
2135
|
+
if (glass) {
|
|
2136
|
+
// Default glass settings for textareas
|
|
2137
|
+
const defaultGlassProps = {
|
|
2138
|
+
displacementScale: 60,
|
|
2139
|
+
blurAmount: 1,
|
|
2140
|
+
saturation: 180,
|
|
2141
|
+
aberrationIntensity: 1,
|
|
2142
|
+
cornerRadius: 8,
|
|
2143
|
+
mode: "shader"
|
|
2144
|
+
}, glassProps = !0 === glass ? defaultGlassProps : {
|
|
2145
|
+
...defaultGlassProps,
|
|
2146
|
+
...glass
|
|
2147
|
+
};
|
|
2148
|
+
return jsx(AtomixGlass, {
|
|
2149
|
+
...glassProps,
|
|
2150
|
+
children: textareaElement
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
return textareaElement;
|
|
2154
|
+
})));
|
|
2155
|
+
|
|
2156
|
+
Textarea.displayName = "Textarea";
|
|
2157
|
+
|
|
2158
|
+
// Adapted from https://github.com/shuding/liquid-glass
|
|
2159
|
+
// Constants
|
|
2160
|
+
const smoothStep = (a, b, t) => {
|
|
2161
|
+
// Add input validation
|
|
2162
|
+
if ("number" != typeof a || "number" != typeof b || "number" != typeof t) return 0;
|
|
2163
|
+
const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
|
|
2164
|
+
return clamped * clamped * (3 - 2 * clamped);
|
|
2165
|
+
}, calculateLength = (x, y) => {
|
|
2166
|
+
// Add input validation and error handling
|
|
2167
|
+
if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
|
|
2168
|
+
// Prevent potential overflow
|
|
2169
|
+
const maxX = Math.max(Math.abs(x), Math.abs(y));
|
|
2170
|
+
if (0 === maxX) return 0;
|
|
2171
|
+
const scaledX = x / maxX, scaledY = y / maxX;
|
|
2172
|
+
return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
|
|
2173
|
+
}, roundedRectSDF = (x, y, width, height, radius) => {
|
|
2174
|
+
// Add input validation
|
|
2175
|
+
if ("number" != typeof x || "number" != typeof y || "number" != typeof width || "number" != typeof height || "number" != typeof radius) return 0;
|
|
2176
|
+
const qx = Math.abs(x) - width + radius, qy = Math.abs(y) - height + radius;
|
|
2177
|
+
return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
|
|
2178
|
+
}, createTexture = (x, y) => ({
|
|
2179
|
+
x: "number" != typeof x || isNaN(x) ? .5 : Math.max(0, Math.min(1, x)),
|
|
2180
|
+
y: "number" != typeof y || isNaN(y) ? .5 : Math.max(0, Math.min(1, y))
|
|
2181
|
+
}), validateVec2 = vec => vec && "number" == typeof vec.x && "number" == typeof vec.y && !isNaN(vec.x) && !isNaN(vec.y), clampValue = (value, min, max) =>
|
|
2182
|
+
// Add input validation
|
|
2183
|
+
"number" != typeof value || "number" != typeof min || "number" != typeof max || isNaN(value) ? min : isNaN(min) ? 0 : isNaN(max) ? 1 : Math.max(min, Math.min(max, value)), easeInOutCubic = t => {
|
|
2184
|
+
// Add input validation
|
|
2185
|
+
if ("number" != typeof t || isNaN(t)) return 0;
|
|
2186
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
2187
|
+
return clampedT < .5 ? 4 * clampedT * clampedT * clampedT : 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
|
|
2188
|
+
}, easeOutQuart = t => {
|
|
2189
|
+
// Add input validation
|
|
2190
|
+
if ("number" != typeof t || isNaN(t)) return 0;
|
|
2191
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
2192
|
+
return 1 - Math.pow(1 - clampedT, 4);
|
|
2193
|
+
}, noise2D = (x, y) => {
|
|
2194
|
+
// Add input validation
|
|
2195
|
+
if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
|
|
2196
|
+
const X = 255 & Math.floor(x), Y = 255 & Math.floor(y), xf = x - Math.floor(x), yf = y - Math.floor(y), u = easeInOutCubic(xf), v = easeInOutCubic(yf), hash = (i, j) => {
|
|
2197
|
+
// Add input validation
|
|
2198
|
+
if ("number" != typeof i || "number" != typeof j) return 0;
|
|
2199
|
+
const n = i + 57 * j, hashed = 43758.5453 * Math.sin(12.9898 * n + 78.233);
|
|
2200
|
+
// Use a more stable hash function
|
|
2201
|
+
return hashed - Math.floor(hashed);
|
|
2202
|
+
}, a = hash(X, Y), b = hash(X + 1, Y), c = hash(X, Y + 1), x1 = a + u * (b - a);
|
|
2203
|
+
return x1 + v * (c + u * (hash(X + 1, Y + 1) - c) - x1);
|
|
2204
|
+
}, fbm = (x, y, octaves = 4) => {
|
|
2205
|
+
// Add input validation
|
|
2206
|
+
if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
|
|
2207
|
+
// Clamp octaves to prevent performance issues
|
|
2208
|
+
const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
|
|
2209
|
+
let value = 0, amplitude = .5, frequency = 1;
|
|
2210
|
+
for (let i = 0; i < clampedOctaves; i++) value += amplitude * noise2D(x * frequency, y * frequency),
|
|
2211
|
+
frequency *= 2, amplitude *= .5;
|
|
2212
|
+
return value;
|
|
2213
|
+
}, calculateParallaxOffset = (x, y, depth, mouseX = 0, mouseY = 0) => {
|
|
2214
|
+
// Add input validation
|
|
2215
|
+
if ("number" != typeof x || "number" != typeof y || "number" != typeof depth || "number" != typeof mouseX || "number" != typeof mouseY || isNaN(x) || isNaN(y) || isNaN(depth) || isNaN(mouseX) || isNaN(mouseY)) return {
|
|
2216
|
+
x: 0,
|
|
2217
|
+
y: 0
|
|
2218
|
+
};
|
|
2219
|
+
const parallaxStrength = Math.min(.02 * depth, .1);
|
|
2220
|
+
// Limit strength to prevent extreme values
|
|
2221
|
+
// Calculate offset based on view angle (simulated by mouse position)
|
|
2222
|
+
return {
|
|
2223
|
+
x: (x - mouseX) * parallaxStrength,
|
|
2224
|
+
y: (y - mouseY) * parallaxStrength
|
|
2225
|
+
};
|
|
2226
|
+
}, fragmentShaders = {
|
|
2227
|
+
liquidGlass: (uv, mousePosition) => {
|
|
2228
|
+
if (!validateVec2(uv)) return {
|
|
2229
|
+
x: .5,
|
|
2230
|
+
y: .5
|
|
2231
|
+
};
|
|
2232
|
+
const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now(), mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(2 * mouseDistance, 1)), organicFlow = fbm(12 * (ix + .5 * mouseX) + time, 12 * (iy + .5 * mouseY) + .7 * time, 3) - .5, distanceToEdge = roundedRectSDF(ix, iy, .4, .3, .35), baseDisplacement = smoothStep(.8, 0, distanceToEdge - .05), radialDist = ((x, y, strength) => {
|
|
2233
|
+
// Add input validation
|
|
2234
|
+
if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y) || isNaN(strength)) return {
|
|
2235
|
+
x: 0,
|
|
2236
|
+
y: 0
|
|
2237
|
+
};
|
|
2238
|
+
const distance = calculateLength(x, y), distortion = Math.pow(Math.min(distance, 10), 2) * strength;
|
|
2239
|
+
// Limit distance to prevent extreme values
|
|
2240
|
+
return {
|
|
2241
|
+
x: x * (1 + distortion),
|
|
2242
|
+
y: y * (1 + distortion)
|
|
2243
|
+
};
|
|
2244
|
+
})(ix, iy, .4 * .1), refractionX = 1.2 * (radialDist.x - ix) * baseDisplacement, refractionY = 1.2 * (radialDist.y - iy) * baseDisplacement, flowX = .018 * Math.sin(8 * (ix + 2 * mouseX) + 2 * time), flowY = .018 * Math.cos(8 * (iy + 2 * mouseY) + 1.5 * time), rippleEffect = (.015 * Math.sin(12 * (ix - mouseX) + 12 * (iy - mouseY) + 3 * time) + .012 * Math.cos(10 * (ix + mouseX) - 10 * (iy - mouseY) - 2 * time)) * mouseFalloff * mouseDistance, depthEffect = (Math.sin(15 * ix + time) * Math.cos(15 * iy - time) * .008 + Math.sin(20 * ix - .5 * time) * Math.cos(20 * iy + .5 * time) * .006) * baseDisplacement, liquidFlow = .85 * (flowX + flowY + .025 * organicFlow), totalDistortionX = refractionX + liquidFlow + rippleEffect + depthEffect, totalDistortionY = refractionY + .8 * liquidFlow + .9 * rippleEffect + depthEffect, chromaticOffset = ((x, y, intensity) => {
|
|
2245
|
+
// Add input validation
|
|
2246
|
+
if ("number" != typeof x || "number" != typeof y || "number" != typeof intensity || isNaN(x) || isNaN(y) || isNaN(intensity)) return {
|
|
2247
|
+
x: 0,
|
|
2248
|
+
y: 0
|
|
2249
|
+
};
|
|
2250
|
+
const distance = calculateLength(x, y);
|
|
2251
|
+
// Prevent division by zero and extreme values
|
|
2252
|
+
if (0 === distance) return {
|
|
2253
|
+
x: 0,
|
|
2254
|
+
y: 0
|
|
2255
|
+
};
|
|
2256
|
+
const angle = Math.atan2(y, x);
|
|
2257
|
+
return {
|
|
2258
|
+
x: Math.cos(angle) * distance * intensity,
|
|
2259
|
+
y: Math.sin(angle) * distance * intensity
|
|
2260
|
+
};
|
|
2261
|
+
})(ix, iy, .015 * baseDisplacement), scaled = smoothStep(0, 1, 1.15 * baseDisplacement), finalX = ix + totalDistortionX + .5 * chromaticOffset.x, finalY = iy + totalDistortionY + .5 * chromaticOffset.y;
|
|
2262
|
+
return createTexture(clampValue(finalX * scaled + .5, 0, 1), clampValue(finalY * scaled + .5, 0, 1));
|
|
2263
|
+
},
|
|
2264
|
+
// Premium Apple-style fluid glass with enhanced organic flow
|
|
2265
|
+
appleFluid: (uv, mousePosition) => {
|
|
2266
|
+
if (!validateVec2(uv)) return {
|
|
2267
|
+
x: .5,
|
|
2268
|
+
y: .5
|
|
2269
|
+
};
|
|
2270
|
+
const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .6, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.5 * mouseDistance, 1)), organicX = fbm(10 * (ix + .3 * mouseX) + time, 10 * (iy + .3 * mouseY), 5) - .5, organicY = fbm(10 * (ix - .3 * mouseX), 10 * (iy - .3 * mouseY) + .8 * time, 5) - .5, distanceToEdge = roundedRectSDF(ix, iy, .42, .32, .38), mask = smoothStep(.85, -.1, distanceToEdge), fluidVelocityX = Math.sin(6 * ix + 2 * time) * Math.cos(4 * iy - time) * .025, fluidVelocityY = Math.cos(4 * ix - time) * Math.sin(6 * iy + 2 * time) * .025, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexStrength = mouseFalloff * mouseDistance * .08, vortexX = Math.cos(vortexAngle + time) * vortexStrength, totalY = iy + (.035 * organicY + fluidVelocityY + Math.sin(vortexAngle + time) * vortexStrength) * mask;
|
|
2271
|
+
return createTexture(clampValue(ix + (.035 * organicX + fluidVelocityX + vortexX) * mask + .5, 0, 1), clampValue(totalY + .5, 0, 1));
|
|
2272
|
+
},
|
|
2273
|
+
// High-end glass with advanced refraction and depth
|
|
2274
|
+
premiumGlass: (uv, mousePosition) => {
|
|
2275
|
+
if (!validateVec2(uv)) return {
|
|
2276
|
+
x: .5,
|
|
2277
|
+
y: .5
|
|
2278
|
+
};
|
|
2279
|
+
const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .4, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), centerDistance = calculateLength(ix, iy), refractionStrength = .3 * Math.pow(Math.min(centerDistance, 1), 1.5), refractionAngle = Math.atan2(iy, ix);
|
|
2280
|
+
// Multi-layer depth effect
|
|
2281
|
+
let depthX = 0, depthY = 0;
|
|
2282
|
+
for (let layer = 0; layer < 3; layer++) {
|
|
2283
|
+
const layerScale = 5 * (layer + 1), layerTime = time * (1 + .3 * layer), layerStrength = .01 / (layer + 1);
|
|
2284
|
+
depthX += Math.sin(ix * layerScale + layerTime) * layerStrength, depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
|
|
2285
|
+
}
|
|
2286
|
+
// Glass refraction with mouse influence
|
|
2287
|
+
const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), organicNoise = fbm(8 * ix + time, 8 * iy - time, 2) - .5, distanceToEdge = roundedRectSDF(ix, iy, .43, .33, .36), edgeMask = smoothStep(.9, -.05, distanceToEdge), finalY = iy + (refractionY + depthY + .015 * organicNoise) * edgeMask;
|
|
2288
|
+
return createTexture(clampValue(ix + (refractionX + depthX + .015 * organicNoise) * edgeMask + .5, 0, 1), clampValue(finalY + .5, 0, 1));
|
|
2289
|
+
},
|
|
2290
|
+
// Metallic liquid effect with shimmer
|
|
2291
|
+
liquidMetal: (uv, mousePosition) => {
|
|
2292
|
+
if (!validateVec2(uv)) return {
|
|
2293
|
+
x: .5,
|
|
2294
|
+
y: .5
|
|
2295
|
+
};
|
|
2296
|
+
const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * 1.2, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, wave1 = Math.sin(20 * ix + 4 * time) * Math.cos(15 * iy - 3 * time) * .02, wave2 = Math.cos(15 * ix - 2 * time) * Math.sin(20 * iy + 5 * time) * .015, shimmer = .025 * fbm(25 * ix + 2 * time, 25 * iy - 2 * time, 4), flowAngle = Math.atan2(iy - mouseY, ix - mouseX), flowDistance = calculateLength(ix - mouseX, iy - mouseY), flowEffect = .02 * Math.sin(15 * flowDistance - 6 * time) * easeOutQuart(1 - Math.min(2 * flowDistance, 1)), distanceToEdge = roundedRectSDF(ix, iy, .41, .31, .37), mask = smoothStep(.88, -.08, distanceToEdge), totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask, totalY = iy + (wave2 + .8 * shimmer + Math.sin(flowAngle) * flowEffect) * mask;
|
|
2297
|
+
return createTexture(clampValue(totalX + .5, 0, 1), clampValue(totalY + .5, 0, 1));
|
|
2298
|
+
},
|
|
2299
|
+
// basiBasi - Expert Premium Glass Shader
|
|
2300
|
+
// The most advanced shader with caustics, spectral dispersion, parallax depth, and volumetric effects
|
|
2301
|
+
basiBasi: (uv, mousePosition) => {
|
|
2302
|
+
if (!validateVec2(uv)) return {
|
|
2303
|
+
x: .5,
|
|
2304
|
+
y: .5
|
|
2305
|
+
};
|
|
2306
|
+
const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .5, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.2 * mouseDistance, 1)), causticIntensity = ((x, y, time, intensity = 1) =>
|
|
2307
|
+
// Add input validation
|
|
2308
|
+
"number" != typeof x || "number" != typeof y || "number" != typeof time || "number" != typeof intensity || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(intensity) ? .5 : .5 * (Math.sin(8 * x + 2 * time) * Math.cos(8 * y - 2 * time) * .5 + Math.sin(8 * (x + .5) * 1.3 - 2 * time * .8) * Math.cos(8 * (y - .3) * 1.3 + 2 * time * .8) * .3 + Math.sin(8 * (x - .3) * .7 + 2 * time * 1.2) * Math.cos(8 * (y + .4) * .7 - 2 * time * 1.2) * .2 + 1) * intensity)(ix, iy, time, .8), causticDistortion = .02 * (causticIntensity - .5), refractionAngle = Math.atan2(iy, ix), spectralDispersion = ((x, y, angle) => {
|
|
2309
|
+
// Add input validation
|
|
2310
|
+
if ("number" != typeof x || "number" != typeof y || "number" != typeof angle || isNaN(x) || isNaN(y) || isNaN(angle) || isNaN(.025)) return {
|
|
2311
|
+
r: {
|
|
2312
|
+
x: 0,
|
|
2313
|
+
y: 0
|
|
2314
|
+
},
|
|
2315
|
+
g: {
|
|
2316
|
+
x: 0,
|
|
2317
|
+
y: 0
|
|
2318
|
+
},
|
|
2319
|
+
b: {
|
|
2320
|
+
x: 0,
|
|
2321
|
+
y: 0
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
const distance = calculateLength(x, y), dispersionStrength = Math.min(.025 * distance, 1), redOffset = .8 * dispersionStrength, greenOffset = 1 * dispersionStrength, blueOffset = 1.2 * dispersionStrength;
|
|
2325
|
+
return {
|
|
2326
|
+
r: {
|
|
2327
|
+
x: Math.cos(angle) * redOffset,
|
|
2328
|
+
y: Math.sin(angle) * redOffset
|
|
2329
|
+
},
|
|
2330
|
+
g: {
|
|
2331
|
+
x: Math.cos(angle) * greenOffset,
|
|
2332
|
+
y: Math.sin(angle) * greenOffset
|
|
2333
|
+
},
|
|
2334
|
+
b: {
|
|
2335
|
+
x: Math.cos(angle) * blueOffset,
|
|
2336
|
+
y: Math.sin(angle) * blueOffset
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
})(ix, iy, refractionAngle), spectralX = (spectralDispersion.r.x + spectralDispersion.g.x + spectralDispersion.b.x) / 3, spectralY = (spectralDispersion.r.y + spectralDispersion.g.y + spectralDispersion.b.y) / 3;
|
|
2340
|
+
// === MULTI-LAYER PARALLAX DEPTH ===
|
|
2341
|
+
// Create depth perception with 7 layers
|
|
2342
|
+
let parallaxX = 0, parallaxY = 0;
|
|
2343
|
+
for (let layer = 0; layer < 7; layer++) {
|
|
2344
|
+
const parallaxOffset = calculateParallaxOffset(ix, iy, (layer + 1) / 7, mouseX, mouseY), layerNoise = fbm((ix + parallaxOffset.x) * (8 + 2 * layer) + time * (.5 + .1 * layer), (iy + parallaxOffset.y) * (8 + 2 * layer) - time * (.5 + .1 * layer), 3) - .5, layerWeight = 1 / (layer + 1);
|
|
2345
|
+
parallaxX += (parallaxOffset.x + .01 * layerNoise) * layerWeight, parallaxY += (parallaxOffset.y + .01 * layerNoise) * layerWeight;
|
|
2346
|
+
}
|
|
2347
|
+
// Normalize parallax effect
|
|
2348
|
+
parallaxX /= 7, parallaxY /= 7;
|
|
2349
|
+
// === VOLUMETRIC SCATTERING ===
|
|
2350
|
+
// Simulate light scattering through glass volume
|
|
2351
|
+
const volumetricDensity = ((x, y, depth, time) =>
|
|
2352
|
+
// Add input validation
|
|
2353
|
+
"number" != typeof x || "number" != typeof y || "number" != typeof time || isNaN(x) || isNaN(y) || isNaN(.5) || isNaN(time) ? .5 : fbm(5 * x + .5 * time, 5 * y - .5 * time, 3) * Math.exp(2 * -Math.max(0, .5)) * .5 + .5)(ix, iy, 0, time), scatteringX = Math.cos(refractionAngle) * volumetricDensity * .015, scatteringY = Math.sin(refractionAngle) * volumetricDensity * .015, turbulence = ((x, y, time, octaves = 5) => {
|
|
2354
|
+
// Add input validation
|
|
2355
|
+
if ("number" != typeof x || "number" != typeof y || "number" != typeof time || "number" != typeof octaves || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(octaves)) return 0;
|
|
2356
|
+
// Clamp octaves to prevent performance issues
|
|
2357
|
+
const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
|
|
2358
|
+
let turbulence = 0, amplitude = 1, frequency = 1;
|
|
2359
|
+
for (let i = 0; i < clampedOctaves; i++) turbulence += Math.abs(noise2D(x * frequency + time, y * frequency - time)) * amplitude,
|
|
2360
|
+
frequency *= 2, amplitude *= .5;
|
|
2361
|
+
return turbulence;
|
|
2362
|
+
})(6 * ix, 6 * iy, time, 6), turbulenceX = .012 * Math.cos(turbulence * Math.PI * 2), turbulenceY = .012 * Math.sin(turbulence * Math.PI * 2), microSurface = ((x, y, time) =>
|
|
2363
|
+
// Add input validation
|
|
2364
|
+
"number" != typeof x || "number" != typeof y || "number" != typeof time || isNaN(x) || isNaN(y) || isNaN(time) ? .5 : .5 * (.7 * fbm(40 * x + .3 * time, 40 * y - .3 * time, 6) + .3 * fbm(80 * x, 80 * y, 4)))(ix, iy, time), microDetailX = .008 * (microSurface - .5), microDetailY = .008 * (microSurface - .5), centerDistance = calculateLength(ix, iy), dynamicRefraction = .35 * Math.pow(Math.min(centerDistance, 1), 1.8) * (1 + mouseFalloff * mouseDistance * .8), refractionX = Math.cos(refractionAngle) * dynamicRefraction, refractionY = Math.sin(refractionAngle) * dynamicRefraction, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexDistance = calculateLength(ix - mouseX, iy - mouseY), vortexStrength = mouseFalloff * Math.sin(10 * vortexDistance - 3 * time) * .025, vortexX = Math.cos(vortexAngle + 2 * time) * vortexStrength, vortexY = Math.sin(vortexAngle + 2 * time) * vortexStrength, fluidX = Math.sin(10 * ix + 5 * mouseX + 2.5 * time) * Math.cos(8 * iy - 2 * time) * .018, fluidY = Math.cos(8 * ix - 2 * time) * Math.sin(10 * iy + 5 * mouseY + 2.5 * time) * .018, rippleEffect = (.012 * Math.sin(15 * Math.min(centerDistance, 10) - 4 * time) + .008 * Math.cos(20 * Math.min(centerDistance, 10) + 3 * time)) * mouseFalloff, rippleX = Math.cos(refractionAngle) * rippleEffect, rippleY = Math.sin(refractionAngle) * rippleEffect, distanceToEdge = roundedRectSDF(ix, iy, .44, .34, .39), edgeMask = smoothStep(.92, -.12, distanceToEdge), edgeSoftness = smoothStep(.85, .1, distanceToEdge), finalY = iy + (1.2 * refractionY + .8 * spectralY + 1.5 * parallaxY + .9 * scatteringY + 1 * turbulenceY + .6 * microDetailY + 1.3 * vortexY + 1.1 * fluidY + .7 * rippleY + .8 * causticDistortion) * edgeMask * edgeSoftness * .85;
|
|
2365
|
+
return createTexture(clampValue(ix + (1.2 * refractionX + .8 * spectralX + 1.5 * parallaxX + .9 * scatteringX + 1 * turbulenceX + .6 * microDetailX + 1.3 * vortexX + 1.1 * fluidX + .7 * rippleX + causticDistortion) * edgeMask * edgeSoftness * .85 + .5, 0, 1), clampValue(finalY + .5, 0, 1));
|
|
2366
|
+
}
|
|
2367
|
+
}, shaderUtils = Object.freeze( Object.defineProperty({
|
|
2368
|
+
__proto__: null,
|
|
2369
|
+
ShaderDisplacementGenerator: class {
|
|
2370
|
+
constructor(options) {
|
|
2371
|
+
if (this.options = options, this.canvasDPI = 1, !this.validateOptions(options)) throw new Error("Invalid shader options provided");
|
|
2372
|
+
this.canvas = document.createElement("canvas"),
|
|
2373
|
+
// Enhanced validation for canvas dimensions
|
|
2374
|
+
this.canvas.width = Math.max(1, Math.min(4096, Math.round(options.width * this.canvasDPI || 256))),
|
|
2375
|
+
this.canvas.height = Math.max(1, Math.min(4096, Math.round(options.height * this.canvasDPI || 256))),
|
|
2376
|
+
this.canvas.style.display = "none";
|
|
2377
|
+
const context = this.canvas.getContext("2d");
|
|
2378
|
+
if (!context) throw new Error("AtomixGlass: Could not get 2D canvas context");
|
|
2379
|
+
this.context = context;
|
|
2380
|
+
}
|
|
2381
|
+
validateOptions(options) {
|
|
2382
|
+
try {
|
|
2383
|
+
return options && "number" == typeof options.width && options.width > 0 && options.width <= 4096 && "number" == typeof options.height && options.height > 0 && options.height <= 4096 && "function" == typeof options.fragment;
|
|
2384
|
+
} catch (e) {
|
|
2385
|
+
// Graceful error handling
|
|
2386
|
+
return !1;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
updateShader(mousePosition) {
|
|
2390
|
+
try {
|
|
2391
|
+
const w = this.options.width * this.canvasDPI, h = this.options.height * this.canvasDPI;
|
|
2392
|
+
let maxScale = 0;
|
|
2393
|
+
const rawValues = [];
|
|
2394
|
+
// Calculate displacement values with enhanced smoothing
|
|
2395
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
2396
|
+
const uv = {
|
|
2397
|
+
x: x / w,
|
|
2398
|
+
y: y / h
|
|
2399
|
+
}, pos = this.options.fragment(uv, mousePosition);
|
|
2400
|
+
let dx = pos.x * w - x, dy = pos.y * h - y;
|
|
2401
|
+
// Apply edge smoothing for Apple-like effect
|
|
2402
|
+
const edgeX = 2 * Math.min(x / w, (w - x) / w), edgeY = 2 * Math.min(y / h, (h - y) / h), edgeFactor = Math.min(edgeX, edgeY);
|
|
2403
|
+
dx *= smoothStep(0, .2, edgeFactor), dy *= smoothStep(0, .2, edgeFactor), maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy)),
|
|
2404
|
+
rawValues.push(dx, dy);
|
|
2405
|
+
}
|
|
2406
|
+
// Improved normalization to prevent artifacts while maintaining intensity
|
|
2407
|
+
maxScale = Math.max(maxScale, 1);
|
|
2408
|
+
// Create ImageData and fill it
|
|
2409
|
+
const imageData = this.context.createImageData(w, h), data = imageData.data;
|
|
2410
|
+
// Convert to image data with smoother normalization
|
|
2411
|
+
let rawIndex = 0;
|
|
2412
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
2413
|
+
const dx = rawValues[rawIndex++] || 0, dy = rawValues[rawIndex++] || 0, edgeDistance = Math.min(x, y, w - x - 1, h - y - 1), edgeFactor = Math.min(1, edgeDistance / 2), r = dx * edgeFactor / maxScale + .5, g = dy * edgeFactor / maxScale + .5, pixelIndex = 4 * (y * w + x);
|
|
2414
|
+
data[pixelIndex] = clampValue(255 * r, 0, 255), // Red channel (X displacement)
|
|
2415
|
+
data[pixelIndex + 1] = clampValue(255 * g, 0, 255), // Green channel (Y displacement)
|
|
2416
|
+
data[pixelIndex + 2] = clampValue(255 * g, 0, 255), // Blue channel (Y displacement for SVG filter compatibility)
|
|
2417
|
+
data[pixelIndex + 3] = 255;
|
|
2418
|
+
}
|
|
2419
|
+
return this.context.putImageData(imageData, 0, 0), this.canvas.toDataURL();
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
// Graceful fallback on error
|
|
2422
|
+
return console.warn("ShaderDisplacementGenerator: Error generating shader map, using fallback", error),
|
|
2423
|
+
"";
|
|
2424
|
+
// Return empty string as fallback
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
destroy() {
|
|
2428
|
+
try {
|
|
2429
|
+
// Clear canvas data to free memory
|
|
2430
|
+
this.context && this.context.clearRect(0, 0, this.canvas.width, this.canvas.height),
|
|
2431
|
+
// Reduce memory footprint by setting dimensions to 0
|
|
2432
|
+
this.canvas.width = 0, this.canvas.height = 0,
|
|
2433
|
+
// Remove from DOM
|
|
2434
|
+
this.canvas.remove();
|
|
2435
|
+
} catch (e) {
|
|
2436
|
+
// Silently handle cleanup errors
|
|
2437
|
+
console.warn("ShaderDisplacementGenerator: Error during cleanup", e);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
getScale() {
|
|
2441
|
+
return this.canvasDPI;
|
|
2442
|
+
}
|
|
2443
|
+
},
|
|
2444
|
+
fragmentShaders: fragmentShaders
|
|
2445
|
+
}, Symbol.toStringTag, {
|
|
2446
|
+
value: "Module"
|
|
2447
|
+
}));
|
|
2448
|
+
|
|
2449
|
+
export { Checkbox, Form, FormGroup, Input, Radio, Select, Textarea };
|
|
2450
|
+
//# sourceMappingURL=forms.js.map
|