@shohojdhara/atomix 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +101 -199
  2. package/atomix.config.ts +241 -0
  3. package/dist/atomix.css +269 -189
  4. package/dist/atomix.css.map +1 -0
  5. package/dist/atomix.min.css +15179 -11
  6. package/dist/atomix.min.css.map +1 -0
  7. package/dist/charts.d.ts +1929 -0
  8. package/dist/charts.js +6477 -0
  9. package/dist/charts.js.map +1 -0
  10. package/dist/core.d.ts +1289 -0
  11. package/dist/core.js +3373 -0
  12. package/dist/core.js.map +1 -0
  13. package/dist/forms.d.ts +1085 -0
  14. package/dist/forms.js +2466 -0
  15. package/dist/forms.js.map +1 -0
  16. package/dist/heavy.d.ts +636 -0
  17. package/dist/heavy.js +4566 -0
  18. package/dist/heavy.js.map +1 -0
  19. package/dist/index.d.ts +5171 -4792
  20. package/dist/index.esm.js +6098 -4563
  21. package/dist/index.esm.js.map +1 -1
  22. package/dist/index.js +6291 -4747
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.min.js.map +1 -1
  26. package/dist/layout.d.ts +300 -0
  27. package/dist/layout.js +336 -0
  28. package/dist/layout.js.map +1 -0
  29. package/dist/theme.d.ts +2122 -0
  30. package/dist/theme.js +6084 -0
  31. package/dist/theme.js.map +1 -0
  32. package/package.json +59 -27
  33. package/scripts/atomix-cli.js +544 -16
  34. package/scripts/cli/__tests__/cli-commands.test.js +204 -0
  35. package/scripts/cli/__tests__/utils.test.js +201 -0
  36. package/scripts/cli/__tests__/vitest.config.js +26 -0
  37. package/scripts/cli/interactive-init.js +1 -1
  38. package/scripts/cli/token-manager.js +32 -7
  39. package/scripts/cli/utils.js +347 -0
  40. package/src/components/Accordion/Accordion.stories.tsx +50 -17
  41. package/src/components/Accordion/Accordion.tsx +5 -54
  42. package/src/components/Accordion/index.ts +1 -1
  43. package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
  44. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
  45. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
  46. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
  47. package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
  48. package/src/components/Avatar/Avatar.stories.tsx +7 -0
  49. package/src/components/Avatar/Avatar.tsx +3 -3
  50. package/src/components/Badge/Badge.stories.tsx +91 -13
  51. package/src/components/Badge/Badge.tsx +3 -3
  52. package/src/components/Block/Block.stories.tsx +7 -23
  53. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
  54. package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
  55. package/src/components/Button/Button.stories.tsx +141 -22
  56. package/src/components/Button/ButtonGroup.stories.tsx +315 -0
  57. package/src/components/Button/ButtonGroup.tsx +67 -0
  58. package/src/components/Button/index.ts +2 -0
  59. package/src/components/Callout/Callout.stories.tsx +8 -6
  60. package/src/components/Card/Card.stories.tsx +82 -28
  61. package/src/components/Card/ElevationCard.tsx +1 -1
  62. package/src/components/Chart/AnimatedChart.tsx +19 -18
  63. package/src/components/Chart/AreaChart.tsx +5 -2
  64. package/src/components/Chart/BarChart.tsx +1 -1
  65. package/src/components/Chart/BubbleChart.tsx +6 -6
  66. package/src/components/Chart/CandlestickChart.tsx +0 -1
  67. package/src/components/Chart/Chart.stories.tsx +5 -7
  68. package/src/components/Chart/Chart.tsx +0 -16
  69. package/src/components/Chart/ChartRenderer.tsx +1 -1
  70. package/src/components/Chart/ChartToolbar.tsx +1 -0
  71. package/src/components/Chart/DonutChart.tsx +0 -1
  72. package/src/components/Chart/FunnelChart.tsx +1 -2
  73. package/src/components/Chart/GaugeChart.tsx +0 -1
  74. package/src/components/Chart/HeatmapChart.tsx +0 -1
  75. package/src/components/Chart/LineChart.tsx +0 -1
  76. package/src/components/Chart/MultiAxisChart.tsx +0 -1
  77. package/src/components/Chart/PieChart.tsx +0 -1
  78. package/src/components/Chart/RadarChart.tsx +19 -13
  79. package/src/components/Chart/ScatterChart.tsx +3 -4
  80. package/src/components/Chart/TreemapChart.tsx +2 -1
  81. package/src/components/Chart/WaterfallChart.tsx +0 -2
  82. package/src/components/Chart/types.ts +12 -2
  83. package/src/components/Chart/utils.ts +4 -3
  84. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
  85. package/src/components/DataTable/DataTable.stories.tsx +23 -16
  86. package/src/components/DataTable/DataTable.tsx +3 -3
  87. package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
  88. package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
  89. package/src/components/Dropdown/Dropdown.tsx +12 -9
  90. package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
  91. package/src/components/Footer/Footer.stories.tsx +8 -6
  92. package/src/components/Footer/FooterLink.tsx +9 -2
  93. package/src/components/Footer/FooterSection.tsx +3 -3
  94. package/src/components/Form/Checkbox.stories.tsx +7 -0
  95. package/src/components/Form/Checkbox.tsx +3 -3
  96. package/src/components/Form/Form.stories.tsx +7 -0
  97. package/src/components/Form/FormGroup.stories.tsx +9 -1
  98. package/src/components/Form/Input.stories.tsx +69 -16
  99. package/src/components/Form/Input.tsx +4 -2
  100. package/src/components/Form/Radio.stories.tsx +9 -1
  101. package/src/components/Form/Radio.tsx +3 -3
  102. package/src/components/Form/Select.stories.tsx +9 -1
  103. package/src/components/Form/Select.tsx +3 -3
  104. package/src/components/Form/Textarea.stories.tsx +10 -2
  105. package/src/components/Form/Textarea.tsx +4 -2
  106. package/src/components/Hero/Hero.stories.tsx +7 -0
  107. package/src/components/List/List.stories.tsx +10 -3
  108. package/src/components/List/List.tsx +3 -3
  109. package/src/components/List/ListGroup.tsx +3 -1
  110. package/src/components/Messages/Messages.stories.tsx +8 -7
  111. package/src/components/Modal/Modal.stories.tsx +17 -6
  112. package/src/components/Modal/Modal.tsx +3 -3
  113. package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
  114. package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
  115. package/src/components/Navigation/Menu/Menu.tsx +9 -3
  116. package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
  117. package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
  118. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
  119. package/src/components/Pagination/Pagination.stories.tsx +188 -111
  120. package/src/components/Pagination/Pagination.tsx +88 -7
  121. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
  122. package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
  123. package/src/components/Popover/Popover.stories.tsx +191 -115
  124. package/src/components/Popover/Popover.tsx +4 -4
  125. package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
  126. package/src/components/Progress/Progress.stories.tsx +79 -49
  127. package/src/components/Progress/Progress.tsx +6 -2
  128. package/src/components/Rating/Rating.stories.tsx +109 -84
  129. package/src/components/Rating/Rating.tsx +5 -2
  130. package/src/components/River/River.stories.tsx +194 -114
  131. package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
  132. package/src/components/Slider/Slider.stories.tsx +7 -0
  133. package/src/components/Slider/Slider.tsx +10 -9
  134. package/src/components/Spinner/Spinner.stories.tsx +15 -11
  135. package/src/components/Spinner/Spinner.tsx +3 -3
  136. package/src/components/Steps/Steps.stories.tsx +132 -98
  137. package/src/components/Tabs/Tabs.stories.tsx +163 -112
  138. package/src/components/Tabs/Tabs.tsx +3 -3
  139. package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
  140. package/src/components/Todo/Todo.stories.tsx +38 -12
  141. package/src/components/Toggle/Toggle.stories.tsx +61 -28
  142. package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
  143. package/src/components/Tooltip/Tooltip.tsx +3 -3
  144. package/src/components/Upload/Upload.stories.tsx +122 -84
  145. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
  146. package/src/components/index.ts +6 -2
  147. package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
  148. package/src/lib/composables/useAtomixGlass.ts +2 -3
  149. package/src/lib/composables/useChartPerformance.ts +102 -78
  150. package/src/lib/composables/useChartScale.ts +10 -0
  151. package/src/lib/composables/useHero.ts +9 -2
  152. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
  153. package/src/lib/composables/useNavbar.ts +0 -10
  154. package/src/lib/composables/useSideMenu.ts +1 -0
  155. package/src/lib/composables/useVideoPlayer.ts +3 -2
  156. package/src/lib/config/loader.ts +57 -14
  157. package/src/lib/constants/components.ts +10 -0
  158. package/src/lib/hooks/index.ts +0 -1
  159. package/src/lib/hooks/useComponentCustomization.ts +11 -15
  160. package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
  161. package/src/lib/patterns/index.ts +2 -2
  162. package/src/lib/patterns/slots.tsx +2 -2
  163. package/src/lib/theme/README.md +174 -0
  164. package/src/lib/theme/adapters/index.ts +31 -0
  165. package/src/lib/theme/adapters/themeAdapter.ts +287 -0
  166. package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
  167. package/src/lib/theme/config/configLoader.ts +254 -0
  168. package/src/lib/theme/config/loader.ts +37 -48
  169. package/src/lib/theme/config/types.ts +2 -2
  170. package/src/lib/theme/config/validator.ts +15 -91
  171. package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
  172. package/src/lib/theme/constants/index.ts +8 -0
  173. package/src/lib/theme/core/ThemeRegistry.ts +19 -6
  174. package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
  175. package/src/lib/theme/core/composeTheme.ts +155 -0
  176. package/src/lib/theme/core/createTheme.ts +94 -0
  177. package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
  178. package/src/lib/theme/core/index.ts +5 -19
  179. package/src/lib/theme/devtools/Comparator.tsx +346 -22
  180. package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
  181. package/src/lib/theme/devtools/Inspector.tsx +335 -51
  182. package/src/lib/theme/devtools/LiveEditor.tsx +489 -112
  183. package/src/lib/theme/devtools/Preview.tsx +471 -221
  184. package/src/lib/theme/{core → devtools}/ThemeValidator.ts +6 -3
  185. package/src/lib/theme/devtools/index.ts +14 -4
  186. package/src/lib/theme/devtools/useHistory.ts +130 -0
  187. package/src/lib/theme/errors/index.ts +12 -0
  188. package/src/lib/theme/generators/cssFile.ts +79 -0
  189. package/src/lib/theme/generators/generateCSS.ts +89 -0
  190. package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +4 -14
  191. package/src/lib/theme/generators/index.ts +19 -0
  192. package/src/lib/theme/i18n/rtl.ts +7 -7
  193. package/src/lib/theme/index.ts +120 -15
  194. package/src/lib/theme/runtime/ThemeApplicator.ts +53 -95
  195. package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
  196. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
  197. package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
  198. package/src/lib/theme/runtime/index.ts +1 -2
  199. package/src/lib/theme/runtime/useTheme.ts +1 -2
  200. package/src/lib/theme/test/testTheme.ts +385 -0
  201. package/src/lib/theme/tokens/index.ts +12 -0
  202. package/src/lib/theme/tokens/tokens.ts +721 -0
  203. package/src/lib/theme/types.ts +6 -42
  204. package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
  205. package/src/lib/theme/utils/index.ts +11 -0
  206. package/src/lib/theme/utils/injectCSS.ts +90 -0
  207. package/src/lib/theme/utils/themeHelpers.ts +78 -0
  208. package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
  209. package/src/lib/theme-tools.ts +8 -9
  210. package/src/lib/types/components.ts +93 -34
  211. package/src/lib/types/partProps.ts +0 -16
  212. package/src/lib/utils/componentUtils.ts +1 -1
  213. package/src/lib/utils/fontPreloader.ts +148 -0
  214. package/src/lib/utils/index.ts +11 -0
  215. package/src/lib/utils/memoryMonitor.ts +189 -0
  216. package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
  217. package/src/styles/01-settings/_settings.fonts.scss +2 -5
  218. package/src/styles/02-tools/_tools.button.scss +66 -79
  219. package/src/styles/06-components/_components.atomix-glass.scss +13 -3
  220. package/src/styles/06-components/_components.navbar.scss +0 -6
  221. package/src/styles/06-components/_components.pagination.scss +88 -0
  222. package/scripts/build-themes.js +0 -208
  223. package/scripts/sync-theme-config.js +0 -309
  224. package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
  225. package/src/lib/theme/composeTheme.ts +0 -370
  226. package/src/lib/theme/core/ThemeCache.ts +0 -283
  227. package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
  228. package/src/lib/theme/core/ThemeEngine.ts +0 -657
  229. package/src/lib/theme/createThemeFromConfig.ts +0 -132
  230. package/src/lib/theme/devtools/CLI.ts +0 -364
  231. package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
  232. package/src/lib/theme/runtime/ThemeManager.ts +0 -442
  233. package/src/styles/03-generic/_generated-root.css +0 -5
  234. package/src/themes/README.md +0 -442
  235. package/src/themes/themes.config.js +0 -35
  236. /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
  237. /package/src/lib/theme/{errors.ts → errors/errors.ts} +0 -0
package/dist/heavy.js ADDED
@@ -0,0 +1,4566 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+
3
+ import React, { memo, forwardRef, useId, useState, useRef, useEffect, useMemo, useCallback, useImperativeHandle, Children, isValidElement } from "react";
4
+
5
+ import * as PhosphorIcons from "@phosphor-icons/react";
6
+
7
+ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Download, Share, ArrowsIn, ArrowsOut } from "@phosphor-icons/react";
8
+
9
+ /**
10
+ * Default theme colors for components
11
+ */
12
+ /**
13
+ * Button-specific constants
14
+ */ const BUTTON = {
15
+ BASE_CLASS: "c-btn",
16
+ ICON_CLASS: "c-btn__icon",
17
+ LABEL_CLASS: "c-btn__label",
18
+ SPINNER_CLASS: "c-btn__spinner",
19
+ VARIANT_PREFIX: "c-btn--",
20
+ CLASSES: {
21
+ BASE: "c-btn",
22
+ LOADING: "c-btn--loading",
23
+ FULL_WIDTH: "c-btn--full-width",
24
+ BLOCK: "c-btn--block",
25
+ ACTIVE: "c-btn--active",
26
+ SELECTED: "c-btn--selected"
27
+ }
28
+ }, VIDEO_PLAYER_CLASSES_BASE = "c-video-player", VIDEO_PLAYER_CLASSES_VIDEO = "c-video-player__video", VIDEO_PLAYER_CLASSES_YOUTUBE = "c-video-player--youtube", VIDEO_PLAYER_CLASSES_LOADING = "c-video-player__loading", VIDEO_PLAYER_CLASSES_SPINNER = "c-video-player__spinner", VIDEO_PLAYER_CLASSES_CONTROLS = "c-video-player__controls", VIDEO_PLAYER_CLASSES_CONTROLS_VISIBLE = "c-video-player__controls--visible", VIDEO_PLAYER_CLASSES_PROGRESS_CONTAINER = "c-video-player__progress-container", VIDEO_PLAYER_CLASSES_PROGRESS_BAR = "c-video-player__progress-bar", VIDEO_PLAYER_CLASSES_PROGRESS_BUFFERED = "c-video-player__progress-buffered", VIDEO_PLAYER_CLASSES_PROGRESS_PLAYED = "c-video-player__progress-played", VIDEO_PLAYER_CLASSES_PROGRESS_THUMB = "c-video-player__progress-thumb", VIDEO_PLAYER_CLASSES_CONTROLS_ROW = "c-video-player__controls-row", VIDEO_PLAYER_CLASSES_CONTROLS_LEFT = "c-video-player__controls-left", VIDEO_PLAYER_CLASSES_CONTROLS_RIGHT = "c-video-player__controls-right", VIDEO_PLAYER_CLASSES_CONTROL_BUTTON = "c-video-player__control-button", VIDEO_PLAYER_CLASSES_VOLUME_CONTAINER = "c-video-player__volume-container", VIDEO_PLAYER_CLASSES_VOLUME_SLIDER = "c-video-player__volume-slider", VIDEO_PLAYER_CLASSES_VOLUME_BAR = "c-video-player__volume-bar", VIDEO_PLAYER_CLASSES_VOLUME_FILL = "c-video-player__volume-fill", VIDEO_PLAYER_CLASSES_TIME_DISPLAY = "c-video-player__time-display", VIDEO_PLAYER_CLASSES_SETTINGS_CONTAINER = "c-video-player__settings-container", VIDEO_PLAYER_CLASSES_SETTINGS_MENU = "c-video-player__settings-menu", VIDEO_PLAYER_CLASSES_SETTINGS_TABS = "c-video-player__settings-tabs", VIDEO_PLAYER_CLASSES_SETTINGS_TAB = "c-video-player__settings-tab", VIDEO_PLAYER_CLASSES_SETTINGS_TAB_ACTIVE = "c-video-player__settings-tab--active", VIDEO_PLAYER_CLASSES_SETTINGS_CONTENT = "c-video-player__settings-content", VIDEO_PLAYER_CLASSES_SETTINGS_OPTIONS = "c-video-player__settings-options", VIDEO_PLAYER_CLASSES_SETTINGS_OPTION = "c-video-player__settings-option", VIDEO_PLAYER_CLASSES_SETTINGS_OPTION_ACTIVE = "c-video-player__settings-option--active", VIDEO_PLAYER_CLASSES_AMBIENT = "c-video-player--ambient", VIDEO_PLAYER_CLASSES_AMBIENT_CANVAS = "c-video-player__ambient-canvas", VIDEO_PLAYER_CLASSES_GLASS = "c-video-player--glass", VIDEO_PLAYER_CLASSES_GLASS_OVERLAY = "c-video-player__glass-overlay", VIDEO_PLAYER_CLASSES_GLASS_CONTENT = "c-video-player__glass-content", ATOMIX_GLASS = {
29
+ BASE_CLASS: "c-atomix-glass",
30
+ CONTAINER_CLASS: "c-atomix-glass__container",
31
+ INNER_CLASS: "c-atomix-glass__inner",
32
+ FILTER_CLASS: "c-atomix-glass__filter",
33
+ FILTER_OVERLAY_CLASS: "c-atomix-glass__filter-overlay",
34
+ FILTER_SHADOW_CLASS: "c-atomix-glass__filter-shadow",
35
+ CONTENT_CLASS: "c-atomix-glass__content",
36
+ BORDER_1_CLASS: "c-atomix-glass__border-1",
37
+ BORDER_2_CLASS: "c-atomix-glass__border-2",
38
+ HOVER_1_CLASS: "c-atomix-glass__hover-1",
39
+ HOVER_2_CLASS: "c-atomix-glass__hover-2",
40
+ HOVER_3_CLASS: "c-atomix-glass__hover-3",
41
+ BASE_LAYER_CLASS: "c-atomix-glass__base",
42
+ OVERLAY_LAYER_CLASS: "c-atomix-glass__overlay",
43
+ OVERLAY_HIGHLIGHT_CLASS: "c-atomix-glass__overlay-highlight",
44
+ BACKGROUND_LAYER_CLASS: "c-atomix-glass__background-layer",
45
+ BACKGROUND_LAYER_DARK_CLASS: "c-atomix-glass__background-layer--dark",
46
+ BACKGROUND_LAYER_BLACK_CLASS: "c-atomix-glass__background-layer--black",
47
+ BACKGROUND_LAYER_OVER_LIGHT_CLASS: "c-atomix-glass__background-layer--over-light",
48
+ BACKGROUND_LAYER_HIDDEN_CLASS: "c-atomix-glass__background-layer--hidden",
49
+ VARIANT_PREFIX: "c-atomix-glass--",
50
+ MODE_PREFIX: "c-atomix-glass--",
51
+ CLASSES: {
52
+ BASE: "c-atomix-glass",
53
+ CONTAINER: "c-atomix-glass__container",
54
+ INNER: "c-atomix-glass__inner",
55
+ FILTER: "c-atomix-glass__filter",
56
+ CONTENT: "c-atomix-glass__content",
57
+ ACTIVE: "active",
58
+ OVER_LIGHT: "c-atomix-glass__container--over-light",
59
+ // Mode variants
60
+ STANDARD: "c-atomix-glass--standard",
61
+ POLAR: "c-atomix-glass--polar",
62
+ PROMINENT: "c-atomix-glass--prominent",
63
+ SHADER: "c-atomix-glass--shader"
64
+ },
65
+ DEFAULTS: {
66
+ DISPLACEMENT_SCALE: 20,
67
+ BLUR_AMOUNT: 1,
68
+ SATURATION: 140,
69
+ ABERRATION_INTENSITY: 2.5,
70
+ ELASTICITY: .05,
71
+ CORNER_RADIUS: 16,
72
+ // Default border-radius matching design system
73
+ PADDING: "0 0",
74
+ MODE: "standard",
75
+ OVER_LIGHT: !1,
76
+ ENABLE_OVER_LIGHT_LAYERS: !0
77
+ },
78
+ CONSTANTS: {
79
+ ACTIVATION_ZONE: 200,
80
+ MIN_BLUR: .1,
81
+ MOUSE_INFLUENCE_DIVISOR: 100,
82
+ EDGE_FADE_PIXELS: 2,
83
+ DEFAULT_CORNER_RADIUS: 16,
84
+ // Fallback value matching design system
85
+ MAX_SIZE: 4096,
86
+ // Maximum width/height for glass size
87
+ // Gradient calculation constants
88
+ GRADIENT: {
89
+ BASE_ANGLE: 135,
90
+ // Base angle for border gradients (degrees)
91
+ ANGLE_MULTIPLIER: 1.2,
92
+ // Multiplier for mouse influence on angle
93
+ BORDER_STOP_1: {
94
+ MIN: 10,
95
+ // Minimum percentage for border stop 1
96
+ BASE: 33,
97
+ // Base percentage for border stop 1
98
+ MULTIPLIER: .3
99
+ },
100
+ BORDER_STOP_2: {
101
+ MAX: 90,
102
+ // Maximum percentage for border stop 2
103
+ BASE: 66,
104
+ // Base percentage for border stop 2
105
+ MULTIPLIER: .4
106
+ },
107
+ BORDER_OPACITY: {
108
+ BASE_1: .12,
109
+ // Base opacity for border gradient 1
110
+ BASE_2: .4,
111
+ // Base opacity for border gradient 2
112
+ BASE_3: .32,
113
+ // Base opacity for border gradient 3
114
+ BASE_4: .6,
115
+ // Base opacity for border gradient 4
116
+ MULTIPLIER_LOW: .008,
117
+ // Low multiplier for mouse influence on opacity
118
+ MULTIPLIER_HIGH: .012
119
+ },
120
+ CENTER_POSITION: 50,
121
+ // Center position percentage (50%)
122
+ HOVER_POSITION: {
123
+ DIVISOR_1: 2,
124
+ // Divisor for hover 1 position calculation
125
+ DIVISOR_2: 1.5,
126
+ // Divisor for hover 2 position calculation
127
+ MULTIPLIER_3: 1
128
+ },
129
+ BASE_LAYER_MULTIPLIER: .5
130
+ },
131
+ // Gradient opacity values for hover effects
132
+ GRADIENT_OPACITY: {
133
+ HOVER_1: {
134
+ BLACK_START: .3,
135
+ // Start opacity for black hover 1
136
+ BLACK_MID: .1,
137
+ // Mid opacity for black hover 1
138
+ BLACK_STOP: 30,
139
+ // Stop percentage for black hover 1
140
+ BLACK_END: 60,
141
+ // End percentage for black hover 1
142
+ WHITE_START: .5,
143
+ // Start opacity for white hover 1
144
+ WHITE_STOP: 50
145
+ },
146
+ HOVER_2: {
147
+ BLACK_START: .4,
148
+ // Start opacity for black hover 2
149
+ BLACK_MID: .15,
150
+ // Mid opacity for black hover 2
151
+ BLACK_STOP: 40,
152
+ // Stop percentage for black hover 2
153
+ BLACK_END: 80,
154
+ // End percentage for black hover 2
155
+ WHITE_START: 1,
156
+ // Start opacity for white hover 2
157
+ WHITE_STOP: 80
158
+ },
159
+ HOVER_3: {
160
+ BLACK_START: .5,
161
+ // Start opacity for black hover 3
162
+ BLACK_MID: .2,
163
+ // Mid opacity for black hover 3
164
+ BLACK_STOP: 50,
165
+ // Stop percentage for black hover 3
166
+ BLACK_END: 100,
167
+ // End percentage for black hover 3
168
+ WHITE_START: 1,
169
+ // Start opacity for white hover 3
170
+ WHITE_STOP: 100
171
+ }
172
+ },
173
+ // Base and overlay gradient constants
174
+ BASE_GRADIENT: {
175
+ ANGLE: 135,
176
+ // Gradient angle in degrees
177
+ BLACK_START_BASE: .15,
178
+ // Base start opacity for black
179
+ BLACK_START_MULTIPLIER: .003,
180
+ // Multiplier for mouse X influence on start
181
+ BLACK_MID_BASE: .1,
182
+ // Base mid opacity for black
183
+ BLACK_MID_MULTIPLIER: .002,
184
+ // Multiplier for mouse Y influence on mid
185
+ BLACK_MID_STOP: 50,
186
+ // Mid stop percentage
187
+ BLACK_END_BASE: .18,
188
+ // Base end opacity for black
189
+ BLACK_END_MULTIPLIER: .004,
190
+ // Multiplier for mouse X influence on end
191
+ WHITE_OPACITY: .1
192
+ },
193
+ OVERLAY_GRADIENT: {
194
+ BLACK_START_BASE: .12,
195
+ // Base start opacity for black overlay
196
+ BLACK_START_MULTIPLIER: .003,
197
+ // Multiplier for mouse X influence on start
198
+ BLACK_MID: .06,
199
+ // Mid opacity for black overlay
200
+ BLACK_MID_STOP: 40,
201
+ // Mid stop percentage
202
+ BLACK_END_BASE: .15,
203
+ // Base end opacity for black overlay
204
+ BLACK_END_MULTIPLIER: .003,
205
+ // Multiplier for mouse Y influence on end
206
+ WHITE_OPACITY: .05
207
+ },
208
+ // Overlay highlight constants
209
+ OVERLAY_HIGHLIGHT: {
210
+ POSITION_X: 20,
211
+ // X position percentage
212
+ POSITION_Y: 20,
213
+ // Y position percentage
214
+ WHITE_OPACITY: .4,
215
+ // White opacity in gradient
216
+ STOP: 60,
217
+ // Stop percentage
218
+ OPACITY_MULTIPLIER: .7
219
+ },
220
+ // Displacement and aberration multipliers
221
+ MULTIPLIERS: {
222
+ SHADER_DISPLACEMENT: .8,
223
+ // Displacement scale multiplier for shader mode
224
+ OVER_LIGHT_DISPLACEMENT: .6,
225
+ // Displacement scale multiplier for over-light mode
226
+ SHADER_ABERRATION: .7
227
+ },
228
+ // Saturation constants
229
+ SATURATION: {
230
+ HIGH_CONTRAST: 200
231
+ }
232
+ }
233
+ }, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
234
+ if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
235
+ const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
236
+ return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
237
+ }, calculateElementCenter = rect => rect ? {
238
+ x: rect.left + rect.width / 2,
239
+ y: rect.top + rect.height / 2
240
+ } : {
241
+ x: 0,
242
+ y: 0
243
+ }, calculateMouseInfluence = mouseOffset => {
244
+ if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
245
+ // More responsive calculation for overlight effects
246
+ const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$1.MOUSE_INFLUENCE_DIVISOR;
247
+ return Math.min(1.5, influence);
248
+ // Cap influence for better control
249
+ }, 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 => {
250
+ if ("number" == typeof value) return Math.max(0, value);
251
+ if ("string" != typeof value || !value.trim()) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
252
+ const trimmedValue = value.trim();
253
+ // Handle px values
254
+ if (trimmedValue.endsWith("px")) {
255
+ const parsed = parseFloat(trimmedValue);
256
+ return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
257
+ }
258
+ // Handle rem values (assume 16px = 1rem)
259
+ if (trimmedValue.endsWith("rem")) {
260
+ const parsed = parseFloat(trimmedValue);
261
+ return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
262
+ }
263
+ // Handle em values (assume 16px = 1em for simplicity)
264
+ if (trimmedValue.endsWith("em")) {
265
+ const parsed = parseFloat(trimmedValue);
266
+ return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
267
+ }
268
+ // Handle percentage (convert to approximate px value, assuming 200px container)
269
+ if (trimmedValue.endsWith("%")) {
270
+ const parsed = parseFloat(trimmedValue);
271
+ return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
272
+ }
273
+ // Handle unitless numbers
274
+ const numValue = parseFloat(trimmedValue);
275
+ return isNaN(numValue) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
276
+ }, extractBorderRadiusFromElement = element => {
277
+ if (!element || !element.props) return null;
278
+ // Check inline styles first (highest priority)
279
+ if (element.props.style) {
280
+ const radiusFromStyle = (style => {
281
+ if (!style) return null;
282
+ // Check various border-radius properties
283
+ const borderRadius = style.borderRadius || style.borderTopLeftRadius || style.borderTopRightRadius || style.borderBottomLeftRadius || style.borderBottomRightRadius;
284
+ return void 0 !== borderRadius ? parseBorderRadiusValue(borderRadius) : null;
285
+ })(element.props.style);
286
+ if (null !== radiusFromStyle && radiusFromStyle > 0) return radiusFromStyle;
287
+ }
288
+ // If element has children, recursively check them
289
+ if (element.props.children) {
290
+ const childRadius = extractBorderRadiusFromChildren(element.props.children);
291
+ if (childRadius > 0 && childRadius !== CONSTANTS$1.DEFAULT_CORNER_RADIUS) return childRadius;
292
+ }
293
+ return null;
294
+ }, extractBorderRadiusFromChildren = children => {
295
+ if (!children) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
296
+ try {
297
+ const childArray = React.Children.toArray(children);
298
+ for (let i = 0; i < childArray.length; i++) {
299
+ const child = childArray[i];
300
+ if ( React.isValidElement(child)) {
301
+ const radius = extractBorderRadiusFromElement(child);
302
+ if (null !== radius) return radius;
303
+ }
304
+ }
305
+ } catch (error) {
306
+ // Silently handle errors
307
+ }
308
+ return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
309
+ }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
310
+ switch (mode) {
311
+ case "standard":
312
+ return displacementMap;
313
+
314
+ case "polar":
315
+ return polarDisplacementMap;
316
+
317
+ case "prominent":
318
+ return prominentDisplacementMap;
319
+
320
+ case "shader":
321
+ return shaderMapUrl || displacementMap;
322
+
323
+ default:
324
+ return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
325
+ }
326
+ }, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
327
+ style: {
328
+ position: "absolute",
329
+ width: "100%",
330
+ height: "100%",
331
+ inset: 0
332
+ },
333
+ "aria-hidden": "true",
334
+ suppressHydrationWarning: !0,
335
+ children: jsxs("defs", {
336
+ children: [ jsxs("radialGradient", {
337
+ id: `${id}-edge-mask`,
338
+ cx: "50%",
339
+ cy: "50%",
340
+ r: "50%",
341
+ children: [ jsx("stop", {
342
+ offset: "0%",
343
+ stopColor: "black",
344
+ stopOpacity: "0"
345
+ }), jsx("stop", {
346
+ offset: `${Math.max(30, 80 - 2 * aberrationIntensity)}%`,
347
+ stopColor: "black",
348
+ stopOpacity: "0"
349
+ }), jsx("stop", {
350
+ offset: "100%",
351
+ stopColor: "white",
352
+ stopOpacity: "1"
353
+ }) ]
354
+ }), jsxs("filter", {
355
+ id: id,
356
+ x: "-35%",
357
+ y: "-35%",
358
+ width: "170%",
359
+ height: "170%",
360
+ colorInterpolationFilters: "sRGB",
361
+ children: [ jsx("feImage", {
362
+ id: "feimage",
363
+ x: "0",
364
+ y: "0",
365
+ width: "100%",
366
+ height: "100%",
367
+ result: "DISPLACEMENT_MAP",
368
+ 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),
369
+ preserveAspectRatio: "xMidYMid slice"
370
+ }), jsx("feColorMatrix", {
371
+ in: "DISPLACEMENT_MAP",
372
+ type: "matrix",
373
+ 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",
374
+ result: "EDGE_INTENSITY"
375
+ }), jsx("feComponentTransfer", {
376
+ in: "EDGE_INTENSITY",
377
+ result: "EDGE_MASK",
378
+ children: jsx("feFuncA", {
379
+ type: "discrete",
380
+ tableValues: `0 ${.05 * aberrationIntensity} 1`
381
+ })
382
+ }), jsx("feOffset", {
383
+ in: "SourceGraphic",
384
+ dx: "0",
385
+ dy: "0",
386
+ result: "CENTER_ORIGINAL"
387
+ }), jsx("feDisplacementMap", {
388
+ in: "SourceGraphic",
389
+ in2: "DISPLACEMENT_MAP",
390
+ scale: displacementScale * ("shader" === mode ? 1 : -1),
391
+ xChannelSelector: "R",
392
+ yChannelSelector: "B",
393
+ result: "RED_DISPLACED"
394
+ }), jsx("feColorMatrix", {
395
+ in: "RED_DISPLACED",
396
+ type: "matrix",
397
+ values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
398
+ result: "RED_CHANNEL"
399
+ }), jsx("feDisplacementMap", {
400
+ in: "SourceGraphic",
401
+ in2: "DISPLACEMENT_MAP",
402
+ scale: displacementScale * (("shader" === mode ? 1 : -1) - .02 * aberrationIntensity),
403
+ xChannelSelector: "R",
404
+ yChannelSelector: "B",
405
+ result: "GREEN_DISPLACED"
406
+ }), jsx("feColorMatrix", {
407
+ in: "GREEN_DISPLACED",
408
+ type: "matrix",
409
+ values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
410
+ result: "GREEN_CHANNEL"
411
+ }), jsx("feDisplacementMap", {
412
+ in: "SourceGraphic",
413
+ in2: "DISPLACEMENT_MAP",
414
+ scale: displacementScale * (("shader" === mode ? 1 : -1) - .03 * aberrationIntensity),
415
+ xChannelSelector: "R",
416
+ yChannelSelector: "B",
417
+ result: "BLUE_DISPLACED"
418
+ }), jsx("feColorMatrix", {
419
+ in: "BLUE_DISPLACED",
420
+ type: "matrix",
421
+ values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0",
422
+ result: "BLUE_CHANNEL"
423
+ }), jsx("feBlend", {
424
+ in: "GREEN_CHANNEL",
425
+ in2: "BLUE_CHANNEL",
426
+ mode: "screen",
427
+ result: "GB_COMBINED"
428
+ }), jsx("feBlend", {
429
+ in: "RED_CHANNEL",
430
+ in2: "GB_COMBINED",
431
+ mode: "screen",
432
+ result: "RGB_COMBINED"
433
+ }), jsx("feGaussianBlur", {
434
+ in: "RGB_COMBINED",
435
+ result: "ABERRATED_BLURRED",
436
+ stdDeviation: blurAmount * aberrationIntensity * .05
437
+ }), jsx("feComposite", {
438
+ in: "ABERRATED_BLURRED",
439
+ in2: "EDGE_MASK",
440
+ operator: "in",
441
+ result: "EDGE_ABERRATION"
442
+ }), jsx("feComponentTransfer", {
443
+ in: "EDGE_MASK",
444
+ result: "INVERTED_MASK",
445
+ children: jsx("feFuncA", {
446
+ type: "table",
447
+ tableValues: "1 0"
448
+ })
449
+ }), jsx("feComposite", {
450
+ in: "CENTER_ORIGINAL",
451
+ in2: "INVERTED_MASK",
452
+ operator: "in",
453
+ result: "CENTER_CLEAN"
454
+ }), jsx("feComposite", {
455
+ in: "EDGE_ABERRATION",
456
+ in2: "CENTER_CLEAN",
457
+ operator: "over"
458
+ }) ]
459
+ }) ]
460
+ })
461
+ });
462
+
463
+ /**
464
+ * Badge-specific constants
465
+ */ GlassFilterComponent.displayName = "GlassFilter";
466
+
467
+ // Memoize component to prevent unnecessary re-renders
468
+ 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 = {
469
+ x: 0,
470
+ y: 0
471
+ }, globalMousePosition: globalMousePosition = {
472
+ x: 0,
473
+ y: 0
474
+ }, 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 = {
475
+ width: 0,
476
+ height: 0
477
+ }, onClick: onClick, mode: mode = "standard", effectiveDisableEffects: effectiveDisableEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", enableLiquidBlur: enableLiquidBlur = !1, elasticity: elasticity = 0, contentRef: contentRef}, ref) => {
478
+ // Generate a stable, deterministic ID for SSR compatibility
479
+ // React's useId() should produce the same ID on server and client for the same
480
+ // component position in the tree. We use useState to ensure the ID is only
481
+ // generated once and remains stable across renders.
482
+ const baseId = useId(), [filterId] = useState((() => `atomix-glass-filter-${baseId.replace(/:/g, "-").replace(/^[^a-z]/i, "atomix-")}`)), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null);
483
+ // Lazy load shader utilities only when shader mode is needed
484
+ useEffect((() => {
485
+ "shader" === mode ?
486
+ // Dynamic import shader utilities
487
+ Promise.resolve().then((() => shaderUtils)).then((shaderUtils => {
488
+ shaderUtilsRef.current = {
489
+ ShaderDisplacementGenerator: shaderUtils.ShaderDisplacementGenerator,
490
+ fragmentShaders: shaderUtils.fragmentShaders
491
+ };
492
+ })).catch((error => {
493
+ console.warn("AtomixGlassContainer: Error loading shader utilities", error);
494
+ })) :
495
+ // Clear shader utils when not in shader mode to free memory
496
+ shaderUtilsRef.current = null;
497
+ }), [ mode ]),
498
+ // Generate shader map with debouncing and caching
499
+ useEffect((() => {
500
+ // Enhanced validation for shader mode
501
+ if ("shader" === mode && glassSize && validateGlassSize(glassSize) && shaderUtilsRef.current) {
502
+ // Create cache key from size and variant
503
+ const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
504
+ const entry = sharedShaderCache.get(key);
505
+ return entry ? (
506
+ // Update access timestamp for LRU
507
+ entry.timestamp = Date.now(), entry.url) : null;
508
+ })(cacheKey);
509
+ // Check shared cache first
510
+ if (cachedUrl) return void setShaderMapUrl(cachedUrl);
511
+ // Clear any pending debounce
512
+ shaderDebounceTimeoutRef.current && clearTimeout(shaderDebounceTimeoutRef.current);
513
+ // Debounce shader generation to avoid blocking on rapid size changes
514
+ const generateShader = () => {
515
+ if (shaderUtilsRef.current) try {
516
+ const {ShaderDisplacementGenerator: ShaderDisplacementGenerator, fragmentShaders: fragmentShaders} = shaderUtilsRef.current;
517
+ shaderGeneratorRef.current?.destroy();
518
+ const selectedShader = fragmentShaders[shaderVariant] || fragmentShaders.liquidGlass;
519
+ shaderGeneratorRef.current = new ShaderDisplacementGenerator({
520
+ width: glassSize.width,
521
+ height: glassSize.height,
522
+ fragment: selectedShader
523
+ });
524
+ // Use requestIdleCallback if available for non-blocking generation
525
+ const generate = () => {
526
+ const url = shaderGeneratorRef.current?.updateShader() || "";
527
+ ((key, url) => {
528
+ // Evict oldest entries if at capacity
529
+ if (sharedShaderCache.size >= 15) {
530
+ const entries = Array.from(sharedShaderCache.entries());
531
+ // Sort by timestamp (oldest first)
532
+ entries.sort(((a, b) => a[1].timestamp - b[1].timestamp));
533
+ // Remove oldest entry
534
+ const oldestEntry = entries[0];
535
+ oldestEntry && sharedShaderCache.delete(oldestEntry[0]);
536
+ }
537
+ sharedShaderCache.set(key, {
538
+ url: url,
539
+ timestamp: Date.now()
540
+ }),
541
+ // Development mode: log cache size
542
+ "production" !== process.env.NODE_ENV && sharedShaderCache.size;
543
+ })(cacheKey, url), setShaderMapUrl(url);
544
+ };
545
+ "undefined" != typeof requestIdleCallback ? requestIdleCallback(generate, {
546
+ timeout: 1e3
547
+ }) :
548
+ // Fallback to setTimeout for browsers without requestIdleCallback
549
+ setTimeout(generate, 0);
550
+ } catch (error) {
551
+ console.warn("AtomixGlassContainer: Error generating shader map", error), setShaderMapUrl("");
552
+ } else
553
+ // Shader utils not loaded yet, retry after a short delay
554
+ shaderDebounceTimeoutRef.current = setTimeout(generateShader, 100);
555
+ };
556
+ // Debounce with 300ms delay
557
+ shaderDebounceTimeoutRef.current = setTimeout(generateShader, 300);
558
+ } else
559
+ // Not in shader mode, clear URL
560
+ setShaderMapUrl("");
561
+ // Cleanup function with error handling
562
+ return () => {
563
+ shaderDebounceTimeoutRef.current && (clearTimeout(shaderDebounceTimeoutRef.current),
564
+ shaderDebounceTimeoutRef.current = null);
565
+ try {
566
+ shaderGeneratorRef.current?.destroy();
567
+ } catch (error) {
568
+ console.warn("AtomixGlassContainer: Error during shader cleanup", error);
569
+ } finally {
570
+ shaderGeneratorRef.current = null;
571
+ }
572
+ };
573
+ }), [ mode, glassSize, shaderVariant ]);
574
+ // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
575
+ const [rectCache, setRectCache] = useState(null);
576
+ useEffect((() => {
577
+ if (!ref || "function" == typeof ref) return;
578
+ const element = ref.current;
579
+ if (element) try {
580
+ setRectCache(element.getBoundingClientRect());
581
+ } catch (error) {
582
+ console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
583
+ }
584
+ }), [ ref, glassSize ]);
585
+ // Pre-calculate static multipliers outside useMemo
586
+ const liquidBlur = useMemo((() => {
587
+ const defaultBlur = {
588
+ baseBlur: blurAmount,
589
+ edgeBlur: 1.25 * blurAmount,
590
+ centerBlur: 1.1 * blurAmount,
591
+ flowBlur: 1.2 * blurAmount
592
+ };
593
+ // Enhanced validation for liquid blur
594
+ if (!enableLiquidBlur || !rectCache || !globalMousePosition || "number" != typeof globalMousePosition.x || "number" != typeof globalMousePosition.y || isNaN(globalMousePosition.x) || isNaN(globalMousePosition.y)) return defaultBlur;
595
+ try {
596
+ // Cache center and distance calculations
597
+ 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);
598
+ return {
599
+ baseBlur: clampBlur(baseBlur * stateMultiplier),
600
+ edgeBlur: clampBlur(edgeBlur * stateMultiplier),
601
+ centerBlur: clampBlur(centerBlur * stateMultiplier),
602
+ flowBlur: clampBlur(flowBlur * stateMultiplier)
603
+ };
604
+ } catch (error) {
605
+ return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
606
+ defaultBlur;
607
+ }
608
+ }), [ enableLiquidBlur, blurAmount, globalMousePosition, mouseOffset, isHovered, isActive, rectCache, style, glassSize ]), backdropStyle = useMemo((() => {
609
+ try {
610
+ 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;
611
+ // Validate blur values before using them
612
+ return !enableLiquidBlur || effectiveReducedMotion || effectiveDisableEffects || area > 18e4 ? {
613
+ 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)`
614
+ } : {
615
+ 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)`
616
+ };
617
+ // Single-pass fallback: stronger radius to match perceived blur of multi-pass
618
+ } catch (error) {
619
+ return console.warn("AtomixGlassContainer: Error calculating backdrop style", error),
620
+ {
621
+ backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
622
+ };
623
+ }
624
+ }), [ filterId, liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveDisableEffects, enableLiquidBlur ]), containerVars = useMemo((() => {
625
+ try {
626
+ // Safe extraction of mouse offset values
627
+ 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;
628
+ return {
629
+ "--atomix-glass-container-width": `${glassSize?.width}`,
630
+ "--atomix-glass-container-height": `${glassSize?.height}`,
631
+ "--atomix-glass-container-padding": padding || "0 0",
632
+ "--atomix-glass-container-radius": `${"number" != typeof cornerRadius || isNaN(cornerRadius) ? 0 : cornerRadius}px`,
633
+ "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
634
+ "--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",
635
+ "--atomix-glass-container-shadow-opacity": effectiveDisableEffects ? 0 : 1,
636
+ // Background and shadow values use design token-aligned RGB values
637
+ "--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",
638
+ "--atomix-glass-container-text-shadow": overLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
639
+ "--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
640
+ };
641
+ } catch (error) {
642
+ return console.warn("AtomixGlassContainer: Error generating container variables", error),
643
+ {
644
+ "--atomix-glass-container-padding": "0 0",
645
+ "--atomix-glass-container-radius": "0px",
646
+ "--atomix-glass-container-backdrop": "none",
647
+ "--atomix-glass-container-shadow": "none",
648
+ "--atomix-glass-container-shadow-opacity": 1,
649
+ "--atomix-glass-container-bg": "none",
650
+ "--atomix-glass-container-text-shadow": "none"
651
+ };
652
+ }
653
+ }), [ glassSize, padding, cornerRadius, backdropStyle, mouseOffset, overLight, effectiveDisableEffects ]);
654
+ return jsx("div", {
655
+ ref: ref,
656
+ className: `${ATOMIX_GLASS.CONTAINER_CLASS} ${className} ${active ? ATOMIX_GLASS.CLASSES.ACTIVE : ""} ${overLight ? ATOMIX_GLASS.CLASSES.OVER_LIGHT : ""}`,
657
+ style: {
658
+ ...style,
659
+ ...containerVars
660
+ },
661
+ onClick: onClick,
662
+ children: jsxs("div", {
663
+ className: ATOMIX_GLASS.INNER_CLASS,
664
+ style: {
665
+ padding: "var(--atomix-glass-container-padding)",
666
+ boxShadow: "var(--atomix-glass-container-box-shadow)"
667
+ },
668
+ onMouseEnter: onMouseEnter,
669
+ onMouseLeave: onMouseLeave,
670
+ onMouseDown: onMouseDown,
671
+ onMouseUp: onMouseUp,
672
+ children: [ jsxs("div", {
673
+ className: ATOMIX_GLASS.FILTER_CLASS,
674
+ children: [ jsx(GlassFilter, {
675
+ blurAmount: blurAmount,
676
+ mode: mode,
677
+ id: filterId,
678
+ displacementScale: "number" != typeof displacementScale || isNaN(displacementScale) ? 0 : displacementScale,
679
+ aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
680
+ shaderMapUrl: shaderMapUrl
681
+ }), jsx("div", {
682
+ className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
683
+ suppressHydrationWarning: !0,
684
+ style: {
685
+ filter: `url(#${filterId})`,
686
+ backdropFilter: "var(--atomix-glass-container-backdrop)",
687
+ borderRadius: "var(--atomix-glass-container-radius)"
688
+ }
689
+ }), jsx("div", {
690
+ className: ATOMIX_GLASS.FILTER_SHADOW_CLASS,
691
+ style: {
692
+ boxShadow: "var(--atomix-glass-container-shadow)",
693
+ opacity: "var(--atomix-glass-container-shadow-opacity)",
694
+ background: "var(--atomix-glass-container-bg)",
695
+ borderRadius: "var(--atomix-glass-container-radius)"
696
+ }
697
+ }) ]
698
+ }), jsx("div", {
699
+ ref: contentRef,
700
+ className: ATOMIX_GLASS.CONTENT_CLASS,
701
+ style: {
702
+ position: "relative",
703
+ textShadow: "var(--atomix-glass-container-text-shadow)",
704
+ ...elasticity > 0 ? {
705
+ zIndex: 100
706
+ } : {}
707
+ },
708
+ children: children
709
+ }) ]
710
+ })
711
+ });
712
+ }));
713
+
714
+ // Module-level shared shader cache with LRU eviction
715
+ AtomixGlassContainer.displayName = "AtomixGlassContainer";
716
+
717
+ // Singleton instance
718
+ const globalMouseTracker = new
719
+ /**
720
+ * Global mouse tracker singleton
721
+ * Tracks mouse position at document level and distributes to subscribers
722
+ * Reduces event processing overhead when multiple AtomixGlass instances are present
723
+ */
724
+ class {
725
+ constructor() {
726
+ this.listeners = new Set, this.position = {
727
+ x: 0,
728
+ y: 0
729
+ }, this.rafId = null, this.lastEvent = null, this.isTracking = !1,
730
+ /**
731
+ * Handle mouse move event
732
+ */
733
+ this.handleMouseMove = e => {
734
+ this.lastEvent = e,
735
+ // Use requestAnimationFrame to throttle updates
736
+ null === this.rafId && (this.rafId = requestAnimationFrame((() => {
737
+ this.lastEvent && (this.position = {
738
+ x: this.lastEvent.clientX,
739
+ y: this.lastEvent.clientY
740
+ },
741
+ // Notify all subscribers
742
+ this.listeners.forEach((callback => {
743
+ try {
744
+ callback(this.position);
745
+ } catch (error) {
746
+ console.error("GlobalMouseTracker: Error in subscriber callback", error);
747
+ }
748
+ }))), this.rafId = null;
749
+ })));
750
+ };
751
+ }
752
+ /**
753
+ * Subscribe to mouse position updates
754
+ * @param callback Function to call when mouse position changes
755
+ * @returns Unsubscribe function
756
+ */ subscribe(callback) {
757
+ // Return unsubscribe function
758
+ return this.listeners.add(callback),
759
+ // Start tracking if this is the first subscriber
760
+ 1 === this.listeners.size && this.startTracking(),
761
+ // Immediately notify with current position
762
+ callback(this.position), () => {
763
+ this.unsubscribe(callback);
764
+ };
765
+ }
766
+ /**
767
+ * Unsubscribe from mouse position updates
768
+ */ unsubscribe(callback) {
769
+ this.listeners.delete(callback),
770
+ // Stop tracking if no more subscribers
771
+ 0 === this.listeners.size && this.stopTracking();
772
+ }
773
+ /**
774
+ * Start tracking mouse movement
775
+ */ startTracking() {
776
+ this.isTracking || (this.isTracking = !0,
777
+ // Use document-level listener for global tracking
778
+ document.addEventListener("mousemove", this.handleMouseMove, {
779
+ passive: !0
780
+ }));
781
+ }
782
+ /**
783
+ * Stop tracking mouse movement
784
+ */ stopTracking() {
785
+ this.isTracking && (this.isTracking = !1, document.removeEventListener("mousemove", this.handleMouseMove),
786
+ // Cancel any pending RAF
787
+ null !== this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null), this.lastEvent = null);
788
+ }
789
+ /**
790
+ * Get current mouse position (synchronous)
791
+ */ getPosition() {
792
+ return {
793
+ ...this.position
794
+ };
795
+ }
796
+ /**
797
+ * Get number of active subscribers (for debugging)
798
+ */ getSubscriberCount() {
799
+ return this.listeners.size;
800
+ }
801
+ }, {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
802
+ parentElement && backgroundDetectionCache.set(parentElement, {
803
+ result: result,
804
+ timestamp: Date.now(),
805
+ config: overLightConfig,
806
+ threshold: threshold
807
+ });
808
+ };
809
+
810
+ /**
811
+ * Composable hook for AtomixGlass component logic
812
+ * Manages all state, calculations, and event handlers
813
+ */
814
+ 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}) {
815
+ // State
816
+ const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), [glassSize, setGlassSize] = useState({
817
+ width: 270,
818
+ height: 69
819
+ }), [internalGlobalMousePosition, setInternalGlobalMousePosition] = useState({
820
+ x: 0,
821
+ y: 0
822
+ }), [internalMouseOffset, setInternalMouseOffset] = useState({
823
+ x: 0,
824
+ y: 0
825
+ }), [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 ]);
826
+ // Extract border-radius from children
827
+ useEffect((() => {
828
+ const extractRadius = () => {
829
+ try {
830
+ let extractedRadius = null, extractionSource = "default";
831
+ if (contentRef.current) {
832
+ const firstChild = contentRef.current.firstElementChild;
833
+ if (firstChild) {
834
+ const domRadius = (element => {
835
+ if (!element || "undefined" == typeof window) return null;
836
+ try {
837
+ const computedStyles = window.getComputedStyle(element), borderRadius = computedStyles.borderRadius || computedStyles.borderTopLeftRadius || computedStyles.borderTopRightRadius || computedStyles.borderBottomLeftRadius || computedStyles.borderBottomRightRadius;
838
+ if (borderRadius && "0px" !== borderRadius && "auto" !== borderRadius) {
839
+ const parsed = parseBorderRadiusValue(borderRadius);
840
+ return parsed > 0 ? parsed : null;
841
+ }
842
+ return null;
843
+ } catch (error) {
844
+ return null;
845
+ }
846
+ })(firstChild);
847
+ null !== domRadius && domRadius > 0 && (extractedRadius = domRadius, extractionSource = "DOM element");
848
+ }
849
+ }
850
+ if (null === extractedRadius) {
851
+ const childRadius = extractBorderRadiusFromChildren(children);
852
+ childRadius > 0 && childRadius !== CONSTANTS.DEFAULT_CORNER_RADIUS && (extractedRadius = childRadius,
853
+ extractionSource = "React children");
854
+ }
855
+ null !== extractedRadius && extractedRadius > 0 ? setDynamicCornerRadius(extractedRadius) : process.env.NODE_ENV;
856
+ } catch (error) {
857
+ "production" !== process.env.NODE_ENV && debugCornerRadius && console.error("[AtomixGlass] Error extracting corner radius:", error);
858
+ }
859
+ };
860
+ extractRadius();
861
+ const timeoutId = setTimeout(extractRadius, 100);
862
+ return () => clearTimeout(timeoutId);
863
+ }), [ children, debugCornerRadius, contentRef ]),
864
+ // Media query handlers and background detection
865
+ useEffect((() => {
866
+ if (("auto" === overLight || "object" == typeof overLight && null !== overLight) && glassRef.current) {
867
+ const element = glassRef.current, cachedResult = ((parentElement, overLightConfig) => {
868
+ if (!parentElement) return null;
869
+ const cached = backgroundDetectionCache.get(parentElement);
870
+ return cached && ((config1, config2) => {
871
+ // Primitive comparison for boolean and 'auto'
872
+ if ("object" != typeof config1 || null === config1) return config1 === config2;
873
+ // Both must be objects at this point
874
+ if ("object" != typeof config2 || null === config2) return !1;
875
+ const obj1 = config1, obj2 = config2, props = [ "threshold", "opacity", "contrast", "brightness", "saturationBoost" ];
876
+ for (const prop of props) {
877
+ const val1 = obj1[prop], val2 = obj2[prop];
878
+ // If both are undefined, they're equal for this property
879
+ if (void 0 !== val1 || void 0 !== val2) {
880
+ // If one is undefined and the other isn't, they're different
881
+ if (void 0 === val1 || void 0 === val2) return !1;
882
+ // Compare numeric values (handle NaN and floating point precision)
883
+ if ("number" == typeof val1 && "number" == typeof val2) {
884
+ // Use Number.isNaN for proper NaN comparison
885
+ if (Number.isNaN(val1) && Number.isNaN(val2)) continue;
886
+ if (Number.isNaN(val1) || Number.isNaN(val2)) return !1;
887
+ // Compare with small epsilon for floating point numbers
888
+ if (Math.abs(val1 - val2) > Number.EPSILON) return !1;
889
+ } else if (val1 !== val2) return !1;
890
+ }
891
+ }
892
+ return !0;
893
+ })(cached.config, overLightConfig) ? (
894
+ // Update timestamp for LRU-like behavior (though WeakMap doesn't support LRU)
895
+ cached.timestamp = Date.now(), cached.result) : null;
896
+ })(element.parentElement, overLight);
897
+ if (null !== cachedResult) return void setDetectedOverLight(cachedResult);
898
+ const timeoutId = setTimeout((() => {
899
+ try {
900
+ if (!element) return void setDetectedOverLight(!1);
901
+ // Validate window context
902
+ if ("undefined" == typeof window || "function" != typeof window.getComputedStyle) return void setDetectedOverLight(!1);
903
+ let totalLuminance = 0, validSamples = 0, hasValidBackground = !1, currentElement = element.parentElement, depth = 0;
904
+ const maxDepth = 20, maxSamples = 10;
905
+ // Limit traversal depth to prevent infinite loops and performance issues
906
+ for (;currentElement && validSamples < maxSamples && depth < maxDepth; ) {
907
+ try {
908
+ const computedStyle = window.getComputedStyle(currentElement);
909
+ if (!computedStyle) {
910
+ currentElement = currentElement.parentElement, depth++;
911
+ continue;
912
+ }
913
+ const bgColor = computedStyle.backgroundColor, bgImage = computedStyle.backgroundImage;
914
+ // Check for solid color backgrounds
915
+ if (bgColor && "rgba(0, 0, 0, 0)" !== bgColor && "transparent" !== bgColor && "initial" !== bgColor && "none" !== bgColor) {
916
+ const rgb = bgColor.match(/\d+/g);
917
+ if (rgb && rgb.length >= 3) {
918
+ const r = Number(rgb[0]), g = Number(rgb[1]), b = Number(rgb[2]);
919
+ // Validate RGB values are valid numbers
920
+ 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)) {
921
+ const luminance = (.299 * r + .587 * g + .114 * b) / 255;
922
+ !isNaN(luminance) && isFinite(luminance) && (totalLuminance += luminance, validSamples++,
923
+ hasValidBackground = !0);
924
+ }
925
+ }
926
+ }
927
+ // Check for image backgrounds
928
+ bgImage && "none" !== bgImage && "initial" !== bgImage && (
929
+ // For image backgrounds, assume medium luminance
930
+ totalLuminance += .5, validSamples++, hasValidBackground = !0);
931
+ } catch (styleError) {
932
+ process.env.NODE_ENV;
933
+ }
934
+ // Move to parent element for next iteration
935
+ if (!currentElement) break;
936
+ // Exit loop if currentElement becomes null
937
+ currentElement = currentElement.parentElement, depth++;
938
+ }
939
+ // More conservative detection with better error handling
940
+ if (hasValidBackground && validSamples > 0) {
941
+ const avgLuminance = totalLuminance / validSamples;
942
+ if (!isNaN(avgLuminance) && isFinite(avgLuminance)) {
943
+ let threshold = .7;
944
+ // Conservative threshold for overlight
945
+ // If overLight is an object, use its threshold property with validation
946
+ if ("object" == typeof overLight && null !== overLight) {
947
+ const objConfig = overLight;
948
+ if (void 0 !== objConfig.threshold) {
949
+ const configThreshold = "number" == typeof objConfig.threshold && !isNaN(objConfig.threshold) && isFinite(objConfig.threshold) ? objConfig.threshold : .7;
950
+ threshold = Math.min(.9, Math.max(.1, configThreshold));
951
+ }
952
+ }
953
+ const isOverLightDetected = avgLuminance > threshold;
954
+ // Cache the result in shared cache
955
+ setCachedBackgroundDetection(element.parentElement, overLight, isOverLightDetected, threshold),
956
+ setDetectedOverLight(isOverLightDetected);
957
+ } else {
958
+ // Invalid luminance calculation, default to false
959
+ const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
960
+ setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
961
+ setDetectedOverLight(result);
962
+ }
963
+ } else {
964
+ // Default to false if no valid background found
965
+ const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
966
+ setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
967
+ setDetectedOverLight(result);
968
+ }
969
+ } catch (error) {
970
+ // Enhanced error logging with context
971
+ "development" === process.env.NODE_ENV && console.warn("AtomixGlass: Error detecting background brightness:", error);
972
+ const result = !1;
973
+ if (element && element.parentElement) {
974
+ const threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
975
+ setCachedBackgroundDetection(element.parentElement, overLight, result, threshold);
976
+ }
977
+ setDetectedOverLight(result);
978
+ }
979
+ }), 150);
980
+ return () => clearTimeout(timeoutId);
981
+ }
982
+ if ("boolean" == typeof overLight &&
983
+ // For boolean values, disable auto-detection
984
+ // Cache is automatically managed by WeakMap (no manual clearing needed)
985
+ setDetectedOverLight(!1), "function" == typeof window.matchMedia) try {
986
+ const mediaQueryReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)"), mediaQueryHighContrast = window.matchMedia("(prefers-contrast: high)");
987
+ setUserPrefersReducedMotion(mediaQueryReducedMotion.matches), setUserPrefersHighContrast(mediaQueryHighContrast.matches);
988
+ const handleReducedMotionChange = e => {
989
+ setUserPrefersReducedMotion(e.matches);
990
+ }, handleHighContrastChange = e => {
991
+ setUserPrefersHighContrast(e.matches);
992
+ };
993
+ return mediaQueryReducedMotion.addEventListener ? (mediaQueryReducedMotion.addEventListener("change", handleReducedMotionChange),
994
+ mediaQueryHighContrast.addEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.addListener && (mediaQueryReducedMotion.addListener(handleReducedMotionChange),
995
+ mediaQueryHighContrast.addListener(handleHighContrastChange)), () => {
996
+ try {
997
+ mediaQueryReducedMotion.removeEventListener ? (mediaQueryReducedMotion.removeEventListener("change", handleReducedMotionChange),
998
+ mediaQueryHighContrast.removeEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.removeListener && (mediaQueryReducedMotion.removeListener(handleReducedMotionChange),
999
+ mediaQueryHighContrast.removeListener(handleHighContrastChange));
1000
+ } catch (cleanupError) {
1001
+ console.error("AtomixGlass: Error cleaning up media query listeners:", cleanupError);
1002
+ }
1003
+ };
1004
+ } catch (error) {
1005
+ return void console.error("AtomixGlass: Error setting up media queries:", error);
1006
+ }
1007
+ }), [ overLight, glassRef, debugOverLight ]);
1008
+ // Mouse tracking using shared global tracker
1009
+ // Cache bounding rect to avoid repeated getBoundingClientRect calls
1010
+ const cachedRectRef = useRef(null), updateRectRef = useRef(null), handleGlobalMousePosition = useCallback((globalPos => {
1011
+ if (externalGlobalMousePosition && externalMouseOffset)
1012
+ // External mouse position provided, skip internal tracking
1013
+ return;
1014
+ if (effectiveDisableEffects) return;
1015
+ const container = mouseContainer?.current || glassRef.current;
1016
+ if (!container) return;
1017
+ enablePerformanceMonitoring && performance.now();
1018
+ // Use cached rect if available, otherwise get new one
1019
+ let rect = cachedRectRef.current;
1020
+ if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
1021
+ cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
1022
+ const center = calculateElementCenter(rect), newOffset = {
1023
+ x: (globalPos.x - center.x) / rect.width * 100,
1024
+ y: (globalPos.y - center.y) / rect.height * 100
1025
+ };
1026
+ // Calculate offset relative to this container
1027
+ // React 18 automatically batches these updates
1028
+ setInternalMouseOffset(newOffset), setInternalGlobalMousePosition(globalPos), "production" !== process.env.NODE_ENV && enablePerformanceMonitoring && performance.now();
1029
+ }), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects, enablePerformanceMonitoring ]);
1030
+ // Subscribe to shared mouse tracker
1031
+ useEffect((() => {
1032
+ if (externalGlobalMousePosition && externalMouseOffset)
1033
+ // External mouse position provided, don't subscribe
1034
+ return;
1035
+ if (effectiveDisableEffects)
1036
+ // Effects disabled, don't subscribe
1037
+ return;
1038
+ // Subscribe to shared tracker
1039
+ const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition), container = mouseContainer?.current || glassRef.current;
1040
+ // Update cached rect when container size changes
1041
+ let resizeObserver = null;
1042
+ return container && "undefined" != typeof ResizeObserver && (resizeObserver = new ResizeObserver((() => {
1043
+ null !== updateRectRef.current && cancelAnimationFrame(updateRectRef.current), updateRectRef.current = requestAnimationFrame((() => {
1044
+ const container = mouseContainer?.current || glassRef.current;
1045
+ container && (cachedRectRef.current = container.getBoundingClientRect()), updateRectRef.current = null;
1046
+ }));
1047
+ })), resizeObserver.observe(container)), () => {
1048
+ unsubscribe(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
1049
+ updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
1050
+ };
1051
+ }), [ handleGlobalMousePosition, mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects ]);
1052
+ // Transform calculations
1053
+ const calculateDirectionalScale = useCallback((() => {
1054
+ if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return "scale(1)";
1055
+ 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({
1056
+ x: edgeDistanceX,
1057
+ y: edgeDistanceY
1058
+ }, {
1059
+ x: 0,
1060
+ y: 0
1061
+ });
1062
+ if (edgeDistance > CONSTANTS.ACTIVATION_ZONE) return "scale(1)";
1063
+ const fadeInFactor = 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE, centerDistance = calculateDistance(globalMousePosition, center);
1064
+ if (0 === centerDistance) return "scale(1)";
1065
+ 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;
1066
+ return `scaleX(${Math.max(.8, scaleX)}) scaleY(${Math.max(.8, scaleY)})`;
1067
+ }), [ globalMousePosition, elasticity, glassSize, glassRef ]), calculateFadeInFactor = useCallback((() => {
1068
+ if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return 0;
1069
+ 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({
1070
+ x: edgeDistanceX,
1071
+ y: edgeDistanceY
1072
+ }, {
1073
+ x: 0,
1074
+ y: 0
1075
+ });
1076
+ return edgeDistance > CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / CONSTANTS.ACTIVATION_ZONE;
1077
+ }), [ globalMousePosition, glassSize, glassRef ]), calculateElasticTranslation = useCallback((() => {
1078
+ if (!glassRef.current) return {
1079
+ x: 0,
1080
+ y: 0
1081
+ };
1082
+ const fadeInFactor = calculateFadeInFactor(), rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect);
1083
+ return {
1084
+ x: (globalMousePosition.x - center.x) * elasticity * .1 * fadeInFactor,
1085
+ y: (globalMousePosition.y - center.y) * elasticity * .1 * fadeInFactor
1086
+ };
1087
+ }), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveDisableEffects ? {
1088
+ x: 0,
1089
+ y: 0
1090
+ } : 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 ]);
1091
+ // Size management
1092
+ useEffect((() => {
1093
+ const isValidElement = element => null !== element && element instanceof HTMLElement && element.isConnected;
1094
+ let rafId = null, lastSize = {
1095
+ width: 0,
1096
+ height: 0
1097
+ }, lastCornerRadius = effectiveCornerRadius;
1098
+ const updateGlassSize = (forceUpdate = !1) => {
1099
+ null !== rafId && cancelAnimationFrame(rafId), rafId = requestAnimationFrame((() => {
1100
+ if (!isValidElement(glassRef.current)) return void (rafId = null);
1101
+ const rect = glassRef.current.getBoundingClientRect();
1102
+ if (rect.width <= 0 || rect.height <= 0) return void (rafId = null);
1103
+ // Measure actual rendered size without artificial offsets to avoid feedback loops
1104
+ const newSize = {
1105
+ width: Math.round(rect.width),
1106
+ height: Math.round(rect.height)
1107
+ }, cornerRadiusChanged = lastCornerRadius !== effectiveCornerRadius, dimensionsChanged = Math.abs(newSize.width - lastSize.width) > 1 || Math.abs(newSize.height - lastSize.height) > 1;
1108
+ var size;
1109
+ (forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateGlassSize(size = newSize) && size.width <= CONSTANTS.MAX_SIZE && size.height <= CONSTANTS.MAX_SIZE && (lastSize = newSize,
1110
+ lastCornerRadius = effectiveCornerRadius, setGlassSize(newSize)), rafId = null;
1111
+ }));
1112
+ };
1113
+ let resizeTimeoutId = null;
1114
+ const debouncedResizeHandler = () => {
1115
+ resizeTimeoutId && clearTimeout(resizeTimeoutId), resizeTimeoutId = setTimeout((() => updateGlassSize(!1)), 16);
1116
+ }, initialTimeoutId = setTimeout((() => updateGlassSize(!0)), 0);
1117
+ let resizeObserver = null, resizeDebounceTimeout = null;
1118
+ // ResizeObserver has 98%+ browser support, no need for fallback
1119
+ if ("undefined" != typeof ResizeObserver && isValidElement(glassRef.current)) try {
1120
+ resizeObserver = new ResizeObserver((entries => {
1121
+ for (const entry of entries) if (entry.target === glassRef.current) {
1122
+ // Update cached rect when size changes
1123
+ glassRef.current && (cachedRectRef.current = glassRef.current.getBoundingClientRect()),
1124
+ // Debounce resize updates to match RAF timing (16ms)
1125
+ resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), resizeDebounceTimeout = setTimeout((() => updateGlassSize(!1)), 16);
1126
+ break;
1127
+ }
1128
+ })), resizeObserver.observe(glassRef.current);
1129
+ } catch (error) {
1130
+ console.warn("AtomixGlass: ResizeObserver not available, using window resize only", error);
1131
+ }
1132
+ return window.addEventListener("resize", debouncedResizeHandler, {
1133
+ passive: !0
1134
+ }), () => {
1135
+ clearTimeout(initialTimeoutId), null !== rafId && cancelAnimationFrame(rafId), resizeTimeoutId && clearTimeout(resizeTimeoutId),
1136
+ resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), window.removeEventListener("resize", debouncedResizeHandler),
1137
+ resizeObserver?.disconnect();
1138
+ };
1139
+ }), [ effectiveCornerRadius, glassRef ]);
1140
+ // OverLight config
1141
+ /**
1142
+ * Get effective overLight value based on configuration
1143
+ * - boolean: returns the boolean value directly
1144
+ * - 'auto': returns detectedOverLight (auto-detected from background)
1145
+ * - object: returns detectedOverLight (auto-detected, but config object provides customization)
1146
+ */
1147
+ 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((() => {
1148
+ const isOverLight = getEffectiveOverLight(), mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, baseConfig = {
1149
+ isOverLight: isOverLight,
1150
+ threshold: .7,
1151
+ opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
1152
+ contrast: Math.min(1.8, Math.max(1, 1.4 + .3 * mouseInfluence)),
1153
+ brightness: Math.min(1.2, Math.max(.7, .85 + .15 * mouseInfluence)),
1154
+ saturationBoost: Math.min(2, Math.max(1, 1.3 + .4 * mouseInfluence)),
1155
+ shadowIntensity: Math.min(1.5, Math.max(.5, .9 + .5 * mouseInfluence)),
1156
+ borderOpacity: Math.min(1, Math.max(.3, .7 + .3 * mouseInfluence))
1157
+ };
1158
+ if ("object" == typeof overLight && null !== overLight) {
1159
+ 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 = {
1160
+ ...baseConfig,
1161
+ threshold: validatedThreshold,
1162
+ opacity: validatedOpacity * hoverIntensity * activeIntensity,
1163
+ contrast: validatedContrast + .3 * mouseInfluence,
1164
+ brightness: validatedBrightness + .15 * mouseInfluence,
1165
+ saturationBoost: validatedSaturationBoost + .4 * mouseInfluence
1166
+ };
1167
+ // Validate and apply object config values with proper clamping
1168
+ return process.env.NODE_ENV, finalConfig;
1169
+ }
1170
+ // Debug logging for non-object configs
1171
+ return process.env.NODE_ENV, baseConfig;
1172
+ }), [ 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 => {
1173
+ !onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
1174
+ }), [ onClick ]), handleMouseMove = useCallback((_e => {}), []);
1175
+ /**
1176
+ * Validate and clamp a numeric config value
1177
+ * @param value - The value to validate
1178
+ * @param min - Minimum allowed value
1179
+ * @param max - Maximum allowed value
1180
+ * @param defaultValue - Default value if validation fails
1181
+ * @returns Validated and clamped value
1182
+ */ return {
1183
+ // State
1184
+ isHovered: isHovered,
1185
+ isActive: isActive,
1186
+ glassSize: glassSize,
1187
+ dynamicCornerRadius: dynamicCornerRadius,
1188
+ effectiveCornerRadius: effectiveCornerRadius,
1189
+ effectiveReducedMotion: effectiveReducedMotion,
1190
+ effectiveHighContrast: effectiveHighContrast,
1191
+ effectiveDisableEffects: effectiveDisableEffects,
1192
+ detectedOverLight: detectedOverLight,
1193
+ globalMousePosition: globalMousePosition,
1194
+ mouseOffset: mouseOffset,
1195
+ // OverLight config
1196
+ overLightConfig: overLightConfig,
1197
+ // Transform calculations
1198
+ elasticTranslation: elasticTranslation,
1199
+ directionalScale: directionalScale,
1200
+ transformStyle: transformStyle,
1201
+ // Event handlers
1202
+ handleMouseEnter: handleMouseEnter,
1203
+ handleMouseLeave: handleMouseLeave,
1204
+ handleMouseDown: handleMouseDown,
1205
+ handleMouseUp: handleMouseUp,
1206
+ handleMouseMove: handleMouseMove,
1207
+ handleKeyDown: handleKeyDown
1208
+ };
1209
+ }
1210
+
1211
+ /**
1212
+ * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
1213
+ *
1214
+ * Features:
1215
+ * - Hardware-accelerated glass effects with SVG filters
1216
+ * - Mouse-responsive liquid distortion
1217
+ * - Dynamic border-radius extraction from children CSS properties
1218
+ * - Automatic light/dark theme detection via overLight prop
1219
+ * - Accessibility and performance optimizations
1220
+ * - Multiple displacement modes (standard, polar, prominent, shader)
1221
+ * - Design token integration for consistent theming
1222
+ * - Focus ring support for keyboard navigation
1223
+ * - Responsive breakpoints for mobile optimization
1224
+ * - Enhanced ARIA attributes for screen readers
1225
+ *
1226
+ * Design System Compliance:
1227
+ * - Uses design tokens for opacity, spacing, and colors
1228
+ * - Follows BEM methodology for class naming
1229
+ * - Implements focus-ring mixin for accessibility
1230
+ * - Supports reduced motion and high contrast preferences
1231
+ *
1232
+ * @example
1233
+ * // Basic usage with dynamic border-radius extraction
1234
+ * <AtomixGlass>
1235
+ * <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
1236
+ * </AtomixGlass>
1237
+ *
1238
+ * @example
1239
+ * // Manual border-radius override
1240
+ * <AtomixGlass cornerRadius={20}>
1241
+ * <div>Content with 20px glass radius</div>
1242
+ * </AtomixGlass>
1243
+ *
1244
+ * @example
1245
+ * // Interactive glass with click handler
1246
+ * <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
1247
+ * <div>Clickable content</div>
1248
+ * </AtomixGlass>
1249
+ *
1250
+ * @example
1251
+ * // OverLight - Boolean mode (explicit control)
1252
+ * <AtomixGlass overLight={true}>
1253
+ * <div>Content on light background</div>
1254
+ * </AtomixGlass>
1255
+ *
1256
+ * @example
1257
+ * // OverLight - Auto-detection mode
1258
+ * <AtomixGlass overLight="auto">
1259
+ * <div>Content with auto-detected background</div>
1260
+ * </AtomixGlass>
1261
+ *
1262
+ * @example
1263
+ * // OverLight - Object config with custom settings
1264
+ * <AtomixGlass
1265
+ * overLight={{
1266
+ * threshold: 0.8,
1267
+ * opacity: 0.6,
1268
+ * contrast: 1.8,
1269
+ * brightness: 1.0,
1270
+ * saturationBoost: 1.5
1271
+ * }}
1272
+ * >
1273
+ * <div>Content with custom overLight config</div>
1274
+ * </AtomixGlass>
1275
+ *
1276
+ * @example
1277
+ * // Debug mode for overLight detection
1278
+ * <AtomixGlass overLight="auto" debugOverLight={true}>
1279
+ * <div>Content with debug logging enabled</div>
1280
+ * </AtomixGlass>
1281
+ */ 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}) {
1282
+ const glassRef = useRef(null), contentRef = useRef(null), opacityCacheRef = useRef(null), rgbCacheRef = 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({
1283
+ glassRef: glassRef,
1284
+ contentRef: contentRef,
1285
+ cornerRadius: cornerRadius,
1286
+ globalMousePosition: externalGlobalMousePosition,
1287
+ mouseOffset: externalMouseOffset,
1288
+ mouseContainer: mouseContainer,
1289
+ overLight: overLight,
1290
+ reducedMotion: reducedMotion,
1291
+ highContrast: highContrast,
1292
+ disableEffects: disableEffects,
1293
+ elasticity: elasticity,
1294
+ onClick: onClick,
1295
+ debugCornerRadius: debugCornerRadius,
1296
+ debugOverLight: debugOverLight,
1297
+ enablePerformanceMonitoring: enablePerformanceMonitoring,
1298
+ children: children
1299
+ }), isOverLight = overLightConfig.isOverLight, shouldRenderOverLightLayers = enableOverLightLayers && isOverLight;
1300
+ // Read CSS custom properties once on mount and cache them
1301
+ useEffect((() => {
1302
+ if ("undefined" != typeof window && glassRef.current) try {
1303
+ const computedStyle = window.getComputedStyle(glassRef.current);
1304
+ // Cache opacity values
1305
+ if (!opacityCacheRef.current) {
1306
+ const 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) => {
1307
+ if (!value) return defaultValue;
1308
+ const parsed = parseFloat(value);
1309
+ return isNaN(parsed) ? defaultValue : parsed;
1310
+ };
1311
+ opacityCacheRef.current = {
1312
+ opacity50: parseOpacity(opacity50Value, .5),
1313
+ opacity40: parseOpacity(opacity40Value, .4),
1314
+ opacity80: parseOpacity(opacity80Value, .8),
1315
+ opacity0: parseOpacity(opacity0Value, 0)
1316
+ };
1317
+ }
1318
+ // Cache RGB color values from design tokens
1319
+ if (!rgbCacheRef.current) {
1320
+ // Try to read from design tokens, fallback to defaults
1321
+ const whiteRgbValue = computedStyle.getPropertyValue("--atomix-light-rgb").trim() || computedStyle.getPropertyValue("--atomix-white-rgb").trim() || "", blackRgbValue = computedStyle.getPropertyValue("--atomix-dark-rgb").trim() || computedStyle.getPropertyValue("--atomix-black-rgb").trim() || "";
1322
+ rgbCacheRef.current = {
1323
+ whiteRgb: whiteRgbValue || "255, 255, 255",
1324
+ // Fallback to white RGB
1325
+ blackRgb: blackRgbValue || "0, 0, 0"
1326
+ };
1327
+ }
1328
+ } catch (error) {
1329
+ // Fallback to defaults if reading fails
1330
+ opacityCacheRef.current || (opacityCacheRef.current = {
1331
+ opacity50: .5,
1332
+ opacity40: .4,
1333
+ opacity80: .8,
1334
+ opacity0: 0
1335
+ }), rgbCacheRef.current || (rgbCacheRef.current = {
1336
+ whiteRgb: "255, 255, 255",
1337
+ blackRgb: "0, 0, 0"
1338
+ });
1339
+ }
1340
+ }), []);
1341
+ // Calculate base style with transforms (only dynamic values)
1342
+ const baseStyle = useMemo((() => ({
1343
+ ...style,
1344
+ ...0 !== elasticity && !effectiveDisableEffects && {
1345
+ transform: transformStyle
1346
+ }
1347
+ })), [ 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((() => ({
1348
+ position: baseStylePosition || "absolute",
1349
+ top: baseStyleTop || 0,
1350
+ left: baseStyleLeft || 0
1351
+ })), [ baseStylePosition, baseStyleTop, baseStyleLeft ]), baseStyleWidth = baseStyle.width, baseStyleHeight = baseStyle.height, glassSizeWidth = glassSize.width, glassSizeHeight = glassSize.height, adjustedSize = useMemo((() => ({
1352
+ width: "fixed" !== baseStylePosition ? "100%" : baseStyleWidth || Math.max(glassSizeWidth, 0),
1353
+ height: "fixed" !== baseStylePosition ? "100%" : baseStyleHeight || Math.max(glassSizeHeight, 0)
1354
+ })), [ baseStylePosition, baseStyleWidth, baseStyleHeight, glassSizeWidth, glassSizeHeight ]), mouseOffsetX = mouseOffset.x, mouseOffsetY = mouseOffset.y, GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, gradientCalculations = useMemo((() => {
1355
+ 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;
1356
+ return {
1357
+ isOverLight: isOverLight,
1358
+ mx: mx,
1359
+ my: my,
1360
+ borderGradientAngle: borderGradientAngle,
1361
+ borderStop1: borderStop1,
1362
+ borderStop2: borderStop2,
1363
+ borderOpacity1: borderOpacity1,
1364
+ borderOpacity2: borderOpacity2,
1365
+ borderOpacity3: borderOpacity3,
1366
+ borderOpacity4: borderOpacity4,
1367
+ hover1X: hover1X,
1368
+ hover1Y: hover1Y,
1369
+ hover2X: hover2X,
1370
+ hover2Y: hover2Y,
1371
+ hover3X: hover3X,
1372
+ hover3Y: hover3Y,
1373
+ baseX: baseX,
1374
+ baseY: baseY
1375
+ };
1376
+ }), [ mouseOffsetX, mouseOffsetY, isOverLight ]), overLightOpacity = overLightConfig.opacity, opacityValues = useMemo((() => {
1377
+ // Use cached values if available, otherwise fallback to defaults
1378
+ const opacity50 = opacityCacheRef.current?.opacity50 ?? .5, opacity40 = opacityCacheRef.current?.opacity40 ?? .4, opacity80 = opacityCacheRef.current?.opacity80 ?? .8, opacity0 = opacityCacheRef.current?.opacity0 ?? 0;
1379
+ // Dynamic multiplier for overlay
1380
+ return {
1381
+ hover1: isHovered || isActive ? opacity50 : opacity0,
1382
+ hover2: isActive ? opacity50 : opacity0,
1383
+ hover3: isHovered ? opacity40 : isActive ? opacity80 : opacity0,
1384
+ base: isOverLight ? overLightOpacity || opacity40 : opacity0,
1385
+ over: isOverLight ? 1.1 * (overLightOpacity || opacity40) : opacity0
1386
+ };
1387
+ }), [ 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((() => {
1388
+ // RGB color values for rgba() functions
1389
+ // Note: CSS doesn't support rgba(var(--rgb), opacity) syntax - this is a CSS specification
1390
+ // limitation, not a browser support issue. We read RGB values from design tokens at mount
1391
+ // and cache them for performance. Falls back to defaults if tokens are not available.
1392
+ // Uses design tokens: --atomix-light-rgb / --atomix-white-rgb and --atomix-dark-rgb / --atomix-black-rgb
1393
+ const whiteColor = rgbCacheRef.current?.whiteRgb || "255, 255, 255", blackColor = rgbCacheRef.current?.blackRgb || "0, 0, 0";
1394
+ return {
1395
+ // Standard CSS custom properties for dynamic values
1396
+ "--atomix-glass-radius": `${effectiveCornerRadius}px`,
1397
+ "--atomix-glass-transform": baseStyleTransform || "none",
1398
+ "--atomix-glass-position": positionStylesPosition,
1399
+ "--atomix-glass-top": "fixed" !== positionStylesTop ? `${positionStylesTop}px` : "0",
1400
+ "--atomix-glass-left": "fixed" !== positionStylesLeft ? `${positionStylesLeft}px` : "0",
1401
+ "--atomix-glass-width": "fixed" !== baseStylePosition ? adjustedSizeWidth : `${adjustedSizeWidth}px`,
1402
+ "--atomix-glass-height": "fixed" !== baseStylePosition ? adjustedSizeHeight : `${adjustedSizeHeight}px`,
1403
+ // Border width: Use spacing token for consistency
1404
+ "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
1405
+ "--atomix-glass-blend-mode": gradientIsOverLight ? "multiply" : "overlay",
1406
+ // Dynamic gradients and backgrounds
1407
+ // Note: RGB values use design token-aligned constants (white: 255,255,255; black: 0,0,0)
1408
+ "--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%)`,
1409
+ "--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%)`,
1410
+ "--atomix-glass-hover-1-opacity": opacityValuesHover1,
1411
+ "--atomix-glass-hover-1-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover1X}% ${gradientHover1Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `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}%)`,
1412
+ "--atomix-glass-hover-2-opacity": opacityValuesHover2,
1413
+ "--atomix-glass-hover-2-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover2X}% ${gradientHover2Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `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}%)`,
1414
+ "--atomix-glass-hover-3-opacity": opacityValuesHover3,
1415
+ "--atomix-glass-hover-3-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientHover3X}% ${gradientHover3Y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `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}%)`,
1416
+ "--atomix-glass-base-opacity": opacityValuesBase,
1417
+ "--atomix-glass-base-gradient": gradientIsOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + gradientMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + gradientMy * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
1418
+ "--atomix-glass-overlay-opacity": opacityValuesOver,
1419
+ "--atomix-glass-overlay-gradient": gradientIsOverLight ? `radial-gradient(circle at ${gradientBaseX}% ${gradientBaseY}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + Math.abs(gradientMx) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + Math.abs(gradientMy) * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`
1420
+ };
1421
+ }), [
1422
+ // Position styles - only specific properties
1423
+ positionStylesPosition, positionStylesTop, positionStylesLeft,
1424
+ // Adjusted size - only specific properties
1425
+ adjustedSizeWidth, adjustedSizeHeight,
1426
+ // Base style - only transform property
1427
+ baseStyleTransform, baseStylePosition,
1428
+ // Other values
1429
+ effectiveCornerRadius, effectiveReducedMotion,
1430
+ // Gradient calculations - extracted properties
1431
+ gradientIsOverLight, gradientMx, gradientMy, gradientBorderGradientAngle, gradientBorderStop1, gradientBorderStop2, gradientBorderOpacity1, gradientBorderOpacity2, gradientBorderOpacity3, gradientBorderOpacity4, gradientHover1X, gradientHover1Y, gradientHover2X, gradientHover2Y, gradientHover3X, gradientHover3Y, gradientBaseX, gradientBaseY,
1432
+ // Opacity values - extracted properties
1433
+ opacityValuesHover1, opacityValuesHover2, opacityValuesHover3, opacityValuesBase, opacityValuesOver ]);
1434
+ // Build className with state modifiers
1435
+ return jsxs("div", {
1436
+ className: componentClassName,
1437
+ style: glassVars,
1438
+ role: role || (onClick ? "button" : void 0),
1439
+ tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
1440
+ "aria-label": ariaLabel,
1441
+ "aria-describedby": ariaDescribedBy,
1442
+ "aria-disabled": !(!onClick || !effectiveDisableEffects) || !onClick && void 0,
1443
+ "aria-pressed": !(!onClick || !isActive) || !onClick && void 0,
1444
+ onKeyDown: onClick ? handleKeyDown : void 0,
1445
+ children: [ jsx(AtomixGlassContainer, {
1446
+ ref: glassRef,
1447
+ contentRef: contentRef,
1448
+ className: className,
1449
+ style: baseStyle,
1450
+ cornerRadius: effectiveCornerRadius,
1451
+ displacementScale: effectiveDisableEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : overLightConfig.isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
1452
+ blurAmount: effectiveDisableEffects ? 0 : blurAmount,
1453
+ saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : overLightConfig.isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
1454
+ aberrationIntensity: effectiveDisableEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
1455
+ glassSize: glassSize,
1456
+ padding: padding,
1457
+ mouseOffset: effectiveDisableEffects ? {
1458
+ x: 0,
1459
+ y: 0
1460
+ } : mouseOffset,
1461
+ globalMousePosition: effectiveDisableEffects ? {
1462
+ x: 0,
1463
+ y: 0
1464
+ } : globalMousePosition,
1465
+ onMouseEnter: handleMouseEnter,
1466
+ onMouseLeave: handleMouseLeave,
1467
+ onMouseDown: handleMouseDown,
1468
+ onMouseUp: handleMouseUp,
1469
+ active: isActive,
1470
+ isHovered: isHovered,
1471
+ isActive: isActive,
1472
+ overLight: overLightConfig.isOverLight,
1473
+ onClick: onClick,
1474
+ mode: mode,
1475
+ transform: baseStyle.transform,
1476
+ effectiveDisableEffects: effectiveDisableEffects,
1477
+ effectiveReducedMotion: effectiveReducedMotion,
1478
+ shaderVariant: shaderVariant,
1479
+ elasticity: elasticity,
1480
+ enableLiquidBlur: enableLiquidBlur,
1481
+ children: children
1482
+ }), Boolean(onClick) && jsxs(Fragment, {
1483
+ children: [ jsx("div", {
1484
+ className: ATOMIX_GLASS.HOVER_1_CLASS
1485
+ }), jsx("div", {
1486
+ className: ATOMIX_GLASS.HOVER_2_CLASS
1487
+ }), jsx("div", {
1488
+ className: ATOMIX_GLASS.HOVER_3_CLASS
1489
+ }) ]
1490
+ }), jsx("div", {
1491
+ 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(" "),
1492
+ style: {
1493
+ ...positionStyles,
1494
+ height: adjustedSize.height,
1495
+ width: adjustedSize.width,
1496
+ borderRadius: `${effectiveCornerRadius}px`,
1497
+ transform: baseStyle.transform
1498
+ }
1499
+ }), jsx("div", {
1500
+ 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(" "),
1501
+ style: {
1502
+ ...positionStyles,
1503
+ height: adjustedSize.height,
1504
+ width: adjustedSize.width,
1505
+ borderRadius: `${effectiveCornerRadius}px`,
1506
+ transform: baseStyle.transform
1507
+ }
1508
+ }), shouldRenderOverLightLayers && jsxs(Fragment, {
1509
+ children: [ jsx("div", {
1510
+ className: ATOMIX_GLASS.BASE_LAYER_CLASS
1511
+ }), jsx("div", {
1512
+ className: ATOMIX_GLASS.OVERLAY_LAYER_CLASS
1513
+ }), jsx("div", {
1514
+ className: ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS,
1515
+ style: {
1516
+ opacity: opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
1517
+ 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}%)`
1518
+ }
1519
+ }) ]
1520
+ }), enableBorderEffect && jsxs(Fragment, {
1521
+ children: [ jsx("span", {
1522
+ className: ATOMIX_GLASS.BORDER_1_CLASS
1523
+ }), jsx("span", {
1524
+ className: ATOMIX_GLASS.BORDER_2_CLASS
1525
+ }) ]
1526
+ }) ]
1527
+ });
1528
+ }
1529
+
1530
+ // Adapted from https://github.com/shuding/liquid-glass
1531
+ // Constants
1532
+ const smoothStep = (a, b, t) => {
1533
+ // Add input validation
1534
+ if ("number" != typeof a || "number" != typeof b || "number" != typeof t) return 0;
1535
+ const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
1536
+ return clamped * clamped * (3 - 2 * clamped);
1537
+ }, calculateLength = (x, y) => {
1538
+ // Add input validation and error handling
1539
+ if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
1540
+ // Prevent potential overflow
1541
+ const maxX = Math.max(Math.abs(x), Math.abs(y));
1542
+ if (0 === maxX) return 0;
1543
+ const scaledX = x / maxX, scaledY = y / maxX;
1544
+ return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
1545
+ }, roundedRectSDF = (x, y, width, height, radius) => {
1546
+ // Add input validation
1547
+ if ("number" != typeof x || "number" != typeof y || "number" != typeof width || "number" != typeof height || "number" != typeof radius) return 0;
1548
+ const qx = Math.abs(x) - width + radius, qy = Math.abs(y) - height + radius;
1549
+ return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
1550
+ }, createTexture = (x, y) => ({
1551
+ x: "number" != typeof x || isNaN(x) ? .5 : Math.max(0, Math.min(1, x)),
1552
+ y: "number" != typeof y || isNaN(y) ? .5 : Math.max(0, Math.min(1, y))
1553
+ }), validateVec2 = vec => vec && "number" == typeof vec.x && "number" == typeof vec.y && !isNaN(vec.x) && !isNaN(vec.y), clampValue = (value, min, max) =>
1554
+ // Add input validation
1555
+ "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 => {
1556
+ // Add input validation
1557
+ if ("number" != typeof t || isNaN(t)) return 0;
1558
+ const clampedT = Math.max(0, Math.min(1, t));
1559
+ return clampedT < .5 ? 4 * clampedT * clampedT * clampedT : 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
1560
+ }, easeOutQuart = t => {
1561
+ // Add input validation
1562
+ if ("number" != typeof t || isNaN(t)) return 0;
1563
+ const clampedT = Math.max(0, Math.min(1, t));
1564
+ return 1 - Math.pow(1 - clampedT, 4);
1565
+ }, noise2D = (x, y) => {
1566
+ // Add input validation
1567
+ if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
1568
+ 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) => {
1569
+ // Add input validation
1570
+ if ("number" != typeof i || "number" != typeof j) return 0;
1571
+ const n = i + 57 * j, hashed = 43758.5453 * Math.sin(12.9898 * n + 78.233);
1572
+ // Use a more stable hash function
1573
+ return hashed - Math.floor(hashed);
1574
+ }, a = hash(X, Y), b = hash(X + 1, Y), c = hash(X, Y + 1), x1 = a + u * (b - a);
1575
+ return x1 + v * (c + u * (hash(X + 1, Y + 1) - c) - x1);
1576
+ }, fbm = (x, y, octaves = 4) => {
1577
+ // Add input validation
1578
+ if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
1579
+ // Clamp octaves to prevent performance issues
1580
+ const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
1581
+ let value = 0, amplitude = .5, frequency = 1;
1582
+ for (let i = 0; i < clampedOctaves; i++) value += amplitude * noise2D(x * frequency, y * frequency),
1583
+ frequency *= 2, amplitude *= .5;
1584
+ return value;
1585
+ }, calculateParallaxOffset = (x, y, depth, mouseX = 0, mouseY = 0) => {
1586
+ // Add input validation
1587
+ 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 {
1588
+ x: 0,
1589
+ y: 0
1590
+ };
1591
+ const parallaxStrength = Math.min(.02 * depth, .1);
1592
+ // Limit strength to prevent extreme values
1593
+ // Calculate offset based on view angle (simulated by mouse position)
1594
+ return {
1595
+ x: (x - mouseX) * parallaxStrength,
1596
+ y: (y - mouseY) * parallaxStrength
1597
+ };
1598
+ }, fragmentShaders = {
1599
+ liquidGlass: (uv, mousePosition) => {
1600
+ if (!validateVec2(uv)) return {
1601
+ x: .5,
1602
+ y: .5
1603
+ };
1604
+ 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) => {
1605
+ // Add input validation
1606
+ if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y) || isNaN(strength)) return {
1607
+ x: 0,
1608
+ y: 0
1609
+ };
1610
+ const distance = calculateLength(x, y), distortion = Math.pow(Math.min(distance, 10), 2) * strength;
1611
+ // Limit distance to prevent extreme values
1612
+ return {
1613
+ x: x * (1 + distortion),
1614
+ y: y * (1 + distortion)
1615
+ };
1616
+ })(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) => {
1617
+ // Add input validation
1618
+ if ("number" != typeof x || "number" != typeof y || "number" != typeof intensity || isNaN(x) || isNaN(y) || isNaN(intensity)) return {
1619
+ x: 0,
1620
+ y: 0
1621
+ };
1622
+ const distance = calculateLength(x, y);
1623
+ // Prevent division by zero and extreme values
1624
+ if (0 === distance) return {
1625
+ x: 0,
1626
+ y: 0
1627
+ };
1628
+ const angle = Math.atan2(y, x);
1629
+ return {
1630
+ x: Math.cos(angle) * distance * intensity,
1631
+ y: Math.sin(angle) * distance * intensity
1632
+ };
1633
+ })(ix, iy, .015 * baseDisplacement), scaled = smoothStep(0, 1, 1.15 * baseDisplacement), finalX = ix + totalDistortionX + .5 * chromaticOffset.x, finalY = iy + totalDistortionY + .5 * chromaticOffset.y;
1634
+ return createTexture(clampValue(finalX * scaled + .5, 0, 1), clampValue(finalY * scaled + .5, 0, 1));
1635
+ },
1636
+ // Premium Apple-style fluid glass with enhanced organic flow
1637
+ appleFluid: (uv, mousePosition) => {
1638
+ if (!validateVec2(uv)) return {
1639
+ x: .5,
1640
+ y: .5
1641
+ };
1642
+ 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;
1643
+ return createTexture(clampValue(ix + (.035 * organicX + fluidVelocityX + vortexX) * mask + .5, 0, 1), clampValue(totalY + .5, 0, 1));
1644
+ },
1645
+ // High-end glass with advanced refraction and depth
1646
+ premiumGlass: (uv, mousePosition) => {
1647
+ if (!validateVec2(uv)) return {
1648
+ x: .5,
1649
+ y: .5
1650
+ };
1651
+ 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);
1652
+ // Multi-layer depth effect
1653
+ let depthX = 0, depthY = 0;
1654
+ for (let layer = 0; layer < 3; layer++) {
1655
+ const layerScale = 5 * (layer + 1), layerTime = time * (1 + .3 * layer), layerStrength = .01 / (layer + 1);
1656
+ depthX += Math.sin(ix * layerScale + layerTime) * layerStrength, depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
1657
+ }
1658
+ // Glass refraction with mouse influence
1659
+ 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;
1660
+ return createTexture(clampValue(ix + (refractionX + depthX + .015 * organicNoise) * edgeMask + .5, 0, 1), clampValue(finalY + .5, 0, 1));
1661
+ },
1662
+ // Metallic liquid effect with shimmer
1663
+ liquidMetal: (uv, mousePosition) => {
1664
+ if (!validateVec2(uv)) return {
1665
+ x: .5,
1666
+ y: .5
1667
+ };
1668
+ 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;
1669
+ return createTexture(clampValue(totalX + .5, 0, 1), clampValue(totalY + .5, 0, 1));
1670
+ },
1671
+ // basiBasi - Expert Premium Glass Shader
1672
+ // The most advanced shader with caustics, spectral dispersion, parallax depth, and volumetric effects
1673
+ basiBasi: (uv, mousePosition) => {
1674
+ if (!validateVec2(uv)) return {
1675
+ x: .5,
1676
+ y: .5
1677
+ };
1678
+ 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) =>
1679
+ // Add input validation
1680
+ "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) => {
1681
+ // Add input validation
1682
+ if ("number" != typeof x || "number" != typeof y || "number" != typeof angle || isNaN(x) || isNaN(y) || isNaN(angle) || isNaN(.025)) return {
1683
+ r: {
1684
+ x: 0,
1685
+ y: 0
1686
+ },
1687
+ g: {
1688
+ x: 0,
1689
+ y: 0
1690
+ },
1691
+ b: {
1692
+ x: 0,
1693
+ y: 0
1694
+ }
1695
+ };
1696
+ const distance = calculateLength(x, y), dispersionStrength = Math.min(.025 * distance, 1), redOffset = .8 * dispersionStrength, greenOffset = 1 * dispersionStrength, blueOffset = 1.2 * dispersionStrength;
1697
+ return {
1698
+ r: {
1699
+ x: Math.cos(angle) * redOffset,
1700
+ y: Math.sin(angle) * redOffset
1701
+ },
1702
+ g: {
1703
+ x: Math.cos(angle) * greenOffset,
1704
+ y: Math.sin(angle) * greenOffset
1705
+ },
1706
+ b: {
1707
+ x: Math.cos(angle) * blueOffset,
1708
+ y: Math.sin(angle) * blueOffset
1709
+ }
1710
+ };
1711
+ })(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;
1712
+ // === MULTI-LAYER PARALLAX DEPTH ===
1713
+ // Create depth perception with 7 layers
1714
+ let parallaxX = 0, parallaxY = 0;
1715
+ for (let layer = 0; layer < 7; layer++) {
1716
+ 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);
1717
+ parallaxX += (parallaxOffset.x + .01 * layerNoise) * layerWeight, parallaxY += (parallaxOffset.y + .01 * layerNoise) * layerWeight;
1718
+ }
1719
+ // Normalize parallax effect
1720
+ parallaxX /= 7, parallaxY /= 7;
1721
+ // === VOLUMETRIC SCATTERING ===
1722
+ // Simulate light scattering through glass volume
1723
+ const volumetricDensity = ((x, y, depth, time) =>
1724
+ // Add input validation
1725
+ "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) => {
1726
+ // Add input validation
1727
+ if ("number" != typeof x || "number" != typeof y || "number" != typeof time || "number" != typeof octaves || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(octaves)) return 0;
1728
+ // Clamp octaves to prevent performance issues
1729
+ const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
1730
+ let turbulence = 0, amplitude = 1, frequency = 1;
1731
+ for (let i = 0; i < clampedOctaves; i++) turbulence += Math.abs(noise2D(x * frequency + time, y * frequency - time)) * amplitude,
1732
+ frequency *= 2, amplitude *= .5;
1733
+ return turbulence;
1734
+ })(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) =>
1735
+ // Add input validation
1736
+ "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;
1737
+ 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));
1738
+ }
1739
+ }, shaderUtils = Object.freeze( Object.defineProperty({
1740
+ __proto__: null,
1741
+ ShaderDisplacementGenerator: class {
1742
+ constructor(options) {
1743
+ if (this.options = options, this.canvasDPI = 1, !this.validateOptions(options)) throw new Error("Invalid shader options provided");
1744
+ this.canvas = document.createElement("canvas"),
1745
+ // Enhanced validation for canvas dimensions
1746
+ this.canvas.width = Math.max(1, Math.min(4096, Math.round(options.width * this.canvasDPI || 256))),
1747
+ this.canvas.height = Math.max(1, Math.min(4096, Math.round(options.height * this.canvasDPI || 256))),
1748
+ this.canvas.style.display = "none";
1749
+ const context = this.canvas.getContext("2d");
1750
+ if (!context) throw new Error("AtomixGlass: Could not get 2D canvas context");
1751
+ this.context = context;
1752
+ }
1753
+ validateOptions(options) {
1754
+ try {
1755
+ 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;
1756
+ } catch (e) {
1757
+ // Graceful error handling
1758
+ return !1;
1759
+ }
1760
+ }
1761
+ updateShader(mousePosition) {
1762
+ try {
1763
+ const w = this.options.width * this.canvasDPI, h = this.options.height * this.canvasDPI;
1764
+ let maxScale = 0;
1765
+ const rawValues = [];
1766
+ // Calculate displacement values with enhanced smoothing
1767
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
1768
+ const uv = {
1769
+ x: x / w,
1770
+ y: y / h
1771
+ }, pos = this.options.fragment(uv, mousePosition);
1772
+ let dx = pos.x * w - x, dy = pos.y * h - y;
1773
+ // Apply edge smoothing for Apple-like effect
1774
+ const edgeX = 2 * Math.min(x / w, (w - x) / w), edgeY = 2 * Math.min(y / h, (h - y) / h), edgeFactor = Math.min(edgeX, edgeY);
1775
+ dx *= smoothStep(0, .2, edgeFactor), dy *= smoothStep(0, .2, edgeFactor), maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy)),
1776
+ rawValues.push(dx, dy);
1777
+ }
1778
+ // Improved normalization to prevent artifacts while maintaining intensity
1779
+ maxScale = Math.max(maxScale, 1);
1780
+ // Create ImageData and fill it
1781
+ const imageData = this.context.createImageData(w, h), data = imageData.data;
1782
+ // Convert to image data with smoother normalization
1783
+ let rawIndex = 0;
1784
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
1785
+ 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);
1786
+ data[pixelIndex] = clampValue(255 * r, 0, 255), // Red channel (X displacement)
1787
+ data[pixelIndex + 1] = clampValue(255 * g, 0, 255), // Green channel (Y displacement)
1788
+ data[pixelIndex + 2] = clampValue(255 * g, 0, 255), // Blue channel (Y displacement for SVG filter compatibility)
1789
+ data[pixelIndex + 3] = 255;
1790
+ }
1791
+ return this.context.putImageData(imageData, 0, 0), this.canvas.toDataURL();
1792
+ } catch (error) {
1793
+ // Graceful fallback on error
1794
+ return console.warn("ShaderDisplacementGenerator: Error generating shader map, using fallback", error),
1795
+ "";
1796
+ // Return empty string as fallback
1797
+ }
1798
+ }
1799
+ destroy() {
1800
+ try {
1801
+ // Clear canvas data to free memory
1802
+ this.context && this.context.clearRect(0, 0, this.canvas.width, this.canvas.height),
1803
+ // Reduce memory footprint by setting dimensions to 0
1804
+ this.canvas.width = 0, this.canvas.height = 0,
1805
+ // Remove from DOM
1806
+ this.canvas.remove();
1807
+ } catch (e) {
1808
+ // Silently handle cleanup errors
1809
+ console.warn("ShaderDisplacementGenerator: Error during cleanup", e);
1810
+ }
1811
+ }
1812
+ getScale() {
1813
+ return this.canvasDPI;
1814
+ }
1815
+ },
1816
+ fragmentShaders: fragmentShaders
1817
+ }, Symbol.toStringTag, {
1818
+ value: "Module"
1819
+ }));
1820
+
1821
+ var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {};
1822
+
1823
+ function getDefaultExportFromCjs(x) {
1824
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
1825
+ }
1826
+
1827
+ var fails$8 = function(exec) {
1828
+ try {
1829
+ return !!exec();
1830
+ } catch (error) {
1831
+ return !0;
1832
+ }
1833
+ }, functionBindNative = !fails$8((function() {
1834
+ // eslint-disable-next-line es/no-function-prototype-bind -- safe
1835
+ var test = function() {/* empty */}.bind();
1836
+ // eslint-disable-next-line no-prototype-builtins -- safe
1837
+ return "function" != typeof test || test.hasOwnProperty("prototype");
1838
+ })), NATIVE_BIND$3 = functionBindNative, FunctionPrototype$1 = Function.prototype, call$5 = FunctionPrototype$1.call, uncurryThisWithBind = NATIVE_BIND$3 && FunctionPrototype$1.bind.bind(call$5, call$5), functionUncurryThis = NATIVE_BIND$3 ? uncurryThisWithBind : function(fn) {
1839
+ return function() {
1840
+ return call$5.apply(fn, arguments);
1841
+ };
1842
+ }, objectIsPrototypeOf = functionUncurryThis({}.isPrototypeOf), check = function(it) {
1843
+ return it && it.Math === Math && it;
1844
+ }, globalThis_1 =
1845
+ // eslint-disable-next-line es/no-global-this -- safe
1846
+ check("object" == typeof globalThis && globalThis) || check("object" == typeof window && window) ||
1847
+ // eslint-disable-next-line no-restricted-globals -- safe
1848
+ check("object" == typeof self && self) || check("object" == typeof commonjsGlobal && commonjsGlobal) || check("object" == typeof commonjsGlobal && commonjsGlobal) ||
1849
+ // eslint-disable-next-line no-new-func -- fallback
1850
+ function() {
1851
+ return this;
1852
+ }() || Function("return this")(), NATIVE_BIND$2 = functionBindNative, FunctionPrototype = Function.prototype, apply$1 = FunctionPrototype.apply, call$4 = FunctionPrototype.call, functionApply = "object" == typeof Reflect && Reflect.apply || (NATIVE_BIND$2 ? call$4.bind(apply$1) : function() {
1853
+ return call$4.apply(apply$1, arguments);
1854
+ }), uncurryThis$7 = functionUncurryThis, toString$3 = uncurryThis$7({}.toString), stringSlice = uncurryThis$7("".slice), classofRaw$2 = function(it) {
1855
+ return stringSlice(toString$3(it), 8, -1);
1856
+ }, classofRaw$1 = classofRaw$2, uncurryThis$6 = functionUncurryThis, functionUncurryThisClause = function(fn) {
1857
+ // Nashorn bug:
1858
+ // https://github.com/zloirock/core-js/issues/1128
1859
+ // https://github.com/zloirock/core-js/issues/1130
1860
+ if ("Function" === classofRaw$1(fn)) return uncurryThis$6(fn);
1861
+ }, documentAll = "object" == typeof document && document.all, isCallable$8 = void 0 === documentAll && void 0 !== documentAll ? function(argument) {
1862
+ return "function" == typeof argument || argument === documentAll;
1863
+ } : function(argument) {
1864
+ return "function" == typeof argument;
1865
+ }, objectGetOwnPropertyDescriptor = {}, descriptors = !fails$8((function() {
1866
+ // eslint-disable-next-line es/no-object-defineproperty -- required for testing
1867
+ return 7 !== Object.defineProperty({}, 1, {
1868
+ get: function() {
1869
+ return 7;
1870
+ }
1871
+ })[1];
1872
+ })), NATIVE_BIND$1 = functionBindNative, call$3 = Function.prototype.call, functionCall = NATIVE_BIND$1 ? call$3.bind(call$3) : function() {
1873
+ return call$3.apply(call$3, arguments);
1874
+ }, objectPropertyIsEnumerable = {}, $propertyIsEnumerable = {}.propertyIsEnumerable, getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor, NASHORN_BUG = getOwnPropertyDescriptor$1 && !$propertyIsEnumerable.call({
1875
+ 1: 2
1876
+ }, 1);
1877
+
1878
+ // `Object.prototype.propertyIsEnumerable` method implementation
1879
+ // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
1880
+ objectPropertyIsEnumerable.f = NASHORN_BUG ? function(V) {
1881
+ var descriptor = getOwnPropertyDescriptor$1(this, V);
1882
+ return !!descriptor && descriptor.enumerable;
1883
+ } : $propertyIsEnumerable;
1884
+
1885
+ var match, version, createPropertyDescriptor$2 = function(bitmap, value) {
1886
+ return {
1887
+ enumerable: !(1 & bitmap),
1888
+ configurable: !(2 & bitmap),
1889
+ writable: !(4 & bitmap),
1890
+ value: value
1891
+ };
1892
+ }, fails$5 = fails$8, classof$3 = classofRaw$2, $Object$3 = Object, split = functionUncurryThis("".split), indexedObject = fails$5((function() {
1893
+ // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
1894
+ // eslint-disable-next-line no-prototype-builtins -- safe
1895
+ return !$Object$3("z").propertyIsEnumerable(0);
1896
+ })) ? function(it) {
1897
+ return "String" === classof$3(it) ? split(it, "") : $Object$3(it);
1898
+ } : $Object$3, isNullOrUndefined$2 = function(it) {
1899
+ return null == it;
1900
+ }, isNullOrUndefined$1 = isNullOrUndefined$2, $TypeError$6 = TypeError, requireObjectCoercible$3 = function(it) {
1901
+ if (isNullOrUndefined$1(it)) throw new $TypeError$6("Can't call method on " + it);
1902
+ return it;
1903
+ }, IndexedObject = indexedObject, requireObjectCoercible$2 = requireObjectCoercible$3, toIndexedObject$2 = function(it) {
1904
+ return IndexedObject(requireObjectCoercible$2(it));
1905
+ }, isCallable$7 = isCallable$8, isObject$5 = function(it) {
1906
+ return "object" == typeof it ? null !== it : isCallable$7(it);
1907
+ }, path$3 = {}, path$2 = path$3, globalThis$a = globalThis_1, isCallable$6 = isCallable$8, aFunction = function(variable) {
1908
+ return isCallable$6(variable) ? variable : void 0;
1909
+ }, navigator$1 = globalThis_1.navigator, userAgent$1 = navigator$1 && navigator$1.userAgent, globalThis$8 = globalThis_1, userAgent = userAgent$1 ? String(userAgent$1) : "", process$1 = globalThis$8.process, Deno = globalThis$8.Deno, versions = process$1 && process$1.versions || Deno && Deno.version, v8 = versions && versions.v8;
1910
+
1911
+ v8 && (
1912
+ // in old Chrome, versions of V8 isn't V8 = Chrome / 10
1913
+ // but their correct versions are not interesting for us
1914
+ version = (match = v8.split("."))[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1])),
1915
+ // BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0`
1916
+ // so check `userAgent` even if `.v8` exists, but 0
1917
+ !version && userAgent && (!(match = userAgent.match(/Edge\/(\d+)/)) || match[1] >= 74) && (match = userAgent.match(/Chrome\/(\d+)/)) && (version = +match[1]);
1918
+
1919
+ var V8_VERSION = version, fails$4 = fails$8, $String$3 = globalThis_1.String, symbolConstructorDetection = !!Object.getOwnPropertySymbols && !fails$4((function() {
1920
+ var symbol = Symbol("symbol detection");
1921
+ // Chrome 38 Symbol has incorrect toString conversion
1922
+ // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances
1923
+ // nb: Do not call `String` directly to avoid this being optimized out to `symbol+''` which will,
1924
+ // of course, fail.
1925
+ return !$String$3(symbol) || !(Object(symbol) instanceof Symbol) ||
1926
+ // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
1927
+ !Symbol.sham && V8_VERSION && V8_VERSION < 41;
1928
+ })), useSymbolAsUid = symbolConstructorDetection && !Symbol.sham && "symbol" == typeof Symbol.iterator, isCallable$5 = isCallable$8, isPrototypeOf$1 = objectIsPrototypeOf, $Object$2 = Object, isSymbol$2 = useSymbolAsUid ? function(it) {
1929
+ return "symbol" == typeof it;
1930
+ } : function(it) {
1931
+ var $Symbol = function(namespace, method) {
1932
+ return arguments.length < 2 ? aFunction(path$2[namespace]) || aFunction(globalThis$a[namespace]) : path$2[namespace] && path$2[namespace][method] || globalThis$a[namespace] && globalThis$a[namespace][method];
1933
+ }("Symbol");
1934
+ return isCallable$5($Symbol) && isPrototypeOf$1($Symbol.prototype, $Object$2(it));
1935
+ }, $String$2 = String, isCallable$4 = isCallable$8, $TypeError$5 = TypeError, aCallable$2 = function(argument) {
1936
+ if (isCallable$4(argument)) return argument;
1937
+ throw new $TypeError$5(function(argument) {
1938
+ try {
1939
+ return $String$2(argument);
1940
+ } catch (error) {
1941
+ return "Object";
1942
+ }
1943
+ }(argument) + " is not a function");
1944
+ }, aCallable$1 = aCallable$2, isNullOrUndefined = isNullOrUndefined$2, call$2 = functionCall, isCallable$3 = isCallable$8, isObject$4 = isObject$5, $TypeError$4 = TypeError, sharedStore = {
1945
+ exports: {}
1946
+ }, globalThis$6 = globalThis_1, defineProperty = Object.defineProperty, globalThis$5 = globalThis_1, store$1 = sharedStore.exports = globalThis$5["__core-js_shared__"] || function(key, value) {
1947
+ try {
1948
+ defineProperty(globalThis$6, key, {
1949
+ value: value,
1950
+ configurable: !0,
1951
+ writable: !0
1952
+ });
1953
+ } catch (error) {
1954
+ globalThis$6[key] = value;
1955
+ }
1956
+ return value;
1957
+ }("__core-js_shared__", {});
1958
+
1959
+ /* eslint-disable es/no-symbol -- required for testing */ (store$1.versions || (store$1.versions = [])).push({
1960
+ version: "3.43.0",
1961
+ mode: "pure",
1962
+ copyright: "© 2014-2025 Denis Pushkarev (zloirock.ru)",
1963
+ license: "https://github.com/zloirock/core-js/blob/v3.43.0/LICENSE",
1964
+ source: "https://github.com/zloirock/core-js"
1965
+ });
1966
+
1967
+ var key, value, store = sharedStore.exports, requireObjectCoercible$1 = requireObjectCoercible$3, $Object$1 = Object, hasOwnProperty = functionUncurryThis({}.hasOwnProperty), hasOwnProperty_1 = Object.hasOwn || function(it, key) {
1968
+ return hasOwnProperty($Object$1(requireObjectCoercible$1(it)), key);
1969
+ }, uncurryThis$3 = functionUncurryThis, id = 0, postfix = Math.random(), toString$2 = uncurryThis$3(1.1.toString), hasOwn$2 = hasOwnProperty_1, NATIVE_SYMBOL = symbolConstructorDetection, USE_SYMBOL_AS_UID = useSymbolAsUid, Symbol$1 = globalThis_1.Symbol, WellKnownSymbolsStore = store[key = "wks"] || (store[key] = value || {}), createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol$1.for || Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || function(key) {
1970
+ return "Symbol(" + (void 0 === key ? "" : key) + ")_" + toString$2(++id + postfix, 36);
1971
+ }, wellKnownSymbol$5 = function(name) {
1972
+ return hasOwn$2(WellKnownSymbolsStore, name) || (WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn$2(Symbol$1, name) ? Symbol$1[name] : createWellKnownSymbol("Symbol." + name)),
1973
+ WellKnownSymbolsStore[name];
1974
+ }, call$1 = functionCall, isObject$3 = isObject$5, isSymbol$1 = isSymbol$2, $TypeError$3 = TypeError, TO_PRIMITIVE = wellKnownSymbol$5("toPrimitive"), toPrimitive = function(input, pref) {
1975
+ if (!isObject$3(input) || isSymbol$1(input)) return input;
1976
+ var result, func, exoticToPrim = (func = input[TO_PRIMITIVE], isNullOrUndefined(func) ? void 0 : aCallable$1(func));
1977
+ if (exoticToPrim) {
1978
+ if (void 0 === pref && (pref = "default"), result = call$1(exoticToPrim, input, pref),
1979
+ !isObject$3(result) || isSymbol$1(result)) return result;
1980
+ throw new $TypeError$3("Can't convert object to primitive value");
1981
+ }
1982
+ return void 0 === pref && (pref = "number"), function(input, pref) {
1983
+ var fn, val;
1984
+ if ("string" === pref && isCallable$3(fn = input.toString) && !isObject$4(val = call$2(fn, input))) return val;
1985
+ if (isCallable$3(fn = input.valueOf) && !isObject$4(val = call$2(fn, input))) return val;
1986
+ if ("string" !== pref && isCallable$3(fn = input.toString) && !isObject$4(val = call$2(fn, input))) return val;
1987
+ throw new $TypeError$4("Can't convert object to primitive value");
1988
+ }(input, pref);
1989
+ }, isSymbol = isSymbol$2, toPropertyKey$2 = function(argument) {
1990
+ var key = toPrimitive(argument, "string");
1991
+ return isSymbol(key) ? key : key + "";
1992
+ }, isObject$2 = isObject$5, document$1 = globalThis_1.document, EXISTS = isObject$2(document$1) && isObject$2(document$1.createElement), ie8DomDefine = !descriptors && !fails$8((function() {
1993
+ // eslint-disable-next-line es/no-object-defineproperty -- required for testing
1994
+ return 7 !== Object.defineProperty((it = "div", EXISTS ? document$1.createElement(it) : {}), "a", {
1995
+ get: function() {
1996
+ return 7;
1997
+ }
1998
+ }).a;
1999
+ var it;
2000
+ })), DESCRIPTORS$3 = descriptors, call = functionCall, propertyIsEnumerableModule = objectPropertyIsEnumerable, createPropertyDescriptor$1 = createPropertyDescriptor$2, toIndexedObject$1 = toIndexedObject$2, toPropertyKey$1 = toPropertyKey$2, hasOwn$1 = hasOwnProperty_1, IE8_DOM_DEFINE$1 = ie8DomDefine, $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor;
2001
+
2002
+ // `Object.getOwnPropertyDescriptor` method
2003
+ // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
2004
+ objectGetOwnPropertyDescriptor.f = DESCRIPTORS$3 ? $getOwnPropertyDescriptor$1 : function(O, P) {
2005
+ if (O = toIndexedObject$1(O), P = toPropertyKey$1(P), IE8_DOM_DEFINE$1) try {
2006
+ return $getOwnPropertyDescriptor$1(O, P);
2007
+ } catch (error) {/* empty */}
2008
+ if (hasOwn$1(O, P)) return createPropertyDescriptor$1(!call(propertyIsEnumerableModule.f, O, P), O[P]);
2009
+ };
2010
+
2011
+ var fails$2 = fails$8, isCallable$2 = isCallable$8, replacement = /#|\.prototype\./, isForced$1 = function(feature, detection) {
2012
+ var value = data[normalize(feature)];
2013
+ return value === POLYFILL || value !== NATIVE && (isCallable$2(detection) ? fails$2(detection) : !!detection);
2014
+ }, normalize = isForced$1.normalize = function(string) {
2015
+ return String(string).replace(replacement, ".").toLowerCase();
2016
+ }, data = isForced$1.data = {}, NATIVE = isForced$1.NATIVE = "N", POLYFILL = isForced$1.POLYFILL = "P", isForced_1 = isForced$1, aCallable = aCallable$2, NATIVE_BIND = functionBindNative, bind$1 = functionUncurryThisClause(functionUncurryThisClause.bind), objectDefineProperty = {}, v8PrototypeDefineBug = descriptors && fails$8((function() {
2017
+ // eslint-disable-next-line es/no-object-defineproperty -- required for testing
2018
+ return 42 !== Object.defineProperty((function() {/* empty */}), "prototype", {
2019
+ value: 42,
2020
+ writable: !1
2021
+ }).prototype;
2022
+ })), isObject$1 = isObject$5, $String$1 = String, $TypeError$2 = TypeError, DESCRIPTORS$1 = descriptors, IE8_DOM_DEFINE = ie8DomDefine, V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug, anObject = function(argument) {
2023
+ if (isObject$1(argument)) return argument;
2024
+ throw new $TypeError$2($String$1(argument) + " is not an object");
2025
+ }, toPropertyKey = toPropertyKey$2, $TypeError$1 = TypeError, $defineProperty = Object.defineProperty, $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
2026
+
2027
+ // `Object.defineProperty` method
2028
+ // https://tc39.es/ecma262/#sec-object.defineproperty
2029
+ objectDefineProperty.f = DESCRIPTORS$1 ? V8_PROTOTYPE_DEFINE_BUG ? function(O, P, Attributes) {
2030
+ if (anObject(O), P = toPropertyKey(P), anObject(Attributes), "function" == typeof O && "prototype" === P && "value" in Attributes && "writable" in Attributes && !Attributes.writable) {
2031
+ var current = $getOwnPropertyDescriptor(O, P);
2032
+ current && current.writable && (O[P] = Attributes.value, Attributes = {
2033
+ configurable: "configurable" in Attributes ? Attributes.configurable : current.configurable,
2034
+ enumerable: "enumerable" in Attributes ? Attributes.enumerable : current.enumerable,
2035
+ writable: !1
2036
+ });
2037
+ }
2038
+ return $defineProperty(O, P, Attributes);
2039
+ } : $defineProperty : function(O, P, Attributes) {
2040
+ if (anObject(O), P = toPropertyKey(P), anObject(Attributes), IE8_DOM_DEFINE) try {
2041
+ return $defineProperty(O, P, Attributes);
2042
+ } catch (error) {/* empty */}
2043
+ if ("get" in Attributes || "set" in Attributes) throw new $TypeError$1("Accessors not supported");
2044
+ return "value" in Attributes && (O[P] = Attributes.value), O;
2045
+ };
2046
+
2047
+ var definePropertyModule = objectDefineProperty, createPropertyDescriptor = createPropertyDescriptor$2, createNonEnumerableProperty$1 = descriptors ? function(object, key, value) {
2048
+ return definePropertyModule.f(object, key, createPropertyDescriptor(1, value));
2049
+ } : function(object, key, value) {
2050
+ return object[key] = value, object;
2051
+ }, globalThis$2 = globalThis_1, apply = functionApply, uncurryThis$1 = functionUncurryThisClause, isCallable$1 = isCallable$8, getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f, isForced = isForced_1, path$1 = path$3, bind = function(fn, that) {
2052
+ return aCallable(fn), void 0 === that ? fn : NATIVE_BIND ? bind$1(fn, that) : function() {
2053
+ return fn.apply(that, arguments);
2054
+ };
2055
+ }, createNonEnumerableProperty = createNonEnumerableProperty$1, hasOwn = hasOwnProperty_1, wrapConstructor = function(NativeConstructor) {
2056
+ var Wrapper = function(a, b, c) {
2057
+ if (this instanceof Wrapper) {
2058
+ switch (arguments.length) {
2059
+ case 0:
2060
+ return new NativeConstructor;
2061
+
2062
+ case 1:
2063
+ return new NativeConstructor(a);
2064
+
2065
+ case 2:
2066
+ return new NativeConstructor(a, b);
2067
+ }
2068
+ return new NativeConstructor(a, b, c);
2069
+ }
2070
+ return apply(NativeConstructor, this, arguments);
2071
+ };
2072
+ return Wrapper.prototype = NativeConstructor.prototype, Wrapper;
2073
+ }, _export = function(options, source) {
2074
+ var FORCED, USE_NATIVE, VIRTUAL_PROTOTYPE, key, sourceProperty, targetProperty, nativeProperty, resultProperty, descriptor, TARGET = options.target, GLOBAL = options.global, STATIC = options.stat, PROTO = options.proto, nativeSource = GLOBAL ? globalThis$2 : STATIC ? globalThis$2[TARGET] : globalThis$2[TARGET] && globalThis$2[TARGET].prototype, target = GLOBAL ? path$1 : path$1[TARGET] || createNonEnumerableProperty(path$1, TARGET, {})[TARGET], targetPrototype = target.prototype;
2075
+ for (key in source)
2076
+ // contains in native
2077
+ USE_NATIVE = !(FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? "." : "#") + key, options.forced)) && nativeSource && hasOwn(nativeSource, key),
2078
+ targetProperty = target[key], USE_NATIVE && (nativeProperty = options.dontCallGetSet ? (descriptor = getOwnPropertyDescriptor(nativeSource, key)) && descriptor.value : nativeSource[key]),
2079
+ // export native or implementation
2080
+ sourceProperty = USE_NATIVE && nativeProperty ? nativeProperty : source[key], (FORCED || PROTO || typeof targetProperty != typeof sourceProperty) && (
2081
+ // bind methods to global for calling from export context
2082
+ resultProperty = options.bind && USE_NATIVE ? bind(sourceProperty, globalThis$2) : options.wrap && USE_NATIVE ? wrapConstructor(sourceProperty) : PROTO && isCallable$1(sourceProperty) ? uncurryThis$1(sourceProperty) : sourceProperty,
2083
+ // add a flag to not completely full polyfills
2084
+ (options.sham || sourceProperty && sourceProperty.sham || targetProperty && targetProperty.sham) && createNonEnumerableProperty(resultProperty, "sham", !0),
2085
+ createNonEnumerableProperty(target, key, resultProperty), PROTO && (hasOwn(path$1, VIRTUAL_PROTOTYPE = TARGET + "Prototype") || createNonEnumerableProperty(path$1, VIRTUAL_PROTOTYPE, {}),
2086
+ // export virtual prototype methods
2087
+ createNonEnumerableProperty(path$1[VIRTUAL_PROTOTYPE], key, sourceProperty),
2088
+ // export real prototype methods
2089
+ options.real && targetPrototype && (FORCED || !targetPrototype[key]) && createNonEnumerableProperty(targetPrototype, key, sourceProperty)));
2090
+ }, ceil = Math.ceil, floor = Math.floor, trunc = Math.trunc || function(x) {
2091
+ var n = +x;
2092
+ return (n > 0 ? floor : ceil)(n);
2093
+ }, toIntegerOrInfinity$2 = function(argument) {
2094
+ var number = +argument;
2095
+ // eslint-disable-next-line no-self-compare -- NaN check
2096
+ return number != number || 0 === number ? 0 : trunc(number);
2097
+ }, toIntegerOrInfinity$1 = toIntegerOrInfinity$2, max = Math.max, min$1 = Math.min, toIntegerOrInfinity = toIntegerOrInfinity$2, min = Math.min, toIndexedObject = toIndexedObject$2, lengthOfArrayLike = function(obj) {
2098
+ return argument = obj.length, (len = toIntegerOrInfinity(argument)) > 0 ? min(len, 9007199254740991) : 0;
2099
+ var argument, len;
2100
+ }, createMethod = function(IS_INCLUDES) {
2101
+ return function($this, el, fromIndex) {
2102
+ var O = toIndexedObject($this), length = lengthOfArrayLike(O);
2103
+ if (0 === length) return !IS_INCLUDES && -1;
2104
+ var value, index = function(index, length) {
2105
+ var integer = toIntegerOrInfinity$1(index);
2106
+ return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
2107
+ }(fromIndex, length);
2108
+ // Array#includes uses SameValueZero equality algorithm
2109
+ // eslint-disable-next-line no-self-compare -- NaN check
2110
+ if (IS_INCLUDES && el != el) {
2111
+ for (;length > index; )
2112
+ // eslint-disable-next-line no-self-compare -- NaN check
2113
+ if ((value = O[index++]) != value) return !0;
2114
+ // Array#indexOf ignores holes, Array#includes - not
2115
+ } else for (;length > index; index++) if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
2116
+ return !IS_INCLUDES && -1;
2117
+ };
2118
+ }, $includes = [ createMethod(!0), createMethod(!1) ][0];
2119
+
2120
+ // `Array.prototype.includes` method
2121
+ // https://tc39.es/ecma262/#sec-array.prototype.includes
2122
+ _export({
2123
+ target: "Array",
2124
+ proto: !0,
2125
+ forced: fails$8((function() {
2126
+ // eslint-disable-next-line es/no-array-prototype-includes -- detection
2127
+ return !Array(1).includes();
2128
+ }))
2129
+ }, {
2130
+ includes: function(el /* , fromIndex = 0 */) {
2131
+ return $includes(this, el, arguments.length > 1 ? arguments[1] : void 0);
2132
+ }
2133
+ });
2134
+
2135
+ var globalThis$1 = globalThis_1, path = path$3, getBuiltInPrototypeMethod$2 = function(CONSTRUCTOR, METHOD) {
2136
+ var Namespace = path[CONSTRUCTOR + "Prototype"], pureMethod = Namespace && Namespace[METHOD];
2137
+ if (pureMethod) return pureMethod;
2138
+ var NativeConstructor = globalThis$1[CONSTRUCTOR], NativePrototype = NativeConstructor && NativeConstructor.prototype;
2139
+ return NativePrototype && NativePrototype[METHOD];
2140
+ }, includes$4 = getBuiltInPrototypeMethod$2("Array", "includes"), isObject = isObject$5, classof$2 = classofRaw$2, MATCH$1 = wellKnownSymbol$5("match"), $TypeError = TypeError, test = {};
2141
+
2142
+ test[wellKnownSymbol$5("toStringTag")] = "z";
2143
+
2144
+ var TO_STRING_TAG_SUPPORT = "[object z]" === String(test), isCallable = isCallable$8, classofRaw = classofRaw$2, TO_STRING_TAG = wellKnownSymbol$5("toStringTag"), $Object = Object, CORRECT_ARGUMENTS = "Arguments" === classofRaw(function() {
2145
+ return arguments;
2146
+ }()), classof = TO_STRING_TAG_SUPPORT ? classofRaw : function(it) {
2147
+ var O, tag, result;
2148
+ return void 0 === it ? "Undefined" : null === it ? "Null" : "string" == typeof (tag = function(it, key) {
2149
+ try {
2150
+ return it[key];
2151
+ } catch (error) {/* empty */}
2152
+ }(O = $Object(it), TO_STRING_TAG)) ? tag : CORRECT_ARGUMENTS ? classofRaw(O) : "Object" === (result = classofRaw(O)) && isCallable(O.callee) ? "Arguments" : result;
2153
+ }, $String = String, MATCH = wellKnownSymbol$5("match"), $ = _export, notARegExp = function(it) {
2154
+ if (function(it) {
2155
+ var isRegExp;
2156
+ return isObject(it) && (void 0 !== (isRegExp = it[MATCH$1]) ? !!isRegExp : "RegExp" === classof$2(it));
2157
+ }(it)) throw new $TypeError("The method doesn't accept regular expressions");
2158
+ return it;
2159
+ }, requireObjectCoercible = requireObjectCoercible$3, toString = function(argument) {
2160
+ if ("Symbol" === classof(argument)) throw new TypeError("Cannot convert a Symbol value to a string");
2161
+ return $String(argument);
2162
+ }, stringIndexOf = functionUncurryThis("".indexOf);
2163
+
2164
+ // `String.prototype.includes` method
2165
+ // https://tc39.es/ecma262/#sec-string.prototype.includes
2166
+ $({
2167
+ target: "String",
2168
+ proto: !0,
2169
+ forced: !function(METHOD_NAME) {
2170
+ var regexp = /./;
2171
+ try {
2172
+ "/./"[METHOD_NAME](regexp);
2173
+ } catch (error1) {
2174
+ try {
2175
+ return regexp[MATCH] = !1, "/./"[METHOD_NAME](regexp);
2176
+ } catch (error2) {/* empty */}
2177
+ }
2178
+ return !1;
2179
+ }("includes")
2180
+ }, {
2181
+ includes: function(searchString /* , position = 0 */) {
2182
+ return !!~stringIndexOf(toString(requireObjectCoercible(this)), toString(notARegExp(searchString)), arguments.length > 1 ? arguments[1] : void 0);
2183
+ }
2184
+ });
2185
+
2186
+ var includes$3 = getBuiltInPrototypeMethod$2("String", "includes"), isPrototypeOf = objectIsPrototypeOf, arrayMethod = includes$4, stringMethod = includes$3, ArrayPrototype = Array.prototype, StringPrototype = String.prototype;
2187
+
2188
+ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
2189
+ var own = it.includes;
2190
+ return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.includes ? arrayMethod : "string" == typeof it || it === StringPrototype || isPrototypeOf(StringPrototype, it) && own === StringPrototype.includes ? stringMethod : own;
2191
+ }));
2192
+
2193
+ /**
2194
+ * Component Utilities
2195
+ *
2196
+ * Helper functions for component development with the new customization system
2197
+ */
2198
+ /**
2199
+ * Check if a URL is a YouTube URL
2200
+ */
2201
+ function isYouTubeUrl(url) {
2202
+ return /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/.test(url);
2203
+ }
2204
+
2205
+ /**
2206
+ * Extract YouTube video ID from URL
2207
+ */
2208
+ /**
2209
+ * Advanced Video Player Component
2210
+ */
2211
+ const VideoPlayer = forwardRef((({src: src, type: type = "video", youtubeId: youtubeId, poster: poster, autoplay: autoplay = !1, loop: loop = !1, muted: muted = !1, controls: controls = !0, preload: preload = "metadata", width: width, height: height, aspectRatio: aspectRatio = "16:9", className: className = "", onPlay: onPlay, onPause: onPause, onEnded: onEnded, onTimeUpdate: onTimeUpdate, onVolumeChange: onVolumeChange, onFullscreenChange: onFullscreenChange, onError: onError, showDownload: showDownload = !1, showShare: showShare = !1, showSettings: showSettings = !0, playbackRates: playbackRates = [ .5, .75, 1, 1.25, 1.5, 2 ], subtitles: subtitles, quality: quality, ambientMode: ambientMode = !1, glass: glass = !1, glassOpacity: glassOpacity = 1, glassContent: glassContent, style: style, ...props}, ref) => {
2212
+ const videoRef = useRef(null), containerRef = useRef(null), canvasRef = useRef(null), iframeRef = useRef(null), [containerBorderRadius, setContainerBorderRadius] = useState(8), isYouTube = "youtube" === type || youtubeId || src && isYouTubeUrl(src), videoId = youtubeId || (isYouTube && src ? function(url) {
2213
+ if (!isYouTubeUrl(url)) return null;
2214
+ const patterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/.*[?&]v=([^&\n?#]+)/ ];
2215
+ for (const pattern of patterns) {
2216
+ const match = url.match(pattern);
2217
+ if (match && match[1]) return match[1];
2218
+ }
2219
+ return null;
2220
+ }(src) : null), {isPlaying: isPlaying, currentTime: currentTime, duration: duration, volume: volume, isMuted: isMuted, isFullscreen: isFullscreen, isLoading: isLoading, playbackRate: playbackRate, currentQuality: currentQuality, showControls: showControls, play: play, pause: pause, togglePlay: togglePlay, seek: seek, setVolume: setVolume, toggleMute: toggleMute, toggleFullscreen: toggleFullscreen, togglePictureInPicture: togglePictureInPicture, setPlaybackRate: setPlaybackRate, setQuality: setQuality, formatTime: formatTime, getProgressPercentage: getProgressPercentage, getBufferedPercentage: getBufferedPercentage} = function({videoRef: videoRef, containerRef: containerRef, onPlay: onPlay, onPause: onPause, onEnded: onEnded, onTimeUpdate: onTimeUpdate, onVolumeChange: onVolumeChange, onFullscreenChange: onFullscreenChange, onError: onError, playbackRates: playbackRates = [ .5, .75, 1, 1.25, 1.5, 2 ], quality: quality}) {
2221
+ const [isPlaying, setIsPlaying] = useState(!1), [currentTime, setCurrentTime] = useState(0), [duration, setDuration] = useState(0), [volume, setVolumeState] = useState(1), [isMuted, setIsMuted] = useState(!1), [isFullscreen, setIsFullscreen] = useState(!1), [isPictureInPicture, setIsPictureInPicture] = useState(!1), [isLoading, setIsLoading] = useState(!1), [buffered, setBuffered] = useState(0), [playbackRate, setPlaybackRateState] = useState(1), [currentQuality, setCurrentQuality] = useState(quality?.[0] || null), [showControls, setShowControls] = useState(!0), controlsTimeoutRef = useRef(null), resetControlsTimeout = useCallback((() => {
2222
+ controlsTimeoutRef.current && clearTimeout(controlsTimeoutRef.current), setShowControls(!0);
2223
+ const timeout = setTimeout((() => {
2224
+ isPlaying && setShowControls(!1);
2225
+ }), 3e3);
2226
+ controlsTimeoutRef.current = timeout;
2227
+ }), [ isPlaying ]), play = useCallback((async () => {
2228
+ if (videoRef.current) try {
2229
+ await videoRef.current.play(), setIsPlaying(!0), onPlay?.();
2230
+ } catch (error) {
2231
+ console.error("Error playing video:", error), onError?.(error), setIsPlaying(!1);
2232
+ }
2233
+ }), [ videoRef, onPlay, onError ]), pause = useCallback((() => {
2234
+ videoRef.current && (videoRef.current.pause(), setIsPlaying(!1), onPause?.());
2235
+ }), [ videoRef, onPause ]), togglePlay = useCallback((() => {
2236
+ isPlaying ? pause() : play();
2237
+ }), [ isPlaying, play, pause ]), seek = useCallback((time => {
2238
+ videoRef.current && (videoRef.current.currentTime = Math.max(0, Math.min(time, duration)));
2239
+ }), [ videoRef, duration ]), setVolume = useCallback((newVolume => {
2240
+ const clampedVolume = Math.max(0, Math.min(1, newVolume));
2241
+ videoRef.current && (videoRef.current.volume = clampedVolume, setVolumeState(clampedVolume),
2242
+ setIsMuted(0 === clampedVolume), onVolumeChange?.(clampedVolume));
2243
+ }), [ videoRef, onVolumeChange ]), toggleMute = useCallback((() => {
2244
+ if (videoRef.current) {
2245
+ const newMuted = !isMuted;
2246
+ videoRef.current.muted = newMuted, setIsMuted(newMuted);
2247
+ }
2248
+ }), [ videoRef, isMuted ]), toggleFullscreen = useCallback((async () => {
2249
+ if (containerRef.current) try {
2250
+ isFullscreen ? document.exitFullscreen && await document.exitFullscreen() : containerRef.current.requestFullscreen && await containerRef.current.requestFullscreen();
2251
+ } catch (error) {
2252
+ console.error("Fullscreen error:", error);
2253
+ }
2254
+ }), [ containerRef, isFullscreen ]), togglePictureInPicture = useCallback((async () => {
2255
+ if (videoRef.current) try {
2256
+ isPictureInPicture ? document.exitPictureInPicture && await document.exitPictureInPicture() : videoRef.current.requestPictureInPicture && await videoRef.current.requestPictureInPicture();
2257
+ } catch (error) {
2258
+ console.error("Picture-in-picture error:", error);
2259
+ }
2260
+ }), [ videoRef, isPictureInPicture ]), setPlaybackRate = useCallback((rate => {
2261
+ videoRef.current && _includesInstanceProperty(playbackRates).call(playbackRates, rate) && (videoRef.current.playbackRate = rate,
2262
+ setPlaybackRateState(rate));
2263
+ }), [ videoRef, playbackRates ]), setQuality = useCallback((newQuality => {
2264
+ if (videoRef.current && quality) {
2265
+ const currentTime = videoRef.current.currentTime, wasPlaying = !videoRef.current.paused, sanitizedSrc = String(newQuality.src).replace(/[<>"']/g, "");
2266
+ sanitizedSrc && (sanitizedSrc.startsWith("http://") || sanitizedSrc.startsWith("https://") || sanitizedSrc.startsWith("blob:") || sanitizedSrc.startsWith("data:")) && (videoRef.current.src = sanitizedSrc,
2267
+ videoRef.current.currentTime = currentTime, wasPlaying && videoRef.current.play(),
2268
+ setCurrentQuality(newQuality));
2269
+ }
2270
+ }), [ videoRef, quality ]), formatTime = useCallback((time => {
2271
+ const hours = Math.floor(time / 3600), minutes = Math.floor(time % 3600 / 60), seconds = Math.floor(time % 60);
2272
+ return hours > 0 ? `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}` : `${minutes}:${seconds.toString().padStart(2, "0")}`;
2273
+ }), []), getProgressPercentage = useCallback((() => duration > 0 ? currentTime / duration * 100 : 0), [ currentTime, duration ]), getBufferedPercentage = useCallback((() => duration > 0 ? buffered / duration * 100 : 0), [ buffered, duration ]);
2274
+ // Event listeners
2275
+ useEffect((() => {
2276
+ const video = videoRef.current;
2277
+ if (!video) return;
2278
+ const handleLoadStart = () => setIsLoading(!0), handleCanPlay = () => setIsLoading(!1), handleLoadedMetadata = () => {
2279
+ setDuration(video.duration), setVolumeState(video.volume), setIsMuted(video.muted);
2280
+ }, handleTimeUpdate = () => {
2281
+ setCurrentTime(video.currentTime), onTimeUpdate?.(video.currentTime);
2282
+ }, handleProgress = () => {
2283
+ video.buffered.length > 0 && setBuffered(video.buffered.end(video.buffered.length - 1));
2284
+ }, handlePlay = () => {
2285
+ setIsPlaying(!0), onPlay?.();
2286
+ }, handlePause = () => {
2287
+ setIsPlaying(!1), onPause?.();
2288
+ }, handleEnded = () => {
2289
+ setIsPlaying(!1), onEnded?.();
2290
+ }, handleVolumeChange = () => {
2291
+ setVolumeState(video.volume), setIsMuted(video.muted), onVolumeChange?.(video.volume);
2292
+ }, handleError = e => {
2293
+ setIsLoading(!1), onError?.(e);
2294
+ }, handleEnterpictureinpicture = () => setIsPictureInPicture(!0), handleLeavepictureinpicture = () => setIsPictureInPicture(!1);
2295
+ return video.addEventListener("loadstart", handleLoadStart), video.addEventListener("canplay", handleCanPlay),
2296
+ video.addEventListener("loadedmetadata", handleLoadedMetadata), video.addEventListener("timeupdate", handleTimeUpdate),
2297
+ video.addEventListener("progress", handleProgress), video.addEventListener("play", handlePlay),
2298
+ video.addEventListener("pause", handlePause), video.addEventListener("ended", handleEnded),
2299
+ video.addEventListener("volumechange", handleVolumeChange), video.addEventListener("error", handleError),
2300
+ video.addEventListener("enterpictureinpicture", handleEnterpictureinpicture), video.addEventListener("leavepictureinpicture", handleLeavepictureinpicture),
2301
+ () => {
2302
+ video.removeEventListener("loadstart", handleLoadStart), video.removeEventListener("canplay", handleCanPlay),
2303
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata), video.removeEventListener("timeupdate", handleTimeUpdate),
2304
+ video.removeEventListener("progress", handleProgress), video.removeEventListener("play", handlePlay),
2305
+ video.removeEventListener("pause", handlePause), video.removeEventListener("ended", handleEnded),
2306
+ video.removeEventListener("volumechange", handleVolumeChange), video.removeEventListener("error", handleError),
2307
+ video.removeEventListener("enterpictureinpicture", handleEnterpictureinpicture),
2308
+ video.removeEventListener("leavepictureinpicture", handleLeavepictureinpicture);
2309
+ };
2310
+ }), [ videoRef, onPlay, onPause, onEnded, onTimeUpdate, onVolumeChange, onError ]),
2311
+ // Fullscreen event listeners
2312
+ useEffect((() => {
2313
+ const handleFullscreenChange = () => {
2314
+ const isCurrentlyFullscreen = !!document.fullscreenElement;
2315
+ setIsFullscreen(isCurrentlyFullscreen), onFullscreenChange?.(isCurrentlyFullscreen);
2316
+ };
2317
+ return document.addEventListener("fullscreenchange", handleFullscreenChange), () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
2318
+ }), [ onFullscreenChange ]);
2319
+ // Keyboard shortcuts
2320
+ const handleKeyDown = useCallback((e => {
2321
+ if (containerRef.current?.contains(document.activeElement)) switch (e.code) {
2322
+ case "Space":
2323
+ e.preventDefault(), togglePlay();
2324
+ break;
2325
+
2326
+ case "ArrowLeft":
2327
+ e.preventDefault(), seek(currentTime - 10);
2328
+ break;
2329
+
2330
+ case "ArrowRight":
2331
+ e.preventDefault(), seek(currentTime + 10);
2332
+ break;
2333
+
2334
+ case "ArrowUp":
2335
+ e.preventDefault(), setVolume(Math.min(1, volume + .1));
2336
+ break;
2337
+
2338
+ case "ArrowDown":
2339
+ e.preventDefault(), setVolume(Math.max(0, volume - .1));
2340
+ break;
2341
+
2342
+ case "KeyM":
2343
+ e.preventDefault(), toggleMute();
2344
+ break;
2345
+
2346
+ case "KeyF":
2347
+ e.preventDefault(), toggleFullscreen();
2348
+ }
2349
+ }), [ togglePlay, seek, currentTime, setVolume, volume, toggleMute, toggleFullscreen, containerRef ]);
2350
+ useEffect((() => (document.addEventListener("keydown", handleKeyDown), () => document.removeEventListener("keydown", handleKeyDown))), [ handleKeyDown ]);
2351
+ // Mouse movement for controls
2352
+ const handleMouseMove = useCallback((() => resetControlsTimeout()), [ resetControlsTimeout ]), handleMouseLeave = useCallback((() => {
2353
+ controlsTimeoutRef.current && clearTimeout(controlsTimeoutRef.current), isPlaying && setShowControls(!1);
2354
+ }), [ isPlaying ]);
2355
+ return useEffect((() => {
2356
+ const container = containerRef.current;
2357
+ if (container) return container.addEventListener("mousemove", handleMouseMove),
2358
+ container.addEventListener("mouseleave", handleMouseLeave), () => {
2359
+ container.removeEventListener("mousemove", handleMouseMove), container.removeEventListener("mouseleave", handleMouseLeave),
2360
+ controlsTimeoutRef.current && clearTimeout(controlsTimeoutRef.current);
2361
+ };
2362
+ }), [ containerRef, handleMouseMove, handleMouseLeave ]), {
2363
+ isPlaying: isPlaying,
2364
+ currentTime: currentTime,
2365
+ duration: duration,
2366
+ volume: volume,
2367
+ isMuted: isMuted,
2368
+ isFullscreen: isFullscreen,
2369
+ isPictureInPicture: isPictureInPicture,
2370
+ isLoading: isLoading,
2371
+ buffered: buffered,
2372
+ playbackRate: playbackRate,
2373
+ currentQuality: currentQuality,
2374
+ showControls: showControls,
2375
+ play: play,
2376
+ pause: pause,
2377
+ togglePlay: togglePlay,
2378
+ seek: seek,
2379
+ setVolume: setVolume,
2380
+ toggleMute: toggleMute,
2381
+ toggleFullscreen: toggleFullscreen,
2382
+ togglePictureInPicture: togglePictureInPicture,
2383
+ setPlaybackRate: setPlaybackRate,
2384
+ setQuality: setQuality,
2385
+ formatTime: formatTime,
2386
+ getProgressPercentage: getProgressPercentage,
2387
+ getBufferedPercentage: getBufferedPercentage
2388
+ };
2389
+ }({
2390
+ videoRef: videoRef,
2391
+ containerRef: containerRef,
2392
+ onPlay: onPlay,
2393
+ onPause: onPause,
2394
+ onEnded: onEnded,
2395
+ onTimeUpdate: onTimeUpdate,
2396
+ onVolumeChange: onVolumeChange,
2397
+ onFullscreenChange: onFullscreenChange,
2398
+ onError: onError,
2399
+ playbackRates: playbackRates,
2400
+ quality: quality
2401
+ });
2402
+ !function({videoRef: videoRef, canvasRef: canvasRef, enabled: enabled, blur: blur = 60, opacity: opacity = .6, scale: scale = 1.2}) {
2403
+ const animationFrameRef = useRef(60);
2404
+ useEffect((() => {
2405
+ if (!enabled || !videoRef.current || !canvasRef.current) return;
2406
+ const video = videoRef.current, canvas = canvasRef.current, ctx = canvas.getContext("2d");
2407
+ if (!ctx) return;
2408
+ const updateAmbientEffect = () => {
2409
+ if (!video || !canvas || !ctx) return;
2410
+ // Set canvas size to match container
2411
+ const rect = video.getBoundingClientRect();
2412
+ canvas.width = rect.width * scale, canvas.height = rect.height * scale,
2413
+ // Draw video frame to canvas
2414
+ ctx.filter = `blur(${blur}px)`, ctx.globalAlpha = opacity;
2415
+ try {
2416
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
2417
+ } catch (e) {
2418
+ // Handle CORS or other drawing errors silently
2419
+ }
2420
+ enabled && (animationFrameRef.current = requestAnimationFrame(updateAmbientEffect));
2421
+ }, handlePlay = () => {
2422
+ enabled && updateAmbientEffect();
2423
+ }, handlePause = () => {
2424
+ animationFrameRef.current && cancelAnimationFrame(animationFrameRef.current);
2425
+ };
2426
+ // Start ambient effect when video plays
2427
+ return video.addEventListener("play", handlePlay), video.addEventListener("pause", handlePause),
2428
+ video.addEventListener("ended", handlePause),
2429
+ // Initial setup if video is already playing
2430
+ video.paused || handlePlay(), () => {
2431
+ video.removeEventListener("play", handlePlay), video.removeEventListener("pause", handlePause),
2432
+ video.removeEventListener("ended", handlePause), animationFrameRef.current && cancelAnimationFrame(animationFrameRef.current);
2433
+ };
2434
+ }), [ enabled, blur, opacity, scale, videoRef, canvasRef ]);
2435
+ }({
2436
+ videoRef: videoRef,
2437
+ canvasRef: canvasRef,
2438
+ enabled: ambientMode
2439
+ });
2440
+ const [showSettingsMenu, setShowSettingsMenu] = useState(!1), [activeSettingsTab, setActiveSettingsTab] = useState("quality"), [activeSubtitle, setActiveSubtitle] = useState(subtitles?.find((sub => sub.default))?.srcLang || null), [videoDimensions, setVideoDimensions] = useState({
2441
+ width: 0,
2442
+ height: 0
2443
+ }), handleProgressClick = useCallback((e => {
2444
+ const rect = e.currentTarget.getBoundingClientRect(), percent = (e.clientX - rect.left) / rect.width;
2445
+ seek(percent * duration);
2446
+ }), [ duration, seek ]), handleVolumeClick = useCallback((e => {
2447
+ const rect = e.currentTarget.getBoundingClientRect(), percent = (e.clientX - rect.left) / rect.width;
2448
+ setVolume(percent);
2449
+ }), [ setVolume ]), handleDownload = useCallback((() => {
2450
+ if (src) {
2451
+ const a = document.createElement("a");
2452
+ a.href = src, a.download = "video", a.click();
2453
+ }
2454
+ }), [ src ]), handleShare = useCallback((async () => {
2455
+ if (navigator.share) try {
2456
+ await navigator.share({
2457
+ title: "Video",
2458
+ url: window.location.href
2459
+ });
2460
+ } catch (err) {}
2461
+ }), []), setSubtitle = useCallback((subtitleLang => {
2462
+ const video = videoRef.current;
2463
+ if (video) {
2464
+ const tracks = video.textTracks;
2465
+ // Hide all tracks first
2466
+ for (let i = 0; i < tracks.length; i++) {
2467
+ const track = tracks[i];
2468
+ track && (track.mode = "hidden");
2469
+ }
2470
+ // Show selected track
2471
+ if (subtitleLang) for (let i = 0; i < tracks.length; i++) {
2472
+ const track = tracks[i];
2473
+ if (track && track.language === subtitleLang) {
2474
+ track.mode = "showing";
2475
+ break;
2476
+ }
2477
+ }
2478
+ setActiveSubtitle(subtitleLang);
2479
+ }
2480
+ }), [ videoRef ]);
2481
+ // Initialize subtitle tracks when video loads
2482
+ useEffect((() => {
2483
+ const video = videoRef.current;
2484
+ if (video && subtitles) {
2485
+ const handleLoadedData = () => {
2486
+ // Wait for tracks to be loaded
2487
+ setTimeout((() => {
2488
+ const defaultSubtitle = subtitles.find((sub => sub.default));
2489
+ defaultSubtitle && setSubtitle(defaultSubtitle.srcLang);
2490
+ }), 100);
2491
+ }, handleCanPlay = () => {
2492
+ // Ensure tracks are ready
2493
+ if (video.textTracks.length > 0) {
2494
+ const defaultSubtitle = subtitles.find((sub => sub.default));
2495
+ defaultSubtitle && setSubtitle(defaultSubtitle.srcLang);
2496
+ }
2497
+ };
2498
+ return video.addEventListener("loadeddata", handleLoadedData), video.addEventListener("canplay", handleCanPlay),
2499
+ () => {
2500
+ video.removeEventListener("loadeddata", handleLoadedData), video.removeEventListener("canplay", handleCanPlay);
2501
+ };
2502
+ }
2503
+ }), [ subtitles, setSubtitle, videoRef ]),
2504
+ // Track video/iframe dimensions for AtomixGlass
2505
+ useEffect((() => {
2506
+ const updateDimensions = () => {
2507
+ if (isYouTube && iframeRef.current) {
2508
+ const rect = iframeRef.current.getBoundingClientRect();
2509
+ setVideoDimensions({
2510
+ width: rect.width,
2511
+ height: rect.height
2512
+ });
2513
+ } else if (videoRef.current) {
2514
+ const rect = videoRef.current.getBoundingClientRect();
2515
+ setVideoDimensions({
2516
+ width: rect.width,
2517
+ height: rect.height
2518
+ });
2519
+ }
2520
+ }, initialTimer = setTimeout(updateDimensions, 100), resizeObserver = new ResizeObserver(updateDimensions);
2521
+ // Initial measurement with slight delay to ensure element is rendered
2522
+ if (isYouTube && iframeRef.current) {
2523
+ const iframe = iframeRef.current;
2524
+ resizeObserver.observe(iframe);
2525
+ // Listen for iframe load event
2526
+ const handleIframeLoad = () => updateDimensions();
2527
+ return iframe.addEventListener("load", handleIframeLoad), () => {
2528
+ clearTimeout(initialTimer), resizeObserver.disconnect(), iframe.removeEventListener("load", handleIframeLoad);
2529
+ };
2530
+ }
2531
+ // Also listen for window resize
2532
+ if (videoRef.current) {
2533
+ const video = videoRef.current;
2534
+ resizeObserver.observe(video);
2535
+ // Listen for video metadata loaded event
2536
+ const handleLoadedMetadata = () => updateDimensions();
2537
+ return video.addEventListener("loadedmetadata", handleLoadedMetadata), () => {
2538
+ clearTimeout(initialTimer), resizeObserver.disconnect(), video.removeEventListener("loadedmetadata", handleLoadedMetadata);
2539
+ };
2540
+ }
2541
+ return window.addEventListener("resize", updateDimensions), () => {
2542
+ clearTimeout(initialTimer), resizeObserver.disconnect(), window.removeEventListener("resize", updateDimensions);
2543
+ };
2544
+ }), [ isYouTube, videoRef, iframeRef ]);
2545
+ const handleContainerClick = useCallback((() => {
2546
+ containerRef.current && containerRef.current.focus();
2547
+ }), []);
2548
+ // Detect container border radius
2549
+ useEffect((() => {
2550
+ const detectBorderRadius = () => {
2551
+ if (!containerRef.current) return;
2552
+ const computedStyle = window.getComputedStyle(containerRef.current), borderRadius = computedStyle.borderRadius || computedStyle.borderTopLeftRadius, radiusValue = parseFloat(borderRadius);
2553
+ isNaN(radiusValue) || setContainerBorderRadius(radiusValue);
2554
+ };
2555
+ // Detect border radius immediately
2556
+ detectBorderRadius();
2557
+ // Create ResizeObserver to watch for style changes
2558
+ let resizeObserver = null;
2559
+ return "undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
2560
+ resizeObserver.observe(containerRef.current)),
2561
+ // Also listen for window resize (in case styles change)
2562
+ window.addEventListener("resize", detectBorderRadius), () => {
2563
+ window.removeEventListener("resize", detectBorderRadius), resizeObserver && containerRef.current && (resizeObserver.unobserve(containerRef.current),
2564
+ resizeObserver.disconnect());
2565
+ };
2566
+ }), []);
2567
+ const handleKeyDown = useCallback((e => {
2568
+ switch (e.key) {
2569
+ case " ":
2570
+ case "k":
2571
+ e.preventDefault(), togglePlay();
2572
+ break;
2573
+
2574
+ case "ArrowLeft":
2575
+ e.preventDefault(), seek(currentTime - 10);
2576
+ break;
2577
+
2578
+ case "ArrowRight":
2579
+ e.preventDefault(), seek(currentTime + 10);
2580
+ break;
2581
+
2582
+ case "ArrowUp":
2583
+ e.preventDefault(), setVolume(Math.min(1, volume + .1));
2584
+ break;
2585
+
2586
+ case "ArrowDown":
2587
+ e.preventDefault(), setVolume(Math.max(0, volume - .1));
2588
+ break;
2589
+
2590
+ case "m":
2591
+ e.preventDefault(), toggleMute();
2592
+ break;
2593
+
2594
+ case "f":
2595
+ e.preventDefault(), toggleFullscreen();
2596
+ }
2597
+ }), [ togglePlay, currentTime, seek, volume, setVolume, toggleMute, toggleFullscreen ]);
2598
+ return jsxs("div", {
2599
+ ref: containerRef,
2600
+ className: `${VIDEO_PLAYER_CLASSES_BASE} ${isYouTube ? VIDEO_PLAYER_CLASSES_YOUTUBE : ""} ${ambientMode ? VIDEO_PLAYER_CLASSES_AMBIENT : ""} ${glass ? VIDEO_PLAYER_CLASSES_GLASS : ""} ${className}`,
2601
+ style: {
2602
+ width: width,
2603
+ height: height,
2604
+ aspectRatio: aspectRatio ? aspectRatio.replace(":", "/") : void 0,
2605
+ ...style
2606
+ },
2607
+ tabIndex: 0,
2608
+ onClick: handleContainerClick,
2609
+ onKeyDown: handleKeyDown,
2610
+ role: "application",
2611
+ "aria-label": "Video player",
2612
+ ...props,
2613
+ children: [ ambientMode && jsx("canvas", {
2614
+ ref: canvasRef,
2615
+ className: VIDEO_PLAYER_CLASSES_AMBIENT_CANVAS,
2616
+ "aria-hidden": "true"
2617
+ }), isYouTube && videoId ? jsx("iframe", {
2618
+ ref: iframeRef,
2619
+ className: VIDEO_PLAYER_CLASSES_VIDEO,
2620
+ src: `https://www.youtube.com/embed/${videoId}?${new URLSearchParams({
2621
+ autoplay: autoplay ? "1" : "0",
2622
+ loop: loop ? "1" : "0",
2623
+ mute: muted ? "1" : "0",
2624
+ controls: controls ? "1" : "0",
2625
+ modestbranding: "1",
2626
+ rel: "0",
2627
+ ...loop && {
2628
+ playlist: videoId
2629
+ }
2630
+ }).toString()}`,
2631
+ title: "YouTube video player",
2632
+ frameBorder: "0",
2633
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
2634
+ allowFullScreen: !0
2635
+ }) : jsx("video", {
2636
+ ref: element => {
2637
+ videoRef && videoRef.current !== element && (videoRef.current = element), "function" == typeof ref ? ref(element) : ref && ref.current !== element && (ref.current = element);
2638
+ },
2639
+ className: VIDEO_PLAYER_CLASSES_VIDEO,
2640
+ src: src,
2641
+ poster: poster,
2642
+ autoPlay: autoplay,
2643
+ loop: loop,
2644
+ muted: muted,
2645
+ preload: preload,
2646
+ controls: !1,
2647
+ crossOrigin: "anonymous",
2648
+ children: subtitles && subtitles.map((subtitle => jsx("track", {
2649
+ kind: "subtitles",
2650
+ src: subtitle.src,
2651
+ srcLang: subtitle.srcLang,
2652
+ label: subtitle.label,
2653
+ default: subtitle.default
2654
+ }, subtitle.srcLang)))
2655
+ }), isLoading && jsx("div", {
2656
+ className: VIDEO_PLAYER_CLASSES_LOADING,
2657
+ children: jsx("div", {
2658
+ className: VIDEO_PLAYER_CLASSES_SPINNER
2659
+ })
2660
+ }), glass && jsx("div", {
2661
+ className: VIDEO_PLAYER_CLASSES_GLASS_OVERLAY,
2662
+ children: jsx(AtomixGlass, {
2663
+ ..."boolean" == typeof glass ? {} : glass,
2664
+ mouseContainer: containerRef,
2665
+ displacementScale: 100,
2666
+ blurAmount: 0,
2667
+ saturation: 100,
2668
+ elasticity: 0,
2669
+ children: !glassContent && jsx("div", {
2670
+ style: {
2671
+ width: videoDimensions.width > 0 ? `${videoDimensions.width}px` : "100%",
2672
+ height: videoDimensions.height > 0 ? `${videoDimensions.height}px` : "100%",
2673
+ display: "flex",
2674
+ alignItems: "center",
2675
+ justifyContent: "center",
2676
+ background: "transparent"
2677
+ }
2678
+ })
2679
+ })
2680
+ }), glass && glassContent && jsx("div", {
2681
+ className: VIDEO_PLAYER_CLASSES_GLASS_CONTENT,
2682
+ style: {
2683
+ display: "flex",
2684
+ alignItems: "center",
2685
+ justifyContent: "center"
2686
+ },
2687
+ children: glassContent
2688
+ }), controls && !isYouTube && jsxs("div", {
2689
+ className: `${VIDEO_PLAYER_CLASSES_CONTROLS} ${showControls ? VIDEO_PLAYER_CLASSES_CONTROLS_VISIBLE : ""}`,
2690
+ style: {
2691
+ zIndex: glass ? 3 : "auto"
2692
+ },
2693
+ children: [ jsx("div", {
2694
+ className: VIDEO_PLAYER_CLASSES_PROGRESS_CONTAINER,
2695
+ children: jsxs("div", {
2696
+ className: VIDEO_PLAYER_CLASSES_PROGRESS_BAR,
2697
+ onClick: handleProgressClick,
2698
+ children: [ jsx("div", {
2699
+ className: VIDEO_PLAYER_CLASSES_PROGRESS_BUFFERED,
2700
+ style: {
2701
+ width: `${getBufferedPercentage()}%`
2702
+ }
2703
+ }), jsx("div", {
2704
+ className: VIDEO_PLAYER_CLASSES_PROGRESS_PLAYED,
2705
+ style: {
2706
+ width: `${getProgressPercentage()}%`
2707
+ }
2708
+ }), jsx("div", {
2709
+ className: VIDEO_PLAYER_CLASSES_PROGRESS_THUMB,
2710
+ style: {
2711
+ left: `${getProgressPercentage()}%`
2712
+ }
2713
+ }) ]
2714
+ })
2715
+ }), jsxs("div", {
2716
+ className: VIDEO_PLAYER_CLASSES_CONTROLS_ROW,
2717
+ children: [ jsxs("div", {
2718
+ className: VIDEO_PLAYER_CLASSES_CONTROLS_LEFT,
2719
+ children: [ jsx("button", {
2720
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2721
+ onClick: togglePlay,
2722
+ "aria-label": isPlaying ? "Pause" : "Play",
2723
+ children: jsx(isPlaying ? Pause : Play, {
2724
+ size: 20
2725
+ })
2726
+ }), jsx("button", {
2727
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2728
+ onClick: () => seek(currentTime - 10),
2729
+ "aria-label": "Skip back 10 seconds",
2730
+ children: jsx(SkipBack, {
2731
+ size: 20
2732
+ })
2733
+ }), jsx("button", {
2734
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2735
+ onClick: () => seek(currentTime + 10),
2736
+ "aria-label": "Skip forward 10 seconds",
2737
+ children: jsx(SkipForward, {
2738
+ size: 20
2739
+ })
2740
+ }), jsxs("div", {
2741
+ className: VIDEO_PLAYER_CLASSES_VOLUME_CONTAINER,
2742
+ children: [ jsx("button", {
2743
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2744
+ onClick: toggleMute,
2745
+ "aria-label": isMuted ? "Unmute" : "Mute",
2746
+ children: jsx(isMuted || 0 === volume ? SpeakerX : SpeakerHigh, {
2747
+ size: 20
2748
+ })
2749
+ }), jsx("div", {
2750
+ className: VIDEO_PLAYER_CLASSES_VOLUME_SLIDER,
2751
+ children: jsx("div", {
2752
+ className: VIDEO_PLAYER_CLASSES_VOLUME_BAR,
2753
+ onClick: handleVolumeClick,
2754
+ children: jsx("div", {
2755
+ className: VIDEO_PLAYER_CLASSES_VOLUME_FILL,
2756
+ style: {
2757
+ width: 100 * volume + "%"
2758
+ }
2759
+ })
2760
+ })
2761
+ }) ]
2762
+ }), jsxs("div", {
2763
+ className: VIDEO_PLAYER_CLASSES_TIME_DISPLAY,
2764
+ children: [ jsx("span", {
2765
+ children: formatTime(currentTime)
2766
+ }), jsx("span", {
2767
+ children: "/"
2768
+ }), jsx("span", {
2769
+ children: formatTime(duration)
2770
+ }) ]
2771
+ }) ]
2772
+ }), jsxs("div", {
2773
+ className: VIDEO_PLAYER_CLASSES_CONTROLS_RIGHT,
2774
+ children: [ showSettings && jsxs("div", {
2775
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_CONTAINER,
2776
+ children: [ jsx("button", {
2777
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2778
+ onClick: () => setShowSettingsMenu(!showSettingsMenu),
2779
+ "aria-label": "Settings",
2780
+ children: jsx(Gear, {
2781
+ size: 20
2782
+ })
2783
+ }), showSettingsMenu && jsxs("div", {
2784
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_MENU,
2785
+ children: [ jsxs("div", {
2786
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_TABS,
2787
+ children: [ quality && quality.length > 1 && jsx("button", {
2788
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_TAB} ${"quality" === activeSettingsTab ? VIDEO_PLAYER_CLASSES_SETTINGS_TAB_ACTIVE : ""}`,
2789
+ onClick: () => setActiveSettingsTab("quality"),
2790
+ children: "Quality"
2791
+ }), jsx("button", {
2792
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_TAB} ${"speed" === activeSettingsTab ? VIDEO_PLAYER_CLASSES_SETTINGS_TAB_ACTIVE : ""}`,
2793
+ onClick: () => setActiveSettingsTab("speed"),
2794
+ children: "Speed"
2795
+ }), jsx("button", {
2796
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_TAB} ${"subtitles" === activeSettingsTab ? VIDEO_PLAYER_CLASSES_SETTINGS_TAB_ACTIVE : ""}`,
2797
+ onClick: () => setActiveSettingsTab("subtitles"),
2798
+ children: "Subtitles"
2799
+ }) ]
2800
+ }), jsxs("div", {
2801
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_CONTENT,
2802
+ children: [ "quality" === activeSettingsTab && quality && jsx("div", {
2803
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_OPTIONS,
2804
+ children: quality.map((q => jsx("button", {
2805
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_OPTION} ${currentQuality?.label === q.label ? VIDEO_PLAYER_CLASSES_SETTINGS_OPTION_ACTIVE : ""}`,
2806
+ onClick: () => setQuality(q),
2807
+ children: q.label
2808
+ }, q.label)))
2809
+ }), "speed" === activeSettingsTab && jsx("div", {
2810
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_OPTIONS,
2811
+ children: playbackRates.map((rate => jsxs("button", {
2812
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_OPTION} ${playbackRate === rate ? VIDEO_PLAYER_CLASSES_SETTINGS_OPTION_ACTIVE : ""}`,
2813
+ onClick: () => setPlaybackRate(rate),
2814
+ children: [ rate, "x" ]
2815
+ }, rate)))
2816
+ }), "subtitles" === activeSettingsTab && jsx("div", {
2817
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_OPTIONS,
2818
+ children: subtitles && subtitles.length > 0 ? jsxs(Fragment, {
2819
+ children: [ jsx("button", {
2820
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_OPTION} ${null === activeSubtitle ? VIDEO_PLAYER_CLASSES_SETTINGS_OPTION_ACTIVE : ""}`,
2821
+ onClick: () => setSubtitle(null),
2822
+ children: "Off"
2823
+ }), subtitles.map((subtitle => jsx("button", {
2824
+ className: `${VIDEO_PLAYER_CLASSES_SETTINGS_OPTION} ${activeSubtitle === subtitle.srcLang ? VIDEO_PLAYER_CLASSES_SETTINGS_OPTION_ACTIVE : ""}`,
2825
+ onClick: () => setSubtitle(subtitle.srcLang),
2826
+ children: subtitle.label
2827
+ }, subtitle.srcLang))) ]
2828
+ }) : jsx("div", {
2829
+ className: VIDEO_PLAYER_CLASSES_SETTINGS_OPTION,
2830
+ style: {
2831
+ opacity: .6,
2832
+ cursor: "default"
2833
+ },
2834
+ children: "No subtitles available"
2835
+ })
2836
+ }) ]
2837
+ }) ]
2838
+ }) ]
2839
+ }), showDownload && jsx("button", {
2840
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2841
+ onClick: handleDownload,
2842
+ "aria-label": "Download video",
2843
+ children: jsx(Download, {
2844
+ size: 20
2845
+ })
2846
+ }), showShare && jsx("button", {
2847
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2848
+ onClick: handleShare,
2849
+ "aria-label": "Share video",
2850
+ children: jsx(Share, {
2851
+ size: 20
2852
+ })
2853
+ }), jsx("button", {
2854
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2855
+ onClick: togglePictureInPicture,
2856
+ "aria-label": "Picture in Picture",
2857
+ children: jsx("svg", {
2858
+ width: "20",
2859
+ height: "20",
2860
+ viewBox: "0 0 24 24",
2861
+ fill: "currentColor",
2862
+ children: jsx("path", {
2863
+ d: "M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"
2864
+ })
2865
+ })
2866
+ }), jsx("button", {
2867
+ className: VIDEO_PLAYER_CLASSES_CONTROL_BUTTON,
2868
+ onClick: toggleFullscreen,
2869
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
2870
+ children: jsx(isFullscreen ? ArrowsIn : ArrowsOut, {
2871
+ size: 20
2872
+ })
2873
+ }) ]
2874
+ }) ]
2875
+ }) ]
2876
+ }) ]
2877
+ });
2878
+ }));
2879
+
2880
+ VideoPlayer.displayName = "VideoPlayer";
2881
+
2882
+ /**
2883
+ * MasonryGrid component for creating a responsive masonry layout.
2884
+ * Uses JavaScript to position items optimally based on available vertical space,
2885
+ * similar to how a mason fits stones in a wall.
2886
+ *
2887
+ * @example
2888
+ * ```tsx
2889
+ * <MasonryGrid xs={1} sm={2} md={3} lg={4}>
2890
+ * <MasonryGridItem>Item 1</MasonryGridItem>
2891
+ * <MasonryGridItem>Item 2</MasonryGridItem>
2892
+ * <MasonryGridItem>Item 3</MasonryGridItem>
2893
+ * </MasonryGrid>
2894
+ * ```
2895
+ */
2896
+ const MasonryGrid = forwardRef((({children: children, className: className = "", xs: xs = 1, sm: sm, md: md, lg: lg, xl: xl, xxl: xxl, gap: gap = 16, animate: animate = !0, imagesLoaded: imagesLoaded = !0, onLayoutComplete: onLayoutComplete, onImageLoad: onImageLoad, ...props}, ref) => {
2897
+ // === REFS & STATE ===
2898
+ const [columns, setColumns] = useState(xs), [positions, setPositions] = useState([]), [layoutComplete, setLayoutComplete] = useState(!1), [loadingImages, setLoadingImages] = useState(!1), containerRef = useRef(null), columnHeights = useRef([]), imagesLoadedCount = useRef(0), totalImagesCount = useRef(0), imageElements = useRef(new Map);
2899
+ useEffect((() => {
2900
+ setLoadingImages(!!imagesLoaded);
2901
+ }), [ columns, imagesLoaded ]),
2902
+ // Forward ref for parent components
2903
+ useImperativeHandle(ref, (() => containerRef.current));
2904
+ // === HANDLE RESPONSIVE COLUMNS ===
2905
+ const getResponsiveColumns = useCallback((() => {
2906
+ const width = window.innerWidth;
2907
+ return width >= 1400 && void 0 !== xxl ? xxl : width >= 1200 && void 0 !== xl ? xl : width >= 992 && void 0 !== lg ? lg : width >= 768 && void 0 !== md ? md : width >= 576 && void 0 !== sm ? sm : xs;
2908
+ }), [ xs, sm, md, lg, xl, xxl ]);
2909
+ useEffect((() => {
2910
+ const handleResize = () => setColumns(getResponsiveColumns());
2911
+ return handleResize(), // Set on mount
2912
+ window.addEventListener("resize", handleResize), () => window.removeEventListener("resize", handleResize);
2913
+ }), [ getResponsiveColumns ]);
2914
+ // === PREPARE ITEMS WITH REFS ===
2915
+ const [items, setItems] = useState([]);
2916
+ useEffect((() => {
2917
+ const newItems = [];
2918
+ Children.forEach(children, ((child, index) => {
2919
+ isValidElement(child) && newItems.push({
2920
+ id: child.key?.toString() || `masonry-item-${index}`,
2921
+ element: child,
2922
+ position: null,
2923
+ ref: React.createRef()
2924
+ });
2925
+ })), setItems(newItems);
2926
+ }), [ children ]);
2927
+ // === TRACK & MANAGE IMAGES ===
2928
+ const handleImageLoad = useCallback((img => {
2929
+ if (!imageElements.current.get(img)) {
2930
+ // Add loaded class for animation
2931
+ if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
2932
+ const itemElement = img.closest(".o-masonry-grid > div");
2933
+ itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
2934
+ itemElement.classList.remove("o-masonry-grid__item-loading"));
2935
+ }
2936
+ // Ensure layout is recalculated after DOM paints the item image (prevents overlap on slow/late image loads)
2937
+ requestAnimationFrame((() => {
2938
+ requestAnimationFrame((() => {
2939
+ calculateLayout();
2940
+ }));
2941
+ })), onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current),
2942
+ // If all images have loaded, update loading state and complete layout
2943
+ imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
2944
+ setLoadingImages(!1), // This ensures the loading class is removed *immediately* after images load
2945
+ // Force a double requestAnimationFrame for final layout calculation after all images are loaded (guarantees DOM paint)
2946
+ requestAnimationFrame((() => {
2947
+ requestAnimationFrame((() => {
2948
+ calculateLayout(),
2949
+ // As a failsafe, if still present for some render lag, force another setLoadingImages(false)
2950
+ setLoadingImages(!1);
2951
+ }));
2952
+ })), onLayoutComplete?.());
2953
+ }
2954
+ }), [ onImageLoad, onLayoutComplete, imagesLoaded ]), trackImages = useCallback((() => {
2955
+ if (!imagesLoaded || !containerRef.current) return;
2956
+ imageElements.current.clear(), imagesLoadedCount.current = 0;
2957
+ const images = containerRef.current.querySelectorAll("img");
2958
+ return totalImagesCount.current = images.length, 0 === images.length ? (setLayoutComplete(!0),
2959
+ setLoadingImages(!1), void onLayoutComplete?.()) : (setLoadingImages(!0), images.forEach((img => {
2960
+ const masonryImg = img, itemElement = img.closest(".o-masonry-grid > div");
2961
+ if (itemElement && itemElement.classList.add("o-masonry-grid__item-loading"), img.complete) handleImageLoad(img); else {
2962
+ const loadHandler = () => handleImageLoad(img);
2963
+ img.addEventListener("load", loadHandler), img.addEventListener("error", loadHandler),
2964
+ masonryImg._masonryLoadHandler = loadHandler;
2965
+ }
2966
+ })), () => {
2967
+ images.forEach((img => {
2968
+ const masonryImg = img;
2969
+ masonryImg._masonryLoadHandler && (img.removeEventListener("load", masonryImg._masonryLoadHandler),
2970
+ img.removeEventListener("error", masonryImg._masonryLoadHandler), delete masonryImg._masonryLoadHandler);
2971
+ }));
2972
+ });
2973
+ }), [ imagesLoaded, handleImageLoad, onLayoutComplete ]), calculateLayout = useCallback((() => {
2974
+ if (!containerRef.current || 0 === items.length) return;
2975
+ const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
2976
+ columnHeights.current = Array(columns).fill(0);
2977
+ const newPositions = [];
2978
+ items.forEach(((item, index) => {
2979
+ if (item.ref.current) {
2980
+ // Find the shortest column
2981
+ const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
2982
+ columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
2983
+ left: left,
2984
+ top: top,
2985
+ width: colWidth,
2986
+ height: height
2987
+ };
2988
+ }
2989
+ })), setPositions(newPositions);
2990
+ }), [ items, columns, gap ]);
2991
+ // === OBSERVE CONTAINER RESIZE ===
2992
+ useEffect((() => {
2993
+ if (!containerRef.current) return;
2994
+ let animationFrame = null;
2995
+ const observer = new ResizeObserver((() => {
2996
+ animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame((() => calculateLayout()));
2997
+ }));
2998
+ return observer.observe(containerRef.current), () => {
2999
+ observer.disconnect(), animationFrame && cancelAnimationFrame(animationFrame);
3000
+ };
3001
+ }), [ calculateLayout ]),
3002
+ // === LAYOUT EFFECT (REPLACES setTimeout) ===
3003
+ React.useLayoutEffect((() => imagesLoaded ? trackImages() : (calculateLayout(),
3004
+ setLayoutComplete(!0), void setLoadingImages(!1))
3005
+ // Only reset layoutComplete when items or columns change
3006
+ ), [ items, columns, calculateLayout, imagesLoaded, trackImages ]),
3007
+ // === NEW: Add ResizeObservers to all grid items for bulletproof image+content measurement ===
3008
+ React.useEffect((() => {
3009
+ // Clean up old observers if items ever change
3010
+ const observers = [];
3011
+ return items.forEach((item => {
3012
+ if (item.ref.current) {
3013
+ const obs = new ResizeObserver((() => {
3014
+ // Double rAF: ensures layout only runs after DOM/paint/async renders
3015
+ requestAnimationFrame((() => {
3016
+ requestAnimationFrame((() => {
3017
+ calculateLayout();
3018
+ }));
3019
+ }));
3020
+ }));
3021
+ obs.observe(item.ref.current), observers.push(obs);
3022
+ }
3023
+ })), () => {
3024
+ observers.forEach((obs => obs.disconnect()));
3025
+ };
3026
+ }), [ items, calculateLayout ]);
3027
+ // Ensure loadingImages state resets when items/columns/imagesLoaded change
3028
+ // === DETERMINE CONTAINER HEIGHT ===
3029
+ const containerHeight = columnHeights.current.length > 0 ? Math.max(...columnHeights.current) : 0, classes = [ "o-masonry-grid", className, animate ? "o-masonry-grid--animate" : "", loadingImages ? "o-masonry-grid--loading-images" : "" ].filter(Boolean).join(" ");
3030
+ // === DETERMINE CLASSES ===
3031
+ // === RENDER ===
3032
+ return jsx("div", {
3033
+ ref: containerRef,
3034
+ className: classes,
3035
+ style: {
3036
+ position: "relative",
3037
+ width: "100%",
3038
+ height: `${containerHeight}px`,
3039
+ ...props.style
3040
+ },
3041
+ ...props,
3042
+ children: items.map(((item, index) => {
3043
+ const position = positions[index];
3044
+ return jsx("div", position ? {
3045
+ ref: item.ref,
3046
+ className: "o-masonry-grid__item",
3047
+ style: {
3048
+ position: "absolute",
3049
+ left: `${position.left}px`,
3050
+ top: `${position.top}px`,
3051
+ width: `${position.width}px`,
3052
+ opacity: 1
3053
+ },
3054
+ children: item.element
3055
+ } : {
3056
+ ref: item.ref,
3057
+ style: {
3058
+ opacity: 0,
3059
+ position: "absolute"
3060
+ },
3061
+ children: item.element
3062
+ }, item.id);
3063
+ }))
3064
+ });
3065
+ }));
3066
+
3067
+ MasonryGrid.displayName = "MasonryGrid", forwardRef((({children: children, className: className = "", ...props}, ref) => {
3068
+ const classes = [ "o-masonry-grid__item-inner" ];
3069
+ return className && classes.push(className), jsx("div", {
3070
+ ref: ref,
3071
+ className: classes.join(" "),
3072
+ ...props,
3073
+ children: children
3074
+ });
3075
+ })).displayName = "MasonryGridItem";
3076
+
3077
+ const Badge = memo((({label: label, variant: variant = "primary", size: size = "md", disabled: disabled = !1, icon: icon, className: className = "", glass: glass, style: style}) => {
3078
+ const {generateBadgeClass: generateBadgeClass} =
3079
+ /**
3080
+ * Badge state and functionality
3081
+ * @param initialProps - Initial badge properties
3082
+ * @returns Badge state and methods
3083
+ */
3084
+ function(initialProps) {
3085
+ // Default badge properties
3086
+ const defaultProps = {
3087
+ variant: "primary",
3088
+ size: "md",
3089
+ disabled: !1,
3090
+ ...initialProps
3091
+ };
3092
+ /**
3093
+ * Generate badge class based on properties
3094
+ * @param props - Badge properties
3095
+ * @returns Class string
3096
+ */ return {
3097
+ defaultProps: defaultProps,
3098
+ generateBadgeClass: props => {
3099
+ const {variant: variant = defaultProps.variant, size: size = defaultProps.size, disabled: disabled = defaultProps.disabled, className: className = ""} = props;
3100
+ return `c-badge ${variant ? `c-badge--${variant}` : ""} ${"md" === size ? "" : `c-badge--${size}`} ${disabled ? "c-badge--disabled" : ""} ${className}`.trim();
3101
+ }
3102
+ };
3103
+ }({
3104
+ variant: variant,
3105
+ size: size,
3106
+ disabled: disabled
3107
+ }), ref = useRef(null), badgeClass = generateBadgeClass({
3108
+ variant: variant,
3109
+ size: size,
3110
+ disabled: disabled,
3111
+ className: `${className} ${glass ? "c-badge--glass" : ""}`.trim()
3112
+ }), badgeElement = jsxs("span", {
3113
+ className: badgeClass,
3114
+ "aria-disabled": disabled,
3115
+ ref: ref,
3116
+ style: style,
3117
+ children: [ icon && jsx("span", {
3118
+ className: "c-badge__icon",
3119
+ children: icon
3120
+ }), jsx("span", {
3121
+ children: label
3122
+ }) ]
3123
+ });
3124
+ if (glass) {
3125
+ // Default glass settings for badges
3126
+ const defaultGlassProps = {
3127
+ displacementScale: 20,
3128
+ cornerRadius: ref.current?.getBoundingClientRect().width ? ref.current?.getBoundingClientRect().width / 2 : 16,
3129
+ className: "c-badge--glass",
3130
+ elasticity: 0
3131
+ }, glassProps = !0 === glass ? defaultGlassProps : {
3132
+ ...defaultGlassProps,
3133
+ ...glass
3134
+ };
3135
+ return jsx(AtomixGlass, {
3136
+ ...glassProps,
3137
+ children: badgeElement
3138
+ });
3139
+ }
3140
+ return badgeElement;
3141
+ }));
3142
+
3143
+ Badge.displayName = "Badge";
3144
+
3145
+ const Spinner = memo((({size: size = "md", variant: variant = "primary", fullscreen: fullscreen = !1, className: className = "", style: style, glass: glass}) => {
3146
+ const {generateSpinnerClass: generateSpinnerClass} =
3147
+ /**
3148
+ * Spinner state and functionality
3149
+ * @param initialProps - Initial spinner properties
3150
+ * @returns Spinner state and methods
3151
+ */
3152
+ function(initialProps) {
3153
+ // Default spinner properties
3154
+ const defaultProps = {
3155
+ variant: "primary",
3156
+ size: "md",
3157
+ fullscreen: !1,
3158
+ ...initialProps
3159
+ };
3160
+ /**
3161
+ * Generate spinner class based on properties
3162
+ * @param props - Spinner properties
3163
+ * @returns Class string
3164
+ */ return {
3165
+ defaultProps: defaultProps,
3166
+ generateSpinnerClass: props => {
3167
+ const {variant: variant = defaultProps.variant, size: size = defaultProps.size, fullscreen: fullscreen = defaultProps.fullscreen, className: className = ""} = props;
3168
+ return `c-spinner ${variant ? `c-spinner--${variant}` : ""} ${"md" !== size ? `c-spinner--${size}` : ""} ${fullscreen ? "c-spinner--fullscreen" : ""} ${className}`.trim();
3169
+ }
3170
+ };
3171
+ }({
3172
+ size: size,
3173
+ variant: variant,
3174
+ fullscreen: fullscreen
3175
+ }), spinnerClass = generateSpinnerClass({
3176
+ size: size,
3177
+ variant: variant,
3178
+ fullscreen: fullscreen,
3179
+ className: `${className} ${glass ? "c-spinner--glass" : ""}`.trim()
3180
+ }), spinnerContent = jsx("div", {
3181
+ className: spinnerClass,
3182
+ style: style,
3183
+ role: "status",
3184
+ children: jsx("span", {
3185
+ className: "u-visually-hidden",
3186
+ children: "Loading..."
3187
+ })
3188
+ });
3189
+ if (glass) {
3190
+ const defaultGlassProps = {
3191
+ displacementScale: 20,
3192
+ blurAmount: 1,
3193
+ cornerRadius: 999,
3194
+ mode: "shader"
3195
+ }, glassProps = !0 === glass ? defaultGlassProps : {
3196
+ ...defaultGlassProps,
3197
+ ...glass
3198
+ };
3199
+ return jsx(AtomixGlass, {
3200
+ ...glassProps,
3201
+ children: spinnerContent
3202
+ });
3203
+ }
3204
+ return spinnerContent;
3205
+ }));
3206
+
3207
+ Spinner.displayName = "Spinner";
3208
+
3209
+ // Map string sizes to pixel values
3210
+ const sizeMap = {
3211
+ xs: 16,
3212
+ sm: 20,
3213
+ md: 24,
3214
+ lg: 32,
3215
+ xl: 40
3216
+ }, Icon = ({name: name, size: size = "md", weight: weight = "regular", color: color, className: className = "", style: style, alt: alt}) => {
3217
+ // Get the icon component from Phosphor
3218
+ const IconComponent = PhosphorIcons[name];
3219
+ return IconComponent ? jsx("span", {
3220
+ className: `c-icon c-icon--${size} ${className}`,
3221
+ style: style,
3222
+ "aria-hidden": !alt,
3223
+ title: alt,
3224
+ children: jsx(IconComponent, {
3225
+ size: "string" == typeof size ? sizeMap[size] || 24 : size,
3226
+ weight: weight,
3227
+ color: color,
3228
+ "aria-label": alt
3229
+ })
3230
+ }) : (console.warn(`Icon "${name}" not found in Phosphor Icons`), null);
3231
+ // Convert string size to pixel value if needed
3232
+ };
3233
+
3234
+ /**
3235
+ * Icon component that displays a Phosphor icon
3236
+ */ Icon.displayName = "Icon";
3237
+
3238
+ const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, ariaExpanded: ariaExpanded, ariaControls: ariaControls, tabIndex: tabIndex, style: style, LinkComponent: LinkComponent, ...props}, ref) => {
3239
+ const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href && !isDisabled), iconElement = useMemo((() => loading ? null : iconName ? jsx(Icon, {
3240
+ name: iconName,
3241
+ size: iconSize
3242
+ }) : icon), [ icon, iconName, iconSize, loading ]), {generateButtonClass: generateButtonClass, handleClick: handleClick} =
3243
+ /**
3244
+ * Button state and functionality
3245
+ * @param initialProps - Initial button properties
3246
+ * @returns Button state and methods
3247
+ */
3248
+ function(initialProps) {
3249
+ // Default button properties
3250
+ const defaultProps = {
3251
+ variant: "primary",
3252
+ size: "md",
3253
+ disabled: !1,
3254
+ rounded: !1,
3255
+ loading: !1,
3256
+ fullWidth: !1,
3257
+ block: !1,
3258
+ active: !1,
3259
+ selected: !1,
3260
+ ...initialProps
3261
+ };
3262
+ /**
3263
+ * Generate button class based on properties
3264
+ * @param props - Button properties
3265
+ * @returns Class string
3266
+ */ return {
3267
+ defaultProps: defaultProps,
3268
+ generateButtonClass: props => {
3269
+ const {variant: variant = defaultProps.variant, size: size = defaultProps.size, disabled: disabled = defaultProps.disabled, rounded: rounded = defaultProps.rounded, iconOnly: iconOnly = !1, glass: glass = defaultProps.glass, loading: loading = defaultProps.loading, fullWidth: fullWidth = defaultProps.fullWidth, block: block = defaultProps.block, active: active = defaultProps.active, selected: selected = defaultProps.selected, className: className = ""} = props, sizeClass = "md" === size ? "" : `c-btn--${size}`, iconOnlyClass = iconOnly ? "c-btn--icon" : "", roundedClass = rounded ? "c-btn--rounded" : "", disabledClass = disabled ? "c-btn--disabled" : "", glassClass = glass ? "c-btn--glass" : "", loadingClass = loading ? BUTTON.CLASSES.LOADING : "", fullWidthClass = fullWidth ? BUTTON.CLASSES.FULL_WIDTH : "", blockClass = block ? BUTTON.CLASSES.BLOCK : "", activeClass = active ? BUTTON.CLASSES.ACTIVE : "", selectedClass = selected ? BUTTON.CLASSES.SELECTED : "";
3270
+ return [ BUTTON.BASE_CLASS, `c-btn--${variant}`, sizeClass, iconOnlyClass, roundedClass, disabledClass, glassClass, loadingClass, fullWidthClass, blockClass, activeClass, selectedClass, className ].filter(Boolean).join(" ");
3271
+ },
3272
+ handleClick: handler => event => {
3273
+ defaultProps.disabled || defaultProps.loading || !handler || handler(event);
3274
+ }
3275
+ };
3276
+ }({
3277
+ variant: variant,
3278
+ size: size,
3279
+ disabled: isDisabled,
3280
+ rounded: rounded,
3281
+ glass: glass,
3282
+ loading: loading,
3283
+ fullWidth: fullWidth,
3284
+ block: block,
3285
+ active: active,
3286
+ selected: selected
3287
+ }), buttonClass = useMemo((() => generateButtonClass({
3288
+ variant: variant,
3289
+ size: size,
3290
+ disabled: isDisabled,
3291
+ rounded: rounded,
3292
+ iconOnly: iconOnly,
3293
+ glass: glass,
3294
+ loading: loading,
3295
+ fullWidth: fullWidth,
3296
+ block: block,
3297
+ active: active,
3298
+ selected: selected,
3299
+ className: className
3300
+ })), [ variant, size, isDisabled, rounded, iconOnly, glass, loading, fullWidth, block, active, selected, className, generateButtonClass ]), handleClickEvent = useCallback((event => {
3301
+ isDisabled ? event.preventDefault() : onClick?.(event);
3302
+ }), [ isDisabled, onClick ]), handleMouseEnter = useCallback((event => {
3303
+ isDisabled || onHover?.(event);
3304
+ }), [ isDisabled, onHover ]), handleFocusEvent = useCallback((event => {
3305
+ isDisabled || onFocus?.(event);
3306
+ }), [ isDisabled, onFocus ]), handleBlurEvent = useCallback((event => {
3307
+ isDisabled || onBlur?.(event);
3308
+ }), [ isDisabled, onBlur ]), buttonText = useMemo((() => loading && loadingText ? loadingText : label || children), [ loading, loadingText, label, children ]), spinnerSize = useMemo((() => "sm" === size ? "sm" : "lg" === size ? "md" : "sm"), [ size ]), buttonContent = useMemo((() => {
3309
+ const iconSpan = iconElement && jsx("span", {
3310
+ className: BUTTON.ICON_CLASS,
3311
+ "aria-hidden": "true",
3312
+ children: iconElement
3313
+ }), spinnerElement = loading && jsx("span", {
3314
+ className: BUTTON.SPINNER_CLASS,
3315
+ "aria-hidden": "true",
3316
+ children: jsx(Spinner, {
3317
+ size: spinnerSize,
3318
+ variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
3319
+ })
3320
+ }), labelElement = !iconOnly && buttonText && jsx("span", {
3321
+ className: BUTTON.LABEL_CLASS,
3322
+ children: buttonText
3323
+ });
3324
+ return jsxs(Fragment, "end" === iconPosition ? {
3325
+ children: [ labelElement, spinnerElement, iconSpan ]
3326
+ } : {
3327
+ children: [ spinnerElement, iconSpan, labelElement ]
3328
+ });
3329
+ }), [ iconElement, iconPosition, iconOnly, buttonText, loading, spinnerSize, variant ]), buttonProps = useMemo((() => ({
3330
+ ref: ref,
3331
+ className: buttonClass,
3332
+ type: "button" !== Component || shouldRenderAsLink ? void 0 : type,
3333
+ onClick: handleClickEvent,
3334
+ onMouseEnter: onHover ? handleMouseEnter : void 0,
3335
+ onFocus: onFocus ? handleFocusEvent : void 0,
3336
+ onBlur: onBlur ? handleBlurEvent : void 0,
3337
+ disabled: isDisabled && "button" === Component && !shouldRenderAsLink,
3338
+ "aria-disabled": isDisabled,
3339
+ "aria-busy": loading,
3340
+ "aria-label": ariaLabel || (iconOnly ? label || children : void 0),
3341
+ "aria-describedby": ariaDescribedBy,
3342
+ "aria-expanded": ariaExpanded,
3343
+ "aria-controls": ariaControls,
3344
+ tabIndex: void 0 !== tabIndex ? tabIndex : isDisabled ? -1 : 0,
3345
+ style: style,
3346
+ ...props
3347
+ })), [ ref, buttonClass, Component, type, handleClickEvent, handleMouseEnter, handleFocusEvent, handleBlurEvent, isDisabled, loading, ariaLabel, iconOnly, label, children, ariaDescribedBy, ariaExpanded, ariaControls, tabIndex, style, props ]);
3348
+ // Determine if we should render as a link
3349
+ // Render as anchor if href is provided
3350
+ if (shouldRenderAsLink) {
3351
+ const {ref: _, ...buttonPropsWithoutRef} = buttonProps, anchorButtonProps = {
3352
+ ...buttonPropsWithoutRef,
3353
+ type: void 0,
3354
+ disabled: void 0
3355
+ };
3356
+ // Use custom LinkComponent if provided (e.g., Next.js Link)
3357
+ if (LinkComponent) {
3358
+ const LinkComp = LinkComponent, linkProps = {
3359
+ ...anchorButtonProps,
3360
+ ref: ref,
3361
+ href: href,
3362
+ target: target,
3363
+ rel: "_blank" === target ? "noopener noreferrer" : void 0
3364
+ }, linkElement = jsx(LinkComp, {
3365
+ ...linkProps,
3366
+ children: buttonContent
3367
+ });
3368
+ if (glass) {
3369
+ const defaultGlassProps = {
3370
+ displacementScale: 20,
3371
+ blurAmount: 0,
3372
+ saturation: 200,
3373
+ elasticity: 0
3374
+ }, glassProps = !0 === glass ? defaultGlassProps : {
3375
+ ...defaultGlassProps,
3376
+ ...glass
3377
+ };
3378
+ return jsx(AtomixGlass, {
3379
+ ...glassProps,
3380
+ children: linkElement
3381
+ });
3382
+ }
3383
+ return linkElement;
3384
+ }
3385
+ // Fallback to regular anchor tag
3386
+ const anchorElement = jsx("a", {
3387
+ ...anchorButtonProps,
3388
+ ref: ref,
3389
+ href: href,
3390
+ target: target,
3391
+ rel: "_blank" === target ? "noopener noreferrer" : void 0,
3392
+ children: buttonContent
3393
+ });
3394
+ if (glass) {
3395
+ const defaultGlassProps = {
3396
+ displacementScale: 20,
3397
+ blurAmount: 0,
3398
+ saturation: 200,
3399
+ elasticity: 0
3400
+ }, glassProps = !0 === glass ? defaultGlassProps : {
3401
+ ...defaultGlassProps,
3402
+ ...glass
3403
+ };
3404
+ return jsx(AtomixGlass, {
3405
+ ...glassProps,
3406
+ children: anchorElement
3407
+ });
3408
+ }
3409
+ return anchorElement;
3410
+ }
3411
+ // Default button rendering
3412
+ if (glass) {
3413
+ const defaultGlassProps = {
3414
+ displacementScale: 20,
3415
+ blurAmount: 0,
3416
+ saturation: 200,
3417
+ elasticity: 0
3418
+ }, glassProps = !0 === glass ? defaultGlassProps : {
3419
+ ...defaultGlassProps,
3420
+ ...glass
3421
+ };
3422
+ return jsx(AtomixGlass, {
3423
+ ...glassProps,
3424
+ children: jsx(Component, {
3425
+ ...buttonProps,
3426
+ children: buttonContent
3427
+ })
3428
+ });
3429
+ }
3430
+ return jsx(Component, {
3431
+ ...buttonProps,
3432
+ children: buttonContent
3433
+ });
3434
+ })));
3435
+
3436
+ Button.displayName = "Button";
3437
+
3438
+ /**
3439
+ * PhotoViewerHeader component - displays controls and counter for the photo viewer
3440
+ *
3441
+ * @param props - PhotoViewerHeaderProps
3442
+ * @returns JSX.Element
3443
+ */
3444
+ const PhotoViewerHeader = ({currentIndex: currentIndex, imagesLength: imagesLength, onZoomOut: onZoomOut, onResetZoom: onResetZoom, onZoomIn: onZoomIn, onToggleFullscreen: onToggleFullscreen, onClose: onClose, isFullscreen: isFullscreen, zoomLevel: zoomLevel, onRotate: onRotate, onDownload: onDownload, onShare: onShare, showInfo: showInfo, onToggleInfo: onToggleInfo, currentImage: currentImage}) => jsxs("div", {
3445
+ className: "c-photo-viewer__header",
3446
+ children: [ jsxs("div", {
3447
+ className: "c-photo-viewer__header-left",
3448
+ children: [ jsx(Badge, {
3449
+ label: `${currentIndex + 1} / ${imagesLength}`,
3450
+ variant: "primary",
3451
+ size: "sm"
3452
+ }), currentImage?.title && jsx("h3", {
3453
+ className: "c-photo-viewer__image-title",
3454
+ children: currentImage.title
3455
+ }) ]
3456
+ }), jsxs("div", {
3457
+ className: "c-photo-viewer__actions",
3458
+ children: [ jsx(Button, {
3459
+ iconOnly: !0,
3460
+ size: "sm",
3461
+ variant: "ghost",
3462
+ rounded: !0,
3463
+ onClick: onZoomOut,
3464
+ disabled: zoomLevel <= .1,
3465
+ "aria-label": "Zoom out",
3466
+ className: "c-photo-viewer__action-button",
3467
+ icon: jsx(Icon, {
3468
+ name: "Minus",
3469
+ size: "sm"
3470
+ })
3471
+ }), jsx(Button, {
3472
+ iconOnly: !0,
3473
+ size: "sm",
3474
+ variant: "ghost",
3475
+ rounded: !0,
3476
+ onClick: onResetZoom,
3477
+ disabled: 1 === zoomLevel,
3478
+ "aria-label": "Reset zoom",
3479
+ className: "c-photo-viewer__action-button",
3480
+ icon: jsx(Icon, {
3481
+ name: "MagnifyingGlass",
3482
+ size: "sm"
3483
+ })
3484
+ }), jsx(Button, {
3485
+ iconOnly: !0,
3486
+ size: "sm",
3487
+ variant: "ghost",
3488
+ rounded: !0,
3489
+ onClick: onZoomIn,
3490
+ disabled: zoomLevel >= 5,
3491
+ "aria-label": "Zoom in",
3492
+ className: "c-photo-viewer__action-button",
3493
+ icon: jsx(Icon, {
3494
+ name: "Plus",
3495
+ size: "sm"
3496
+ })
3497
+ }), jsx("div", {
3498
+ className: "c-photo-viewer__divider"
3499
+ }), jsx(Button, {
3500
+ iconOnly: !0,
3501
+ size: "sm",
3502
+ variant: "ghost",
3503
+ rounded: !0,
3504
+ onClick: onRotate,
3505
+ "aria-label": "Rotate image",
3506
+ className: "c-photo-viewer__action-button",
3507
+ icon: jsx(Icon, {
3508
+ name: "ArrowsClockwise",
3509
+ size: "sm"
3510
+ })
3511
+ }), jsx(Button, {
3512
+ iconOnly: !0,
3513
+ size: "sm",
3514
+ variant: "ghost",
3515
+ rounded: !0,
3516
+ onClick: onDownload,
3517
+ "aria-label": "Download image",
3518
+ className: "c-photo-viewer__action-button",
3519
+ icon: jsx(Icon, {
3520
+ name: "Download",
3521
+ size: "sm"
3522
+ })
3523
+ }), "share" in navigator && "function" == typeof navigator.share && jsx(Button, {
3524
+ iconOnly: !0,
3525
+ size: "sm",
3526
+ variant: "ghost",
3527
+ rounded: !0,
3528
+ onClick: onShare,
3529
+ "aria-label": "Share image",
3530
+ className: "c-photo-viewer__action-button",
3531
+ icon: jsx(Icon, {
3532
+ name: "Share",
3533
+ size: "sm"
3534
+ })
3535
+ }), jsx(Button, {
3536
+ iconOnly: !0,
3537
+ size: "sm",
3538
+ variant: "ghost",
3539
+ rounded: !0,
3540
+ onClick: onToggleInfo,
3541
+ "aria-label": "Toggle info panel",
3542
+ className: "c-photo-viewer__action-button " + (showInfo ? "is-active" : ""),
3543
+ icon: jsx(Icon, {
3544
+ name: "Info",
3545
+ size: "sm"
3546
+ })
3547
+ }), jsx("div", {
3548
+ className: "c-photo-viewer__divider"
3549
+ }), jsx(Button, {
3550
+ iconOnly: !0,
3551
+ size: "sm",
3552
+ variant: "ghost",
3553
+ rounded: !0,
3554
+ onClick: onToggleFullscreen,
3555
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
3556
+ className: "c-photo-viewer__action-button",
3557
+ icon: jsx(Icon, {
3558
+ name: isFullscreen ? "ArrowsIn" : "ArrowsOut",
3559
+ size: "sm"
3560
+ })
3561
+ }), jsx(Button, {
3562
+ iconOnly: !0,
3563
+ size: "sm",
3564
+ variant: "ghost",
3565
+ rounded: !0,
3566
+ onClick: onClose,
3567
+ "aria-label": "Close viewer",
3568
+ className: "c-photo-viewer__action-button c-photo-viewer__close-button",
3569
+ icon: jsx(Icon, {
3570
+ name: "X",
3571
+ size: "sm"
3572
+ })
3573
+ }) ]
3574
+ }) ]
3575
+ })
3576
+ /**
3577
+ * PhotoViewerNavigation component - handles navigation between images
3578
+ *
3579
+ * @param props - PhotoViewerNavigationProps
3580
+ * @returns JSX.Element
3581
+ */ , PhotoViewerNavigation = ({show: show, onPrev: onPrev, onNext: onNext, currentIndex: currentIndex, imagesLength: imagesLength, enableKeyboardNav: enableKeyboardNav, onClose: onClose}) => (
3582
+ // Add keyboard navigation
3583
+ useEffect((() => {
3584
+ if (!enableKeyboardNav) return;
3585
+ const handleKeyDown = e => {
3586
+ "ArrowLeft" === e.key && onPrev(), "ArrowRight" === e.key && onNext(), "Escape" === e.key && onClose();
3587
+ };
3588
+ return window.addEventListener("keydown", handleKeyDown), () => window.removeEventListener("keydown", handleKeyDown);
3589
+ }), [ enableKeyboardNav, onPrev, onNext, onClose ]), show ? jsxs(Fragment, {
3590
+ children: [ jsx(Button, {
3591
+ iconOnly: !0,
3592
+ size: "md",
3593
+ variant: "ghost",
3594
+ rounded: !0,
3595
+ onClick: onPrev,
3596
+ disabled: 0 === currentIndex,
3597
+ "aria-label": "Previous image",
3598
+ className: "c-photo-viewer__nav-button c-photo-viewer__nav-button--prev",
3599
+ icon: jsx(Icon, {
3600
+ name: "CaretLeft",
3601
+ size: "md"
3602
+ })
3603
+ }), jsx(Button, {
3604
+ iconOnly: !0,
3605
+ size: "md",
3606
+ variant: "ghost",
3607
+ rounded: !0,
3608
+ onClick: onNext,
3609
+ disabled: currentIndex === imagesLength - 1,
3610
+ "aria-label": "Next image",
3611
+ className: "c-photo-viewer__nav-button c-photo-viewer__nav-button--next",
3612
+ icon: jsx(Icon, {
3613
+ name: "CaretRight",
3614
+ size: "md"
3615
+ })
3616
+ }) ]
3617
+ }) : null), PhotoViewerImage = ({imageRef: imageRef, containerRef: containerRef, src: src, alt: alt, zoomLevel: zoomLevel, dragPosition: dragPosition, isDragging: isDragging, rotationAngle: rotationAngle, isTransitioning: isTransitioning = !1, onMouseDown: onMouseDown, onMouseMove: onMouseMove, onMouseUp: onMouseUp, onWheel: onWheel, onTouchStart: onTouchStart, onTouchMove: onTouchMove, onTouchEnd: onTouchEnd, onDoubleClick: onDoubleClick}) => {
3618
+ const internalContainerRef = useRef(null), effectiveContainerRef = containerRef || internalContainerRef, [isMounted, setIsMounted] = useState(!1);
3619
+ // Track mounting state
3620
+ return useEffect((() => (setIsMounted(!0), () => setIsMounted(!1))), []),
3621
+ // Add non-passive event listeners to prevent page scrolling/zooming
3622
+ useEffect((() => {
3623
+ const container = effectiveContainerRef.current;
3624
+ if (!container) return;
3625
+ const handleWheelEvent = e => {
3626
+ // Only call if mounted and handler exists
3627
+ isMounted && container && onWheel && onWheel(e);
3628
+ }, handleTouchStartEvent = e => {
3629
+ // Only call if mounted and handler exists
3630
+ isMounted && container && onTouchStart && onTouchStart(e);
3631
+ }, handleTouchMoveEvent = e => {
3632
+ // Only call if mounted and handler exists
3633
+ isMounted && container && onTouchMove && onTouchMove(e);
3634
+ }, handleTouchEndEvent = e => {
3635
+ // Only call if mounted and handler exists
3636
+ isMounted && container && onTouchEnd && onTouchEnd(e);
3637
+ };
3638
+ // Clean up
3639
+ // Only add event listeners if mounted
3640
+ return isMounted && (container.addEventListener("wheel", handleWheelEvent, {
3641
+ passive: !1
3642
+ }), container.addEventListener("touchstart", handleTouchStartEvent, {
3643
+ passive: !1
3644
+ }), container.addEventListener("touchmove", handleTouchMoveEvent, {
3645
+ passive: !1
3646
+ }), container.addEventListener("touchend", handleTouchEndEvent, {
3647
+ passive: !1
3648
+ })), () => {
3649
+ container.removeEventListener("wheel", handleWheelEvent), container.removeEventListener("touchstart", handleTouchStartEvent),
3650
+ container.removeEventListener("touchmove", handleTouchMoveEvent), container.removeEventListener("touchend", handleTouchEndEvent);
3651
+ };
3652
+ }), [ isMounted, onWheel, onTouchStart, onTouchMove, onTouchEnd, effectiveContainerRef ]),
3653
+ jsx("div", {
3654
+ ref: effectiveContainerRef,
3655
+ className: "c-photo-viewer__image-container " + (isTransitioning ? "is-transitioning" : ""),
3656
+ style: {
3657
+ cursor: isDragging ? "grabbing" : zoomLevel > 1 ? "grab" : "default",
3658
+ opacity: isTransitioning ? .7 : 1,
3659
+ touchAction: "none"
3660
+ },
3661
+ onMouseDown: onMouseDown,
3662
+ onMouseMove: onMouseMove,
3663
+ onMouseUp: onMouseUp,
3664
+ onMouseLeave: onMouseUp,
3665
+ onDoubleClick: e => {
3666
+ isMounted && onDoubleClick && onDoubleClick(e);
3667
+ },
3668
+ children: jsx("img", {
3669
+ ref: imageRef,
3670
+ src: src,
3671
+ alt: alt,
3672
+ className: "c-photo-viewer__image",
3673
+ style: {
3674
+ transform: `scale(${zoomLevel}) translate(${dragPosition.x}px, ${dragPosition.y}px) rotate(${rotationAngle}deg)`,
3675
+ transition: isDragging ? "none" : isTransitioning ? "opacity 0.15s ease-out" : "transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)",
3676
+ transformOrigin: "center center",
3677
+ willChange: isDragging ? "transform" : "auto",
3678
+ touchAction: "none"
3679
+ },
3680
+ draggable: !1,
3681
+ onContextMenu: e => e.preventDefault()
3682
+ })
3683
+ });
3684
+ }, PhotoViewerThumbnails = ({images: images, currentIndex: currentIndex, goToImage: goToImage}) => images.length <= 1 ? null : jsx("div", {
3685
+ className: "c-photo-viewer__thumbnails",
3686
+ children: jsx("div", {
3687
+ className: "c-photo-viewer__thumbnails-container",
3688
+ children: images.map(((image, index) => {
3689
+ const thumbnailSrc = image.thumbnail || image.src, isActive = index === currentIndex;
3690
+ return jsx(Button, {
3691
+ variant: "ghost",
3692
+ className: "c-photo-viewer__thumbnail " + (isActive ? "is-active" : ""),
3693
+ onClick: () => goToImage(index),
3694
+ "aria-label": `View image ${index + 1}${image.title ? `: ${image.title}` : ""}`,
3695
+ "aria-current": isActive,
3696
+ children: jsxs("div", {
3697
+ className: "c-photo-viewer__thumbnail-wrapper",
3698
+ children: [ jsx("img", {
3699
+ loading: "lazy",
3700
+ src: thumbnailSrc,
3701
+ alt: image.alt || `Thumbnail ${index + 1}`,
3702
+ className: "c-photo-viewer__thumbnail-img"
3703
+ }), isActive && jsx("div", {
3704
+ className: "c-photo-viewer__thumbnail-indicator"
3705
+ }) ]
3706
+ })
3707
+ }, index);
3708
+ }))
3709
+ })
3710
+ }), PhotoViewerInfo = ({show: show, image: image, onClose: onClose}) => show && image ? jsxs("div", {
3711
+ className: "c-photo-viewer__info-panel",
3712
+ children: [ jsxs("div", {
3713
+ className: "c-photo-viewer__info-header",
3714
+ children: [ jsx("h4", {
3715
+ className: "c-photo-viewer__info-panel-title",
3716
+ children: "Image Details"
3717
+ }), jsx(Button, {
3718
+ iconOnly: !0,
3719
+ size: "sm",
3720
+ variant: "ghost",
3721
+ rounded: !0,
3722
+ onClick: onClose,
3723
+ "aria-label": "Close info panel",
3724
+ className: "c-photo-viewer__info-close",
3725
+ icon: jsx(Icon, {
3726
+ name: "X",
3727
+ size: "sm"
3728
+ })
3729
+ }) ]
3730
+ }), jsxs("div", {
3731
+ className: "c-photo-viewer__info-content",
3732
+ children: [ image.title && jsx("div", {
3733
+ className: "c-photo-viewer__info-section",
3734
+ children: jsx("h5", {
3735
+ className: "c-photo-viewer__info-title",
3736
+ children: image.title
3737
+ })
3738
+ }), image.description && jsx("div", {
3739
+ className: "c-photo-viewer__info-section",
3740
+ children: jsx("p", {
3741
+ className: "c-photo-viewer__info-description",
3742
+ children: image.description
3743
+ })
3744
+ }), (image.date || image.author) && jsx("div", {
3745
+ className: "c-photo-viewer__info-section",
3746
+ children: jsxs("div", {
3747
+ className: "c-photo-viewer__info-meta",
3748
+ children: [ image.date && jsxs("div", {
3749
+ className: "c-photo-viewer__info-meta-item",
3750
+ children: [ jsx(Icon, {
3751
+ name: "Calendar",
3752
+ size: 14
3753
+ }), jsx("span", {
3754
+ children: image.date
3755
+ }) ]
3756
+ }), image.author && jsxs("div", {
3757
+ className: "c-photo-viewer__info-meta-item",
3758
+ children: [ jsx(Icon, {
3759
+ name: "User",
3760
+ size: 14
3761
+ }), jsx("span", {
3762
+ children: image.author
3763
+ }) ]
3764
+ }) ]
3765
+ })
3766
+ }), image.tags && image.tags.length > 0 && jsxs("div", {
3767
+ className: "c-photo-viewer__info-section",
3768
+ children: [ jsx("h6", {
3769
+ className: "c-photo-viewer__info-section-title",
3770
+ children: "Tags"
3771
+ }), jsx("div", {
3772
+ className: "c-photo-viewer__info-tags",
3773
+ children: image.tags.map(((tag, index) => jsx(Badge, {
3774
+ label: tag,
3775
+ variant: "secondary",
3776
+ size: "sm"
3777
+ }, index)))
3778
+ }) ]
3779
+ }) ]
3780
+ }) ]
3781
+ }) : null, PhotoViewer = ({images: images, startIndex: startIndex = 0, className: className = "", disabled: disabled = !1, enableKeyboardNavigation: enableKeyboardNavigation = !0, enableGestures: enableGestures = !0, enableFullscreen: enableFullscreen = !0, thumbnailPosition: thumbnailPosition = "bottom", onImageChange: onImageChange, onClose: onClose}) => {
3782
+ // Use the external composable hook with enhanced features
3783
+ const {currentIndex: currentIndex, zoomLevel: zoomLevel, imagePosition: dragPosition, isDragging: isDragging, isFullscreen: isFullscreen, rotationAngle: rotationAngle, showInfo: showInfo, imageRef: imageRef, containerRef: containerRef, isTransitioning: isTransitioning, setZoomLevel: setZoomLevel, setImagePosition: setDragPosition, setIsDragging: setIsDragging, setIsFullscreen: setIsFullscreen, setRotationAngle: setRotationAngle, setShowInfo: setShowInfo, closeModal: closeModal, goToPrevious: goToPrevious, goToNext: goToNext, setCurrentIndex: goToImage, handleMouseDown: handleMouseDown, handleMouseMove: handleMouseMove, handleMouseUp: handleMouseUp, handleWheel: handleWheel, handleTouchStart: handleTouchStart, handleTouchMove: handleTouchMove, handleTouchEnd: handleTouchEnd, handleDoubleClick: handleDoubleClick, resetImageState: resetImageState} = (({images: images, startIndex: startIndex = 0, enableGestures: enableGestures = !0, onImageChange: onImageChange, onClose: onClose}) => {
3784
+ const [currentIndex, setCurrentIndex] = useState(startIndex), [isModalOpen, setIsModalOpen] = useState(!1), [isDragging, setIsDragging] = useState(!1), [startDragPosition, setStartDragPosition] = useState({
3785
+ x: 0,
3786
+ y: 0
3787
+ }), [isFullscreen, setIsFullscreen] = useState(!1), [showInfo, setShowInfo] = useState(!1), [imageStates, setImageStates] = useState({}), [isTransitioning, setIsTransitioning] = useState(!1), [isMounted, setIsMounted] = useState(!1), [momentumZoom, setMomentumZoom] = useState({
3788
+ velocity: 0,
3789
+ timestamp: 0
3790
+ }), imageRef = useRef(null), containerRef = useRef(null), touchPointsRef = useRef([]), lastDistanceRef = useRef(null), lastMidpointRef = useRef(null), lastWheelTime = useRef(0), momentumTimeoutRef = useRef(null), calculateBounds = useCallback(((zoomLevel, rotation) => {
3791
+ if (!isMounted || !imageRef.current || !containerRef.current) return {
3792
+ minX: 0,
3793
+ maxX: 0,
3794
+ minY: 0,
3795
+ maxY: 0
3796
+ };
3797
+ const image = imageRef.current, container = containerRef.current;
3798
+ // Additional safety check for DOM readiness
3799
+ if (!image.naturalWidth && !image.width) return {
3800
+ minX: 0,
3801
+ maxX: 0,
3802
+ minY: 0,
3803
+ maxY: 0
3804
+ };
3805
+ // Get natural image dimensions
3806
+ const imageWidth = image.naturalWidth || image.width || 800, imageHeight = image.naturalHeight || image.height || 600;
3807
+ // Get container dimensions with null check
3808
+ try {
3809
+ const containerRect = container.getBoundingClientRect();
3810
+ if (!containerRect || 0 === containerRect.width || 0 === containerRect.height) return {
3811
+ minX: 0,
3812
+ maxX: 0,
3813
+ minY: 0,
3814
+ maxY: 0
3815
+ };
3816
+ const containerWidth = containerRect.width, containerHeight = containerRect.height, rotationRad = rotation * Math.PI / 180, cos = Math.abs(Math.cos(rotationRad)), sin = Math.abs(Math.sin(rotationRad)), aspectRatio = imageWidth / imageHeight;
3817
+ let displayWidth, displayHeight;
3818
+ containerWidth / containerHeight > aspectRatio ? (displayHeight = Math.min(.9 * containerHeight, imageHeight),
3819
+ displayWidth = displayHeight * aspectRatio) : (displayWidth = Math.min(.9 * containerWidth, imageWidth),
3820
+ displayHeight = displayWidth / aspectRatio);
3821
+ // Account for rotation in bounds calculation
3822
+ const scaledWidth = (displayWidth * cos + displayHeight * sin) * zoomLevel, scaledHeight = (displayWidth * sin + displayHeight * cos) * zoomLevel, maxX = Math.max(0, (scaledWidth - containerWidth) / 2), maxY = Math.max(0, (scaledHeight - containerHeight) / 2);
3823
+ return {
3824
+ minX: -maxX,
3825
+ maxX: maxX,
3826
+ minY: -maxY,
3827
+ maxY: maxY
3828
+ };
3829
+ } catch (error) {
3830
+ return console.warn("PhotoViewer: Error calculating bounds", error), {
3831
+ minX: 0,
3832
+ maxX: 0,
3833
+ minY: 0,
3834
+ maxY: 0
3835
+ };
3836
+ }
3837
+ }), [ isMounted ]), constrainPosition = useCallback(((position, bounds) => ({
3838
+ x: Math.max(bounds.minX, Math.min(bounds.maxX, position.x)),
3839
+ y: Math.max(bounds.minY, Math.min(bounds.maxY, position.y))
3840
+ })), []);
3841
+ // Mount tracking and ensure the current index is within bounds
3842
+ useEffect((() => (setIsMounted(!0), startIndex < 0 || startIndex >= images.length ? setCurrentIndex(0) : setCurrentIndex(startIndex),
3843
+ () => setIsMounted(!1))), [ images, startIndex ]),
3844
+ // Handle modal open/close body class
3845
+ useEffect((() => {
3846
+ isModalOpen ? document.body.classList.add("is-open-photoviewer") : document.body.classList.remove("is-open-photoviewer");
3847
+ }), [ isModalOpen ]),
3848
+ // Initialize state for current image when index changes
3849
+ useEffect((() => {
3850
+ isModalOpen && setImageStates((prev => prev[currentIndex] ? prev : {
3851
+ ...prev,
3852
+ [currentIndex]: {
3853
+ zoomLevel: 1,
3854
+ position: {
3855
+ x: 0,
3856
+ y: 0
3857
+ },
3858
+ rotation: 0,
3859
+ bounds: {
3860
+ minX: 0,
3861
+ maxX: 0,
3862
+ minY: 0,
3863
+ maxY: 0
3864
+ }
3865
+ }
3866
+ }));
3867
+ }), [ isModalOpen, currentIndex ]),
3868
+ // Call onImageChange callback when current index changes
3869
+ useEffect((() => {
3870
+ onImageChange && onImageChange(currentIndex);
3871
+ }), [ currentIndex, onImageChange ]),
3872
+ // Update bounds when image loads or dimensions change
3873
+ useEffect((() => {
3874
+ const image = imageRef.current, container = containerRef.current, updateImageBounds = () => {
3875
+ isMounted && image && container && setImageStates((prev => {
3876
+ const currentState = prev[currentIndex] || {
3877
+ zoomLevel: 1,
3878
+ position: {
3879
+ x: 0,
3880
+ y: 0
3881
+ },
3882
+ rotation: 0,
3883
+ bounds: {
3884
+ minX: 0,
3885
+ maxX: 0,
3886
+ minY: 0,
3887
+ maxY: 0
3888
+ }
3889
+ }, newBounds = calculateBounds(currentState.zoomLevel, currentState.rotation), constrainedPosition = constrainPosition(currentState.position, newBounds);
3890
+ return {
3891
+ ...prev,
3892
+ [currentIndex]: {
3893
+ ...currentState,
3894
+ bounds: newBounds,
3895
+ position: constrainedPosition
3896
+ }
3897
+ };
3898
+ }));
3899
+ };
3900
+ if (!(image && container && image.complete && isMounted)) return image && container && isMounted ? (image.addEventListener("load", updateImageBounds),
3901
+ () => image.removeEventListener("load", updateImageBounds)) : void 0;
3902
+ updateImageBounds();
3903
+ }), [ currentIndex, calculateBounds, constrainPosition, isMounted ]),
3904
+ // Handle window resize
3905
+ useEffect((() => {
3906
+ const handleResize = () => {
3907
+ isMounted && imageRef.current && containerRef.current && setImageStates((prev => {
3908
+ const currentState = prev[currentIndex] || {
3909
+ zoomLevel: 1,
3910
+ position: {
3911
+ x: 0,
3912
+ y: 0
3913
+ },
3914
+ rotation: 0,
3915
+ bounds: {
3916
+ minX: 0,
3917
+ maxX: 0,
3918
+ minY: 0,
3919
+ maxY: 0
3920
+ }
3921
+ }, newBounds = calculateBounds(currentState.zoomLevel, currentState.rotation), constrainedPosition = constrainPosition(currentState.position, newBounds);
3922
+ return {
3923
+ ...prev,
3924
+ [currentIndex]: {
3925
+ ...currentState,
3926
+ bounds: newBounds,
3927
+ position: constrainedPosition
3928
+ }
3929
+ };
3930
+ }));
3931
+ };
3932
+ return window.addEventListener("resize", handleResize), () => window.removeEventListener("resize", handleResize);
3933
+ }), [ currentIndex, calculateBounds, constrainPosition, isMounted ]);
3934
+ const openModal = useCallback((() => {
3935
+ setIsModalOpen(!0);
3936
+ }), []), closeModal = useCallback((() => {
3937
+ setIsModalOpen(!1), onClose && onClose();
3938
+ }), [ onClose ]), goToPrevious = useCallback((() => {
3939
+ currentIndex > 0 && (setIsTransitioning(!0), setTimeout((() => {
3940
+ setCurrentIndex((prev => prev - 1)), setIsTransitioning(!1);
3941
+ }), 150));
3942
+ }), [ currentIndex ]), goToNext = useCallback((() => {
3943
+ currentIndex < images.length - 1 && (setIsTransitioning(!0), setTimeout((() => {
3944
+ setCurrentIndex((prev => prev + 1)), setIsTransitioning(!1);
3945
+ }), 150));
3946
+ }), [ currentIndex, images.length ]), setZoomLevel = useCallback((zoom => {
3947
+ setImageStates((prev => {
3948
+ const currentState = prev[currentIndex] || {
3949
+ zoomLevel: 1,
3950
+ position: {
3951
+ x: 0,
3952
+ y: 0
3953
+ },
3954
+ rotation: 0,
3955
+ bounds: {
3956
+ minX: 0,
3957
+ maxX: 0,
3958
+ minY: 0,
3959
+ maxY: 0
3960
+ }
3961
+ }, newZoom = "function" == typeof zoom ? zoom(currentState.zoomLevel) : zoom, clampedZoom = Math.max(.1, Math.min(5, newZoom)), newBounds = calculateBounds(clampedZoom, currentState.rotation), constrainedPosition = constrainPosition(currentState.position, newBounds);
3962
+ return {
3963
+ ...prev,
3964
+ [currentIndex]: {
3965
+ ...currentState,
3966
+ zoomLevel: clampedZoom,
3967
+ bounds: newBounds,
3968
+ position: constrainedPosition
3969
+ }
3970
+ };
3971
+ }));
3972
+ }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), setImagePosition = useCallback((position => {
3973
+ setImageStates((prev => {
3974
+ const currentState = prev[currentIndex] || {
3975
+ zoomLevel: 1,
3976
+ position: {
3977
+ x: 0,
3978
+ y: 0
3979
+ },
3980
+ rotation: 0,
3981
+ bounds: {
3982
+ minX: 0,
3983
+ maxX: 0,
3984
+ minY: 0,
3985
+ maxY: 0
3986
+ }
3987
+ }, newPosition = "function" == typeof position ? position(currentState.position) : position, constrainedPosition = constrainPosition(newPosition, currentState.bounds);
3988
+ return {
3989
+ ...prev,
3990
+ [currentIndex]: {
3991
+ ...currentState,
3992
+ position: constrainedPosition
3993
+ }
3994
+ };
3995
+ }));
3996
+ }), [ currentIndex, constrainPosition ]), setRotationAngle = useCallback((rotation => {
3997
+ setImageStates((prev => {
3998
+ const currentState = prev[currentIndex] || {
3999
+ zoomLevel: 1,
4000
+ position: {
4001
+ x: 0,
4002
+ y: 0
4003
+ },
4004
+ rotation: 0,
4005
+ bounds: {
4006
+ minX: 0,
4007
+ maxX: 0,
4008
+ minY: 0,
4009
+ maxY: 0
4010
+ }
4011
+ }, normalizedRotation = (("function" == typeof rotation ? rotation(currentState.rotation) : rotation) % 360 + 360) % 360, newBounds = calculateBounds(currentState.zoomLevel, normalizedRotation), constrainedPosition = constrainPosition(currentState.position, newBounds);
4012
+ return {
4013
+ ...prev,
4014
+ [currentIndex]: {
4015
+ ...currentState,
4016
+ rotation: normalizedRotation,
4017
+ bounds: newBounds,
4018
+ position: constrainedPosition
4019
+ }
4020
+ };
4021
+ }));
4022
+ }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), handleWheel = useCallback((event => {
4023
+ var _context;
4024
+ if (!isMounted || !event || !event.currentTarget) return;
4025
+ // Additional safety check for the target element
4026
+ const target = event.currentTarget;
4027
+ if (target && "function" == typeof target.getBoundingClientRect) {
4028
+ // Storybook-specific safety check - ensure DOM is ready
4029
+ if ("undefined" != typeof window && (null == (_context = window.location?.href) ? void 0 : Function.call.bind(_includesInstanceProperty(_context), _context))?.("storybook")) try {
4030
+ // Test if getBoundingClientRect works before proceeding
4031
+ const testRect = target.getBoundingClientRect();
4032
+ if (!testRect || 0 === testRect.width || 0 === testRect.height) return;
4033
+ } catch (e) {
4034
+ return;
4035
+ }
4036
+ setImageStates((prev => {
4037
+ const currentState = prev[currentIndex] || {
4038
+ zoomLevel: 1,
4039
+ position: {
4040
+ x: 0,
4041
+ y: 0
4042
+ },
4043
+ rotation: 0,
4044
+ bounds: {
4045
+ minX: 0,
4046
+ maxX: 0,
4047
+ minY: 0,
4048
+ maxY: 0
4049
+ }
4050
+ }, isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform), isTrackpadPinch = event.ctrlKey && isMac, hasHorizontalScroll = Math.abs(event.deltaX) > 0, isTrackpadScroll = !event.ctrlKey && hasHorizontalScroll && isMac, isMagicMouse = !event.ctrlKey && !hasHorizontalScroll && isMac, isRegularMouse = !isMac;
4051
+ // Advanced gesture detection for different input methods
4052
+ // Handle different input methods with appropriate sensitivity
4053
+ let zoomAmount, shouldPreventDefault = !1;
4054
+ if (isTrackpadPinch)
4055
+ // MacBook trackpad pinch zoom - natural, high sensitivity
4056
+ zoomAmount = -.02 * event.deltaY, shouldPreventDefault = !0; else if (isTrackpadScroll) {
4057
+ // MacBook trackpad scroll with two fingers
4058
+ if (!(currentState.zoomLevel > 1)) return prev;
4059
+ // Allow page scroll when not zoomed
4060
+ // Only zoom when already zoomed in, otherwise allow natural scroll
4061
+ zoomAmount = -.003 * event.deltaY, shouldPreventDefault = !0;
4062
+ } else isMagicMouse ? (
4063
+ // Apple Magic Mouse - less sensitive
4064
+ zoomAmount = -.004 * event.deltaY, shouldPreventDefault = !0) : isRegularMouse ? (
4065
+ // Regular mouse wheel - medium sensitivity
4066
+ zoomAmount = -.006 * event.deltaY, shouldPreventDefault = !0) : (
4067
+ // Fallback for other input methods
4068
+ zoomAmount = -.005 * event.deltaY, shouldPreventDefault = !0);
4069
+ shouldPreventDefault && (event.preventDefault(), event.stopPropagation());
4070
+ // Add momentum for trackpad gestures
4071
+ const currentTime = Date.now(), timeDelta = currentTime - lastWheelTime.current;
4072
+ // Calculate velocity for momentum (trackpad specific)
4073
+ if (lastWheelTime.current = currentTime, isTrackpadPinch && timeDelta < 100) {
4074
+ const velocity = Math.abs(zoomAmount) / timeDelta;
4075
+ setMomentumZoom({
4076
+ velocity: velocity,
4077
+ timestamp: currentTime
4078
+ }),
4079
+ // Clear any existing momentum timeout
4080
+ momentumTimeoutRef.current && clearTimeout(momentumTimeoutRef.current),
4081
+ // Apply momentum decay after gesture ends
4082
+ momentumTimeoutRef.current = setTimeout((() => {
4083
+ const applyMomentum = () => {
4084
+ setMomentumZoom((prev => {
4085
+ if (prev.velocity < .001) return prev;
4086
+ const newVelocity = .95 * prev.velocity, momentumZoomAmount = newVelocity * (zoomAmount > 0 ? 1 : -1);
4087
+ // Apply momentum zoom
4088
+ return setImageStates((current => {
4089
+ const state = current[currentIndex] || {
4090
+ zoomLevel: 1,
4091
+ position: {
4092
+ x: 0,
4093
+ y: 0
4094
+ },
4095
+ rotation: 0,
4096
+ bounds: {
4097
+ minX: 0,
4098
+ maxX: 0,
4099
+ minY: 0,
4100
+ maxY: 0
4101
+ }
4102
+ }, newZoom = Math.max(.1, Math.min(5, state.zoomLevel + momentumZoomAmount));
4103
+ if (newZoom === state.zoomLevel) return current;
4104
+ const newBounds = calculateBounds(newZoom, state.rotation), constrainedPosition = constrainPosition(state.position, newBounds);
4105
+ return {
4106
+ ...current,
4107
+ [currentIndex]: {
4108
+ ...state,
4109
+ zoomLevel: newZoom,
4110
+ bounds: newBounds,
4111
+ position: constrainedPosition
4112
+ }
4113
+ };
4114
+ })), newVelocity >= .001 && requestAnimationFrame(applyMomentum), {
4115
+ velocity: newVelocity,
4116
+ timestamp: Date.now()
4117
+ };
4118
+ }));
4119
+ };
4120
+ requestAnimationFrame(applyMomentum);
4121
+ }), 50);
4122
+ }
4123
+ // Safe getBoundingClientRect call with error handling
4124
+ let rect;
4125
+ try {
4126
+ rect = target.getBoundingClientRect();
4127
+ } catch (error) {
4128
+ return console.warn("PhotoViewer: Error getting bounding rect", error), prev;
4129
+ }
4130
+ if (!rect || 0 === rect.width || 0 === rect.height) return prev;
4131
+ const centerX = rect.width / 2, centerY = rect.height / 2, cursorX = event.clientX - rect.left - centerX, cursorY = event.clientY - rect.top - centerY, oldZoom = currentState.zoomLevel, newZoom = Math.max(.1, Math.min(5, oldZoom + zoomAmount));
4132
+ if (newZoom !== oldZoom) {
4133
+ const zoomFactor = newZoom / oldZoom, newBounds = calculateBounds(newZoom, currentState.rotation), newPosition = {
4134
+ x: currentState.position.x + cursorX * (1 - zoomFactor) * .5,
4135
+ y: currentState.position.y + cursorY * (1 - zoomFactor) * .5
4136
+ }, constrainedPosition = constrainPosition(newPosition, newBounds);
4137
+ return {
4138
+ ...prev,
4139
+ [currentIndex]: {
4140
+ ...currentState,
4141
+ zoomLevel: newZoom,
4142
+ bounds: newBounds,
4143
+ position: constrainedPosition
4144
+ }
4145
+ };
4146
+ }
4147
+ return prev;
4148
+ }));
4149
+ }
4150
+ }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), handleDoubleClick = useCallback((event => {
4151
+ if (!isMounted || !event || !event.currentTarget) return;
4152
+ const target = event.currentTarget;
4153
+ target && "function" == typeof target.getBoundingClientRect && setImageStates((prev => {
4154
+ const currentState = prev[currentIndex] || {
4155
+ zoomLevel: 1,
4156
+ position: {
4157
+ x: 0,
4158
+ y: 0
4159
+ },
4160
+ rotation: 0,
4161
+ bounds: {
4162
+ minX: 0,
4163
+ maxX: 0,
4164
+ minY: 0,
4165
+ maxY: 0
4166
+ }
4167
+ };
4168
+ let rect;
4169
+ try {
4170
+ rect = target.getBoundingClientRect();
4171
+ } catch (error) {
4172
+ return console.warn("PhotoViewer: Error getting bounding rect in double click", error),
4173
+ prev;
4174
+ }
4175
+ if (!rect || 0 === rect.width || 0 === rect.height) return prev;
4176
+ const centerX = rect.width / 2, centerY = rect.height / 2, cursorX = event.clientX - rect.left - centerX, cursorY = event.clientY - rect.top - centerY;
4177
+ let newZoom, newPosition = {
4178
+ x: 0,
4179
+ y: 0
4180
+ };
4181
+ currentState.zoomLevel < 1.5 ? (newZoom = 2,
4182
+ // Zoom towards cursor
4183
+ newPosition = {
4184
+ x: .5 * -cursorX,
4185
+ y: .5 * -cursorY
4186
+ }) : currentState.zoomLevel < 3 ? (newZoom = 4, newPosition = {
4187
+ x: .75 * -cursorX,
4188
+ y: .75 * -cursorY
4189
+ }) : (newZoom = 1, newPosition = {
4190
+ x: 0,
4191
+ y: 0
4192
+ });
4193
+ const newBounds = calculateBounds(newZoom, currentState.rotation), constrainedPosition = constrainPosition(newPosition, newBounds);
4194
+ return {
4195
+ ...prev,
4196
+ [currentIndex]: {
4197
+ ...currentState,
4198
+ zoomLevel: newZoom,
4199
+ bounds: newBounds,
4200
+ position: constrainedPosition
4201
+ }
4202
+ };
4203
+ }));
4204
+ }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), handleMouseDown = useCallback((event => {
4205
+ setImageStates((prev => {
4206
+ const currentState = prev[currentIndex] || {
4207
+ zoomLevel: 1,
4208
+ position: {
4209
+ x: 0,
4210
+ y: 0
4211
+ },
4212
+ rotation: 0,
4213
+ bounds: {
4214
+ minX: 0,
4215
+ maxX: 0,
4216
+ minY: 0,
4217
+ maxY: 0
4218
+ }
4219
+ };
4220
+ return currentState.zoomLevel > 1 && (event.preventDefault(), setIsDragging(!0),
4221
+ setStartDragPosition({
4222
+ x: event.clientX - currentState.position.x,
4223
+ y: event.clientY - currentState.position.y
4224
+ })), prev;
4225
+ }));
4226
+ }), [ currentIndex ]), handleMouseMove = useCallback((event => {
4227
+ isDragging && setImageStates((prev => {
4228
+ const currentState = prev[currentIndex] || {
4229
+ zoomLevel: 1,
4230
+ position: {
4231
+ x: 0,
4232
+ y: 0
4233
+ },
4234
+ rotation: 0,
4235
+ bounds: {
4236
+ minX: 0,
4237
+ maxX: 0,
4238
+ minY: 0,
4239
+ maxY: 0
4240
+ }
4241
+ }, dragSensitivity = Math.min(1, 1 / currentState.zoomLevel), deltaX = (event.clientX - startDragPosition.x) * dragSensitivity, deltaY = (event.clientY - startDragPosition.y) * dragSensitivity, constrainedPosition = constrainPosition({
4242
+ x: deltaX,
4243
+ y: deltaY
4244
+ }, currentState.bounds);
4245
+ // Apply scaling factor to reduce drag sensitivity
4246
+ return {
4247
+ ...prev,
4248
+ [currentIndex]: {
4249
+ ...currentState,
4250
+ position: constrainedPosition
4251
+ }
4252
+ };
4253
+ }));
4254
+ }), [ isDragging, startDragPosition, currentIndex, constrainPosition ]), handleMouseUp = useCallback((() => {
4255
+ setIsDragging(!1);
4256
+ }), []), handleTouchStart = useCallback((event => {
4257
+ if (!enableGestures) return;
4258
+ const touches = event.touches;
4259
+ // Always prevent default for multi-touch to stop page zoom
4260
+ touches.length > 1 && (event.preventDefault(), event.stopPropagation()),
4261
+ touchPointsRef.current = Array.from(touches).map((touch => ({
4262
+ x: touch.clientX,
4263
+ y: touch.clientY
4264
+ }))), setImageStates((prev => {
4265
+ const currentState = prev[currentIndex] || {
4266
+ zoomLevel: 1,
4267
+ position: {
4268
+ x: 0,
4269
+ y: 0
4270
+ },
4271
+ rotation: 0,
4272
+ bounds: {
4273
+ minX: 0,
4274
+ maxX: 0,
4275
+ minY: 0,
4276
+ maxY: 0
4277
+ }
4278
+ };
4279
+ if (1 === touches.length && currentState.zoomLevel > 1) {
4280
+ setIsDragging(!0);
4281
+ const touch = touches[0];
4282
+ touch && setStartDragPosition({
4283
+ x: touch.clientX - currentState.position.x,
4284
+ y: touch.clientY - currentState.position.y
4285
+ });
4286
+ } else if (2 === touches.length) {
4287
+ const touch1 = touches[0], touch2 = touches[1];
4288
+ if (touch1 && touch2) {
4289
+ const dx = touch1.clientX - touch2.clientX, dy = touch1.clientY - touch2.clientY;
4290
+ lastDistanceRef.current = Math.sqrt(dx * dx + dy * dy), lastMidpointRef.current = {
4291
+ x: (touch1.clientX + touch2.clientX) / 2,
4292
+ y: (touch1.clientY + touch2.clientY) / 2
4293
+ };
4294
+ }
4295
+ }
4296
+ return prev;
4297
+ }));
4298
+ }), [ enableGestures, currentIndex ]), handleTouchMove = useCallback((event => {
4299
+ if (!enableGestures) return;
4300
+ const touches = event.touches;
4301
+ // Always prevent default for multi-touch gestures to stop page zoom
4302
+ touches.length > 1 && (event.preventDefault(), event.stopPropagation()),
4303
+ setImageStates((prev => {
4304
+ const currentState = prev[currentIndex] || {
4305
+ zoomLevel: 1,
4306
+ position: {
4307
+ x: 0,
4308
+ y: 0
4309
+ },
4310
+ rotation: 0,
4311
+ bounds: {
4312
+ minX: 0,
4313
+ maxX: 0,
4314
+ minY: 0,
4315
+ maxY: 0
4316
+ }
4317
+ };
4318
+ // Prevent default for single touch when zoomed in to avoid conflicts
4319
+ currentState.zoomLevel > 1 && 1 === touches.length && event.preventDefault();
4320
+ let newPosition = null, zoomDelta = 0, currentMidpoint = null;
4321
+ if (1 === touches.length && isDragging && currentState.zoomLevel > 1) {
4322
+ const touch = touches[0];
4323
+ if (touch) {
4324
+ // Apply scaling factor to reduce touch drag sensitivity
4325
+ const dragSensitivity = Math.min(1, 1 / currentState.zoomLevel);
4326
+ newPosition = {
4327
+ x: (touch.clientX - startDragPosition.x) * dragSensitivity,
4328
+ y: (touch.clientY - startDragPosition.y) * dragSensitivity
4329
+ };
4330
+ }
4331
+ if (newPosition) {
4332
+ const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
4333
+ return {
4334
+ ...prev,
4335
+ [currentIndex]: {
4336
+ ...currentState,
4337
+ position: constrainedPosition
4338
+ }
4339
+ };
4340
+ }
4341
+ } else if (2 === touches.length && null !== lastDistanceRef.current) {
4342
+ const touch1 = touches[0], touch2 = touches[1];
4343
+ if (touch1 && touch2) {
4344
+ const dx = touch1.clientX - touch2.clientX, dy = touch1.clientY - touch2.clientY, distance = Math.sqrt(dx * dx + dy * dy);
4345
+ zoomDelta = .005 * (distance - lastDistanceRef.current), lastDistanceRef.current = distance,
4346
+ currentMidpoint = {
4347
+ x: (touch1.clientX + touch2.clientX) / 2,
4348
+ y: (touch1.clientY + touch2.clientY) / 2
4349
+ };
4350
+ const oldZoom = currentState.zoomLevel, newZoom = Math.max(.1, Math.min(5, oldZoom + zoomDelta));
4351
+ if (newZoom !== oldZoom && lastMidpointRef.current && currentMidpoint) {
4352
+ let rect;
4353
+ try {
4354
+ rect = event.currentTarget.getBoundingClientRect();
4355
+ } catch (error) {
4356
+ return console.warn("PhotoViewer: Error getting bounding rect in touch move", error),
4357
+ prev;
4358
+ }
4359
+ if (!rect || 0 === rect.width || 0 === rect.height) return prev;
4360
+ const centerX = rect.width / 2, centerY = rect.height / 2, midpointX = currentMidpoint.x - rect.left - centerX, midpointY = currentMidpoint.y - rect.top - centerY, zoomFactor = newZoom / oldZoom, newBounds = calculateBounds(newZoom, currentState.rotation), newPosition = {
4361
+ x: currentState.position.x + midpointX * (1 - zoomFactor) * .5,
4362
+ y: currentState.position.y + midpointY * (1 - zoomFactor) * .5
4363
+ }, constrainedPosition = constrainPosition(newPosition, newBounds);
4364
+ return lastMidpointRef.current = currentMidpoint, {
4365
+ ...prev,
4366
+ [currentIndex]: {
4367
+ ...currentState,
4368
+ zoomLevel: newZoom,
4369
+ bounds: newBounds,
4370
+ position: constrainedPosition
4371
+ }
4372
+ };
4373
+ }
4374
+ currentMidpoint && (lastMidpointRef.current = currentMidpoint);
4375
+ }
4376
+ }
4377
+ return prev;
4378
+ }));
4379
+ }), [ isMounted, enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = useCallback((() => {
4380
+ setIsDragging(!1), lastDistanceRef.current = null, lastMidpointRef.current = null;
4381
+ }), []), currentState = imageStates[currentIndex] || {
4382
+ zoomLevel: 1,
4383
+ position: {
4384
+ x: 0,
4385
+ y: 0
4386
+ },
4387
+ rotation: 0,
4388
+ bounds: {
4389
+ minX: 0,
4390
+ maxX: 0,
4391
+ minY: 0,
4392
+ maxY: 0
4393
+ }
4394
+ };
4395
+ return {
4396
+ currentIndex: currentIndex,
4397
+ isModalOpen: isModalOpen,
4398
+ zoomLevel: currentState.zoomLevel,
4399
+ imagePosition: currentState.position,
4400
+ isDragging: isDragging,
4401
+ isFullscreen: isFullscreen,
4402
+ rotationAngle: currentState.rotation,
4403
+ showInfo: showInfo,
4404
+ imageRef: imageRef,
4405
+ containerRef: containerRef,
4406
+ isTransitioning: isTransitioning,
4407
+ setCurrentIndex: setCurrentIndex,
4408
+ setZoomLevel: setZoomLevel,
4409
+ setImagePosition: setImagePosition,
4410
+ setIsDragging: setIsDragging,
4411
+ setIsFullscreen: setIsFullscreen,
4412
+ setRotationAngle: setRotationAngle,
4413
+ setShowInfo: setShowInfo,
4414
+ openModal: openModal,
4415
+ closeModal: closeModal,
4416
+ goToPrevious: goToPrevious,
4417
+ goToNext: goToNext,
4418
+ handleWheel: handleWheel,
4419
+ handleMouseDown: handleMouseDown,
4420
+ handleMouseMove: handleMouseMove,
4421
+ handleMouseUp: handleMouseUp,
4422
+ handleTouchStart: handleTouchStart,
4423
+ handleTouchMove: handleTouchMove,
4424
+ handleTouchEnd: handleTouchEnd,
4425
+ handleDoubleClick: handleDoubleClick,
4426
+ resetImageState: () => {
4427
+ setImageStates((prev => ({
4428
+ ...prev,
4429
+ [currentIndex]: {
4430
+ zoomLevel: 1,
4431
+ position: {
4432
+ x: 0,
4433
+ y: 0
4434
+ },
4435
+ rotation: 0,
4436
+ bounds: {
4437
+ minX: 0,
4438
+ maxX: 0,
4439
+ minY: 0,
4440
+ maxY: 0
4441
+ }
4442
+ }
4443
+ })));
4444
+ }
4445
+ };
4446
+ })({
4447
+ images: images,
4448
+ startIndex: startIndex,
4449
+ enableGestures: enableGestures,
4450
+ onImageChange: onImageChange,
4451
+ onClose: onClose || (() => {})
4452
+ }), processedImages = useMemo((() => images.map((img => "string" == typeof img ? {
4453
+ src: img
4454
+ } : img))), [ images ]), currentImage = processedImages[currentIndex], photoViewerClasses = useMemo((() => [ "c-photo-viewer", `c-photo-viewer--thumbnails-${thumbnailPosition}`, isDragging ? "c-photo-viewer--dragging" : "", isFullscreen ? "c-photo-viewer--fullscreen" : "", showInfo ? "c-photo-viewer--info-open" : "", disabled ? "is-disabled" : "", className ].filter(Boolean).join(" ")), [ isDragging, isFullscreen, showInfo, disabled, thumbnailPosition, className ]);
4455
+ // Process images to handle both string arrays and object arrays, ensuring ImageType structure
4456
+ // Early return for empty images array
4457
+ // Listen for fullscreen changes
4458
+ return useEffect((() => {
4459
+ const handleFullscreenChange = () => {
4460
+ setIsFullscreen(!!document.fullscreenElement);
4461
+ };
4462
+ return document.addEventListener("fullscreenchange", handleFullscreenChange), () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
4463
+ }), [ setIsFullscreen ]),
4464
+ // Add/remove is-open-photoviewer class on body
4465
+ useEffect((() => (document.body.classList.add("is-open-photoviewer"), () => {
4466
+ document.body.classList.remove("is-open-photoviewer");
4467
+ })), []), images.length ? jsxs("div", {
4468
+ className: photoViewerClasses,
4469
+ role: "dialog",
4470
+ "aria-modal": "true",
4471
+ "aria-label": "Photo viewer",
4472
+ children: [ jsx("div", {
4473
+ className: "c-photo-viewer__backdrop",
4474
+ onClick: closeModal
4475
+ }), jsxs("div", {
4476
+ className: "c-photo-viewer__container",
4477
+ children: [ jsx(PhotoViewerHeader, {
4478
+ currentIndex: currentIndex,
4479
+ imagesLength: images.length,
4480
+ onZoomOut: () => setZoomLevel((z => Math.max(z - .25, .1))),
4481
+ onResetZoom: () => {
4482
+ resetImageState();
4483
+ },
4484
+ onZoomIn: () => setZoomLevel((z => Math.min(z + .25, 5))),
4485
+ onToggleFullscreen: () => {
4486
+ if (enableFullscreen) {
4487
+ if (isFullscreen) document.exitFullscreen && document.exitFullscreen(); else {
4488
+ const element = document.documentElement;
4489
+ element.requestFullscreen && element.requestFullscreen();
4490
+ }
4491
+ setIsFullscreen(!isFullscreen);
4492
+ }
4493
+ },
4494
+ onClose: onClose || closeModal,
4495
+ isFullscreen: isFullscreen,
4496
+ zoomLevel: zoomLevel,
4497
+ onRotate: () => {
4498
+ setRotationAngle((angle => (angle + 90) % 360));
4499
+ },
4500
+ onDownload: () => {
4501
+ if (!currentImage?.src) return;
4502
+ const link = document.createElement("a");
4503
+ link.href = currentImage.src;
4504
+ const sanitizedTitle = (currentImage.title || `image-${currentIndex + 1}`).replace(/[^a-zA-Z0-9.-]/g, "_");
4505
+ link.download = sanitizedTitle, document.body.appendChild(link), link.click(), document.body.removeChild(link);
4506
+ },
4507
+ onShare: async () => {
4508
+ if (navigator.share && currentImage?.src) try {
4509
+ await navigator.share({
4510
+ title: currentImage.title || "Shared Image",
4511
+ text: currentImage.description || "Check out this image",
4512
+ url: currentImage.src
4513
+ });
4514
+ } catch (error) {
4515
+ console.error("Error sharing:", error);
4516
+ }
4517
+ },
4518
+ showInfo: showInfo,
4519
+ onToggleInfo: () => setShowInfo(!showInfo),
4520
+ currentImage: currentImage
4521
+ }), jsxs("div", {
4522
+ className: "c-photo-viewer__content",
4523
+ children: [ jsx(PhotoViewerNavigation, {
4524
+ show: images.length > 1,
4525
+ onPrev: goToPrevious,
4526
+ onNext: goToNext,
4527
+ currentIndex: currentIndex,
4528
+ imagesLength: images.length,
4529
+ enableKeyboardNav: enableKeyboardNavigation,
4530
+ onClose: onClose || closeModal
4531
+ }), currentImage?.src && jsx(PhotoViewerImage, {
4532
+ imageRef: imageRef,
4533
+ containerRef: containerRef,
4534
+ src: currentImage.src,
4535
+ alt: currentImage?.alt || `Image ${currentIndex + 1}`,
4536
+ zoomLevel: zoomLevel,
4537
+ dragPosition: dragPosition,
4538
+ isDragging: isDragging,
4539
+ rotationAngle: rotationAngle,
4540
+ isTransitioning: isTransitioning,
4541
+ onMouseDown: handleMouseDown,
4542
+ onMouseMove: handleMouseMove,
4543
+ onMouseUp: handleMouseUp,
4544
+ onWheel: handleWheel,
4545
+ onTouchStart: handleTouchStart,
4546
+ onTouchMove: handleTouchMove,
4547
+ onTouchEnd: handleTouchEnd,
4548
+ onDoubleClick: handleDoubleClick
4549
+ }) ]
4550
+ }), "none" !== thumbnailPosition && jsx(PhotoViewerThumbnails, {
4551
+ images: processedImages,
4552
+ currentIndex: currentIndex,
4553
+ goToImage: goToImage
4554
+ }), jsx(PhotoViewerInfo, {
4555
+ show: showInfo,
4556
+ image: currentImage,
4557
+ onClose: () => setShowInfo(!1)
4558
+ }) ]
4559
+ }) ]
4560
+ }) : null;
4561
+ };
4562
+
4563
+ PhotoViewer.displayName = "PhotoViewer";
4564
+
4565
+ export { AtomixGlass, MasonryGrid, PhotoViewer, VideoPlayer };
4566
+ //# sourceMappingURL=heavy.js.map