@shohojdhara/atomix 0.4.7 → 0.4.9

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 (176) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +172 -157
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +4 -4
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1274 -164
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1099 -83
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2106 -1050
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1663 -638
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +442 -270
  19. package/dist/index.esm.js +1947 -680
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1982 -712
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.min.js +1 -1
  24. package/dist/index.min.js.map +1 -1
  25. package/package.json +6 -3
  26. package/scripts/atomix-cli.js +136 -1827
  27. package/scripts/cli/__tests__/basic.test.js +3 -2
  28. package/scripts/cli/__tests__/clean.test.js +278 -0
  29. package/scripts/cli/__tests__/component-validator.test.js +433 -0
  30. package/scripts/cli/__tests__/generator.test.js +613 -0
  31. package/scripts/cli/__tests__/glass-motion.test.js +256 -0
  32. package/scripts/cli/__tests__/integration.test.js +719 -108
  33. package/scripts/cli/__tests__/migrate.test.js +74 -0
  34. package/scripts/cli/__tests__/security.test.js +206 -0
  35. package/scripts/cli/__tests__/test-setup.js +3 -1
  36. package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
  37. package/scripts/cli/__tests__/token-provider.test.js +361 -0
  38. package/scripts/cli/__tests__/utils.test.js +5 -5
  39. package/scripts/cli/commands/benchmark.js +105 -0
  40. package/scripts/cli/commands/build-theme.js +115 -0
  41. package/scripts/cli/commands/clean.js +109 -0
  42. package/scripts/cli/commands/doctor.js +88 -0
  43. package/scripts/cli/commands/generate.js +218 -0
  44. package/scripts/cli/commands/init.js +73 -0
  45. package/scripts/cli/commands/migrate.js +106 -0
  46. package/scripts/cli/commands/sync-tokens.js +206 -0
  47. package/scripts/cli/commands/theme-bridge.js +248 -0
  48. package/scripts/cli/commands/tokens.js +157 -0
  49. package/scripts/cli/commands/validate.js +194 -0
  50. package/scripts/cli/internal/ai-engine.js +156 -0
  51. package/scripts/cli/internal/compiler.js +114 -0
  52. package/scripts/cli/internal/component-validator.js +443 -0
  53. package/scripts/cli/internal/config-loader.js +162 -0
  54. package/scripts/cli/internal/filesystem.js +158 -0
  55. package/scripts/cli/internal/generator.js +430 -0
  56. package/scripts/cli/internal/glass-generator.js +398 -0
  57. package/scripts/cli/internal/hook-generator.js +369 -0
  58. package/scripts/cli/internal/hooks.js +61 -0
  59. package/scripts/cli/internal/itcss-generator.js +565 -0
  60. package/scripts/cli/internal/motion-generator.js +679 -0
  61. package/scripts/cli/internal/template-engine.js +301 -0
  62. package/scripts/cli/internal/theme-bridge.js +664 -0
  63. package/scripts/cli/internal/tokens/engine.js +122 -0
  64. package/scripts/cli/internal/tokens/provider.js +34 -0
  65. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  66. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  67. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  68. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  69. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  70. package/scripts/cli/internal/validator.js +276 -0
  71. package/scripts/cli/internal/wizard.js +115 -0
  72. package/scripts/cli/mappings.js +23 -0
  73. package/scripts/cli/migration-tools.js +164 -94
  74. package/scripts/cli/plugins/style-dictionary.js +46 -0
  75. package/scripts/cli/templates/README.md +525 -95
  76. package/scripts/cli/templates/common-templates.js +40 -14
  77. package/scripts/cli/templates/components/react-component.ts +282 -0
  78. package/scripts/cli/templates/config/project-config.ts +112 -0
  79. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  80. package/scripts/cli/templates/index.js +19 -4
  81. package/scripts/cli/templates/index.ts +171 -0
  82. package/scripts/cli/templates/next-templates.js +72 -0
  83. package/scripts/cli/templates/react-templates.js +70 -126
  84. package/scripts/cli/templates/scss-templates.js +35 -35
  85. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  86. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  87. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  88. package/scripts/cli/templates/token-templates.js +337 -1
  89. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  90. package/scripts/cli/templates/types/component-types.ts +145 -0
  91. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  92. package/scripts/cli/templates/vanilla-templates.js +39 -0
  93. package/scripts/cli/token-manager.js +8 -2
  94. package/scripts/cli/utils/cache-manager.js +240 -0
  95. package/scripts/cli/utils/detector.js +46 -0
  96. package/scripts/cli/utils/diagnostics.js +289 -0
  97. package/scripts/cli/utils/error.js +89 -0
  98. package/scripts/cli/utils/helpers.js +67 -0
  99. package/scripts/cli/utils/logger.js +75 -0
  100. package/scripts/cli/utils/security.js +302 -0
  101. package/scripts/cli/utils/telemetry.js +115 -0
  102. package/scripts/cli/utils/validation.js +37 -0
  103. package/scripts/cli/utils.js +28 -341
  104. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  105. package/src/components/Accordion/Accordion.test.tsx +0 -17
  106. package/src/components/Accordion/Accordion.tsx +0 -4
  107. package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
  108. package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
  109. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
  110. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  111. package/src/components/AtomixGlass/README.md +25 -10
  112. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
  113. package/src/components/AtomixGlass/animation-system.ts +578 -0
  114. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  115. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  116. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  117. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  118. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  119. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  120. package/src/components/Avatar/Avatar.tsx +1 -1
  121. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  122. package/src/components/Button/Button.stories.tsx +10 -0
  123. package/src/components/Button/Button.test.tsx +16 -11
  124. package/src/components/Button/Button.tsx +4 -4
  125. package/src/components/Card/Card.tsx +1 -1
  126. package/src/components/Dropdown/Dropdown.tsx +12 -12
  127. package/src/components/Form/Select.tsx +62 -3
  128. package/src/components/Modal/Modal.tsx +14 -3
  129. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  130. package/src/components/Slider/Slider.stories.tsx +3 -3
  131. package/src/components/Slider/Slider.tsx +38 -0
  132. package/src/components/Steps/Steps.tsx +3 -3
  133. package/src/components/Tabs/Tabs.tsx +77 -8
  134. package/src/components/Testimonial/Testimonial.tsx +1 -1
  135. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  136. package/src/components/TypedButton/TypedButton.tsx +39 -0
  137. package/src/components/TypedButton/index.ts +2 -0
  138. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  139. package/src/lib/composables/index.ts +4 -7
  140. package/src/lib/composables/types.ts +45 -0
  141. package/src/lib/composables/useAccordion.ts +0 -7
  142. package/src/lib/composables/useAtomixGlass.ts +148 -6
  143. package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
  144. package/src/lib/composables/useChartExport.ts +3 -13
  145. package/src/lib/composables/useDropdown.ts +66 -0
  146. package/src/lib/composables/useFocusTrap.ts +80 -0
  147. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  148. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  149. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  150. package/src/lib/composables/useTooltip.ts +16 -0
  151. package/src/lib/composables/useTypedButton.ts +66 -0
  152. package/src/lib/config/index.ts +62 -5
  153. package/src/lib/constants/components.ts +62 -7
  154. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  155. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  156. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  157. package/src/lib/types/components.ts +37 -11
  158. package/src/lib/types/glass.ts +35 -0
  159. package/src/lib/types/index.ts +1 -0
  160. package/src/lib/utils/displacement-generator.ts +1 -1
  161. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  162. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  163. package/src/styles/06-components/_components.atomix-glass.scss +17 -21
  164. package/src/styles/06-components/_components.edge-panel.scss +1 -5
  165. package/src/styles/06-components/_components.modal.scss +1 -4
  166. package/src/styles/06-components/_components.navbar.scss +1 -1
  167. package/src/styles/06-components/_components.testbutton.scss +212 -0
  168. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  169. package/src/styles/06-components/_components.tooltip.scss +9 -5
  170. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  171. package/src/styles/99-utilities/_index.scss +1 -0
  172. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  173. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  174. package/scripts/cli/component-generator.js +0 -564
  175. package/scripts/cli/interactive-init.js +0 -357
  176. package/src/styles/06-components/old.chart.styles.scss +0 -2788
package/dist/index.esm.js CHANGED
@@ -390,6 +390,14 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
390
390
  ICON_ELEMENT: "icon",
391
391
  LABEL_ELEMENT: "label",
392
392
  SPINNER_ELEMENT: "spinner"
393
+ }, TYPEDBUTTON = {
394
+ BASE_CLASS: "c-typedbutton",
395
+ VARIANT_PREFIX: "c-typedbutton--",
396
+ CLASSES: {
397
+ BASE: "c-typedbutton",
398
+ DISABLED: "c-typedbutton--disabled",
399
+ GLASS: "c-typedbutton--glass"
400
+ }
393
401
  }, BUTTON = {
394
402
  BASE_CLASS: "c-btn",
395
403
  ICON_CLASS: "c-btn__icon",
@@ -1717,20 +1725,29 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1717
1725
  SHADER: "c-atomix-glass--shader"
1718
1726
  },
1719
1727
  DEFAULTS: {
1720
- DISPLACEMENT_SCALE: 20,
1721
- BLUR_AMOUNT: 1,
1728
+ DISPLACEMENT_SCALE: 70,
1729
+ BLUR_AMOUNT: 0,
1722
1730
  SATURATION: 140,
1723
- ABERRATION_INTENSITY: 2.5,
1724
- ELASTICITY: .05,
1725
- CORNER_RADIUS: 16,
1731
+ ABERRATION_INTENSITY: 2,
1732
+ ELASTICITY: .15,
1733
+ CORNER_RADIUS: 20,
1726
1734
  // Default border-radius matching design system
1727
- PADDING: "0 0",
1735
+ PADDING: "0",
1728
1736
  MODE: "standard",
1729
1737
  OVER_LIGHT: !1,
1730
- ENABLE_OVER_LIGHT_LAYERS: !0
1738
+ ENABLE_OVER_LIGHT_LAYERS: !0,
1739
+ // Phase 1: Time-Based Animation System defaults
1740
+ WITH_TIME_ANIMATION: !0,
1741
+ ANIMATION_SPEED: 1,
1742
+ // Phase 1: Multi-Layer Distortion System defaults
1743
+ WITH_MULTI_LAYER_DISTORTION: !1,
1744
+ DISTORTION_OCTAVES: 5,
1745
+ DISTORTION_LACUNARITY: 2,
1746
+ DISTORTION_GAIN: .5,
1747
+ DISTORTION_QUALITY: "high"
1731
1748
  },
1732
1749
  CONSTANTS: {
1733
- ACTIVATION_ZONE: 350,
1750
+ ACTIVATION_ZONE: 200,
1734
1751
  LERP_FACTOR: .08,
1735
1752
  SMOOTHSTEP_POWER: 2.5,
1736
1753
  MIN_BLUR: .1,
@@ -1890,6 +1907,44 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1890
1907
  // Saturation constants
1891
1908
  SATURATION: {
1892
1909
  HIGH_CONTRAST: 200
1910
+ },
1911
+ // Phase 1: Animation System Constants
1912
+ ANIMATION: {
1913
+ // Breathing effect timing (in milliseconds)
1914
+ BREATHING_CYCLE: 2e3,
1915
+ // 2-second breathing cycle
1916
+ // Flow animation speed
1917
+ FLOW_SPEED_X: .1,
1918
+ // Horizontal flow speed
1919
+ FLOW_SPEED_Y: .15,
1920
+ // Vertical flow speed
1921
+ // Wave propagation
1922
+ WAVE_SPEED: .05,
1923
+ // Radial wave speed
1924
+ WAVE_AMPLITUDE: .02
1925
+ },
1926
+ // Phase 1: Multi-Layer Distortion Quality Presets
1927
+ DISTORTION_QUALITY_PRESETS: {
1928
+ low: {
1929
+ octaves: 2,
1930
+ lacunarity: 2,
1931
+ gain: .5
1932
+ },
1933
+ medium: {
1934
+ octaves: 4,
1935
+ lacunarity: 2,
1936
+ gain: .5
1937
+ },
1938
+ high: {
1939
+ octaves: 5,
1940
+ lacunarity: 2,
1941
+ gain: .5
1942
+ },
1943
+ ultra: {
1944
+ octaves: 7,
1945
+ lacunarity: 2,
1946
+ gain: .5
1947
+ }
1893
1948
  }
1894
1949
  }
1895
1950
  };
@@ -1938,9 +1993,7 @@ function useAccordion(initialProps) {
1938
1993
  toggle: () => {
1939
1994
  if (!defaultProps.disabled) {
1940
1995
  const nextOpen = !isOpen;
1941
- isControlled || setInternalOpen(nextOpen), defaultProps.onOpenChange?.(nextOpen),
1942
- // Call legacy handlers
1943
- nextOpen ? defaultProps.onOpen?.() : defaultProps.onClose?.();
1996
+ isControlled || setInternalOpen(nextOpen), defaultProps.onOpenChange?.(nextOpen);
1944
1997
  }
1945
1998
  },
1946
1999
  updatePanelHeight: updatePanelHeight,
@@ -2027,7 +2080,7 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateDistance = (pos1, pos2)
2027
2080
  // Silently handle errors
2028
2081
  }
2029
2082
  return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
2030
- }, lerp = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
2083
+ }, lerp$1 = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
2031
2084
  switch (mode) {
2032
2085
  case "standard":
2033
2086
  return displacementMap;
@@ -2194,20 +2247,19 @@ let idCounter = 0;
2194
2247
  const 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 = {
2195
2248
  x: 0,
2196
2249
  y: 0
2197
- }, globalMousePosition: globalMousePosition = {
2198
- x: 0,
2199
- y: 0
2200
- }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isHovered: isHovered = !1, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
2250
+ }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
2201
2251
  width: 0,
2202
2252
  height: 0
2203
- }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, contentRef: contentRef}, ref) => {
2253
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1,
2254
+ // Phase 1: Animation System props
2255
+ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
2204
2256
  // Generate a stable, deterministic ID for SSR compatibility
2205
2257
  // Use a module-level counter that's consistent across server and client
2206
- const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null);
2258
+ const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
2207
2259
  // Lazy load shader utilities only when shader mode is needed
2208
2260
  useEffect((() => {
2209
2261
  "shader" === mode ?
2210
- // Dynamic import shader utilities
2262
+ // Dynamic import shader utilities with animation support
2211
2263
  Promise.resolve().then((() => shaderUtils)).then((shaderUtils => {
2212
2264
  shaderUtilsRef.current = {
2213
2265
  ShaderDisplacementGenerator: shaderUtils.ShaderDisplacementGenerator,
@@ -2222,7 +2274,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2222
2274
  // Generate shader map with debouncing and caching
2223
2275
  useEffect((() => {
2224
2276
  // Enhanced validation for shader mode
2225
- if ("shader" === mode && glassSize && validateGlassSize(glassSize) && shaderUtilsRef.current) {
2277
+ if ("shader" === mode && glassSize && validateGlassSize(glassSize)) {
2226
2278
  // Create cache key from size and variant
2227
2279
  const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
2228
2280
  const entry = sharedShaderCache.get(key);
@@ -2244,11 +2296,9 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2244
2296
  width: glassSize.width,
2245
2297
  height: glassSize.height,
2246
2298
  fragment: selectedShader
2247
- }),
2248
- // Defer shader generation with longer delay to avoid blocking
2249
- setTimeout((() => {
2299
+ }), shaderUpdateTimeoutRef.current = setTimeout((() => {
2250
2300
  const url = shaderGeneratorRef.current?.updateShader() || "";
2251
- ((key, url) => {
2301
+ url && ((key, url) => {
2252
2302
  // Evict oldest entries if at capacity
2253
2303
  if (sharedShaderCache.size >= 15) {
2254
2304
  const entries = Array.from(sharedShaderCache.entries());
@@ -2280,7 +2330,8 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2280
2330
  // Cleanup function with error handling
2281
2331
  return () => {
2282
2332
  shaderDebounceTimeoutRef.current && (clearTimeout(shaderDebounceTimeoutRef.current),
2283
- shaderDebounceTimeoutRef.current = null);
2333
+ shaderDebounceTimeoutRef.current = null), shaderUpdateTimeoutRef.current && (clearTimeout(shaderUpdateTimeoutRef.current),
2334
+ shaderUpdateTimeoutRef.current = null);
2284
2335
  try {
2285
2336
  shaderGeneratorRef.current?.destroy();
2286
2337
  } catch (error) {
@@ -2289,7 +2340,37 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2289
2340
  shaderGeneratorRef.current = null;
2290
2341
  }
2291
2342
  };
2292
- }), [ mode, glassSize, shaderVariant ]);
2343
+ }), [ mode, glassSize, shaderVariant ]),
2344
+ // Phase 1: Time-Based Animation Loop - Continuous shader regeneration
2345
+ useEffect((() => {
2346
+ // Only run animations in shader mode with time animation enabled
2347
+ if ("shader" !== mode || !withTimeAnimation || effectiveReducedMotion || effectiveWithoutEffects)
2348
+ // Cancel any existing animation frame
2349
+ return void (null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
2350
+ animationFrameRef.current = null));
2351
+ const baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed || 1)), complexity = withMultiLayerDistortion ? Math.max(1, (distortionOctaves || 3) / 3 + .25 * Math.max(0, (distortionLacunarity || 2) - 2) + Math.max(0, (distortionGain || .5) - .5)) : 1, frameInterval = 1e3 / Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
2352
+ let lastUpdate = 0, isCancelled = !1;
2353
+ const animate = currentTime => {
2354
+ if (!isCancelled) {
2355
+ if (currentTime - lastUpdate >= frameInterval && shaderGeneratorRef.current) {
2356
+ lastUpdate = currentTime;
2357
+ try {
2358
+ const animatedShaderUrl = shaderGeneratorRef.current.updateShader();
2359
+ animatedShaderUrl && setShaderMapUrl(animatedShaderUrl);
2360
+ } catch (error) {
2361
+ console.warn("AtomixGlassContainer: Error in animation loop", error);
2362
+ }
2363
+ }
2364
+ animationFrameRef.current = requestAnimationFrame(animate);
2365
+ }
2366
+ };
2367
+ // Start animation loop
2368
+ // Cleanup animation on unmount or dependency change
2369
+ return animationFrameRef.current = requestAnimationFrame(animate), () => {
2370
+ isCancelled = !0, null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
2371
+ animationFrameRef.current = null);
2372
+ };
2373
+ }), [ mode, withTimeAnimation, animationSpeed, displacementScale, withMultiLayerDistortion, distortionOctaves, distortionLacunarity, distortionGain, distortionQuality, effectiveReducedMotion, effectiveWithoutEffects, glassSize ]);
2293
2374
  // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
2294
2375
  const [rectCache, setRectCache] = useState(null);
2295
2376
  useEffect((() => {
@@ -2341,21 +2422,18 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2341
2422
  backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
2342
2423
  };
2343
2424
  }
2344
- }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur ]), containerVars = useMemo((() => {
2425
+ }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur, overLightConfig ]), containerVars = useMemo((() => {
2345
2426
  try {
2346
2427
  // Safe extraction of mouse offset values
2347
2428
  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;
2348
2429
  return {
2349
- "--atomix-glass-container-width": `${glassSize?.width}`,
2350
- "--atomix-glass-container-height": `${glassSize?.height}`,
2351
- "--atomix-glass-container-padding": padding || "0 0",
2352
2430
  "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`,
2353
2431
  "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
2354
2432
  "--atomix-glass-container-shadow": overLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig?.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset",
2355
2433
  "--atomix-glass-container-shadow-opacity": effectiveWithoutEffects ? 0 : 1,
2356
2434
  // Background and shadow values use design token-aligned RGB values
2357
2435
  "--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",
2358
- "--atomix-glass-container-text-shadow": overLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
2436
+ "--atomix-glass-container-text-shadow": overLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
2359
2437
  "--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
2360
2438
  };
2361
2439
  } catch (error) {
@@ -2370,7 +2448,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2370
2448
  "--atomix-glass-container-text-shadow": "none"
2371
2449
  };
2372
2450
  }
2373
- }), [ glassSize, padding, borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects ]);
2451
+ }), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
2374
2452
  return jsx("div", {
2375
2453
  ref: el => {
2376
2454
  // Apply force no-transition
@@ -2399,10 +2477,6 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2399
2477
  aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
2400
2478
  shaderMapUrl: shaderMapUrl
2401
2479
  }), jsx("div", {
2402
- ref: el => {
2403
- el && (el.style.setProperty("transition-duration", "0s", "important"), el.style.setProperty("animation-duration", "0s", "important"),
2404
- el.style.setProperty("transition-delay", "0s", "important"));
2405
- },
2406
2480
  className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
2407
2481
  style: {
2408
2482
  filter: `url(#${filterId})`
@@ -2507,7 +2581,7 @@ class {
2507
2581
  }
2508
2582
  }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
2509
2583
  if (!wrapperElement && !containerElement) return;
2510
- const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, directionalScale: directionalScale, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
2584
+ const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, directionalScale: directionalScale, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, isFixedOrSticky: isFixedOrSticky = !1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
2511
2585
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
2512
2586
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
2513
2587
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
@@ -2597,7 +2671,7 @@ class {
2597
2671
  }
2598
2672
  // Update Container Styles (containerVars)
2599
2673
  if (containerElement) {
2600
- const mx = mouseOffset.x, my = mouseOffset.y, EDGE_BLUR_MULTIPLIER = 1.25, CENTER_BLUR_MULTIPLIER = 1.1, FLOW_BLUR_MULTIPLIER = 1.2, MOUSE_INFLUENCE_BLUR_FACTOR = .15, EDGE_INTENSITY_MOUSE_FACTOR = .15, CENTER_INTENSITY_MOUSE_FACTOR = .1, MAX_BLUR_RELATIVE = 2, rect = containerElement.getBoundingClientRect();
2674
+ const mx = mouseOffset.x, my = mouseOffset.y, EDGE_BLUR_MULTIPLIER = .5, CENTER_BLUR_MULTIPLIER = .2, FLOW_BLUR_MULTIPLIER = .3, MOUSE_INFLUENCE_BLUR_FACTOR = .4, EDGE_INTENSITY_MOUSE_FACTOR = .15, CENTER_INTENSITY_MOUSE_FACTOR = .1, MAX_BLUR_RELATIVE = 2, rect = containerElement.getBoundingClientRect();
2601
2675
  let liquidBlur = {
2602
2676
  baseBlur: blurAmount,
2603
2677
  edgeBlur: blurAmount * EDGE_BLUR_MULTIPLIER,
@@ -2619,22 +2693,155 @@ class {
2619
2693
  backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
2620
2694
  // Container variables
2621
2695
  const style = containerElement.style;
2622
- style.setProperty("--atomix-glass-container-width", `${glassSize.width}`), style.setProperty("--atomix-glass-container-height", `${glassSize.height}`),
2696
+ style.setProperty("--atomix-glass-container-width", isFixedOrSticky ? `${glassSize.width}` : "100%"),
2697
+ style.setProperty("--atomix-glass-container-height", isFixedOrSticky ? `${glassSize.height}` : "100%"),
2623
2698
  style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
2624
2699
  style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
2625
2700
  // Shadows
2626
2701
  style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset"),
2627
2702
  style.setProperty("--atomix-glass-container-shadow-opacity", effectiveWithoutEffects ? "0" : "1"),
2628
2703
  style.setProperty("--atomix-glass-container-bg", isOverLight ? `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"),
2629
- style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
2704
+ style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
2630
2705
  style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)");
2631
2706
  }
2632
- }, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
2707
+ };
2633
2708
 
2634
2709
  /**
2635
2710
  * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2636
2711
  * to avoid React re-renders on mouse movement.
2637
- */ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
2712
+ */
2713
+ /**
2714
+ * Animation System for AtomixGlass Component
2715
+ *
2716
+ * Implements Phase 1 features from the AtomixGlass Feature Implementation Roadmap:
2717
+ * - Feature 1.1: Time-Based Animation System
2718
+ * - Feature 1.2: Multi-Layer Distortion System (FBM)
2719
+ *
2720
+ * @packageDocumentation
2721
+ */
2722
+ // ============================================================================
2723
+ // Noise Functions for FBM (Feature 1.2)
2724
+ // ============================================================================
2725
+ /**
2726
+ * Perlin noise implementation for smooth gradient noise
2727
+ *
2728
+ * @param x - X coordinate
2729
+ * @param y - Y coordinate
2730
+ * @returns Noise value in range [0, 1]
2731
+ */
2732
+ function perlinNoise(x, y) {
2733
+ // Simplified Perlin noise using pseudo-random gradients
2734
+ const X = 255 & Math.floor(x), Y = 255 & Math.floor(y), xf = x - Math.floor(x), yf = y - Math.floor(y), u = fade(xf), v = fade(yf), A = p[X] + Y & 255, B = p[X + 1] + Y & 255, ga = grad(p[A], xf, yf), gb = grad(p[B], xf - 1, yf), gc = grad(p[A + 1 & 255], xf, yf - 1), gd = grad(p[B + 1 & 255], xf - 1, yf - 1), lerpX1 = lerp(ga, gb, u), lerpX2 = lerp(gc, gd, u);
2735
+ // Scale to [0, 1] range
2736
+ return (lerp(lerpX1, lerpX2, v) + 1) / 2;
2737
+ }
2738
+
2739
+ // ============================================================================
2740
+ // Fractal Brownian Motion (FBM) Engine (Feature 1.2)
2741
+ // ============================================================================
2742
+ /**
2743
+ * Creates an FBM engine with configurable parameters
2744
+ *
2745
+ * @param config - FBM configuration (octaves, lacunarity, gain)
2746
+ * @returns Object with fbm function
2747
+ *
2748
+ * @example
2749
+ * ```typescript
2750
+ * const fbmEngine = createFBMEngine({ octaves: 5, lacunarity: 2, gain: 0.5 });
2751
+ *
2752
+ * // Generate noise at position (0.5, 0.5) with time animation
2753
+ * const noiseValue = fbmEngine.fbm(0.5, 0.5, Date.now());
2754
+ * ```
2755
+ */ function createFBMEngine(config) {
2756
+ /**
2757
+ * Fractal Brownian Motion function
2758
+ * Combines multiple octaves of noise for complex, natural patterns
2759
+ *
2760
+ * @param x - X coordinate
2761
+ * @param y - Y coordinate
2762
+ * @param time - Optional time value for animation
2763
+ * @returns FBM noise value in range [0, 1]
2764
+ */
2765
+ const fbm = (x, y, time = 0) => {
2766
+ let value = 0, amplitude = .5, frequency = 1, phase = .001 * time;
2767
+ // Convert to seconds for reasonable animation speed
2768
+ for (let i = 0; i < config.octaves; i++)
2769
+ // Apply time-based phase shift to all octaves
2770
+ value += perlinNoise(x * frequency + phase, y * frequency + phase) * amplitude,
2771
+ frequency *= config.lacunarity, // Increase frequency
2772
+ amplitude *= config.gain;
2773
+ return value;
2774
+ };
2775
+ /**
2776
+ * Get FBM with simple time factor
2777
+ */ return {
2778
+ fbm: fbm,
2779
+ fbmWithTime: (x, y, time) => fbm(x, y, time)
2780
+ };
2781
+ }
2782
+
2783
+ /**
2784
+ * Gets optimal FBM config based on quality preset
2785
+ *
2786
+ * @param quality - Quality preset level
2787
+ * @returns FBM configuration for the quality level
2788
+ */ const fbmEngineCache = new Map;
2789
+
2790
+ // ============================================================================
2791
+ // Shader Utility Functions for Time-Based Effects
2792
+ // ============================================================================
2793
+ /**
2794
+ * Liquid glass distortion with time-based animation
2795
+ * Uses FBM to create organic, flowing liquid effects
2796
+ *
2797
+ * @param uv - UV coordinates (normalized 0-1)
2798
+ * @param time - Elapsed time in milliseconds
2799
+ * @param config - FBM configuration
2800
+ * @returns Distorted UV coordinates
2801
+ */ function liquidGlassWithTime(uv, time, config) {
2802
+ const configKey = `${config.octaves}-${config.lacunarity}-${config.gain}`;
2803
+ let fbmEngine = fbmEngineCache.get(configKey);
2804
+ fbmEngine || (fbmEngine = createFBMEngine(config), fbmEngineCache.set(configKey, fbmEngine));
2805
+ // Animate noise with time
2806
+ const animatedNoise = fbmEngine.fbmWithTime(2 * uv.x + 1e-4 * time, 2 * uv.y + 15e-5 * time, time);
2807
+ return {
2808
+ x: uv.x + .04 * (animatedNoise - .5),
2809
+ y: uv.y + .04 * (animatedNoise - .5)
2810
+ };
2811
+ }
2812
+
2813
+ // ============================================================================
2814
+ // Helper Functions
2815
+ // ============================================================================
2816
+ /**
2817
+ * Fade curve for smooth interpolation (Perlin's fade function)
2818
+ */ function fade(t) {
2819
+ return t * t * t * (t * (6 * t - 15) + 10);
2820
+ }
2821
+
2822
+ /**
2823
+ * Linear interpolation
2824
+ */ function lerp(a, b, t) {
2825
+ return a + t * (b - a);
2826
+ }
2827
+
2828
+ /**
2829
+ * Gradient calculation for Perlin noise
2830
+ */ function grad(hash, x, y) {
2831
+ const h = 15 & hash, u = h < 8 ? x : y, v = h < 4 ? y : 12 === h || 14 === h ? x : 0;
2832
+ return (1 & h ? -u : u) + (2 & h ? -v : v);
2833
+ }
2834
+
2835
+ /**
2836
+ * Permutation table for Perlin noise
2837
+ */ const p = (() => {
2838
+ const permutation = [];
2839
+ for (let i = 0; i < 256; i++) permutation[i] = Math.floor(256 * Math.random());
2840
+ // Duplicate for overflow handling
2841
+ return [ ...permutation, ...permutation ];
2842
+ })(), {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
2843
+
2844
+ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
2638
2845
  parentElement && backgroundDetectionCache.set(parentElement, {
2639
2846
  result: result,
2640
2847
  timestamp: Date.now(),
@@ -2647,7 +2854,9 @@ class {
2647
2854
  * Composable hook for AtomixGlass component logic
2648
2855
  * Manages all state, calculations, and event handlers
2649
2856
  */
2650
- function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, debugPerformance: debugPerformance = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur}) {
2857
+ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, withTimeAnimation:
2858
+ // Phase 1: Animation System Props
2859
+ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED, withMultiLayerDistortion: withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION, distortionOctaves: distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES, distortionLacunarity: distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY, distortionGain: distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN, distortionQuality: distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY}) {
2651
2860
  // State
2652
2861
  const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
2653
2862
  x: 0,
@@ -2661,7 +2870,47 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2661
2870
  }), targetGlobalMousePositionRef = useRef({
2662
2871
  x: 0,
2663
2872
  y: 0
2664
- }), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), effectiveBorderRadius = useMemo((() => void 0 !== borderRadius ? Math.max(0, borderRadius) : Math.max(0, dynamicBorderRadius)), [ borderRadius, dynamicBorderRadius ]), {glassSize: glassSize} = function({glassRef: glassRef, effectiveBorderRadius: effectiveBorderRadius, cachedRectRef: cachedRectRef}) {
2873
+ }), lerpRafRef = useRef(null), lerpActiveRef = useRef(!1), [dynamicBorderRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), animationFrameIdRef = useRef(null), animationStartTimeRef = useRef(0), elapsedTimeRef = useRef(0), shaderTimeRef = useRef(0), fbmConfig = useMemo((() => {
2874
+ // If quality preset is provided, use it as base
2875
+ const preset = (quality = distortionQuality, ATOMIX_GLASS.CONSTANTS.DISTORTION_QUALITY_PRESETS[quality]);
2876
+ // Override with custom values if provided
2877
+ var quality;
2878
+ return {
2879
+ octaves: distortionOctaves ?? preset.octaves,
2880
+ lacunarity: distortionLacunarity ?? preset.lacunarity,
2881
+ gain: distortionGain ?? preset.gain
2882
+ };
2883
+ }), [ distortionQuality, distortionOctaves, distortionLacunarity, distortionGain ]), fbmEngine = useMemo((() => withMultiLayerDistortion ? createFBMEngine(fbmConfig) : null), [ withMultiLayerDistortion, fbmConfig ]), effectiveReducedMotion = useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveWithTimeAnimation = useMemo((() => withTimeAnimation && !effectiveReducedMotion), [ withTimeAnimation, effectiveReducedMotion ]);
2884
+ /**
2885
+ * Animation loop for time-based effects
2886
+ */
2887
+ useEffect((() => {
2888
+ if (!effectiveWithTimeAnimation || "undefined" == typeof window) return;
2889
+ let lastFrameTime = performance.now();
2890
+ /**
2891
+ * Animation frame handler
2892
+ */ const animate = currentTime => {
2893
+ // Calculate delta time
2894
+ const deltaTime = currentTime - lastFrameTime;
2895
+ lastFrameTime = currentTime;
2896
+ // Apply animation speed multiplier
2897
+ const scaledDelta = deltaTime * animationSpeed;
2898
+ elapsedTimeRef.current += scaledDelta, shaderTimeRef.current = elapsedTimeRef.current,
2899
+ // Continue animation loop
2900
+ animationFrameIdRef.current = requestAnimationFrame(animate);
2901
+ };
2902
+ // Start animation
2903
+ // Cleanup
2904
+ return animationStartTimeRef.current = performance.now(), animationFrameIdRef.current = requestAnimationFrame(animate),
2905
+ () => {
2906
+ null !== animationFrameIdRef.current && (cancelAnimationFrame(animationFrameIdRef.current),
2907
+ animationFrameIdRef.current = null);
2908
+ };
2909
+ }), [ effectiveWithTimeAnimation, animationSpeed ]);
2910
+ /**
2911
+ * Get current shader time for animations
2912
+ */
2913
+ const getShaderTime = useCallback((() => shaderTimeRef.current), []), applyTimeBasedDistortion = useCallback((uv => effectiveWithTimeAnimation && fbmEngine ? liquidGlassWithTime(uv, shaderTimeRef.current, fbmConfig) : uv), [ effectiveWithTimeAnimation, fbmEngine, fbmConfig ]), effectiveBorderRadius = useMemo((() => void 0 !== borderRadius ? Math.max(0, borderRadius) : Math.max(0, dynamicBorderRadius)), [ borderRadius, dynamicBorderRadius ]), {glassSize: glassSize} = function({glassRef: glassRef, effectiveBorderRadius: effectiveBorderRadius, cachedRectRef: cachedRectRef}) {
2665
2914
  const [glassSize, setGlassSize] = useState({
2666
2915
  width: 270,
2667
2916
  height: 69
@@ -2720,7 +2969,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2720
2969
  glassRef: glassRef,
2721
2970
  effectiveBorderRadius: effectiveBorderRadius,
2722
2971
  cachedRectRef: cachedRectRef
2723
- }), effectiveReducedMotion = useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveHighContrast = useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveWithoutEffects = useMemo((() => withoutEffects || effectiveReducedMotion), [ withoutEffects, effectiveReducedMotion ]), globalMousePosition = externalGlobalMousePosition || internalGlobalMousePositionRef.current, mouseOffset = externalMouseOffset || internalMouseOffsetRef.current;
2972
+ }), effectiveHighContrast = useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveWithoutEffects = useMemo((() => withoutEffects || effectiveReducedMotion), [ withoutEffects, effectiveReducedMotion ]), globalMousePosition = externalGlobalMousePosition || internalGlobalMousePositionRef.current, mouseOffset = externalMouseOffset || internalMouseOffsetRef.current;
2973
+ /**
2974
+ * Apply time-based distortion to UV coordinates
2975
+ */
2724
2976
  // Extract border-radius from children
2725
2977
  useEffect((() => {
2726
2978
  const extractRadius = () => {
@@ -2923,6 +3175,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2923
3175
  lerpActiveRef.current = !0;
2924
3176
  const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
2925
3177
  if (!lerpActiveRef.current) return;
3178
+ // Add ref validity check to prevent memory leaks
3179
+ if (!glassRef.current || !wrapperRef?.current) return void (lerpActiveRef.current = !1);
2926
3180
  const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
2927
3181
  // If we're close enough, snap and park
2928
3182
  if (Math.abs(dx) < .05 && Math.abs(dy) < .05) internalMouseOffsetRef.current = {
@@ -2931,17 +3185,17 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2931
3185
  ...targetGlobalMousePositionRef.current
2932
3186
  }; else {
2933
3187
  internalMouseOffsetRef.current = {
2934
- x: lerp(cur.x, tgt.x, LERP_T),
2935
- y: lerp(cur.y, tgt.y, LERP_T)
3188
+ x: lerp$1(cur.x, tgt.x, LERP_T),
3189
+ y: lerp$1(cur.y, tgt.y, LERP_T)
2936
3190
  };
2937
3191
  const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
2938
3192
  internalGlobalMousePositionRef.current = {
2939
- x: lerp(curG.x, tgtG.x, LERP_T),
2940
- y: lerp(curG.y, tgtG.y, LERP_T)
3193
+ x: lerp$1(curG.x, tgtG.x, LERP_T),
3194
+ y: lerp$1(curG.y, tgtG.y, LERP_T)
2941
3195
  };
2942
3196
  }
2943
3197
  // Imperative style update with the smoothed values
2944
- updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
3198
+ updateAtomixGlassStyles(wrapperRef.current, glassRef.current, {
2945
3199
  mouseOffset: internalMouseOffsetRef.current,
2946
3200
  globalMousePosition: internalGlobalMousePositionRef.current,
2947
3201
  glassSize: glassSize,
@@ -2958,12 +3212,13 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2958
3212
  withLiquidBlur: withLiquidBlur,
2959
3213
  blurAmount: blurAmount,
2960
3214
  saturation: saturation,
2961
- padding: padding
3215
+ padding: padding,
3216
+ isFixedOrSticky: isFixedOrSticky
2962
3217
  }), lerpRafRef.current = requestAnimationFrame(tick);
2963
3218
  };
2964
3219
  // 0.08 – lower = more viscous
2965
3220
  lerpRafRef.current = requestAnimationFrame(tick);
2966
- }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding ]), stopLerpLoop = useCallback((() => {
3221
+ }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky ]), stopLerpLoop = useCallback((() => {
2967
3222
  lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
2968
3223
  lerpRafRef.current = null);
2969
3224
  }), []);
@@ -3031,6 +3286,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3031
3286
  // This is now static (refs or props) unless prop changes
3032
3287
  overLightConfig: overLightConfig,
3033
3288
  transformStyle: transformStyle,
3289
+ getShaderTime: getShaderTime,
3290
+ applyTimeBasedDistortion: applyTimeBasedDistortion,
3034
3291
  handleMouseEnter: handleMouseEnter,
3035
3292
  handleMouseLeave: handleMouseLeave,
3036
3293
  handleMouseDown: handleMouseDown,
@@ -3039,6 +3296,800 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3039
3296
  };
3040
3297
  }
3041
3298
 
3299
+ /**
3300
+ * Default responsive breakpoints configuration
3301
+ *
3302
+ * These breakpoints are optimized for glass effect performance across device classes:
3303
+ * - Mobile: Reduced complexity for 60 FPS target
3304
+ * - Tablet: Balanced quality and performance
3305
+ * - Desktop: Full fidelity effects
3306
+ */ const DEFAULT_BREAKPOINTS = {
3307
+ mobile: {
3308
+ maxWidth: 640,
3309
+ params: {
3310
+ distortionOctaves: 3,
3311
+ displacementScale: .7,
3312
+ blurAmount: .8,
3313
+ animationSpeed: .8,
3314
+ chromaticIntensity: .5
3315
+ }
3316
+ },
3317
+ tablet: {
3318
+ minWidth: 641,
3319
+ maxWidth: 1024,
3320
+ params: {
3321
+ distortionOctaves: 4,
3322
+ displacementScale: .85,
3323
+ blurAmount: .9,
3324
+ animationSpeed: .9,
3325
+ chromaticIntensity: .75
3326
+ }
3327
+ },
3328
+ desktop: {
3329
+ minWidth: 1025,
3330
+ params: {
3331
+ distortionOctaves: 5,
3332
+ displacementScale: 1,
3333
+ blurAmount: 1,
3334
+ animationSpeed: 1,
3335
+ chromaticIntensity: 1
3336
+ }
3337
+ }
3338
+ };
3339
+
3340
+ /**
3341
+ * Device performance tier detection
3342
+ *
3343
+ * Uses Device Memory API and Hardware Concurrency API to classify devices
3344
+ * into performance tiers for automatic quality adjustment.
3345
+ *
3346
+ * @returns Performance tier classification
3347
+ */
3348
+ /**
3349
+ * Responsive Glass Parameters Hook
3350
+ *
3351
+ * Automatically adjusts glass effect parameters based on:
3352
+ * 1. Screen size (mobile/tablet/desktop breakpoints)
3353
+ * 2. Device performance (RAM and CPU detection)
3354
+ * 3. Custom breakpoint configuration
3355
+ *
3356
+ * Features:
3357
+ * - Debounced resize handling
3358
+ * - Performance-based quality adjustment
3359
+ * - Smooth parameter transitions
3360
+ * - Debug mode for development
3361
+ *
3362
+ * @example
3363
+ * ```typescript
3364
+ * const { responsiveParams, currentBreakpoint } = useResponsiveGlass({
3365
+ * baseParams: {
3366
+ * distortionOctaves: 5,
3367
+ * displacementScale: 20,
3368
+ * blurAmount: 10,
3369
+ * },
3370
+ * debug: true,
3371
+ * });
3372
+ * ```
3373
+ *
3374
+ * @param options Hook configuration options
3375
+ * @returns Responsive parameters and metadata
3376
+ */
3377
+ function useResponsiveGlass({baseParams: baseParams, breakpoints: breakpoints = DEFAULT_BREAKPOINTS, enabled: enabled = !0, enablePerformanceAdjustment: enablePerformanceAdjustment = !0, debug: debug = !1}) {
3378
+ const [responsiveParams, setResponsiveParams] = useState(baseParams), [currentBreakpoint, setCurrentBreakpoint] = useState("desktop"), [performanceTier, setPerformanceTier] = useState("high"), [isActive, setIsActive] = useState(enabled), baseParamsRef = useRef(baseParams), breakpointsRef = useRef(breakpoints);
3379
+ // Update refs when props change
3380
+ baseParamsRef.current = baseParams, breakpointsRef.current = breakpoints;
3381
+ /**
3382
+ * Calculate and apply responsive parameters
3383
+ */
3384
+ const calculateParams = useCallback((() => {
3385
+ if (!enabled || "undefined" == typeof window) return setIsActive(!1), setResponsiveParams(baseParamsRef.current),
3386
+ void setCurrentBreakpoint("disabled");
3387
+ setIsActive(!0);
3388
+ // Get current screen width
3389
+ const width = window.innerWidth, {name: name, params: breakpointParams} = ((width, breakpoints) => {
3390
+ // Convert breakpoints to array and sort by minWidth descending
3391
+ const sortedBreakpoints = Object.entries(breakpoints).filter((([_, bp]) => void 0 !== bp.minWidth)).sort(((a, b) => (b[1].minWidth || 0) - (a[1].minWidth || 0)));
3392
+ // Find first breakpoint where width >= minWidth
3393
+ for (const [name, bp] of sortedBreakpoints) if (width >= (bp.minWidth || 0)) return {
3394
+ name: name,
3395
+ params: bp.params
3396
+ };
3397
+ // If no minWidth matched, check maxWidth breakpoints
3398
+ const maxWidthBreakpoints = Object.entries(breakpoints).filter((([_, bp]) => void 0 !== bp.maxWidth)).sort(((a, b) => (a[1].maxWidth || 1 / 0) - (b[1].maxWidth || 1 / 0)));
3399
+ for (const [name, bp] of maxWidthBreakpoints) if (width <= (bp.maxWidth || 1 / 0)) return {
3400
+ name: name,
3401
+ params: bp.params
3402
+ };
3403
+ // Fallback to first available breakpoint
3404
+ const entries = Object.entries(breakpoints);
3405
+ if (0 === entries.length)
3406
+ // Ultimate fallback - return sensible defaults
3407
+ return {
3408
+ name: "desktop",
3409
+ params: {
3410
+ distortionOctaves: 5,
3411
+ displacementScale: 1,
3412
+ blurAmount: 1
3413
+ }
3414
+ };
3415
+ const firstEntry = entries[0];
3416
+ if (!firstEntry) return {
3417
+ name: "desktop",
3418
+ params: {
3419
+ distortionOctaves: 5,
3420
+ displacementScale: 1,
3421
+ blurAmount: 1
3422
+ }
3423
+ };
3424
+ const [fallbackName, fallbackBreakpoint] = firstEntry;
3425
+ return {
3426
+ name: fallbackName,
3427
+ params: fallbackBreakpoint.params
3428
+ };
3429
+ })(width, breakpointsRef.current);
3430
+ // Determine current breakpoint
3431
+ setCurrentBreakpoint(name);
3432
+ // Merge base params with breakpoint params
3433
+ let mergedParams = ((baseParams, breakpointParams) => {
3434
+ const result = {
3435
+ ...baseParams
3436
+ }, scaleProperties = [ "displacementScale", "blurAmount", "saturation", "aberrationIntensity", "animationSpeed", "chromaticIntensity" ];
3437
+ // Apply scaling for specific properties
3438
+ for (const prop of scaleProperties) void 0 !== breakpointParams[prop] && void 0 !== baseParams[prop] && (result[prop] = baseParams[prop] * breakpointParams[prop]);
3439
+ // Override properties that should be set directly (not scaled)
3440
+ const overrideProperties = [ "distortionOctaves", "distortionLacunarity", "distortionGain" ];
3441
+ for (const prop of overrideProperties) void 0 !== breakpointParams[prop] && (result[prop] = breakpointParams[prop]);
3442
+ return result;
3443
+ })(baseParamsRef.current, breakpointParams);
3444
+ // Apply performance adjustments if enabled
3445
+ if (enablePerformanceAdjustment) {
3446
+ const tier = (() => {
3447
+ // Check if we're in a browser environment
3448
+ if ("undefined" == typeof window || "undefined" == typeof navigator) return "high"; // Default to high for SSR
3449
+ // Device Memory API (Chrome, Edge, Opera)
3450
+ // Returns RAM in GB: 0.25, 0.5, 1, 2, 4, 8
3451
+ const deviceMemory = navigator.deviceMemory || 4, hardwareConcurrency = navigator.hardwareConcurrency || 4;
3452
+ // Hardware Concurrency API (logical CPU cores)
3453
+ // Low-end: ≤2GB RAM OR ≤2 CPU cores
3454
+ return deviceMemory <= 2 || hardwareConcurrency <= 2 ? "low" :
3455
+ // High-end: ≥4GB RAM AND ≥4 CPU cores
3456
+ deviceMemory >= 4 && hardwareConcurrency >= 4 ? "high" : "medium";
3457
+ })();
3458
+ setPerformanceTier(tier), mergedParams = ((baseParams, performanceTier) => {
3459
+ if ("high" === performanceTier) return baseParams;
3460
+ // No adjustment needed
3461
+ const multiplier = "low" === performanceTier ? .7 : .85;
3462
+ return {
3463
+ ...baseParams,
3464
+ distortionOctaves: Math.max(2, Math.round((baseParams.distortionOctaves || 5) * multiplier)),
3465
+ displacementScale: (baseParams.displacementScale || 1) * multiplier,
3466
+ blurAmount: (baseParams.blurAmount || 1) * multiplier,
3467
+ animationSpeed: (baseParams.animationSpeed || 1) * multiplier,
3468
+ chromaticIntensity: (baseParams.chromaticIntensity || 1) * multiplier
3469
+ };
3470
+ })(mergedParams, tier);
3471
+ }
3472
+ setResponsiveParams(mergedParams);
3473
+ }), [ enabled, enablePerformanceAdjustment, debug ]), debouncedCalculate = (func => {
3474
+ const timeoutRef = useRef(null);
3475
+ return useEffect((() => () => {
3476
+ timeoutRef.current && clearTimeout(timeoutRef.current);
3477
+ }), []), useCallback(((...args) => {
3478
+ timeoutRef.current && clearTimeout(timeoutRef.current), timeoutRef.current = setTimeout((() => {
3479
+ func(...args);
3480
+ }), 200);
3481
+ }), [ func, 200 ]);
3482
+ })(calculateParams);
3483
+ /**
3484
+ * Debounced parameter calculation for resize events
3485
+ */
3486
+ /**
3487
+ * Handle window resize
3488
+ */
3489
+ return useEffect((() => {
3490
+ if (enabled)
3491
+ // Cleanup
3492
+ // Initial calculation
3493
+ return calculateParams(),
3494
+ // Listen for resize events
3495
+ window.addEventListener("resize", debouncedCalculate), () => {
3496
+ window.removeEventListener("resize", debouncedCalculate);
3497
+ };
3498
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3499
+ }), [ enabled ]), {
3500
+ responsiveParams: responsiveParams,
3501
+ currentBreakpoint: currentBreakpoint,
3502
+ performanceTier: performanceTier,
3503
+ isActive: isActive,
3504
+ recalculate: useCallback((() => {
3505
+ calculateParams();
3506
+ }), [ calculateParams ])
3507
+ };
3508
+ }
3509
+
3510
+ /**
3511
+ * Utility function to get default breakpoints
3512
+ * Useful for documentation and debugging
3513
+ */ function getDefaultBreakpoints() {
3514
+ return {
3515
+ ...DEFAULT_BREAKPOINTS
3516
+ };
3517
+ }
3518
+
3519
+ /**
3520
+ * Utility function to create custom breakpoints
3521
+ *
3522
+ * @param customBreakpoints Partial breakpoint overrides
3523
+ * @returns Complete breakpoint configuration
3524
+ */ function createBreakpoints$1(customBreakpoints) {
3525
+ return {
3526
+ ...DEFAULT_BREAKPOINTS,
3527
+ ...customBreakpoints
3528
+ };
3529
+ }
3530
+
3531
+ /**
3532
+ * Get GPU memory info if available (Chrome DevTools only)
3533
+ */
3534
+ /**
3535
+ * Performance Monitor Hook
3536
+ *
3537
+ * Real-time performance tracking with automatic quality scaling.
3538
+ * Monitors FPS, frame time, and GPU memory to optimize glass effects.
3539
+ *
3540
+ * Features:
3541
+ * - Real-time FPS measurement
3542
+ * - Frame timing analysis
3543
+ * - Automatic quality scaling
3544
+ * - Debug overlay option
3545
+ * - Manual override capability
3546
+ *
3547
+ * @example
3548
+ * ```typescript
3549
+ * const { metrics, recommendedQuality, setQualityLevel } = usePerformanceMonitor({
3550
+ * targetFps: 60,
3551
+ * minFps: 45,
3552
+ * debug: true,
3553
+ * });
3554
+ * ```
3555
+ *
3556
+ * @param config Monitor configuration
3557
+ * @returns Performance metrics and controls
3558
+ */
3559
+ function usePerformanceMonitor(config = {}) {
3560
+ const {enabled: enabled = !0, targetFps: targetFps = 60, minFps: minFps = 45, scaleUpThreshold: scaleUpThreshold = 58, lowFpsFrames: lowFpsFrames = 3, highFpsFrames: highFpsFrames = 10, debug: debug = !1, showOverlay: showOverlay = !1} = config, [metrics, setMetrics] = useState({
3561
+ fps: 0,
3562
+ frameTime: 0,
3563
+ gpuMemory: null,
3564
+ qualityLevel: "medium",
3565
+ timestamp: 0,
3566
+ isAutoScaling: !0,
3567
+ lowFpsCount: 0
3568
+ }), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled), frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
3569
+ setMetrics((prev => ({
3570
+ ...prev,
3571
+ ...newMetrics,
3572
+ timestamp: performance.now()
3573
+ })));
3574
+ }), []), applyAutoScaling = useCallback((currentFps => {
3575
+ if (manualOverride) return;
3576
+ const currentQuality = qualityLevelRef.current;
3577
+ // Check for low FPS
3578
+ if (currentFps < minFps) lowFpsCountRef.current++, highFpsCountRef.current = 0,
3579
+ // Scale down after N consecutive low-FPS frames
3580
+ lowFpsCountRef.current >= lowFpsFrames && "low" !== currentQuality && (qualityLevelRef.current = "low",
3581
+ updateMetrics({
3582
+ qualityLevel: "low",
3583
+ lowFpsCount: lowFpsCountRef.current
3584
+ })); else if (currentFps >= scaleUpThreshold) {
3585
+ // Scale up after N consecutive high-FPS frames
3586
+ if (highFpsCountRef.current++, lowFpsCountRef.current = 0, highFpsCountRef.current >= highFpsFrames) {
3587
+ const newQuality = "low" === currentQuality ? "medium" : "high";
3588
+ qualityLevelRef.current = newQuality, updateMetrics({
3589
+ qualityLevel: newQuality,
3590
+ lowFpsCount: 0
3591
+ }), highFpsCountRef.current = 0;
3592
+ }
3593
+ } else
3594
+ // FPS in normal range, reset counters
3595
+ lowFpsCountRef.current = 0, highFpsCountRef.current = 0;
3596
+ }), [ manualOverride, minFps, scaleUpThreshold, lowFpsFrames, highFpsFrames, debug, updateMetrics ]), measureFrame = useCallback((currentTime => {
3597
+ if (!isEnabled) return;
3598
+ frameCountRef.current++;
3599
+ // Calculate frame time
3600
+ const frameTime = currentTime - lastFrameTimeRef.current;
3601
+ // Update FPS every 100ms for responsiveness
3602
+ if (lastFrameTimeRef.current = currentTime, currentTime - lastFpsUpdateRef.current >= 100) {
3603
+ const elapsed = currentTime - lastFpsUpdateRef.current, fps = Math.round(1e3 * frameCountRef.current / elapsed);
3604
+ // Apply auto-scaling
3605
+ applyAutoScaling(fps), updateMetrics({
3606
+ fps: fps,
3607
+ frameTime: frameTime,
3608
+ qualityLevel: qualityLevelRef.current,
3609
+ lowFpsCount: lowFpsCountRef.current
3610
+ }),
3611
+ // Reset for next measurement period
3612
+ frameCountRef.current = 0, lastFpsUpdateRef.current = currentTime;
3613
+ }
3614
+ // Continue measurement loop
3615
+ animationFrameRef.current = requestAnimationFrame(measureFrame);
3616
+ }), [ isEnabled, applyAutoScaling, updateMetrics ]);
3617
+ /**
3618
+ * Initialize GPU memory tracking
3619
+ */
3620
+ useEffect((() => {
3621
+ if (!isEnabled || "undefined" == typeof window) return;
3622
+ let mounted = !0;
3623
+ return (async () => {
3624
+ const memory = await new Promise((resolve => {
3625
+ // Check for WebGL debug renderer info
3626
+ if ("undefined" != typeof window && "undefined" != typeof document) try {
3627
+ const canvas = document.createElement("canvas"), gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
3628
+ if (gl) {
3629
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
3630
+ if (debugInfo) {
3631
+ var _context, _context2, _context3;
3632
+ // Note: Actual memory info is not directly available via WebGL
3633
+ // We estimate based on renderer
3634
+ const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
3635
+ // Rough estimation based on renderer type
3636
+ return void ((null == (_context = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context), _context))?.("Integrated") ? resolve(256) : (null == (_context2 = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context2), _context2))?.("AMD") || (null == (_context3 = renderer) ? void 0 : Function.call.bind(_includesInstanceProperty(_context3), _context3))?.("NVIDIA") ? resolve(512) : resolve(null));
3637
+ }
3638
+ }
3639
+ } catch (e) {
3640
+ // WebGL not available or error occurred
3641
+ }
3642
+ resolve(null);
3643
+ }));
3644
+ mounted && updateMetrics({
3645
+ gpuMemory: memory
3646
+ });
3647
+ })(), () => {
3648
+ mounted = !1;
3649
+ };
3650
+ }), [ isEnabled, updateMetrics ]),
3651
+ /**
3652
+ * Start/stop monitoring based on enabled state
3653
+ */
3654
+ useEffect((() => {
3655
+ if (isEnabled)
3656
+ // Cleanup
3657
+ // Initialize
3658
+ return lastFpsUpdateRef.current = performance.now(), lastFrameTimeRef.current = performance.now(),
3659
+ animationFrameRef.current = requestAnimationFrame(measureFrame), () => {
3660
+ null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
3661
+ animationFrameRef.current = null);
3662
+ };
3663
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3664
+ null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
3665
+ animationFrameRef.current = null);
3666
+ }), [ isEnabled ]);
3667
+ // measureFrame is stable via useCallback, avoid re-creating RAF loop
3668
+ /**
3669
+ * Manually set quality level (disables auto-scaling)
3670
+ */
3671
+ const setQualityLevel = useCallback((level => {
3672
+ setManualOverride(!0), qualityLevelRef.current = level, updateMetrics({
3673
+ qualityLevel: level,
3674
+ isAutoScaling: !1
3675
+ });
3676
+ }), [ updateMetrics, debug ]), resetAutoScaling = useCallback((() => {
3677
+ setManualOverride(!1), lowFpsCountRef.current = 0, highFpsCountRef.current = 0,
3678
+ updateMetrics({
3679
+ isAutoScaling: !0,
3680
+ lowFpsCount: 0
3681
+ });
3682
+ }), [ updateMetrics, debug ]), toggleMonitoring = useCallback((() => {
3683
+ setIsEnabled((prev => !prev));
3684
+ }), []);
3685
+ /**
3686
+ * Reset to auto-scaling mode
3687
+ */ var fps, currentQuality;
3688
+ return {
3689
+ metrics: metrics,
3690
+ recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
3691
+ isUnderperforming: metrics.fps < minFps,
3692
+ setQualityLevel: setQualityLevel,
3693
+ resetAutoScaling: resetAutoScaling,
3694
+ toggleMonitoring: toggleMonitoring
3695
+ };
3696
+ }
3697
+
3698
+ /**
3699
+ * Debug Overlay Component (Optional)
3700
+ *
3701
+ * Shows real-time performance metrics on screen.
3702
+ * Only rendered when showOverlay is enabled.
3703
+ */ function PerformanceOverlay({metrics: metrics}) {
3704
+ return null;
3705
+ // Performance overlay removed - will be implemented as separate component
3706
+ }
3707
+
3708
+ /**
3709
+ * Utility to get quality multipliers for glass parameters
3710
+ */ function getQualityMultipliers(quality) {
3711
+ switch (quality) {
3712
+ case "low":
3713
+ return {
3714
+ distortionOctaves: 2,
3715
+ displacementScale: .6,
3716
+ blurAmount: .7,
3717
+ animationSpeed: .8,
3718
+ chromaticIntensity: .5
3719
+ };
3720
+
3721
+ case "medium":
3722
+ return {
3723
+ distortionOctaves: 4,
3724
+ displacementScale: .85,
3725
+ blurAmount: .9,
3726
+ animationSpeed: .95,
3727
+ chromaticIntensity: .75
3728
+ };
3729
+
3730
+ case "high":
3731
+ return {
3732
+ distortionOctaves: 5,
3733
+ displacementScale: 1,
3734
+ blurAmount: 1,
3735
+ animationSpeed: 1,
3736
+ chromaticIntensity: 1
3737
+ };
3738
+ }
3739
+ }
3740
+
3741
+ /**
3742
+ * PerformanceDashboard - Real-time performance monitoring overlay
3743
+ *
3744
+ * Displays:
3745
+ * - Current FPS with color coding
3746
+ * - Frame time statistics
3747
+ * - Quality level indicator
3748
+ * - GPU memory usage (if available)
3749
+ * - Auto-scaling status
3750
+ */ const PerformanceDashboard = ({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
3751
+ // Get color for FPS display
3752
+ const getFpsColor = fps => fps >= 58 ? "#4ade80" : // Green - good
3753
+ fps >= 45 ? "#fbbf24" : "#ef4444" // Red - critical
3754
+ , dashboardStyle = useMemo((() => ({
3755
+ position: "fixed",
3756
+ top: "16px",
3757
+ right: "16px",
3758
+ padding: "12px 16px",
3759
+ backgroundColor: "rgba(17, 24, 39, 0.95)",
3760
+ borderRadius: "8px",
3761
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
3762
+ fontFamily: "monospace",
3763
+ fontSize: "12px",
3764
+ color: "#fff",
3765
+ zIndex: 9999,
3766
+ minWidth: "200px",
3767
+ backdropFilter: "blur(8px)",
3768
+ border: "1px solid rgba(255, 255, 255, 0.1)",
3769
+ transition: "opacity 0.3s ease",
3770
+ opacity: isVisible ? 1 : 0,
3771
+ pointerEvents: isVisible ? "auto" : "none"
3772
+ })), [ isVisible ]), headerStyle = useMemo((() => ({
3773
+ display: "flex",
3774
+ justifyContent: "space-between",
3775
+ alignItems: "center",
3776
+ marginBottom: "8px",
3777
+ paddingBottom: "8px",
3778
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)"
3779
+ })), []), titleStyle = useMemo((() => ({
3780
+ fontWeight: "bold",
3781
+ fontSize: "13px",
3782
+ color: "#fff"
3783
+ })), []), closeButtonStyle = useMemo((() => ({
3784
+ background: "transparent",
3785
+ border: "none",
3786
+ color: "#9ca3af",
3787
+ cursor: "pointer",
3788
+ fontSize: "16px",
3789
+ padding: "0",
3790
+ lineHeight: "1"
3791
+ })), []), metricRowStyle = useMemo((() => ({
3792
+ display: "flex",
3793
+ justifyContent: "space-between",
3794
+ alignItems: "center",
3795
+ marginBottom: "6px"
3796
+ })), []), labelStyle = useMemo((() => ({
3797
+ color: "#9ca3af",
3798
+ marginRight: "12px"
3799
+ })), []), valueStyle = useMemo((() => ({
3800
+ fontWeight: "bold"
3801
+ })), []);
3802
+ // Get quality level badge color
3803
+ return isVisible ? jsxs("div", {
3804
+ style: dashboardStyle,
3805
+ children: [ jsxs("div", {
3806
+ style: headerStyle,
3807
+ children: [ jsx("span", {
3808
+ style: titleStyle,
3809
+ children: "Performance Monitor"
3810
+ }), onClose && jsx("button", {
3811
+ style: closeButtonStyle,
3812
+ onClick: onClose,
3813
+ "aria-label": "Close performance dashboard",
3814
+ children: "×"
3815
+ }) ]
3816
+ }), jsxs("div", {
3817
+ style: metricRowStyle,
3818
+ children: [ jsx("span", {
3819
+ style: labelStyle,
3820
+ children: "FPS"
3821
+ }), jsx("span", {
3822
+ style: {
3823
+ ...valueStyle,
3824
+ color: getFpsColor(metrics.fps)
3825
+ },
3826
+ children: Math.round(metrics.fps)
3827
+ }) ]
3828
+ }), jsxs("div", {
3829
+ style: metricRowStyle,
3830
+ children: [ jsx("span", {
3831
+ style: labelStyle,
3832
+ children: "Frame Time"
3833
+ }), jsxs("span", {
3834
+ style: valueStyle,
3835
+ children: [ metrics.frameTime.toFixed(2), "ms" ]
3836
+ }) ]
3837
+ }), jsxs("div", {
3838
+ style: metricRowStyle,
3839
+ children: [ jsx("span", {
3840
+ style: labelStyle,
3841
+ children: "Quality"
3842
+ }), jsx("span", {
3843
+ style: {
3844
+ ...valueStyle,
3845
+ color: (quality => {
3846
+ switch (quality) {
3847
+ case "high":
3848
+ return "#4ade80";
3849
+
3850
+ case "medium":
3851
+ return "#fbbf24";
3852
+
3853
+ case "low":
3854
+ return "#ef4444";
3855
+
3856
+ default:
3857
+ return "#9ca3af";
3858
+ }
3859
+ })(metrics.qualityLevel),
3860
+ textTransform: "uppercase",
3861
+ fontSize: "11px"
3862
+ },
3863
+ children: metrics.qualityLevel
3864
+ }) ]
3865
+ }), metrics.gpuMemory && jsxs("div", {
3866
+ style: metricRowStyle,
3867
+ children: [ jsx("span", {
3868
+ style: labelStyle,
3869
+ children: "GPU Memory"
3870
+ }), jsxs("span", {
3871
+ style: valueStyle,
3872
+ children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
3873
+ }) ]
3874
+ }), metrics.isAutoScaling && jsx("div", {
3875
+ style: {
3876
+ marginTop: "8px",
3877
+ paddingTop: "8px",
3878
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
3879
+ fontSize: "10px",
3880
+ color: "#6b7280",
3881
+ textAlign: "center"
3882
+ },
3883
+ children: "Auto-scaling active"
3884
+ }), jsxs("div", {
3885
+ style: {
3886
+ marginTop: "8px",
3887
+ paddingTop: "8px",
3888
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
3889
+ display: "flex",
3890
+ alignItems: "center",
3891
+ gap: "6px"
3892
+ },
3893
+ children: [ jsx("div", {
3894
+ style: {
3895
+ width: "8px",
3896
+ height: "8px",
3897
+ borderRadius: "50%",
3898
+ backgroundColor: getFpsColor(metrics.fps),
3899
+ animation: metrics.fps < 45 ? "pulse 1s infinite" : "none"
3900
+ }
3901
+ }), jsx("span", {
3902
+ style: {
3903
+ fontSize: "10px",
3904
+ color: metrics.fps >= 58 ? "#4ade80" : metrics.fps >= 45 ? "#fbbf24" : "#ef4444"
3905
+ },
3906
+ children: metrics.fps >= 58 ? "Optimal" : metrics.fps >= 45 ? "Warning" : "Critical"
3907
+ }) ]
3908
+ }) ]
3909
+ }) : null;
3910
+ };
3911
+
3912
+ // Add pulse animation for critical FPS
3913
+ if ("undefined" != typeof document) {
3914
+ const styleSheet = document.createElement("style");
3915
+ styleSheet.textContent = "\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n ",
3916
+ document.head.appendChild(styleSheet);
3917
+ }
3918
+
3919
+ /**
3920
+ * Mobile optimization presets
3921
+ *
3922
+ * These presets adjust glass effect parameters based on device performance tier
3923
+ * to ensure smooth animations and responsive interactions.
3924
+ */
3925
+ /**
3926
+ * Performance preset - Maximum FPS, reduced quality
3927
+ * Best for low-end devices or when battery saving is priority
3928
+ */ const PERFORMANCE_PRESET = {
3929
+ distortionOctaves: 2,
3930
+ // Minimal FBM layers
3931
+ displacementScale: 50,
3932
+ // Subtle displacement
3933
+ blurAmount: 5,
3934
+ // Light blur
3935
+ saturation: 80,
3936
+ // Reduced saturation
3937
+ aberrationIntensity: .3,
3938
+ // Minimal chromatic aberration
3939
+ animationSpeed: .8,
3940
+ // Slightly slower for performance
3941
+ chromaticIntensity: .3,
3942
+ // Low chromatic effect
3943
+ distortionLacunarity: 1.5,
3944
+ // Simpler noise pattern
3945
+ distortionGain: .3
3946
+ }, BALANCED_PRESET = {
3947
+ distortionOctaves: 3,
3948
+ // Moderate FBM layers
3949
+ displacementScale: 75,
3950
+ // Medium displacement
3951
+ blurAmount: 8,
3952
+ // Moderate blur
3953
+ saturation: 90,
3954
+ // Near-full saturation
3955
+ aberrationIntensity: .5,
3956
+ // Moderate chromatic aberration
3957
+ animationSpeed: 1,
3958
+ // Normal speed
3959
+ chromaticIntensity: .5,
3960
+ // Moderate chromatic effect
3961
+ distortionLacunarity: 2,
3962
+ // Standard noise pattern
3963
+ distortionGain: .4
3964
+ }, QUALITY_PRESET = {
3965
+ distortionOctaves: 4,
3966
+ // More FBM layers for detail
3967
+ displacementScale: 100,
3968
+ // Stronger displacement
3969
+ blurAmount: 12,
3970
+ // Smoother blur
3971
+ saturation: 100,
3972
+ // Full saturation
3973
+ aberrationIntensity: .7,
3974
+ // Pronounced chromatic aberration
3975
+ animationSpeed: 1.2,
3976
+ // Slightly faster for drama
3977
+ chromaticIntensity: .7,
3978
+ // Strong chromatic effect
3979
+ distortionLacunarity: 2.2,
3980
+ // Richer noise pattern
3981
+ distortionGain: .5
3982
+ };
3983
+
3984
+ /**
3985
+ * Balanced preset - Good quality with reasonable performance
3986
+ * Default preset for most mobile devices
3987
+ */
3988
+ /**
3989
+ * Get preset by name
3990
+ */
3991
+ function getDevicePreset(presetName) {
3992
+ switch (presetName) {
3993
+ case "performance":
3994
+ return PERFORMANCE_PRESET;
3995
+
3996
+ case "balanced":
3997
+ default:
3998
+ return BALANCED_PRESET;
3999
+
4000
+ case "quality":
4001
+ return QUALITY_PRESET;
4002
+ }
4003
+ }
4004
+
4005
+ /**
4006
+ * Mobile-optimized responsive breakpoints
4007
+ * Automatically applies appropriate presets based on viewport size
4008
+ */ const MOBILE_OPTIMIZED_BREAKPOINTS = {
4009
+ /** Desktop - Full quality */
4010
+ desktop: {
4011
+ minWidth: 1024,
4012
+ params: {
4013
+ distortionOctaves: 6,
4014
+ displacementScale: 150,
4015
+ blurAmount: 15,
4016
+ saturation: 100,
4017
+ aberrationIntensity: 1,
4018
+ animationSpeed: 1,
4019
+ chromaticIntensity: 1,
4020
+ distortionLacunarity: 2.5,
4021
+ distortionGain: .6
4022
+ }
4023
+ },
4024
+ /** Laptop - High quality */
4025
+ laptop: {
4026
+ minWidth: 768,
4027
+ params: {
4028
+ ...QUALITY_PRESET,
4029
+ distortionOctaves: 5,
4030
+ displacementScale: 120
4031
+ }
4032
+ },
4033
+ /** Tablet - Balanced quality */
4034
+ tablet: {
4035
+ minWidth: 640,
4036
+ params: {
4037
+ ...BALANCED_PRESET,
4038
+ distortionOctaves: 4,
4039
+ displacementScale: 90
4040
+ }
4041
+ },
4042
+ /** Mobile - Performance optimized */
4043
+ mobile: {
4044
+ maxWidth: 639,
4045
+ params: {
4046
+ ...PERFORMANCE_PRESET,
4047
+ distortionOctaves: 3,
4048
+ displacementScale: 75,
4049
+ blurAmount: 6
4050
+ }
4051
+ },
4052
+ /** Small mobile - Maximum performance */
4053
+ mobileSmall: {
4054
+ maxWidth: 375,
4055
+ params: {
4056
+ ...PERFORMANCE_PRESET,
4057
+ distortionOctaves: 2,
4058
+ displacementScale: 50,
4059
+ blurAmount: 4,
4060
+ saturation: 70
4061
+ }
4062
+ }
4063
+ };
4064
+
4065
+ /**
4066
+ * Get mobile-optimized parameters for current viewport
4067
+ * Can be used standalone or with useResponsiveGlass hook
4068
+ */ function getMobileOptimizedParams(viewportWidth) {
4069
+ return viewportWidth >= 1024 ? MOBILE_OPTIMIZED_BREAKPOINTS.desktop?.params || BALANCED_PRESET : viewportWidth >= 768 ? MOBILE_OPTIMIZED_BREAKPOINTS.laptop?.params || BALANCED_PRESET : viewportWidth >= 640 ? MOBILE_OPTIMIZED_BREAKPOINTS.tablet?.params || BALANCED_PRESET : viewportWidth >= 375 ? MOBILE_OPTIMIZED_BREAKPOINTS.mobile?.params || PERFORMANCE_PRESET : MOBILE_OPTIMIZED_BREAKPOINTS.mobileSmall?.params || PERFORMANCE_PRESET;
4070
+ }
4071
+
4072
+ /**
4073
+ * Device detection utilities
4074
+ */ const DeviceDetector = {
4075
+ /** Check if device is mobile */
4076
+ isMobile: () => "undefined" != typeof window && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
4077
+ /** Check if device is tablet */
4078
+ isTablet() {
4079
+ if ("undefined" == typeof window) return !1;
4080
+ const width = window.innerWidth;
4081
+ return width >= 640 && width < 1024 && this.isMobile();
4082
+ },
4083
+ /** Get recommended preset based on device type */
4084
+ getRecommendedPreset() {
4085
+ return this.isMobile() ? this.isTablet() ? "balanced" : "performance" : "quality";
4086
+ },
4087
+ /** Get device pixel ratio */
4088
+ getPixelRatio: () => "undefined" == typeof window ? 1 : window.devicePixelRatio || 1,
4089
+ /** Check if device has touch support */
4090
+ hasTouchSupport: () => "undefined" != typeof window && ("ontouchstart" in window || navigator.maxTouchPoints > 0)
4091
+ };
4092
+
3042
4093
  /**
3043
4094
  * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
3044
4095
  *
@@ -3053,6 +4104,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3053
4104
  * - Focus ring support for keyboard navigation
3054
4105
  * - Responsive breakpoints for mobile optimization
3055
4106
  * - Enhanced ARIA attributes for screen readers
4107
+ * - Time-based animation system with FBM distortion
4108
+ * - Device preset optimization for performance/quality balance
3056
4109
  *
3057
4110
  * Design System Compliance:
3058
4111
  * - Uses design tokens for opacity, spacing, and colors
@@ -3109,8 +4162,14 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3109
4162
  * <AtomixGlass overLight="auto" debugOverLight={true}>
3110
4163
  * <div>Content with debug logging enabled</div>
3111
4164
  * </AtomixGlass>
3112
- */ 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, borderRadius: borderRadius, 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, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, height: height, width: width, ...rest}) {
3113
- const glassRef = useRef(null), contentRef = useRef(null), {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
4165
+ *
4166
+ * @example
4167
+ * // Performance-optimized for mobile devices
4168
+ * <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
4169
+ * <div>Mobile-optimized glass effect</div>
4170
+ * </AtomixGlass>
4171
+ */ 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, borderRadius: borderRadius, 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, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, ...rest}) {
4172
+ const glassRef = useRef(null), contentRef = useRef(null), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, applyTimeBasedDistortion: applyTimeBasedDistortion, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
3114
4173
  glassRef: glassRef,
3115
4174
  contentRef: contentRef,
3116
4175
  borderRadius: borderRadius,
@@ -3123,7 +4182,6 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3123
4182
  withoutEffects: withoutEffects,
3124
4183
  elasticity: elasticity,
3125
4184
  onClick: onClick,
3126
- debugBorderRadius: debugBorderRadius,
3127
4185
  debugOverLight: debugOverLight,
3128
4186
  debugPerformance: debugPerformance,
3129
4187
  children: children,
@@ -3131,8 +4189,49 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3131
4189
  saturation: saturation,
3132
4190
  withLiquidBlur: withLiquidBlur,
3133
4191
  padding: padding,
3134
- style: style
3135
- }), isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = "fixed" === restStyle.position || "sticky" === restStyle.position, rootLayoutStyle = useMemo((() => {
4192
+ style: style,
4193
+ isFixedOrSticky: isFixedOrSticky,
4194
+ // Phase 1: Animation System props
4195
+ withTimeAnimation: withTimeAnimation,
4196
+ animationSpeed: animationSpeed,
4197
+ withMultiLayerDistortion: withMultiLayerDistortion,
4198
+ distortionOctaves: distortionOctaves,
4199
+ distortionLacunarity: distortionLacunarity,
4200
+ distortionGain: distortionGain,
4201
+ distortionQuality: distortionQuality
4202
+ });
4203
+ // Re-calculate only when devicePreset changes
4204
+ // Responsive breakpoint system - automatically adjusts parameters based on viewport
4205
+ useResponsiveGlass({
4206
+ baseParams: {
4207
+ ...useMemo((() => getDevicePreset(devicePreset)), [ devicePreset ]),
4208
+ distortionOctaves: Math.round((displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE) / 25),
4209
+ displacementScale: displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
4210
+ blurAmount: blurAmount || ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
4211
+ saturation: saturation || ATOMIX_GLASS.DEFAULTS.SATURATION,
4212
+ aberrationIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
4213
+ animationSpeed: 1,
4214
+ chromaticIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY
4215
+ },
4216
+ breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
4217
+ enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
4218
+ // Enable unless disabled
4219
+ debug: !1
4220
+ });
4221
+ // Performance monitoring - tracks FPS, frame time, memory usage
4222
+ const {metrics: performanceMetrics, recommendedQuality: recommendedQuality, isUnderperforming: isUnderperforming, setQualityLevel: setQualityLevel, toggleMonitoring: toggleMonitoring} = usePerformanceMonitor({
4223
+ enabled: !1,
4224
+ // We'll toggle manually based on prop
4225
+ debug: !1,
4226
+ showOverlay: !1
4227
+ });
4228
+ // Auto-start performance monitoring if enabled (only in development)
4229
+ React.useEffect((() => {
4230
+ "development" === process.env.NODE_ENV && window?.enablePerformanceMonitoring && toggleMonitoring();
4231
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4232
+ }), []);
4233
+ // Only run once on mount
4234
+ const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
3136
4235
  if (!isFixedOrSticky) return {};
3137
4236
  const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
3138
4237
  return {
@@ -3152,7 +4251,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3152
4251
  bottom: b
3153
4252
  }
3154
4253
  };
3155
- }), [ isFixedOrSticky, restStyle ]), baseStyle = useMemo((() => {
4254
+ }), [ isFixedOrSticky, restStyle ]);
4255
+ // Calculate base style with transforms
4256
+ // When layout is hoisted to the root, strip those props from the container
4257
+ useMemo((() => {
3156
4258
  if (isFixedOrSticky) {
3157
4259
  const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
3158
4260
  return {
@@ -3168,18 +4270,20 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3168
4270
  transform: transformStyle
3169
4271
  }
3170
4272
  };
3171
- }), [ isFixedOrSticky, restStyle, effectiveWithoutEffects, transformStyle ]), componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
4273
+ }), [ isFixedOrSticky, restStyle, effectiveWithoutEffects, transformStyle ]);
4274
+ // Build className with state modifiers
4275
+ const componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
3172
4276
  position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
3173
4277
  top: isFixedOrSticky ? 0 : restStyle.top || 0,
3174
4278
  left: isFixedOrSticky ? 0 : restStyle.left || 0
3175
4279
  })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left ]), adjustedSize = useMemo((() => {
3176
- const resolveSize = (propValue, styleValue, measuredSize) => {
3177
- const explicitSize = propValue ?? styleValue;
3178
- return void 0 !== explicitSize ? "number" == typeof explicitSize ? `${explicitSize}px` : explicitSize : "fixed" === positionStyles.position ? `${Math.max(measuredSize, 0)}px` : "100%";
3179
- };
4280
+ // Keep a reference to positionStyles to avoid unused-variable lint,
4281
+ // but sizing is driven by explicit width/height or measured size.
4282
+ positionStyles.position;
4283
+ const resolveLength = (value, measured) => void 0 !== value ? "number" == typeof value ? `${value}px` : value : measured > 0 ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
3180
4284
  return {
3181
- width: resolveSize(width, restStyle.width, glassSize.width),
3182
- height: resolveSize(height, restStyle.height, glassSize.height)
4285
+ width: resolveLength(effectiveWidth, glassSize.width),
4286
+ height: resolveLength(effectiveHeight, glassSize.height)
3183
4287
  };
3184
4288
  }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height ]), gradientValues = useMemo((() => {
3185
4289
  const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
@@ -3228,9 +4332,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3228
4332
  },
3229
4333
  "--atomix-glass-radius": `${effectiveBorderRadius}px`,
3230
4334
  "--atomix-glass-transform": transformStyle || "none",
3231
- "--atomix-glass-position": positionStyles.position,
3232
- "--atomix-glass-top": "fixed" !== positionStyles.top ? `${positionStyles.top}px` : "0",
3233
- "--atomix-glass-left": "fixed" !== positionStyles.left ? `${positionStyles.left}px` : "0",
4335
+ // Internal decorative layers are positioned relative to the root;
4336
+ "--atomix-glass-position": rootLayoutStyle.position,
4337
+ "--atomix-glass-top": `${isFixedOrSticky ? rootLayoutStyle.top : 0}px`,
4338
+ "--atomix-glass-left": `${isFixedOrSticky ? rootLayoutStyle.left : 0}px`,
3234
4339
  "--atomix-glass-width": adjustedSize.width,
3235
4340
  "--atomix-glass-height": adjustedSize.height,
3236
4341
  "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
@@ -3250,10 +4355,12 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3250
4355
  "--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
3251
4356
  "--atomix-glass-overlay-highlight-bg": `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}%)`
3252
4357
  };
3253
- }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, positionStyles, adjustedSize, isOverLight, overLightConfig.borderOpacity, customZIndex ]), renderBackgroundLayer = layerType => jsx("div", {
4358
+ }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, overLightConfig.borderOpacity, customZIndex, rootLayoutStyle, isFixedOrSticky ]), renderBackgroundLayer = layerType => jsx("div", {
3254
4359
  className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, "dark" === layerType ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" ")
3255
4360
  });
3256
- return jsxs("div", {
4361
+ // Calculate position and size styles for internal layers
4362
+ // When root is fixed/sticky, internal layers use absolute (relative to root)
4363
+ return jsxs("div", {
3257
4364
  ...rest,
3258
4365
  className: componentClassName,
3259
4366
  style: {
@@ -3270,7 +4377,12 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3270
4377
  ref: glassRef,
3271
4378
  contentRef: contentRef,
3272
4379
  className: className,
3273
- style: rootLayoutStyle,
4380
+ style: {
4381
+ ...restStyle,
4382
+ ...!isFixedOrSticky && {
4383
+ position: "relative"
4384
+ }
4385
+ },
3274
4386
  borderRadius: effectiveBorderRadius,
3275
4387
  displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
3276
4388
  blurAmount: effectiveWithoutEffects ? 0 : blurAmount,
@@ -3301,11 +4413,19 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3301
4413
  },
3302
4414
  onClick: onClick,
3303
4415
  mode: mode,
3304
- transform: baseStyle.transform,
3305
4416
  effectiveWithoutEffects: effectiveWithoutEffects,
3306
4417
  effectiveReducedMotion: effectiveReducedMotion,
3307
4418
  shaderVariant: shaderVariant,
3308
4419
  withLiquidBlur: withLiquidBlur,
4420
+ // Phase 1: Animation System props
4421
+ shaderTime: getShaderTime(),
4422
+ withTimeAnimation: withTimeAnimation,
4423
+ animationSpeed: animationSpeed,
4424
+ withMultiLayerDistortion: withMultiLayerDistortion,
4425
+ distortionOctaves: distortionOctaves,
4426
+ distortionLacunarity: distortionLacunarity,
4427
+ distortionGain: distortionGain,
4428
+ distortionQuality: distortionQuality,
3309
4429
  children: children
3310
4430
  }), Boolean(onClick) && jsxs(Fragment, {
3311
4431
  children: [ jsx("div", {
@@ -3329,6 +4449,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3329
4449
  }), jsx("span", {
3330
4450
  className: ATOMIX_GLASS.BORDER_2_CLASS
3331
4451
  }) ]
4452
+ }), debugPerformance && performanceMetrics && jsx(PerformanceDashboard, {
4453
+ metrics: performanceMetrics,
4454
+ isVisible: !0,
4455
+ onClose: () => {}
3332
4456
  }) ]
3333
4457
  });
3334
4458
  }
@@ -3395,16 +4519,14 @@ const AccordionBody = forwardRef((({children: children, className: className =
3395
4519
 
3396
4520
  AccordionBody.displayName = "AccordionBody";
3397
4521
 
3398
- const AccordionImpl = memo((({title: title, children: children, defaultOpen: defaultOpen = !1, isOpen: controlledOpen, onOpenChange: onOpenChange, onOpen: onOpen, onClose: onClose, disabled: disabled = !1, iconPosition: iconPosition = "right", icon: icon, className: className = "", style: style, glass: glass}) => {
4522
+ const AccordionImpl = memo((({title: title, children: children, defaultOpen: defaultOpen = !1, isOpen: controlledOpen, onOpenChange: onOpenChange, disabled: disabled = !1, iconPosition: iconPosition = "right", icon: icon, className: className = "", style: style, glass: glass}) => {
3399
4523
  // Generate unique IDs for accessibility
3400
4524
  const instanceId = useId(), buttonId = `accordion-header-${instanceId}`, panelId = `accordion-panel-${instanceId}`, {state: state, toggle: toggle, updatePanelHeight: updatePanelHeight, panelRef: panelRef, contentRef: contentRef, generateClassNames: generateClassNames, generateHeaderClassNames: generateHeaderClassNames} = useAccordion({
3401
4525
  defaultOpen: defaultOpen,
3402
4526
  disabled: disabled,
3403
4527
  iconPosition: iconPosition,
3404
4528
  isOpen: controlledOpen,
3405
- onOpenChange: onOpenChange,
3406
- onOpen: onOpen,
3407
- onClose: onClose
4529
+ onOpenChange: onOpenChange
3408
4530
  }), headerClassNames = generateHeaderClassNames(), panelClassNames = ACCORDION.SELECTORS.PANEL.replace(".", ""), hasCompoundComponents = React.Children.toArray(children).some((child => {
3409
4531
  var _context;
3410
4532
 
@@ -3785,7 +4907,9 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
3785
4907
  return this.canvasDPI;
3786
4908
  }
3787
4909
  },
3788
- fragmentShaders: fragmentShaders
4910
+ createFBMEngine: createFBMEngine,
4911
+ fragmentShaders: fragmentShaders,
4912
+ liquidGlassWithTime: liquidGlassWithTime
3789
4913
  }, Symbol.toStringTag, {
3790
4914
  value: "Module"
3791
4915
  })), sizeMap = {
@@ -3814,7 +4938,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
3814
4938
 
3815
4939
  Icon.displayName = "Icon";
3816
4940
 
3817
- const Avatar = memo((({src: src, alt: alt = "Avatar", initials: initials, icon: icon, size: size = "md", circle: circle = !1, className: className = "", disabled: disabled = !1, onClick: onClick, style: style, glass: glass}) => {
4941
+ const Avatar = memo((({src: src, alt: alt = "User avatar", initials: initials, icon: icon, size: size = "md", circle: circle = !1, className: className = "", disabled: disabled = !1, onClick: onClick, style: style, glass: glass}) => {
3818
4942
  const [imageError, setImageError] = useState(!1), avatarClasses = [ AVATAR.CLASSES.BASE, "md" !== size && `c-avatar--${size}`, circle && AVATAR.CLASSES.CIRCLE, disabled && "is-disabled", className ].filter(Boolean).join(" ");
3819
4943
  return jsx("div", {
3820
4944
  className: avatarClasses,
@@ -4287,7 +5411,7 @@ class ThemeNaming {
4287
5411
  ThemeNaming.prefix = "atomix";
4288
5412
 
4289
5413
  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, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, ...props}, ref) => {
4290
- const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href && !isDisabled), iconElement = iconName ? jsx(Icon, {
5414
+ const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsx(Icon, {
4291
5415
  name: iconName,
4292
5416
  size: iconSize
4293
5417
  }) : icon, buttonClass = [ BUTTON.BASE_CLASS, ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant), "md" !== size ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : "", iconOnly ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT) : "", rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, "rounded") : "", isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, "disabled") : "", glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, "glass") : "", loading ? BUTTON.CLASSES.LOADING : "", fullWidth ? BUTTON.CLASSES.FULL_WIDTH : "", block ? BUTTON.CLASSES.BLOCK : "", active ? BUTTON.CLASSES.ACTIVE : "", selected ? BUTTON.CLASSES.SELECTED : "", className ].filter(Boolean).join(" "), handleClickEvent = useCallback((event => {
@@ -4343,8 +5467,8 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4343
5467
  ...buttonProps,
4344
5468
  ref: ref,
4345
5469
  // linkComponent usually forwards ref to anchor
4346
- href: href,
4347
- to: href,
5470
+ href: isDisabled ? void 0 : href,
5471
+ to: isDisabled ? void 0 : href,
4348
5472
  target: target,
4349
5473
  rel: "_blank" === target ? "noopener noreferrer" : void 0
4350
5474
  };
@@ -4357,7 +5481,7 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4357
5481
  content = jsx("a", {
4358
5482
  ...buttonProps,
4359
5483
  ref: ref,
4360
- href: href,
5484
+ href: isDisabled ? void 0 : href,
4361
5485
  target: target,
4362
5486
  rel: "_blank" === target ? "noopener noreferrer" : void 0,
4363
5487
  children: buttonContent
@@ -4636,7 +5760,7 @@ row: row = !1, flat: flat = !1,
4636
5760
  // States
4637
5761
  active: active = !1, disabled: disabled = !1, loading: loading = !1, selected: selected = !1, interactive: interactive = !1,
4638
5762
  // Content
4639
- header: header, image: image, imageAlt: imageAlt = "", title: title, text: text, actions: actions, icon: icon, footer: footer, children: children,
5763
+ header: header, image: image, imageAlt: imageAlt = "Card image", title: title, text: text, actions: actions, icon: icon, footer: footer, children: children,
4640
5764
  // Interaction
4641
5765
  onClick: onClick, onHover: onHover, onFocus: onFocus, href: href, target: target,
4642
5766
  // Custom Link
@@ -4827,50 +5951,50 @@ Card.Header = CardHeader, Card.Body = CardBody, Card.Footer = CardFooter;
4827
5951
  * @param options - Configuration options for the card
4828
5952
  * @returns Card state and handlers
4829
5953
  */
4830
- const useCard = (options = {}) => {
4831
- const {elevationEffect: elevationEffect = !1, elevationClass: elevationClass = CARD.CLASSES.ACTIVE, flipEffect: flipEffect = !1, flipTrigger: flipTrigger = "click", focusEffect: focusEffect = !1, clickable: clickable = !1, onClick: onClick} = options, cardRef = useRef(null), frontRef = useRef(null), backRef = useRef(null), [isFlipped, setIsFlipped] = useState(!1), [isElevated, setIsElevated] = useState(!1), [isFocused, setIsFocused] = useState(!1), [isHovered, setIsHovered] = useState(!1), handleClick = useCallback((event => {
4832
- flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)), onClick && onClick(event);
4833
- }), [ flipEffect, flipTrigger, onClick ]), handleKeyDown = useCallback((event => {
4834
- "Enter" !== event.key && " " !== event.key || (event.preventDefault(), flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)),
4835
- onClick && onClick(event));
4836
- }), [ flipEffect, flipTrigger, onClick ]), handleMouseEnter = useCallback((() => {
4837
- setIsHovered(!0), elevationEffect && setIsElevated(!0), flipEffect && "hover" === flipTrigger && setIsFlipped(!0);
4838
- }), [ elevationEffect, flipEffect, flipTrigger ]), handleMouseLeave = useCallback((() => {
4839
- setIsHovered(!1), elevationEffect && setIsElevated(!1), flipEffect && "hover" === flipTrigger && setIsFlipped(!1);
4840
- }), [ elevationEffect, flipEffect, flipTrigger ]), handleFocus = useCallback((() => {
4841
- setIsFocused(!0);
4842
- }), []), handleBlur = useCallback((() => {
4843
- setIsFocused(!1);
4844
- }), []), getCardProps = useCallback((() => ({
4845
- className: [ CARD.CLASSES.BASE, isElevated ? elevationClass : "", isFlipped ? CARD.CLASSES.FLIPPED : "", isFocused && focusEffect ? CARD.CLASSES.FOCUSED : "", clickable ? CARD.CLASSES.CLICKABLE : "" ].filter(Boolean).join(" "),
4846
- ref: cardRef,
4847
- tabxwIndex: clickable || flipEffect ? 0 : -1,
4848
- role: clickable ? "button" : void 0,
4849
- onMouseEnter: handleMouseEnter,
4850
- onMouseLeave: handleMouseLeave,
4851
- onFocus: handleFocus,
4852
- onBlur: handleBlur,
4853
- onClick: handleClick,
4854
- onKeyDown: handleKeyDown
4855
- })), [ isElevated, isFlipped, isFocused, elevationClass, focusEffect, clickable, handleMouseEnter, handleMouseLeave, handleFocus, handleBlur, handleClick, handleKeyDown, flipEffect ]);
4856
- return {
4857
- cardRef: cardRef,
4858
- frontRef: frontRef,
4859
- backRef: backRef,
4860
- isFlipped: isFlipped,
4861
- isElevated: isElevated,
4862
- isFocused: isFocused,
4863
- isHovered: isHovered,
4864
- handleClick: handleClick,
4865
- handleKeyDown: handleKeyDown,
4866
- handleMouseEnter: handleMouseEnter,
4867
- handleMouseLeave: handleMouseLeave,
4868
- handleFocus: handleFocus,
4869
- handleBlur: handleBlur,
4870
- getCardProps: getCardProps
4871
- };
4872
- }, ElevationCard = ({elevationClass: elevationClass = "is-elevated", className: className = "", style: style, children: children, onClick: onClick, ...props}) => {
4873
- const {getCardProps: getCardProps} = useCard({
5954
+ const ElevationCard = ({elevationClass: elevationClass = "is-elevated", className: className = "", style: style, children: children, onClick: onClick, ...props}) => {
5955
+ const {getCardProps: getCardProps} = ((options = {}) => {
5956
+ const {elevationEffect: elevationEffect = !1, elevationClass: elevationClass = CARD.CLASSES.ACTIVE, flipEffect: flipEffect = !1, flipTrigger: flipTrigger = "click", focusEffect: focusEffect = !1, clickable: clickable = !1, onClick: onClick} = options, cardRef = useRef(null), frontRef = useRef(null), backRef = useRef(null), [isFlipped, setIsFlipped] = useState(!1), [isElevated, setIsElevated] = useState(!1), [isFocused, setIsFocused] = useState(!1), [isHovered, setIsHovered] = useState(!1), handleClick = useCallback((event => {
5957
+ flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)), onClick && onClick(event);
5958
+ }), [ flipEffect, flipTrigger, onClick ]), handleKeyDown = useCallback((event => {
5959
+ "Enter" !== event.key && " " !== event.key || (event.preventDefault(), flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)),
5960
+ onClick && onClick(event));
5961
+ }), [ flipEffect, flipTrigger, onClick ]), handleMouseEnter = useCallback((() => {
5962
+ setIsHovered(!0), elevationEffect && setIsElevated(!0), flipEffect && "hover" === flipTrigger && setIsFlipped(!0);
5963
+ }), [ elevationEffect, flipEffect, flipTrigger ]), handleMouseLeave = useCallback((() => {
5964
+ setIsHovered(!1), elevationEffect && setIsElevated(!1), flipEffect && "hover" === flipTrigger && setIsFlipped(!1);
5965
+ }), [ elevationEffect, flipEffect, flipTrigger ]), handleFocus = useCallback((() => {
5966
+ setIsFocused(!0);
5967
+ }), []), handleBlur = useCallback((() => {
5968
+ setIsFocused(!1);
5969
+ }), []), getCardProps = useCallback((() => ({
5970
+ className: [ CARD.CLASSES.BASE, isElevated ? elevationClass : "", isFlipped ? CARD.CLASSES.FLIPPED : "", isFocused && focusEffect ? CARD.CLASSES.FOCUSED : "", clickable ? CARD.CLASSES.CLICKABLE : "" ].filter(Boolean).join(" "),
5971
+ ref: cardRef,
5972
+ tabxwIndex: clickable || flipEffect ? 0 : -1,
5973
+ role: clickable ? "button" : void 0,
5974
+ onMouseEnter: handleMouseEnter,
5975
+ onMouseLeave: handleMouseLeave,
5976
+ onFocus: handleFocus,
5977
+ onBlur: handleBlur,
5978
+ onClick: handleClick,
5979
+ onKeyDown: handleKeyDown
5980
+ })), [ isElevated, isFlipped, isFocused, elevationClass, focusEffect, clickable, handleMouseEnter, handleMouseLeave, handleFocus, handleBlur, handleClick, handleKeyDown, flipEffect ]);
5981
+ return {
5982
+ cardRef: cardRef,
5983
+ frontRef: frontRef,
5984
+ backRef: backRef,
5985
+ isFlipped: isFlipped,
5986
+ isElevated: isElevated,
5987
+ isFocused: isFocused,
5988
+ isHovered: isHovered,
5989
+ handleClick: handleClick,
5990
+ handleKeyDown: handleKeyDown,
5991
+ handleMouseEnter: handleMouseEnter,
5992
+ handleMouseLeave: handleMouseLeave,
5993
+ handleFocus: handleFocus,
5994
+ handleBlur: handleBlur,
5995
+ getCardProps: getCardProps
5996
+ };
5997
+ })({
4874
5998
  elevationEffect: !0,
4875
5999
  elevationClass: elevationClass,
4876
6000
  clickable: Boolean(onClick),
@@ -9251,216 +10375,13 @@ function getRowId$1(row, rowKey) {
9251
10375
 
9252
10376
  /**
9253
10377
  * Hook for managing DataTable state and behavior
9254
- */ function useDataTable({data: data = [], columns: columns = [], sortable: sortable = !1, paginated: paginated = !1, pageSize: pageSize = 10, onSort: onSort, initialSortConfig: initialSortConfig, selectionMode: selectionMode = "none", selectedRowIds: controlledSelectedRowIds, onSelectionChange: onSelectionChange, rowKey: rowKey, columnFilters: columnFilters = !1, reorderable: reorderable = !1, onColumnReorder: onColumnReorder, onColumnVisibilityChange: onColumnVisibilityChange}) {
9255
- // Sort state
9256
- const [sortConfig, setSortConfig] = useState(initialSortConfig || null), [currentPage, setCurrentPage] = useState(1), [searchQuery, setSearchQuery] = useState(""), [internalSelectedRowIds, setInternalSelectedRowIds] = useState([]), selectedRowIds = controlledSelectedRowIds ?? internalSelectedRowIds, [columnOrder, setColumnOrder] = useState((() => columns.map((col => col.key)))), [columnVisibility, setColumnVisibility] = useState((() => {
9257
- const visibility = {};
9258
- return columns.forEach((col => {
9259
- visibility[col.key] = !1 !== col.visible;
9260
- })), visibility;
9261
- })), [columnFilterValues, setColumnFilterValues] = useState({});
9262
- // Pagination state
9263
- // Update column order when columns prop changes
9264
- useEffect((() => {
9265
- const newOrder = columns.map((col => col.key)), currentOrderSet = new Set(columnOrder), newOrderSet = new Set(newOrder);
9266
- // Only update if there are actual differences
9267
- newOrder.length === columnOrder.length && newOrder.every((key => currentOrderSet.has(key))) && columnOrder.every((key => newOrderSet.has(key))) || setColumnOrder(newOrder);
9268
- }), [ columns ]),
9269
- // Update column visibility when columns prop changes
9270
- useEffect((() => {
9271
- setColumnVisibility((prev => {
9272
- const updated = {
9273
- ...prev
9274
- };
9275
- return columns.forEach((col => {
9276
- col.key in updated || (updated[col.key] = !1 !== col.visible);
9277
- })), updated;
9278
- }));
9279
- }), [ columns ]);
9280
- // Visible columns based on order and visibility
9281
- const visibleColumns = useMemo((() => columnOrder.map((key => columns.find((col => col.key === key)))).filter((col => void 0 !== col && !1 !== columnVisibility[col.key]))), [ columns, columnOrder, columnVisibility ]), handleSort = useCallback((key => {
9282
- if (!sortable) return;
9283
- let direction = "asc";
9284
- sortConfig && sortConfig.key === key && "asc" === sortConfig.direction && (direction = "desc");
9285
- const newSortConfig = {
9286
- key: key,
9287
- direction: direction
9288
- };
9289
- setSortConfig(newSortConfig), onSort && onSort(newSortConfig);
9290
- }), [ sortable, sortConfig, onSort ]), handlePageChange = useCallback((page => {
9291
- page < 1 || setCurrentPage(page);
9292
- }), []), handleSearch = useCallback((query => {
9293
- setSearchQuery(query), setCurrentPage(1);
9294
- } // Reset to first page when searching
9295
- ), []), handleColumnFilterChange = useCallback(((columnKey, value) => {
9296
- setColumnFilterValues((prev => ({
9297
- ...prev,
9298
- [columnKey]: value
9299
- }))), setCurrentPage(1);
9300
- } // Reset to first page when filtering
9301
- ), []), clearColumnFilters = useCallback((() => {
9302
- setColumnFilterValues({}), setCurrentPage(1);
9303
- }), []), activeColumnFilters = useMemo((() => columnFilters ? Object.entries(columnFilterValues).filter((([, value]) => null != value && "" !== value)).map((([columnKey, value]) => {
9304
- const column = columns.find((col => col.key === columnKey));
9305
- return column && column.filterable ? {
9306
- key: columnKey,
9307
- value: value,
9308
- lowercaseValue: "string" == typeof value ? value.toLowerCase() : String(value).toLowerCase(),
9309
- column: column
9310
- } : null;
9311
- })).filter((f => null !== f)) : []), [ columnFilters, columnFilterValues, columns ]), filteredData = useMemo((() => {
9312
- if (!searchQuery && 0 === activeColumnFilters.length) return data;
9313
- const lowercaseQuery = searchQuery ? searchQuery.toLowerCase() : "";
9314
- return data.filter((row => {
9315
- // Apply global search
9316
- if (searchQuery && !visibleColumns.some((column => {
9317
- var _context;
9318
- const value = row[column.key];
9319
- return null != value && _includesInstanceProperty(_context = String(value).toLowerCase()).call(_context, lowercaseQuery);
9320
- }))) return !1;
9321
- // Apply column-specific filters
9322
- for (let i = 0; i < activeColumnFilters.length; i++) {
9323
- const filter = activeColumnFilters[i];
9324
- if (!filter) continue;
9325
- const {key: key, value: value, lowercaseValue: lowercaseValue, column: column} = filter, cellValue = row[key];
9326
- if (null == cellValue) return !1;
9327
- // Use custom filter function if provided
9328
- var _context2;
9329
- // Default text filter
9330
- if (column.filterFunction) {
9331
- if (!column.filterFunction(cellValue, value)) return !1;
9332
- } else if (!_includesInstanceProperty(_context2 = String(cellValue).toLowerCase()).call(_context2, lowercaseValue)) return !1;
9333
- }
9334
- return !0;
9335
- }));
9336
- }), [ data, visibleColumns, searchQuery, activeColumnFilters ]), sortedData = useMemo((() => sortConfig && sortable ? [ ...filteredData ].sort(((a, b) => {
9337
- const aValue = a[sortConfig.key], bValue = b[sortConfig.key];
9338
- return null == aValue ? "asc" === sortConfig.direction ? -1 : 1 : null == bValue ? "asc" === sortConfig.direction ? 1 : -1 : "string" == typeof aValue && "string" == typeof bValue ? "asc" === sortConfig.direction ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue) : "asc" === sortConfig.direction ? aValue > bValue ? 1 : -1 : aValue > bValue ? -1 : 1;
9339
- })) : filteredData), [ filteredData, sortConfig, sortable ]), paginatedData = useMemo((() => {
9340
- if (!paginated) return sortedData;
9341
- const startIndex = (currentPage - 1) * pageSize;
9342
- return sortedData.slice(startIndex, startIndex + pageSize);
9343
- }), [ sortedData, paginated, currentPage, pageSize ]), totalPages = useMemo((() => paginated ? Math.max(1, Math.ceil(sortedData.length / pageSize)) : 1), [ sortedData.length, paginated, pageSize ]), selectedRows = useMemo((() => "none" === selectionMode || 0 === selectedRowIds.length ? [] : sortedData.filter((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey))))), [ sortedData, selectedRowIds, selectionMode, rowKey ]), handleRowSelect = useCallback(((rowId, selected) => {
9344
- if ("none" === selectionMode) return;
9345
- let newSelectedIds;
9346
- if (newSelectedIds = "single" === selectionMode ? selected ? [ rowId ] : [] :
9347
- // multiple
9348
- selected ? [ ...selectedRowIds, rowId ] : selectedRowIds.filter((id => id !== rowId)),
9349
- controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
9350
- const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
9351
- onSelectionChange(selectedRowsData, newSelectedIds);
9352
- }
9353
- }), [ selectionMode, selectedRowIds, controlledSelectedRowIds, onSelectionChange, sortedData, rowKey ]), handleSelectAll = useCallback((selected => {
9354
- if ("multiple" !== selectionMode) return;
9355
- const newSelectedIds = selected ? paginatedData.map((row => getRowId$1(row, rowKey))) : [];
9356
- if (controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
9357
- const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
9358
- onSelectionChange(selectedRowsData, newSelectedIds);
9359
- }
9360
- }), [ selectionMode, paginatedData, sortedData, controlledSelectedRowIds, onSelectionChange, rowKey ]), isAllSelected = useMemo((() => "multiple" === selectionMode && 0 !== paginatedData.length && paginatedData.every((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey))))), [ selectionMode, paginatedData, selectedRowIds, rowKey ]), isIndeterminate = useMemo((() => {
9361
- if ("multiple" !== selectionMode || 0 === paginatedData.length) return !1;
9362
- const selectedCount = paginatedData.filter((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey)))).length;
9363
- return selectedCount > 0 && selectedCount < paginatedData.length;
9364
- }), [ selectionMode, paginatedData, selectedRowIds, rowKey ]), handleColumnVisibilityToggle = useCallback((columnKey => {
9365
- setColumnVisibility((prev => {
9366
- const updated = {
9367
- ...prev,
9368
- [columnKey]: !prev[columnKey]
9369
- };
9370
- if (onColumnVisibilityChange) {
9371
- const visibleKeys = Object.entries(updated).filter((([, visible]) => visible)).map((([key]) => key));
9372
- onColumnVisibilityChange(visibleKeys);
9373
- }
9374
- return updated;
9375
- }));
9376
- }), [ onColumnVisibilityChange ]);
9377
- // Handle sorting
9378
- // Handle column reorder
9379
- return useCallback(((fromIndex, toIndex) => {
9380
- const newOrder = [ ...columnOrder ], [removed] = newOrder.splice(fromIndex, 1);
9381
- removed && (newOrder.splice(toIndex, 0, removed), setColumnOrder(newOrder), onColumnReorder && onColumnReorder(newOrder));
9382
- }), [ columnOrder, onColumnReorder ]),
9383
- // Reset to first page when data changes
9384
- useEffect((() => {
9385
- setCurrentPage(1);
9386
- }), [ data ]),
9387
- // Reset current page if it's out of bounds
9388
- useEffect((() => {
9389
- currentPage > totalPages && totalPages > 0 && setCurrentPage(Math.max(1, totalPages));
9390
- }), [ currentPage, totalPages ]), {
9391
- displayData: paginatedData,
9392
- sortConfig: sortConfig,
9393
- currentPage: currentPage,
9394
- totalPages: totalPages,
9395
- handleSort: handleSort,
9396
- handlePageChange: handlePageChange,
9397
- handleSearch: handleSearch,
9398
- selectedRowIds: selectedRowIds,
9399
- selectedRows: selectedRows,
9400
- handleRowSelect: handleRowSelect,
9401
- handleSelectAll: handleSelectAll,
9402
- isAllSelected: isAllSelected,
9403
- isIndeterminate: isIndeterminate,
9404
- columnOrder: columnOrder,
9405
- visibleColumns: visibleColumns,
9406
- columnVisibility: columnVisibility,
9407
- handleColumnVisibilityToggle: handleColumnVisibilityToggle,
9408
- columnFilterValues: columnFilterValues,
9409
- handleColumnFilterChange: handleColumnFilterChange,
9410
- clearColumnFilters: clearColumnFilters
9411
- };
9412
- }
9413
-
9414
- Countdown.displayName = "Countdown";
10378
+ */ Countdown.displayName = "Countdown";
9415
10379
 
9416
- const DOTS = "...", range = (start, end) => {
10380
+ const range = (start, end) => {
9417
10381
  const length = end - start + 1;
9418
10382
  return Array.from({
9419
10383
  length: length
9420
10384
  }, ((_, idx) => idx + start));
9421
- }, usePagination = ({currentPage: currentPage, totalPages: totalPages, siblingCount: siblingCount = 1, onPageChange: onPageChange}) => {
9422
- const paginationRange = useMemo((() => {
9423
- // siblingCount + firstPage + lastPage + currentPage + 2*DOTS
9424
- /*
9425
- Case 1: If the number of pages is less than the page numbers we want to show in our
9426
- paginationComponent, we return the range [1..totalPages]
9427
- */
9428
- if (siblingCount + 5 >= totalPages) return range(1, totalPages);
9429
- const leftSiblingIndex = Math.max(currentPage - siblingCount, 1), rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages), shouldShowLeftDots = leftSiblingIndex > 2, shouldShowRightDots = rightSiblingIndex < totalPages - 2, lastPageIndex = totalPages;
9430
- /*
9431
- Case 2: No left dots to show, but rights dots to be shown
9432
- */
9433
- return !shouldShowLeftDots && shouldShowRightDots ? [ ...range(1, 3 + 2 * siblingCount), "...", totalPages ] :
9434
- /*
9435
- Case 3: No right dots to show, but left dots to be shown
9436
- */
9437
- shouldShowLeftDots && !shouldShowRightDots ? [ 1, "...", ...range(totalPages - (3 + 2 * siblingCount) + 1, totalPages) ] :
9438
- /*
9439
- Case 4: Both left and right dots to be shown
9440
- */
9441
- shouldShowLeftDots && shouldShowRightDots ? [ 1, "...", ...range(leftSiblingIndex, rightSiblingIndex), "...", lastPageIndex ] : [];
9442
- }), [ totalPages, siblingCount, currentPage ]), goToPage = page => {
9443
- page >= 1 && page <= totalPages && page !== currentPage && onPageChange(page);
9444
- };
9445
- return {
9446
- paginationRange: paginationRange,
9447
- currentPage: currentPage,
9448
- totalPages: totalPages,
9449
- goToPage: goToPage,
9450
- nextPage: () => {
9451
- goToPage(currentPage + 1);
9452
- },
9453
- prevPage: () => {
9454
- goToPage(currentPage - 1);
9455
- },
9456
- firstPage: () => {
9457
- goToPage(1);
9458
- },
9459
- lastPage: () => {
9460
- goToPage(totalPages);
9461
- },
9462
- DOTS: "..."
9463
- };
9464
10385
  }, PaginationNavButton = memo((({type: type, onClick: onClick, disabled: disabled, label: label, iconName: iconName}) => jsx("li", {
9465
10386
  className: `c-pagination__item c-pagination__item--${type} ${disabled ? "is-disabled" : ""}`,
9466
10387
  "aria-disabled": disabled,
@@ -9477,7 +10398,50 @@ const DOTS = "...", range = (start, end) => {
9477
10398
  })
9478
10399
  })
9479
10400
  }))), Pagination = memo((({currentPage: currentPage = PAGINATION_DEFAULTS.currentPage, totalPages: totalPages = PAGINATION_DEFAULTS.totalPages, onPageChange: onPageChange, siblingCount: siblingCount = PAGINATION_DEFAULTS.siblingCount, showFirstLastButtons: showFirstLastButtons = PAGINATION_DEFAULTS.showFirstLastButtons, showPrevNextButtons: showPrevNextButtons = PAGINATION_DEFAULTS.showPrevNextButtons, showSearch: showSearch = !1, searchPlaceholder: searchPlaceholder = "Go to page", size: size = PAGINATION_DEFAULTS.size, className: className = "", style: style, "aria-label": ariaLabel = "Pagination", glass: glass}) => {
9480
- const {paginationRange: paginationRange, goToPage: goToPage, nextPage: nextPage, prevPage: prevPage, firstPage: firstPage, lastPage: lastPage} = usePagination({
10401
+ const {paginationRange: paginationRange, goToPage: goToPage, nextPage: nextPage, prevPage: prevPage, firstPage: firstPage, lastPage: lastPage} = (({currentPage: currentPage, totalPages: totalPages, siblingCount: siblingCount = 1, onPageChange: onPageChange}) => {
10402
+ const paginationRange = useMemo((() => {
10403
+ // siblingCount + firstPage + lastPage + currentPage + 2*DOTS
10404
+ /*
10405
+ Case 1: If the number of pages is less than the page numbers we want to show in our
10406
+ paginationComponent, we return the range [1..totalPages]
10407
+ */
10408
+ if (siblingCount + 5 >= totalPages) return range(1, totalPages);
10409
+ const leftSiblingIndex = Math.max(currentPage - siblingCount, 1), rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages), shouldShowLeftDots = leftSiblingIndex > 2, shouldShowRightDots = rightSiblingIndex < totalPages - 2, lastPageIndex = totalPages;
10410
+ /*
10411
+ Case 2: No left dots to show, but rights dots to be shown
10412
+ */
10413
+ return !shouldShowLeftDots && shouldShowRightDots ? [ ...range(1, 3 + 2 * siblingCount), "...", totalPages ] :
10414
+ /*
10415
+ Case 3: No right dots to show, but left dots to be shown
10416
+ */
10417
+ shouldShowLeftDots && !shouldShowRightDots ? [ 1, "...", ...range(totalPages - (3 + 2 * siblingCount) + 1, totalPages) ] :
10418
+ /*
10419
+ Case 4: Both left and right dots to be shown
10420
+ */
10421
+ shouldShowLeftDots && shouldShowRightDots ? [ 1, "...", ...range(leftSiblingIndex, rightSiblingIndex), "...", lastPageIndex ] : [];
10422
+ }), [ totalPages, siblingCount, currentPage ]), goToPage = page => {
10423
+ page >= 1 && page <= totalPages && page !== currentPage && onPageChange(page);
10424
+ };
10425
+ return {
10426
+ paginationRange: paginationRange,
10427
+ currentPage: currentPage,
10428
+ totalPages: totalPages,
10429
+ goToPage: goToPage,
10430
+ nextPage: () => {
10431
+ goToPage(currentPage + 1);
10432
+ },
10433
+ prevPage: () => {
10434
+ goToPage(currentPage - 1);
10435
+ },
10436
+ firstPage: () => {
10437
+ goToPage(1);
10438
+ },
10439
+ lastPage: () => {
10440
+ goToPage(totalPages);
10441
+ },
10442
+ DOTS: "..."
10443
+ };
10444
+ })({
9481
10445
  currentPage: currentPage,
9482
10446
  totalPages: totalPages,
9483
10447
  siblingCount: siblingCount,
@@ -9689,14 +10653,13 @@ const DropdownContext = createContext({
9689
10653
  id: "",
9690
10654
  trigger: "click"
9691
10655
  }), DropdownMenu = forwardRef((({children: children, className: className = "", ...props}, ref) => {
9692
- const {glass: glass} = useContext(DropdownStyleContext);
9693
- // We need to access glass prop here?
9694
- // Wait, the original code wrapped <ul> in Context Provider.
9695
- // And applied glass wrapper around <ul>.
9696
- // If we use Compound Component, DropdownMenu should be the list.
9697
- return jsx("ul", {
10656
+ const {glass: glass} = useContext(DropdownStyleContext), {id: id} = useContext(DropdownContext);
10657
+ return jsx("ul", {
9698
10658
  ref: ref,
10659
+ id: `${id}-menu`,
9699
10660
  className: `c-dropdown__menu ${glass ? "c-dropdown__menu--glass" : ""} ${className}`.trim(),
10661
+ role: "menu",
10662
+ "aria-labelledby": `${id}-trigger`,
9700
10663
  ...props,
9701
10664
  children: children
9702
10665
  });
@@ -9705,14 +10668,23 @@ const DropdownContext = createContext({
9705
10668
  // Compound Components
9706
10669
  DropdownMenu.displayName = "DropdownMenu";
9707
10670
 
9708
- const DropdownTrigger = forwardRef((({children: children, className: className = "", onClick: onClick, onKeyDown: onKeyDown, ...props}, ref) => jsx("div", {
9709
- ref: ref,
9710
- className: `c-dropdown__toggle ${className}`.trim(),
9711
- onClick: onClick,
9712
- onKeyDown: onKeyDown,
9713
- ...props,
9714
- children: children
9715
- })));
10671
+ const DropdownTrigger = forwardRef((({children: children, className: className = "", onClick: onClick, onKeyDown: onKeyDown, ...props}, ref) => {
10672
+ const {isOpen: isOpen, id: id} = useContext(DropdownContext);
10673
+ return jsx("div", {
10674
+ ref: ref,
10675
+ id: `${id}-trigger`,
10676
+ className: `c-dropdown__toggle ${className}`.trim(),
10677
+ onClick: onClick,
10678
+ onKeyDown: onKeyDown,
10679
+ "aria-haspopup": "true",
10680
+ "aria-expanded": isOpen,
10681
+ "aria-controls": `${id}-menu`,
10682
+ tabIndex: 0,
10683
+ role: "button",
10684
+ ...props,
10685
+ children: children
10686
+ });
10687
+ }));
9716
10688
 
9717
10689
  DropdownTrigger.displayName = "DropdownTrigger";
9718
10690
 
@@ -9952,7 +10924,165 @@ Dropdown.Item = DropdownItem, Dropdown.Divider = DropdownDivider, Dropdown.Heade
9952
10924
  * ```
9953
10925
  */
9954
10926
  const DataTable = memo((({data: data, columns: columns, className: className, style: style, sortable: sortable = !1, filterable: filterable = !1, paginated: paginated = !1, pageSize: pageSize = 10, striped: striped = !1, bordered: bordered = !1, dense: dense = !1, loading: loading = !1, emptyMessage: emptyMessage = "No data available", onRowClick: onRowClick, onSort: onSort, selectionMode: selectionMode = "none", selectedRowIds: controlledSelectedRowIds, onSelectionChange: onSelectionChange, rowKey: rowKey, resizable: resizable = !1, reorderable: reorderable = !1, onColumnReorder: onColumnReorder, showColumnVisibility: showColumnVisibility = !1, onColumnVisibilityChange: onColumnVisibilityChange, stickyHeader: stickyHeader = !1, stickyHeaderOffset: stickyHeaderOffset = "0px", virtualScrolling: virtualScrolling = !1, estimatedRowHeight: estimatedRowHeight = 50, overscan: overscan = 5, exportable: exportable = !1, exportFormats: exportFormats = [ "csv", "excel", "json" ], exportFilename: exportFilename = "data-table", onExport: onExport, columnFilters: columnFilters = !1, ...props}) => {
9955
- const tableRef = useRef(null), headerRef = useRef(null), [resizingColumn, setResizingColumn] = useState(null), [columnWidths, setColumnWidths] = useState({}), [dragStartIndex, setDragStartIndex] = useState(null), [dragOverIndex, setDragOverIndex] = useState(null), {displayData: displayData, sortConfig: sortConfig, currentPage: currentPage, totalPages: totalPages, handleSort: handleSort, handlePageChange: handlePageChange, handleSearch: handleSearch, selectedRowIds: selectedRowIds, selectedRows: selectedRows, handleRowSelect: handleRowSelect, handleSelectAll: handleSelectAll, isAllSelected: isAllSelected, isIndeterminate: isIndeterminate, visibleColumns: visibleColumns, columnVisibility: columnVisibility, handleColumnVisibilityToggle: handleColumnVisibilityToggle, columnFilterValues: columnFilterValues, handleColumnFilterChange: handleColumnFilterChange, clearColumnFilters: clearColumnFilters} = useDataTable({
10927
+ const tableRef = useRef(null), headerRef = useRef(null), [resizingColumn, setResizingColumn] = useState(null), [columnWidths, setColumnWidths] = useState({}), [dragStartIndex, setDragStartIndex] = useState(null), [dragOverIndex, setDragOverIndex] = useState(null), {displayData: displayData, sortConfig: sortConfig, currentPage: currentPage, totalPages: totalPages, handleSort: handleSort, handlePageChange: handlePageChange, handleSearch: handleSearch, selectedRowIds: selectedRowIds, selectedRows: selectedRows, handleRowSelect: handleRowSelect, handleSelectAll: handleSelectAll, isAllSelected: isAllSelected, isIndeterminate: isIndeterminate, visibleColumns: visibleColumns, columnVisibility: columnVisibility, handleColumnVisibilityToggle: handleColumnVisibilityToggle, columnFilterValues: columnFilterValues, handleColumnFilterChange: handleColumnFilterChange, clearColumnFilters: clearColumnFilters} = function({data: data = [], columns: columns = [], sortable: sortable = !1, paginated: paginated = !1, pageSize: pageSize = 10, onSort: onSort, initialSortConfig: initialSortConfig, selectionMode: selectionMode = "none", selectedRowIds: controlledSelectedRowIds, onSelectionChange: onSelectionChange, rowKey: rowKey, columnFilters: columnFilters = !1, reorderable: reorderable = !1, onColumnReorder: onColumnReorder, onColumnVisibilityChange: onColumnVisibilityChange}) {
10928
+ // Sort state
10929
+ const [sortConfig, setSortConfig] = useState(initialSortConfig || null), [currentPage, setCurrentPage] = useState(1), [searchQuery, setSearchQuery] = useState(""), [internalSelectedRowIds, setInternalSelectedRowIds] = useState([]), selectedRowIds = controlledSelectedRowIds ?? internalSelectedRowIds, [columnOrder, setColumnOrder] = useState((() => columns.map((col => col.key)))), [columnVisibility, setColumnVisibility] = useState((() => {
10930
+ const visibility = {};
10931
+ return columns.forEach((col => {
10932
+ visibility[col.key] = !1 !== col.visible;
10933
+ })), visibility;
10934
+ })), [columnFilterValues, setColumnFilterValues] = useState({});
10935
+ // Pagination state
10936
+ // Update column order when columns prop changes
10937
+ useEffect((() => {
10938
+ const newOrder = columns.map((col => col.key)), currentOrderSet = new Set(columnOrder), newOrderSet = new Set(newOrder);
10939
+ // Only update if there are actual differences
10940
+ newOrder.length === columnOrder.length && newOrder.every((key => currentOrderSet.has(key))) && columnOrder.every((key => newOrderSet.has(key))) || setColumnOrder(newOrder);
10941
+ }), [ columns ]),
10942
+ // Update column visibility when columns prop changes
10943
+ useEffect((() => {
10944
+ setColumnVisibility((prev => {
10945
+ const updated = {
10946
+ ...prev
10947
+ };
10948
+ return columns.forEach((col => {
10949
+ col.key in updated || (updated[col.key] = !1 !== col.visible);
10950
+ })), updated;
10951
+ }));
10952
+ }), [ columns ]);
10953
+ // Visible columns based on order and visibility
10954
+ const visibleColumns = useMemo((() => columnOrder.map((key => columns.find((col => col.key === key)))).filter((col => void 0 !== col && !1 !== columnVisibility[col.key]))), [ columns, columnOrder, columnVisibility ]), handleSort = useCallback((key => {
10955
+ if (!sortable) return;
10956
+ let direction = "asc";
10957
+ sortConfig && sortConfig.key === key && "asc" === sortConfig.direction && (direction = "desc");
10958
+ const newSortConfig = {
10959
+ key: key,
10960
+ direction: direction
10961
+ };
10962
+ setSortConfig(newSortConfig), onSort && onSort(newSortConfig);
10963
+ }), [ sortable, sortConfig, onSort ]), handlePageChange = useCallback((page => {
10964
+ page < 1 || setCurrentPage(page);
10965
+ }), []), handleSearch = useCallback((query => {
10966
+ setSearchQuery(query), setCurrentPage(1);
10967
+ } // Reset to first page when searching
10968
+ ), []), handleColumnFilterChange = useCallback(((columnKey, value) => {
10969
+ setColumnFilterValues((prev => ({
10970
+ ...prev,
10971
+ [columnKey]: value
10972
+ }))), setCurrentPage(1);
10973
+ } // Reset to first page when filtering
10974
+ ), []), clearColumnFilters = useCallback((() => {
10975
+ setColumnFilterValues({}), setCurrentPage(1);
10976
+ }), []), activeColumnFilters = useMemo((() => columnFilters ? Object.entries(columnFilterValues).filter((([, value]) => null != value && "" !== value)).map((([columnKey, value]) => {
10977
+ const column = columns.find((col => col.key === columnKey));
10978
+ return column && column.filterable ? {
10979
+ key: columnKey,
10980
+ value: value,
10981
+ lowercaseValue: "string" == typeof value ? value.toLowerCase() : String(value).toLowerCase(),
10982
+ column: column
10983
+ } : null;
10984
+ })).filter((f => null !== f)) : []), [ columnFilters, columnFilterValues, columns ]), filteredData = useMemo((() => {
10985
+ if (!searchQuery && 0 === activeColumnFilters.length) return data;
10986
+ const lowercaseQuery = searchQuery ? searchQuery.toLowerCase() : "";
10987
+ return data.filter((row => {
10988
+ // Apply global search
10989
+ if (searchQuery && !visibleColumns.some((column => {
10990
+ var _context;
10991
+ const value = row[column.key];
10992
+ return null != value && _includesInstanceProperty(_context = String(value).toLowerCase()).call(_context, lowercaseQuery);
10993
+ }))) return !1;
10994
+ // Apply column-specific filters
10995
+ for (let i = 0; i < activeColumnFilters.length; i++) {
10996
+ const filter = activeColumnFilters[i];
10997
+ if (!filter) continue;
10998
+ const {key: key, value: value, lowercaseValue: lowercaseValue, column: column} = filter, cellValue = row[key];
10999
+ if (null == cellValue) return !1;
11000
+ // Use custom filter function if provided
11001
+ var _context2;
11002
+ // Default text filter
11003
+ if (column.filterFunction) {
11004
+ if (!column.filterFunction(cellValue, value)) return !1;
11005
+ } else if (!_includesInstanceProperty(_context2 = String(cellValue).toLowerCase()).call(_context2, lowercaseValue)) return !1;
11006
+ }
11007
+ return !0;
11008
+ }));
11009
+ }), [ data, visibleColumns, searchQuery, activeColumnFilters ]), sortedData = useMemo((() => sortConfig && sortable ? [ ...filteredData ].sort(((a, b) => {
11010
+ const aValue = a[sortConfig.key], bValue = b[sortConfig.key];
11011
+ return null == aValue ? "asc" === sortConfig.direction ? -1 : 1 : null == bValue ? "asc" === sortConfig.direction ? 1 : -1 : "string" == typeof aValue && "string" == typeof bValue ? "asc" === sortConfig.direction ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue) : "asc" === sortConfig.direction ? aValue > bValue ? 1 : -1 : aValue > bValue ? -1 : 1;
11012
+ })) : filteredData), [ filteredData, sortConfig, sortable ]), paginatedData = useMemo((() => {
11013
+ if (!paginated) return sortedData;
11014
+ const startIndex = (currentPage - 1) * pageSize;
11015
+ return sortedData.slice(startIndex, startIndex + pageSize);
11016
+ }), [ sortedData, paginated, currentPage, pageSize ]), totalPages = useMemo((() => paginated ? Math.max(1, Math.ceil(sortedData.length / pageSize)) : 1), [ sortedData.length, paginated, pageSize ]), selectedRows = useMemo((() => "none" === selectionMode || 0 === selectedRowIds.length ? [] : sortedData.filter((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey))))), [ sortedData, selectedRowIds, selectionMode, rowKey ]), handleRowSelect = useCallback(((rowId, selected) => {
11017
+ if ("none" === selectionMode) return;
11018
+ let newSelectedIds;
11019
+ if (newSelectedIds = "single" === selectionMode ? selected ? [ rowId ] : [] :
11020
+ // multiple
11021
+ selected ? [ ...selectedRowIds, rowId ] : selectedRowIds.filter((id => id !== rowId)),
11022
+ controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
11023
+ const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
11024
+ onSelectionChange(selectedRowsData, newSelectedIds);
11025
+ }
11026
+ }), [ selectionMode, selectedRowIds, controlledSelectedRowIds, onSelectionChange, sortedData, rowKey ]), handleSelectAll = useCallback((selected => {
11027
+ if ("multiple" !== selectionMode) return;
11028
+ const newSelectedIds = selected ? paginatedData.map((row => getRowId$1(row, rowKey))) : [];
11029
+ if (controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
11030
+ const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
11031
+ onSelectionChange(selectedRowsData, newSelectedIds);
11032
+ }
11033
+ }), [ selectionMode, paginatedData, sortedData, controlledSelectedRowIds, onSelectionChange, rowKey ]), isAllSelected = useMemo((() => "multiple" === selectionMode && 0 !== paginatedData.length && paginatedData.every((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey))))), [ selectionMode, paginatedData, selectedRowIds, rowKey ]), isIndeterminate = useMemo((() => {
11034
+ if ("multiple" !== selectionMode || 0 === paginatedData.length) return !1;
11035
+ const selectedCount = paginatedData.filter((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey)))).length;
11036
+ return selectedCount > 0 && selectedCount < paginatedData.length;
11037
+ }), [ selectionMode, paginatedData, selectedRowIds, rowKey ]), handleColumnVisibilityToggle = useCallback((columnKey => {
11038
+ setColumnVisibility((prev => {
11039
+ const updated = {
11040
+ ...prev,
11041
+ [columnKey]: !prev[columnKey]
11042
+ };
11043
+ if (onColumnVisibilityChange) {
11044
+ const visibleKeys = Object.entries(updated).filter((([, visible]) => visible)).map((([key]) => key));
11045
+ onColumnVisibilityChange(visibleKeys);
11046
+ }
11047
+ return updated;
11048
+ }));
11049
+ }), [ onColumnVisibilityChange ]);
11050
+ // Handle sorting
11051
+ // Handle column reorder
11052
+ return useCallback(((fromIndex, toIndex) => {
11053
+ const newOrder = [ ...columnOrder ], [removed] = newOrder.splice(fromIndex, 1);
11054
+ removed && (newOrder.splice(toIndex, 0, removed), setColumnOrder(newOrder), onColumnReorder && onColumnReorder(newOrder));
11055
+ }), [ columnOrder, onColumnReorder ]),
11056
+ // Reset to first page when data changes
11057
+ useEffect((() => {
11058
+ setCurrentPage(1);
11059
+ }), [ data ]),
11060
+ // Reset current page if it's out of bounds
11061
+ useEffect((() => {
11062
+ currentPage > totalPages && totalPages > 0 && setCurrentPage(Math.max(1, totalPages));
11063
+ }), [ currentPage, totalPages ]), {
11064
+ displayData: paginatedData,
11065
+ sortConfig: sortConfig,
11066
+ currentPage: currentPage,
11067
+ totalPages: totalPages,
11068
+ handleSort: handleSort,
11069
+ handlePageChange: handlePageChange,
11070
+ handleSearch: handleSearch,
11071
+ selectedRowIds: selectedRowIds,
11072
+ selectedRows: selectedRows,
11073
+ handleRowSelect: handleRowSelect,
11074
+ handleSelectAll: handleSelectAll,
11075
+ isAllSelected: isAllSelected,
11076
+ isIndeterminate: isIndeterminate,
11077
+ columnOrder: columnOrder,
11078
+ visibleColumns: visibleColumns,
11079
+ columnVisibility: columnVisibility,
11080
+ handleColumnVisibilityToggle: handleColumnVisibilityToggle,
11081
+ columnFilterValues: columnFilterValues,
11082
+ handleColumnFilterChange: handleColumnFilterChange,
11083
+ clearColumnFilters: clearColumnFilters
11084
+ };
11085
+ }({
9956
11086
  data: data,
9957
11087
  columns: columns,
9958
11088
  sortable: sortable,
@@ -12306,297 +13436,6 @@ function useTodo(initialProps) {
12306
13436
  };
12307
13437
  }
12308
13438
 
12309
- /**
12310
- * Breadcrumb state and functionality
12311
- * @param initialOptions - Initial breadcrumb options
12312
- * @returns Breadcrumb state and methods
12313
- */ function useBreadcrumb(initialOptions) {
12314
- return {
12315
- defaultOptions: {
12316
- items: [],
12317
- divider: BREADCRUMB.DEFAULTS.DIVIDER,
12318
- className: "",
12319
- "aria-label": "Breadcrumb",
12320
- ...initialOptions
12321
- },
12322
- generateBreadcrumbClass: options => {
12323
- const {className: className = ""} = options;
12324
- return [ BREADCRUMB.CLASSES.BASE, className ].filter(Boolean).join(" ").trim();
12325
- },
12326
- generateItemClass: (item, isLast) => [ BREADCRUMB.CLASSES.ITEM, item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : "" ].filter(Boolean).join(" ").trim(),
12327
- isItemLink: (item, isLast) => Boolean(item.href && !item.active && !isLast),
12328
- parseItemsFromJson: jsonString => {
12329
- try {
12330
- return JSON.parse(jsonString);
12331
- } catch (error) {
12332
- return console.error("Error parsing breadcrumb items:", error), [];
12333
- }
12334
- }
12335
- };
12336
- }
12337
-
12338
- /**
12339
- * Hook for managing modal state
12340
- */ function useModal$1({isOpen: isOpenProp, onOpenChange: onOpenChange, onOpen: onOpen, onClose: onClose} = {}) {
12341
- // For uncontrolled usage
12342
- const [isOpenState, setIsOpenState] = useState(!1), isControlled = void 0 !== isOpenProp, isOpen = isControlled ? !!isOpenProp : isOpenState;
12343
- // Determine if we're in controlled or uncontrolled mode
12344
- // Update internal state when prop changes (for controlled mode)
12345
- useEffect((() => {
12346
- isControlled && setIsOpenState(!!isOpenProp);
12347
- }), [ isOpenProp, isControlled ]);
12348
- const updateOpen = useCallback((nextIsOpen => {
12349
- // For uncontrolled mode, update internal state
12350
- isControlled || setIsOpenState(nextIsOpen),
12351
- // Call the change handler in either mode
12352
- onOpenChange && onOpenChange(nextIsOpen),
12353
- // Call the specific handler
12354
- nextIsOpen && onOpen ? onOpen() : !nextIsOpen && onClose && onClose();
12355
- }), [ isControlled, onOpenChange, onOpen, onClose ]), open = useCallback((() => {
12356
- updateOpen(!0);
12357
- }), [ updateOpen ]), close = useCallback((() => {
12358
- updateOpen(!1);
12359
- }), [ updateOpen ]), toggle = useCallback((() => {
12360
- updateOpen(!isOpen);
12361
- }), [ isOpen, updateOpen ]);
12362
- return {
12363
- isOpen: isOpen,
12364
- open: open,
12365
- close: close,
12366
- toggle: toggle
12367
- };
12368
- }
12369
-
12370
- function useSlider(options) {
12371
- const {slides: rawSlides, slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, autoplay: autoplay, onSlideChange: onSlideChange} = options, slides = Array.isArray(rawSlides) ? rawSlides : [], containerRef = useRef(null), wrapperRef = useRef(null), repositioningRef = useRef(!1), autoplayRef = useRef(null), [autoplayRunning, setAutoplayRunning] = useState(!1), sliderStateRef = useRef({
12372
- isTransitioning: !1,
12373
- loop: loop,
12374
- slides: slides,
12375
- slidesToShow: slidesToShow,
12376
- speed: speed,
12377
- onSlideChange: onSlideChange
12378
- }), [realIndex, setRealIndex] = useState(initialSlide), [internalIndex, setInternalIndex] = useState(0), [isTransitioning, setIsTransitioning] = useState(!1), [containerSize, setContainerSize] = useState(0), [touching, setTouching] = useState(!1), [touchStart, setTouchStart] = useState(0), [dragOffset, setDragOffset] = useState(0), slideWidth = useMemo((() => 0 === containerSize ? 0 : (containerSize - spaceBetween * (slidesToShow - 1)) / slidesToShow), [ containerSize, spaceBetween, slidesToShow ]), allSlides = useMemo((() => loop && 0 !== slides.length ? [ ...slides.map(((slide, i) => ({
12379
- ...slide,
12380
- id: `set1-${slide.id || i}`
12381
- }))), ...slides.map(((slide, i) => ({
12382
- ...slide,
12383
- id: `set2-${slide.id || i}`
12384
- }))), ...slides.map(((slide, i) => ({
12385
- ...slide,
12386
- id: `set3-${slide.id || i}`
12387
- }))) ] : slides), [ slides, loop ]), loopedSlides = slides.length, translateValue = useMemo((() => 0 === slideWidth ? 0 : -internalIndex * slideWidth + dragOffset), [ slideWidth, internalIndex, dragOffset ]);
12388
- // Update the ref whenever the relevant state/props change
12389
- useEffect((() => {
12390
- sliderStateRef.current = {
12391
- isTransitioning: isTransitioning,
12392
- loop: loop,
12393
- slides: slides,
12394
- slidesToShow: slidesToShow,
12395
- speed: speed,
12396
- onSlideChange: onSlideChange
12397
- };
12398
- }), [ isTransitioning, loop, slides, slidesToShow, speed, onSlideChange ]),
12399
- // Autoplay effect
12400
- useEffect((() => {
12401
- if (!autoplay) return autoplayRef.current && (clearInterval(autoplayRef.current),
12402
- autoplayRef.current = null), void setAutoplayRunning(!1);
12403
- const autoplayParams = "boolean" == typeof autoplay ? {
12404
- delay: 3e3
12405
- } : autoplay, {delay: delay = 3e3, pauseOnMouseEnter: pauseOnMouseEnter = !1, disableOnInteraction: disableOnInteraction = !1, reverseDirection: reverseDirection = !1} = autoplayParams;
12406
- // Clear any existing interval
12407
- autoplayRef.current && clearInterval(autoplayRef.current),
12408
- // Create new interval
12409
- autoplayRef.current = setInterval((() => {
12410
- // Use ref to get the latest state without resetting the interval
12411
- const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
12412
- // We need to use a functional update to get the latest values
12413
- setRealIndex((prevRealIndex => {
12414
- if (currentIsTransitioning) return prevRealIndex;
12415
- // Stop autoplay on interaction if disableOnInteraction is true
12416
- let nextIndex;
12417
- // Trigger the slide change
12418
- if (disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12419
- autoplayRef.current = null, setAutoplayRunning(!1)), nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
12420
- reverseDirection) {
12421
- // For reverse direction, we would go to previous slide
12422
- const prevIndex = currentLoop ? 0 === prevRealIndex ? currentSlides.length - 1 : prevRealIndex - 1 : Math.max(prevRealIndex - 1, 0);
12423
- return setInternalIndex(currentLoop ? currentSlides.length + prevIndex : prevIndex),
12424
- setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
12425
- setIsTransitioning(!1), currentOnSlideChange?.(prevIndex);
12426
- }), currentSpeed), prevIndex;
12427
- }
12428
- // Normal direction
12429
- return setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex),
12430
- setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
12431
- setIsTransitioning(!1), currentOnSlideChange?.(nextIndex),
12432
- // Reposition after transition ends for looped sliders
12433
- currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
12434
- setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
12435
- repositioningRef.current = !1;
12436
- }), 0));
12437
- }), currentSpeed), nextIndex;
12438
- }));
12439
- }), delay), setAutoplayRunning(!0);
12440
- // Handle pause on mouse enter/leave if enabled
12441
- let containerElement = null;
12442
- const handleMouseEnter = () => {
12443
- autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null,
12444
- setAutoplayRunning(!1));
12445
- }, handleMouseLeave = () => {
12446
- // Restart autoplay
12447
- autoplayRef.current && clearInterval(autoplayRef.current), autoplayRef.current = setInterval((() => {
12448
- const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
12449
- setRealIndex((prevRealIndex => {
12450
- if (currentIsTransitioning) return prevRealIndex;
12451
- let nextIndex;
12452
- return nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
12453
- setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex), setIsTransitioning(!0),
12454
- setDragOffset(0), setTimeout((() => {
12455
- setIsTransitioning(!1), currentOnSlideChange?.(nextIndex), currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
12456
- setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
12457
- repositioningRef.current = !1;
12458
- }), 0));
12459
- }), currentSpeed), nextIndex;
12460
- }));
12461
- }), delay), setAutoplayRunning(!0);
12462
- };
12463
- // Cleanup
12464
- return pauseOnMouseEnter && containerRef.current && (containerElement = containerRef.current,
12465
- containerElement.addEventListener("mouseenter", handleMouseEnter), containerElement.addEventListener("mouseleave", handleMouseLeave)),
12466
- () => {
12467
- autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null),
12468
- containerElement && (containerElement.removeEventListener("mouseenter", handleMouseEnter),
12469
- containerElement.removeEventListener("mouseleave", handleMouseLeave)), setAutoplayRunning(!1);
12470
- };
12471
- }), [ autoplay, repositioningRef ]),
12472
- // Initialize
12473
- useEffect((() => {
12474
- setInternalIndex(loop ? slides.length + initialSlide : initialSlide);
12475
- }), [ loop, slides.length, initialSlide ]), useEffect((() => {
12476
- const updateSize = () => {
12477
- if (containerRef.current) {
12478
- const size = "horizontal" === direction ? containerRef.current.offsetWidth : containerRef.current.offsetHeight;
12479
- setContainerSize(size);
12480
- }
12481
- };
12482
- return updateSize(), window.addEventListener("resize", updateSize), () => window.removeEventListener("resize", updateSize);
12483
- }), [ direction ]);
12484
- const slideNext = useCallback((() => {
12485
- if (!isTransitioning) if (
12486
- // Stop autoplay on interaction if disableOnInteraction is true
12487
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12488
- autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
12489
- const nextRealIndex = (realIndex + 1) % slides.length, nextInternalIndex = internalIndex + 1;
12490
- setRealIndex(nextRealIndex), setInternalIndex(nextInternalIndex), setIsTransitioning(!0),
12491
- setDragOffset(0), setTimeout((() => {
12492
- setIsTransitioning(!1), onSlideChange?.(nextRealIndex),
12493
- // Reposition after transition ends
12494
- nextInternalIndex >= 2 * slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + nextRealIndex),
12495
- setTimeout((() => {
12496
- repositioningRef.current = !1;
12497
- }), 0));
12498
- }), speed);
12499
- } else {
12500
- const nextIndex = Math.min(realIndex + 1, slides.length - slidesToShow);
12501
- setRealIndex(nextIndex), setInternalIndex(nextIndex), setIsTransitioning(!0), setDragOffset(0),
12502
- setTimeout((() => {
12503
- setIsTransitioning(!1), onSlideChange?.(nextIndex);
12504
- }), speed);
12505
- }
12506
- }), [ realIndex, internalIndex, slides.length, slidesToShow, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), slidePrev = useCallback((() => {
12507
- if (!isTransitioning) if (
12508
- // Stop autoplay on interaction if disableOnInteraction is true
12509
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12510
- autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
12511
- const prevRealIndex = 0 === realIndex ? slides.length - 1 : realIndex - 1, prevInternalIndex = internalIndex - 1;
12512
- setRealIndex(prevRealIndex), setInternalIndex(prevInternalIndex), setIsTransitioning(!0),
12513
- setDragOffset(0), setTimeout((() => {
12514
- setIsTransitioning(!1), onSlideChange?.(prevRealIndex),
12515
- // Reposition after transition ends
12516
- prevInternalIndex < slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + prevRealIndex),
12517
- setTimeout((() => {
12518
- repositioningRef.current = !1;
12519
- }), 0));
12520
- }), speed);
12521
- } else {
12522
- const prevIndex = Math.max(realIndex - 1, 0);
12523
- setRealIndex(prevIndex), setInternalIndex(prevIndex), setIsTransitioning(!0), setDragOffset(0),
12524
- setTimeout((() => {
12525
- setIsTransitioning(!1), onSlideChange?.(prevIndex);
12526
- }), speed);
12527
- }
12528
- }), [ realIndex, internalIndex, slides.length, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), goToSlide = useCallback((index => {
12529
- isTransitioning || index === realIndex || (
12530
- // Stop autoplay on interaction if disableOnInteraction is true
12531
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12532
- autoplayRef.current = null, setAutoplayRunning(!1)), setIsTransitioning(!0), setDragOffset(0),
12533
- setRealIndex(index), setInternalIndex(loop ? slides.length + index : index), setTimeout((() => {
12534
- setIsTransitioning(!1), onSlideChange?.(index);
12535
- }), speed));
12536
- }), [ realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay ]), handleTouchStart = useCallback((e => {
12537
- if (!allowTouchMove) return;
12538
- // Stop autoplay on interaction if disableOnInteraction is true
12539
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12540
- autoplayRef.current = null, setAutoplayRunning(!1));
12541
- const client = "horizontal" === direction ? "touches" in e ? e.touches[0]?.clientX || 0 : e.clientX : "touches" in e ? e.touches[0]?.clientY || 0 : e.clientY;
12542
- setTouchStart(client), setTouching(!0), setDragOffset(0);
12543
- }), [ allowTouchMove, direction, autoplay ]), handleTouchMove = useCallback((e => {
12544
- if (!touching || !allowTouchMove) return;
12545
- const client = "horizontal" === direction ? "touches" in e ? e.touches[0]?.clientX || 0 : e.clientX : "touches" in e ? e.touches[0]?.clientY || 0 : e.clientY, diff = touchStart - client;
12546
- Math.abs(diff) > 10 && (e.preventDefault(), setDragOffset(.5 * -diff));
12547
- }), [ touching, touchStart, allowTouchMove, direction ]), handleTouchEnd = useCallback((e => {
12548
- if (!touching || !allowTouchMove) return;
12549
- const client = "horizontal" === direction ? "changedTouches" in e ? e.changedTouches[0]?.clientX || 0 : e.clientX : "changedTouches" in e ? e.changedTouches[0]?.clientY || 0 : e.clientY, diff = touchStart - client;
12550
- setTouching(!1), setDragOffset(0), Math.abs(diff) > threshold && (diff > 0 ? slideNext() : slidePrev());
12551
- }), [ touching, touchStart, threshold, slideNext, slidePrev, allowTouchMove, direction ]), canSlideNext = loop || realIndex < slides.length - slidesToShow, canSlidePrev = loop || realIndex > 0;
12552
- return {
12553
- activeIndex: realIndex,
12554
- realIndex: realIndex,
12555
- previousIndex: realIndex,
12556
- isBeginning: !loop && 0 === realIndex,
12557
- isEnd: !loop && realIndex >= slides.length - slidesToShow,
12558
- progress: slides.length > 0 ? realIndex / (slides.length - 1) : 0,
12559
- autoplayRunning: autoplayRunning,
12560
- transitioning: isTransitioning,
12561
- touching: touching,
12562
- translate: translateValue,
12563
- slidesPerView: slidesToShow,
12564
- slidesCount: slides.length,
12565
- isLocked: !1,
12566
- destroyed: !1,
12567
- size: containerSize,
12568
- touches: {
12569
- startX: 0,
12570
- startY: 0,
12571
- currentX: 0,
12572
- currentY: 0,
12573
- diff: 0
12574
- },
12575
- allowSlideNext: canSlideNext,
12576
- allowSlidePrev: canSlidePrev,
12577
- allowTouchMove: allowTouchMove,
12578
- animating: isTransitioning,
12579
- enabled: !0,
12580
- initialized: !0,
12581
- slideNext: slideNext,
12582
- slidePrev: slidePrev,
12583
- goToSlide: goToSlide,
12584
- canSlideNext: canSlideNext,
12585
- canSlidePrev: canSlidePrev,
12586
- containerRef: containerRef,
12587
- wrapperRef: wrapperRef,
12588
- handleTouchStart: handleTouchStart,
12589
- handleTouchMove: handleTouchMove,
12590
- handleTouchEnd: handleTouchEnd,
12591
- allSlides: allSlides,
12592
- translateValue: translateValue,
12593
- slideWidth: slideWidth,
12594
- currentSlidesToShow: slidesToShow,
12595
- loopedSlides: loopedSlides,
12596
- repositioningRef: repositioningRef
12597
- };
12598
- }
12599
-
12600
13439
  /**
12601
13440
  * Simplified hook for chart data processing
12602
13441
  */ function useChartData(data) {
@@ -12697,37 +13536,44 @@ function useSlider(options) {
12697
13536
  // Accordion composables
12698
13537
  const composablesImport = Object.freeze( Object.defineProperty({
12699
13538
  __proto__: null,
12700
- DOTS: "...",
13539
+ BALANCED_PRESET: BALANCED_PRESET,
13540
+ DEFAULT_BREAKPOINTS: DEFAULT_BREAKPOINTS,
13541
+ DeviceDetector: DeviceDetector,
13542
+ MOBILE_OPTIMIZED_BREAKPOINTS: MOBILE_OPTIMIZED_BREAKPOINTS,
13543
+ PERFORMANCE_PRESET: PERFORMANCE_PRESET,
13544
+ PerformanceOverlay: PerformanceOverlay,
13545
+ QUALITY_PRESET: QUALITY_PRESET,
13546
+ createBreakpoints: createBreakpoints$1,
13547
+ getDefaultBreakpoints: getDefaultBreakpoints,
13548
+ getDevicePreset: getDevicePreset,
13549
+ getMobileOptimizedParams: getMobileOptimizedParams,
13550
+ getQualityMultipliers: getQualityMultipliers,
12701
13551
  useAccordion: useAccordion,
12702
13552
  useAtomixGlass: useAtomixGlass,
12703
13553
  useBadge: useBadge,
12704
13554
  useBarChart: useBarChart,
12705
13555
  useBlock: useBlock,
12706
- useBreadcrumb: useBreadcrumb,
12707
- useCard: useCard,
12708
13556
  useChartData: useChartData,
12709
13557
  useChartInteraction: useChartInteraction,
12710
13558
  useChartScale: useChartScale,
12711
- useDataTable: useDataTable,
12712
13559
  useEdgePanel: useEdgePanel,
12713
13560
  useForm: useForm,
12714
13561
  useFormGroup: useFormGroup,
12715
13562
  useHero: useHero,
12716
13563
  useInput: useInput,
12717
13564
  useLineChart: useLineChart,
12718
- useModal: useModal$1,
12719
13565
  useNav: useNav,
12720
13566
  useNavDropdown: useNavDropdown,
12721
13567
  useNavItem: useNavItem,
12722
13568
  useNavbar: useNavbar,
12723
- usePagination: usePagination,
13569
+ usePerformanceMonitor: usePerformanceMonitor,
12724
13570
  usePieChart: usePieChart,
12725
13571
  useRadio: useRadio,
13572
+ useResponsiveGlass: useResponsiveGlass,
12726
13573
  useRiver: useRiver,
12727
13574
  useSelect: useSelect,
12728
13575
  useSideMenu: useSideMenu,
12729
13576
  useSideMenuItem: useSideMenuItem,
12730
- useSlider: useSlider,
12731
13577
  useSpinner: useSpinner,
12732
13578
  useTextarea: useTextarea,
12733
13579
  useTodo: useTodo
@@ -12818,7 +13664,10 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12818
13664
  };
12819
13665
  }), []);
12820
13666
  // Toggle dropdown
12821
- const handleItemClick = useCallback((option => {
13667
+ const handleToggle = () => {
13668
+ disabled || (!isOpen && bodyRef.current && panelRef.current ? bodyRef.current.style.height = `${panelRef.current.clientHeight}px` : bodyRef.current && (bodyRef.current.style.height = "0px"),
13669
+ setIsOpen(!isOpen));
13670
+ }, handleItemClick = useCallback((option => {
12822
13671
  if (setSelectedLabel(option.label), setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"),
12823
13672
  nativeSelectRef.current && (nativeSelectRef.current.value = option.value), onChange) {
12824
13673
  // Create a synthetic event
@@ -12862,7 +13711,15 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12862
13711
  "aria-describedby": ariaDescribedBy,
12863
13712
  "aria-invalid": invalid,
12864
13713
  style: {
12865
- display: "none"
13714
+ position: "absolute",
13715
+ width: "1px",
13716
+ height: "1px",
13717
+ padding: "0",
13718
+ margin: "-1px",
13719
+ overflow: "hidden",
13720
+ clip: "rect(0, 0, 0, 0)",
13721
+ whiteSpace: "nowrap",
13722
+ border: "0"
12866
13723
  },
12867
13724
  children: [ placeholder && jsx("option", {
12868
13725
  value: "",
@@ -12875,11 +13732,29 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12875
13732
  }, option.value))) ]
12876
13733
  }), jsx("div", {
12877
13734
  className: SELECT.CLASSES.SELECTED,
12878
- onClick: () => {
12879
- disabled || (!isOpen && bodyRef.current && panelRef.current ? bodyRef.current.style.height = `${panelRef.current.clientHeight}px` : bodyRef.current && (bodyRef.current.style.height = "0px"),
12880
- setIsOpen(!isOpen));
13735
+ onClick: handleToggle,
13736
+ onKeyDown: event => {
13737
+ if (!disabled) switch (event.key) {
13738
+ case "Enter":
13739
+ case " ":
13740
+ event.preventDefault(), handleToggle();
13741
+ break;
13742
+
13743
+ case "Escape":
13744
+ isOpen && (event.preventDefault(), setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"));
13745
+ break;
13746
+
13747
+ case "ArrowDown":
13748
+ case "ArrowUp":
13749
+ isOpen || (event.preventDefault(), handleToggle());
13750
+ }
12881
13751
  },
12882
13752
  "aria-disabled": disabled,
13753
+ tabIndex: disabled ? -1 : 0,
13754
+ role: "combobox",
13755
+ "aria-haspopup": "listbox",
13756
+ "aria-expanded": isOpen,
13757
+ "aria-controls": id ? `${id}-listbox` : void 0,
12883
13758
  children: selectedLabel
12884
13759
  }), jsx("i", {
12885
13760
  className: `${SELECT.CLASSES.ICON_CARET} ${SELECT.CLASSES.TOGGLE_ICON}`
@@ -12894,10 +13769,16 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12894
13769
  ref: panelRef,
12895
13770
  children: jsx("ul", {
12896
13771
  className: SELECT.CLASSES.SELECT_ITEMS,
13772
+ role: "listbox",
13773
+ id: id ? `${id}-listbox` : void 0,
13774
+ "aria-labelledby": id,
12897
13775
  children: hasOptionsProp ? options.map(((option, index) => jsx("li", {
12898
13776
  className: SELECT.CLASSES.SELECT_ITEM,
12899
13777
  "data-value": option.value,
12900
13778
  onClick: () => !option.disabled && handleItemClick(option),
13779
+ role: "option",
13780
+ "aria-selected": value === option.value,
13781
+ "aria-disabled": option.disabled,
12901
13782
  children: jsxs("label", {
12902
13783
  htmlFor: `SelectItem${index}`,
12903
13784
  className: "c-checkbox",
@@ -14433,7 +15314,45 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14433
15314
  onOpenChange: onOpenChange,
14434
15315
  onClose: onClose,
14435
15316
  onOpen: onOpen
14436
- });
15317
+ }), contentRef =
15318
+ /**
15319
+ * Hook to trap focus within an element
15320
+ */
15321
+ function(isOpen) {
15322
+ const containerRef = useRef(null), previouslyFocusedElement = useRef(null), getFocusableElements = useCallback((() => containerRef.current ? Array.from(containerRef.current.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter((el => {
15323
+ // Check if visible
15324
+ const style = window.getComputedStyle(el);
15325
+ return "none" !== style.display && "hidden" !== style.visibility;
15326
+ })) : []), []), handleKeyDown = useCallback((event => {
15327
+ if ("Tab" !== event.key) return;
15328
+ const focusableElements = getFocusableElements();
15329
+ if (0 === focusableElements.length) return;
15330
+ const firstElement = focusableElements[0], lastElement = focusableElements[focusableElements.length - 1];
15331
+ event.shiftKey ?
15332
+ // Shift + Tab
15333
+ document.activeElement === firstElement && (event.preventDefault(), lastElement.focus()) :
15334
+ // Tab
15335
+ document.activeElement === lastElement && (event.preventDefault(), firstElement.focus());
15336
+ }), [ getFocusableElements ]);
15337
+ return useEffect((() => {
15338
+ if (isOpen) {
15339
+ previouslyFocusedElement.current = document.activeElement;
15340
+ const focusableElements = getFocusableElements();
15341
+ if (focusableElements.length > 0 && focusableElements[0]) {
15342
+ // Delay focus slightly to ensure element is rendered
15343
+ const firstEl = focusableElements[0];
15344
+ setTimeout((() => {
15345
+ firstEl.focus();
15346
+ }), 0);
15347
+ }
15348
+ document.addEventListener("keydown", handleKeyDown);
15349
+ } else previouslyFocusedElement.current && previouslyFocusedElement.current.focus(),
15350
+ document.removeEventListener("keydown", handleKeyDown);
15351
+ return () => {
15352
+ document.removeEventListener("keydown", handleKeyDown);
15353
+ };
15354
+ }), [ isOpen, getFocusableElements, handleKeyDown ]), containerRef;
15355
+ }(isOpenState), instanceId = useId(), titleId = `modal-title-${instanceId}`, descId = `modal-desc-${instanceId}`;
14437
15356
  // Handle keyboard events for Escape key
14438
15357
  useEffect((() => {
14439
15358
  if (!keyboard) return;
@@ -14451,15 +15370,19 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14451
15370
  return React.isValidElement(child) && _includesInstanceProperty(_context = [ "ModalHeader", "ModalBody", "ModalFooter" ]).call(_context, child.type.displayName);
14452
15371
  })), modalContent = jsx("div", {
14453
15372
  className: "c-modal__content",
15373
+ ref: contentRef,
14454
15374
  children: hasCompoundComponents ? React.Children.map(children, (child => React.isValidElement(child) && "ModalHeader" === child.type.displayName ? React.cloneElement(child, {
14455
- onClose: child.props.onClose || close
15375
+ onClose: child.props.onClose || close,
15376
+ id: titleId
14456
15377
  }) : child)) : jsxs(Fragment, {
14457
15378
  children: [ (title || closeButton) && jsx(ModalHeader, {
15379
+ id: titleId,
14458
15380
  title: title,
14459
15381
  subtitle: subtitle,
14460
15382
  closeButton: closeButton,
14461
15383
  onClose: close
14462
15384
  }), jsx(ModalBody, {
15385
+ id: descId,
14463
15386
  children: children
14464
15387
  }), footer && jsx(ModalFooter, {
14465
15388
  children: footer
@@ -14477,6 +15400,8 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14477
15400
  role: "dialog",
14478
15401
  "aria-modal": "true",
14479
15402
  "aria-hidden": !isOpenState,
15403
+ "aria-labelledby": titleId,
15404
+ "aria-describedby": descId,
14480
15405
  ...props,
14481
15406
  children: [ jsx("div", {
14482
15407
  ref: backdropRef,
@@ -14723,7 +15648,27 @@ const Navbar = forwardRef((({brand: brand, children: children, variant: variant
14723
15648
  return window.addEventListener("resize", handleResize), () => {
14724
15649
  window.removeEventListener("resize", handleResize);
14725
15650
  };
14726
- }), [ collapsible, expanded, onToggle ]);
15651
+ }), [ collapsible, expanded, onToggle ]),
15652
+ // Handle Escape key to close mobile menu
15653
+ useEffect((() => {
15654
+ if (!navbarExpanded || !closeOnEscape) return;
15655
+ const handleEscapeKey = event => {
15656
+ "Escape" === event.key && ("function" == typeof onToggle ? onToggle(!1) : setNavbarExpanded(!1));
15657
+ };
15658
+ return document.addEventListener("keydown", handleEscapeKey), () => {
15659
+ document.removeEventListener("keydown", handleEscapeKey);
15660
+ };
15661
+ }), [ navbarExpanded, closeOnEscape, onToggle ]),
15662
+ // Handle outside click to close mobile menu
15663
+ useEffect((() => {
15664
+ if (!navbarExpanded || !closeOnOutsideClick) return;
15665
+ const handleClickOutside = event => {
15666
+ !collapseRef.current || collapseRef.current.contains(event.target) || event.target.closest(".c-navbar__toggler") || ("function" == typeof onToggle ? onToggle(!1) : setNavbarExpanded(!1));
15667
+ };
15668
+ return document.addEventListener("mousedown", handleClickOutside), () => {
15669
+ document.removeEventListener("mousedown", handleClickOutside);
15670
+ };
15671
+ }), [ navbarExpanded, closeOnOutsideClick, onToggle ]);
14727
15672
  // Generate the navbar class
14728
15673
  const navbarClass = generateNavbarClass({
14729
15674
  position: position,
@@ -17315,7 +18260,235 @@ const SectionIntro = ({title: title, label: label, text: text, actions: actions,
17315
18260
  SectionIntro.displayName = "SectionIntro";
17316
18261
 
17317
18262
  const Slider = forwardRef(((props, ref) => {
17318
- const {slides: slides = [], height: height = 300, width: width = "100%", slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, grabCursor: grabCursor = !0, autoplay: autoplay, navigation: navigation, pagination: pagination, className: className, style: style, onSlideChange: onSlideChange, ...rest} = props, validSlides = Array.isArray(slides) ? slides : [], slider = useSlider({
18263
+ const {slides: slides = [], height: height = 300, width: width = "100%", slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, grabCursor: grabCursor = !0, autoplay: autoplay, navigation: navigation, pagination: pagination, className: className, style: style, onSlideChange: onSlideChange, ...rest} = props, validSlides = Array.isArray(slides) ? slides : [], slider = function(options) {
18264
+ const {slides: rawSlides, slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, autoplay: autoplay, onSlideChange: onSlideChange} = options, slides = Array.isArray(rawSlides) ? rawSlides : [], containerRef = useRef(null), wrapperRef = useRef(null), repositioningRef = useRef(!1), autoplayRef = useRef(null), [autoplayRunning, setAutoplayRunning] = useState(!1), sliderStateRef = useRef({
18265
+ isTransitioning: !1,
18266
+ loop: loop,
18267
+ slides: slides,
18268
+ slidesToShow: slidesToShow,
18269
+ speed: speed,
18270
+ onSlideChange: onSlideChange
18271
+ }), [realIndex, setRealIndex] = useState(initialSlide), [internalIndex, setInternalIndex] = useState(0), [isTransitioning, setIsTransitioning] = useState(!1), [containerSize, setContainerSize] = useState(0), [touching, setTouching] = useState(!1), [touchStart, setTouchStart] = useState(0), [dragOffset, setDragOffset] = useState(0), slideWidth = useMemo((() => 0 === containerSize ? 0 : (containerSize - spaceBetween * (slidesToShow - 1)) / slidesToShow), [ containerSize, spaceBetween, slidesToShow ]), allSlides = useMemo((() => loop && 0 !== slides.length ? [ ...slides.map(((slide, i) => ({
18272
+ ...slide,
18273
+ id: `set1-${slide.id || i}`
18274
+ }))), ...slides.map(((slide, i) => ({
18275
+ ...slide,
18276
+ id: `set2-${slide.id || i}`
18277
+ }))), ...slides.map(((slide, i) => ({
18278
+ ...slide,
18279
+ id: `set3-${slide.id || i}`
18280
+ }))) ] : slides), [ slides, loop ]), loopedSlides = slides.length, translateValue = useMemo((() => 0 === slideWidth ? 0 : -internalIndex * slideWidth + dragOffset), [ slideWidth, internalIndex, dragOffset ]);
18281
+ // Update the ref whenever the relevant state/props change
18282
+ useEffect((() => {
18283
+ sliderStateRef.current = {
18284
+ isTransitioning: isTransitioning,
18285
+ loop: loop,
18286
+ slides: slides,
18287
+ slidesToShow: slidesToShow,
18288
+ speed: speed,
18289
+ onSlideChange: onSlideChange
18290
+ };
18291
+ }), [ isTransitioning, loop, slides, slidesToShow, speed, onSlideChange ]),
18292
+ // Autoplay effect
18293
+ useEffect((() => {
18294
+ if (!autoplay) return autoplayRef.current && (clearInterval(autoplayRef.current),
18295
+ autoplayRef.current = null), void setAutoplayRunning(!1);
18296
+ const autoplayParams = "boolean" == typeof autoplay ? {
18297
+ delay: 3e3
18298
+ } : autoplay, {delay: delay = 3e3, pauseOnMouseEnter: pauseOnMouseEnter = !1, disableOnInteraction: disableOnInteraction = !1, reverseDirection: reverseDirection = !1} = autoplayParams;
18299
+ // Clear any existing interval
18300
+ autoplayRef.current && clearInterval(autoplayRef.current),
18301
+ // Create new interval
18302
+ autoplayRef.current = setInterval((() => {
18303
+ // Use ref to get the latest state without resetting the interval
18304
+ const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
18305
+ // We need to use a functional update to get the latest values
18306
+ setRealIndex((prevRealIndex => {
18307
+ if (currentIsTransitioning) return prevRealIndex;
18308
+ // Stop autoplay on interaction if disableOnInteraction is true
18309
+ let nextIndex;
18310
+ // Trigger the slide change
18311
+ if (disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
18312
+ autoplayRef.current = null, setAutoplayRunning(!1)), nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
18313
+ reverseDirection) {
18314
+ // For reverse direction, we would go to previous slide
18315
+ const prevIndex = currentLoop ? 0 === prevRealIndex ? currentSlides.length - 1 : prevRealIndex - 1 : Math.max(prevRealIndex - 1, 0);
18316
+ return setInternalIndex(currentLoop ? currentSlides.length + prevIndex : prevIndex),
18317
+ setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
18318
+ setIsTransitioning(!1), currentOnSlideChange?.(prevIndex);
18319
+ }), currentSpeed), prevIndex;
18320
+ }
18321
+ // Normal direction
18322
+ return setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex),
18323
+ setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
18324
+ setIsTransitioning(!1), currentOnSlideChange?.(nextIndex),
18325
+ // Reposition after transition ends for looped sliders
18326
+ currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
18327
+ setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
18328
+ repositioningRef.current = !1;
18329
+ }), 0));
18330
+ }), currentSpeed), nextIndex;
18331
+ }));
18332
+ }), delay), setAutoplayRunning(!0);
18333
+ // Handle pause on mouse enter/leave if enabled
18334
+ let containerElement = null;
18335
+ const handleMouseEnter = () => {
18336
+ autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null,
18337
+ setAutoplayRunning(!1));
18338
+ }, handleMouseLeave = () => {
18339
+ // Restart autoplay
18340
+ autoplayRef.current && clearInterval(autoplayRef.current), autoplayRef.current = setInterval((() => {
18341
+ const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
18342
+ setRealIndex((prevRealIndex => {
18343
+ if (currentIsTransitioning) return prevRealIndex;
18344
+ let nextIndex;
18345
+ return nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
18346
+ setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex), setIsTransitioning(!0),
18347
+ setDragOffset(0), setTimeout((() => {
18348
+ setIsTransitioning(!1), currentOnSlideChange?.(nextIndex), currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
18349
+ setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
18350
+ repositioningRef.current = !1;
18351
+ }), 0));
18352
+ }), currentSpeed), nextIndex;
18353
+ }));
18354
+ }), delay), setAutoplayRunning(!0);
18355
+ };
18356
+ // Cleanup
18357
+ return pauseOnMouseEnter && containerRef.current && (containerElement = containerRef.current,
18358
+ containerElement.addEventListener("mouseenter", handleMouseEnter), containerElement.addEventListener("mouseleave", handleMouseLeave)),
18359
+ () => {
18360
+ autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null),
18361
+ containerElement && (containerElement.removeEventListener("mouseenter", handleMouseEnter),
18362
+ containerElement.removeEventListener("mouseleave", handleMouseLeave)), setAutoplayRunning(!1);
18363
+ };
18364
+ }), [ autoplay, repositioningRef ]),
18365
+ // Initialize
18366
+ useEffect((() => {
18367
+ setInternalIndex(loop ? slides.length + initialSlide : initialSlide);
18368
+ }), [ loop, slides.length, initialSlide ]), useEffect((() => {
18369
+ const updateSize = () => {
18370
+ if (containerRef.current) {
18371
+ const size = "horizontal" === direction ? containerRef.current.offsetWidth : containerRef.current.offsetHeight;
18372
+ setContainerSize(size);
18373
+ }
18374
+ };
18375
+ return updateSize(), window.addEventListener("resize", updateSize), () => window.removeEventListener("resize", updateSize);
18376
+ }), [ direction ]);
18377
+ const slideNext = useCallback((() => {
18378
+ if (!isTransitioning) if (
18379
+ // Stop autoplay on interaction if disableOnInteraction is true
18380
+ autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
18381
+ autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
18382
+ const nextRealIndex = (realIndex + 1) % slides.length, nextInternalIndex = internalIndex + 1;
18383
+ setRealIndex(nextRealIndex), setInternalIndex(nextInternalIndex), setIsTransitioning(!0),
18384
+ setDragOffset(0), setTimeout((() => {
18385
+ setIsTransitioning(!1), onSlideChange?.(nextRealIndex),
18386
+ // Reposition after transition ends
18387
+ nextInternalIndex >= 2 * slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + nextRealIndex),
18388
+ setTimeout((() => {
18389
+ repositioningRef.current = !1;
18390
+ }), 0));
18391
+ }), speed);
18392
+ } else {
18393
+ const nextIndex = Math.min(realIndex + 1, slides.length - slidesToShow);
18394
+ setRealIndex(nextIndex), setInternalIndex(nextIndex), setIsTransitioning(!0), setDragOffset(0),
18395
+ setTimeout((() => {
18396
+ setIsTransitioning(!1), onSlideChange?.(nextIndex);
18397
+ }), speed);
18398
+ }
18399
+ }), [ realIndex, internalIndex, slides.length, slidesToShow, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), slidePrev = useCallback((() => {
18400
+ if (!isTransitioning) if (
18401
+ // Stop autoplay on interaction if disableOnInteraction is true
18402
+ autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
18403
+ autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
18404
+ const prevRealIndex = 0 === realIndex ? slides.length - 1 : realIndex - 1, prevInternalIndex = internalIndex - 1;
18405
+ setRealIndex(prevRealIndex), setInternalIndex(prevInternalIndex), setIsTransitioning(!0),
18406
+ setDragOffset(0), setTimeout((() => {
18407
+ setIsTransitioning(!1), onSlideChange?.(prevRealIndex),
18408
+ // Reposition after transition ends
18409
+ prevInternalIndex < slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + prevRealIndex),
18410
+ setTimeout((() => {
18411
+ repositioningRef.current = !1;
18412
+ }), 0));
18413
+ }), speed);
18414
+ } else {
18415
+ const prevIndex = Math.max(realIndex - 1, 0);
18416
+ setRealIndex(prevIndex), setInternalIndex(prevIndex), setIsTransitioning(!0), setDragOffset(0),
18417
+ setTimeout((() => {
18418
+ setIsTransitioning(!1), onSlideChange?.(prevIndex);
18419
+ }), speed);
18420
+ }
18421
+ }), [ realIndex, internalIndex, slides.length, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), goToSlide = useCallback((index => {
18422
+ isTransitioning || index === realIndex || (
18423
+ // Stop autoplay on interaction if disableOnInteraction is true
18424
+ autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
18425
+ autoplayRef.current = null, setAutoplayRunning(!1)), setIsTransitioning(!0), setDragOffset(0),
18426
+ setRealIndex(index), setInternalIndex(loop ? slides.length + index : index), setTimeout((() => {
18427
+ setIsTransitioning(!1), onSlideChange?.(index);
18428
+ }), speed));
18429
+ }), [ realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay ]), handleTouchStart = useCallback((e => {
18430
+ if (!allowTouchMove) return;
18431
+ // Stop autoplay on interaction if disableOnInteraction is true
18432
+ autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
18433
+ autoplayRef.current = null, setAutoplayRunning(!1));
18434
+ const client = "horizontal" === direction ? "touches" in e ? e.touches[0]?.clientX || 0 : e.clientX : "touches" in e ? e.touches[0]?.clientY || 0 : e.clientY;
18435
+ setTouchStart(client), setTouching(!0), setDragOffset(0);
18436
+ }), [ allowTouchMove, direction, autoplay ]), handleTouchMove = useCallback((e => {
18437
+ if (!touching || !allowTouchMove) return;
18438
+ const client = "horizontal" === direction ? "touches" in e ? e.touches[0]?.clientX || 0 : e.clientX : "touches" in e ? e.touches[0]?.clientY || 0 : e.clientY, diff = touchStart - client;
18439
+ Math.abs(diff) > 10 && (e.preventDefault(), setDragOffset(.5 * -diff));
18440
+ }), [ touching, touchStart, allowTouchMove, direction ]), handleTouchEnd = useCallback((e => {
18441
+ if (!touching || !allowTouchMove) return;
18442
+ const client = "horizontal" === direction ? "changedTouches" in e ? e.changedTouches[0]?.clientX || 0 : e.clientX : "changedTouches" in e ? e.changedTouches[0]?.clientY || 0 : e.clientY, diff = touchStart - client;
18443
+ setTouching(!1), setDragOffset(0), Math.abs(diff) > threshold && (diff > 0 ? slideNext() : slidePrev());
18444
+ }), [ touching, touchStart, threshold, slideNext, slidePrev, allowTouchMove, direction ]), canSlideNext = loop || realIndex < slides.length - slidesToShow, canSlidePrev = loop || realIndex > 0;
18445
+ return {
18446
+ activeIndex: realIndex,
18447
+ realIndex: realIndex,
18448
+ previousIndex: realIndex,
18449
+ isBeginning: !loop && 0 === realIndex,
18450
+ isEnd: !loop && realIndex >= slides.length - slidesToShow,
18451
+ progress: slides.length > 0 ? realIndex / (slides.length - 1) : 0,
18452
+ autoplayRunning: autoplayRunning,
18453
+ transitioning: isTransitioning,
18454
+ touching: touching,
18455
+ translate: translateValue,
18456
+ slidesPerView: slidesToShow,
18457
+ slidesCount: slides.length,
18458
+ isLocked: !1,
18459
+ destroyed: !1,
18460
+ size: containerSize,
18461
+ touches: {
18462
+ startX: 0,
18463
+ startY: 0,
18464
+ currentX: 0,
18465
+ currentY: 0,
18466
+ diff: 0
18467
+ },
18468
+ allowSlideNext: canSlideNext,
18469
+ allowSlidePrev: canSlidePrev,
18470
+ allowTouchMove: allowTouchMove,
18471
+ animating: isTransitioning,
18472
+ enabled: !0,
18473
+ initialized: !0,
18474
+ slideNext: slideNext,
18475
+ slidePrev: slidePrev,
18476
+ goToSlide: goToSlide,
18477
+ canSlideNext: canSlideNext,
18478
+ canSlidePrev: canSlidePrev,
18479
+ containerRef: containerRef,
18480
+ wrapperRef: wrapperRef,
18481
+ handleTouchStart: handleTouchStart,
18482
+ handleTouchMove: handleTouchMove,
18483
+ handleTouchEnd: handleTouchEnd,
18484
+ allSlides: allSlides,
18485
+ translateValue: translateValue,
18486
+ slideWidth: slideWidth,
18487
+ currentSlidesToShow: slidesToShow,
18488
+ loopedSlides: loopedSlides,
18489
+ repositioningRef: repositioningRef
18490
+ };
18491
+ }({
17319
18492
  slides: validSlides,
17320
18493
  slidesToShow: slidesToShow,
17321
18494
  spaceBetween: spaceBetween,
@@ -17342,7 +18515,8 @@ const Slider = forwardRef(((props, ref) => {
17342
18515
  })
17343
18516
  });
17344
18517
  const containerClasses = [ "c-slider", "vertical" === direction && "c-slider--vertical", grabCursor && "c-slider--grab-cursor", touching && "c-slider--grabbing", loop && "c-slider--loop", className ].filter(Boolean).join(" ");
17345
- return jsxs("div", {
18518
+ // Keyboard navigation
18519
+ return jsxs("div", {
17346
18520
  ref: ref || containerRef,
17347
18521
  className: containerClasses,
17348
18522
  style: {
@@ -17360,9 +18534,32 @@ const Slider = forwardRef(((props, ref) => {
17360
18534
  onMouseMove: handleTouchMove,
17361
18535
  onMouseUp: handleTouchEnd,
17362
18536
  onMouseLeave: handleTouchEnd,
18537
+ onKeyDown: event => {
18538
+ switch (event.key) {
18539
+ case "ArrowLeft":
18540
+ "horizontal" === direction && (event.preventDefault(), slidePrev());
18541
+ break;
18542
+
18543
+ case "ArrowRight":
18544
+ "horizontal" === direction && (event.preventDefault(), slideNext());
18545
+ break;
18546
+
18547
+ case "ArrowUp":
18548
+ "vertical" === direction && (event.preventDefault(), slidePrev());
18549
+ break;
18550
+
18551
+ case "ArrowDown":
18552
+ "vertical" === direction && (event.preventDefault(), slideNext());
18553
+ }
18554
+ },
18555
+ role: "region",
18556
+ "aria-roledescription": "carousel",
18557
+ "aria-label": rest["aria-label"] || "Image slider",
18558
+ tabIndex: 0,
17363
18559
  children: [ jsx("div", {
17364
18560
  ref: wrapperRef,
17365
18561
  className: "c-slider__wrapper",
18562
+ "aria-live": autoplay ? "off" : "polite",
17366
18563
  style: {
17367
18564
  display: "flex",
17368
18565
  flexDirection: "vertical" === direction ? "column" : "row",
@@ -17540,15 +18737,24 @@ Steps.displayName = "Steps", Steps.Item = StepsItem, Steps.Step = StepsItem;
17540
18737
  // Context for compound usage
17541
18738
  const TabsContext = createContext({
17542
18739
  currentTab: 0,
17543
- handleTabClick: () => {}
17544
- }), TabsList = forwardRef((({children: children, className: className = "", ...props}, ref) => jsx("ul", {
17545
- ref: ref,
17546
- className: `c-tabs__nav ${className}`.trim(),
17547
- ...props,
17548
- children: React.Children.map(children, ((child, index) => React.isValidElement(child) ? React.cloneElement(child, {
17549
- index: index
17550
- }) : child))
17551
- })));
18740
+ handleTabClick: () => {},
18741
+ handleKeyDown: () => {},
18742
+ totalTabs: 0
18743
+ }), TabsList = forwardRef((({children: children, className: className = "", onKeyDown: onKeyDown, ...props}, ref) => {
18744
+ const {handleKeyDown: contextHandleKeyDown} = useContext(TabsContext), totalTabs = React.Children.count(children);
18745
+ return jsx("ul", {
18746
+ ref: ref,
18747
+ className: `c-tabs__nav ${className}`.trim(),
18748
+ role: "tablist",
18749
+ onKeyDown: e => {
18750
+ contextHandleKeyDown(e, totalTabs), onKeyDown?.(e);
18751
+ },
18752
+ ...props,
18753
+ children: React.Children.map(children, ((child, index) => React.isValidElement(child) ? React.cloneElement(child, {
18754
+ index: index
18755
+ }) : child))
18756
+ });
18757
+ }));
17552
18758
 
17553
18759
  // Compound components
17554
18760
  TabsList.displayName = "TabsList";
@@ -17560,8 +18766,10 @@ const TabsTrigger = forwardRef((({children: children, className: className = ""
17560
18766
  const isActive = void 0 !== index && currentTab === index;
17561
18767
  return jsx("li", {
17562
18768
  className: "c-tabs__nav-item",
18769
+ role: "presentation",
17563
18770
  children: jsx("button", {
17564
18771
  ref: ref,
18772
+ id: `tab-nav-${index}`,
17565
18773
  className: `c-tabs__nav-btn ${isActive ? TAB.CLASSES.ACTIVE : ""} ${className}`.trim(),
17566
18774
  onClick: e => {
17567
18775
  void 0 !== index && handleTabClick(index), onClick?.(e);
@@ -17570,6 +18778,7 @@ const TabsTrigger = forwardRef((({children: children, className: className = ""
17570
18778
  role: "tab",
17571
18779
  "aria-selected": isActive,
17572
18780
  "aria-controls": `tab-panel-${index}`,
18781
+ tabIndex: isActive ? 0 : -1,
17573
18782
  type: "button",
17574
18783
  ...props,
17575
18784
  children: children
@@ -17619,24 +18828,59 @@ TabsPanel.displayName = "TabsPanel";
17619
18828
  const Tabs = memo((({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTIVE_INDEX, onTabChange: onTabChange, className: className = "", style: style, glass: glass, children: children}) => {
17620
18829
  const [currentTab, setCurrentTab] = useState(activeIndex), handleTabClick = index => {
17621
18830
  setCurrentTab(index), onTabChange && onTabChange(index);
18831
+ }, handleKeyDown = (event, totalTabs) => {
18832
+ let newIndex = currentTab;
18833
+ switch (event.key) {
18834
+ case "ArrowRight":
18835
+ newIndex = (currentTab + 1) % totalTabs;
18836
+ break;
18837
+
18838
+ case "ArrowLeft":
18839
+ newIndex = (currentTab - 1 + totalTabs) % totalTabs;
18840
+ break;
18841
+
18842
+ case "Home":
18843
+ newIndex = 0;
18844
+ break;
18845
+
18846
+ case "End":
18847
+ newIndex = totalTabs - 1;
18848
+ break;
18849
+
18850
+ default:
18851
+ return;
18852
+ }
18853
+ event.preventDefault(), handleTabClick(newIndex),
18854
+ // Focus the newly active tab after it renders
18855
+ setTimeout((() => {
18856
+ const tabElement = document.getElementById(`tab-nav-${newIndex}`);
18857
+ tabElement && tabElement.focus();
18858
+ }), 0);
17622
18859
  };
17623
18860
  // Handle tab change
17624
18861
  // Determine content based on mode (legacy items vs compound children)
17625
18862
  let content;
17626
18863
  // Use items prop if provided
17627
- // Legacy mode
17628
- content = items && items.length > 0 ? jsxs(Fragment, {
18864
+ if (items && items.length > 0)
18865
+ // Legacy mode
18866
+ content = jsxs(Fragment, {
17629
18867
  children: [ jsx("ul", {
17630
18868
  className: "c-tabs__nav",
18869
+ role: "tablist",
18870
+ onKeyDown: e => handleKeyDown(e, items.length),
17631
18871
  children: items.map(((item, index) => jsx("li", {
17632
18872
  className: "c-tabs__nav-item",
18873
+ role: "presentation",
17633
18874
  children: jsx("button", {
18875
+ id: `tab-nav-${index}`,
17634
18876
  className: `c-tabs__nav-btn ${index === currentTab ? TAB.CLASSES.ACTIVE : ""}`,
17635
18877
  onClick: () => handleTabClick(index),
17636
18878
  "data-tabindex": index,
17637
18879
  role: "tab",
17638
18880
  "aria-selected": index === currentTab,
17639
18881
  "aria-controls": `tab-panel-${index}`,
18882
+ tabIndex: index === currentTab ? 0 : -1,
18883
+ type: "button",
17640
18884
  children: item.label
17641
18885
  })
17642
18886
  }, `tab-nav-${index}`)))
@@ -17660,13 +18904,19 @@ const Tabs = memo((({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTI
17660
18904
  })
17661
18905
  }, `tab-panel-${index}`)))
17662
18906
  }) ]
17663
- }) : jsx(TabsContext.Provider, {
17664
- value: {
17665
- currentTab: currentTab,
17666
- handleTabClick: handleTabClick
17667
- },
17668
- children: children
17669
- });
18907
+ }); else {
18908
+ // Compound mode
18909
+ const tabsList = React.Children.toArray(children).find((child => React.isValidElement(child) && "TabsList" === child.type.displayName)), totalTabsCount = tabsList ? React.Children.count(tabsList.props.children) : 0;
18910
+ content = jsx(TabsContext.Provider, {
18911
+ value: {
18912
+ currentTab: currentTab,
18913
+ handleTabClick: handleTabClick,
18914
+ handleKeyDown: handleKeyDown,
18915
+ totalTabs: totalTabsCount
18916
+ },
18917
+ children: children
18918
+ });
18919
+ }
17670
18920
  const wrapper = jsx("div", {
17671
18921
  className: `c-tabs js-atomix-tab ${className}`,
17672
18922
  style: style,
@@ -17757,7 +19007,7 @@ const Testimonial = ({quote: quote, author: author, size: size = "", skeleton: s
17757
19007
  className: "c-testimonial__author",
17758
19008
  children: [ author.avatarSrc && jsx("img", {
17759
19009
  src: author.avatarSrc,
17760
- alt: author.avatarAlt || "",
19010
+ alt: author.avatarAlt || `${author.name}'s avatar`,
17761
19011
  className: "c-testimonial__author-avatar c-avatar c-avatar--xxl c-avatar--circle"
17762
19012
  }), jsxs("div", {
17763
19013
  className: "c-testimonial__info",
@@ -18033,7 +19283,17 @@ const Tooltip = memo((({content: content, children: children, position: positio
18033
19283
  cancelAnimationFrame(rafId), window.removeEventListener("resize", handleUpdate),
18034
19284
  window.removeEventListener("scroll", handleUpdate, !0);
18035
19285
  };
18036
- }), [ isVisible, updatePosition ]);
19286
+ }), [ isVisible, updatePosition ]),
19287
+ // Handle Escape key to close tooltip
19288
+ useEffect((() => {
19289
+ if (!isVisible) return;
19290
+ const handleKeyDown = event => {
19291
+ "Escape" === event.key && hideTooltip();
19292
+ };
19293
+ return document.addEventListener("keydown", handleKeyDown), () => {
19294
+ document.removeEventListener("keydown", handleKeyDown);
19295
+ };
19296
+ }), [ isVisible, hideTooltip ]);
18037
19297
  // Setup trigger props
18038
19298
  const triggerProps = {
18039
19299
  "aria-describedby": isVisible ? tooltipId : void 0
@@ -18722,9 +19982,15 @@ const VideoPlayer = forwardRef((({src: src, type: type = "video", youtubeId: yo
18722
19982
  const rect = e.currentTarget.getBoundingClientRect(), percent = (e.clientX - rect.left) / rect.width;
18723
19983
  setVolume(percent);
18724
19984
  }), [ setVolume ]), handleDownload = useCallback((() => {
18725
- if (src) {
18726
- const a = document.createElement("a");
18727
- a.href = src, a.download = "video", a.click();
19985
+ if (src) try {
19986
+ var _context;
19987
+ const url = new URL(src, window.location.origin);
19988
+ if (_includesInstanceProperty(_context = [ "http:", "https:", "blob:", "data:" ]).call(_context, url.protocol)) {
19989
+ const a = document.createElement("a");
19990
+ a.href = url.href, a.download = "video", a.click();
19991
+ }
19992
+ } catch (e) {
19993
+ // Ignore invalid URLs
18728
19994
  }
18729
19995
  }), [ src ]), handleShare = useCallback((async () => {
18730
19996
  if (navigator.share) try {
@@ -19301,6 +20567,7 @@ const components = Object.freeze( Object.defineProperty({
19301
20567
  TODO: TODO,
19302
20568
  TOGGLE: TOGGLE,
19303
20569
  TOOLTIP: TOOLTIP,
20570
+ TYPEDBUTTON: TYPEDBUTTON,
19304
20571
  UPLOAD: UPLOAD,
19305
20572
  VIDEO_PLAYER: VIDEO_PLAYER,
19306
20573
  sliderConstants: sliderConstants
@@ -25243,5 +26510,5 @@ const atomix = {
25243
26510
  types: types
25244
26511
  };
25245
26512
 
25246
- export { ACCORDION, ATOMIX_GLASS, AVATAR, AVATAR_GROUP, Accordion, AreaChart, AtomixGlass, AtomixLogo, Avatar, AvatarGroup, BADGE, BADGE_CSS_VARS, BLOCK, BREADCRUMB, BUTTON, BUTTON_CSS_VARS, BUTTON_GROUP, Badge, BarChart, Block, Breadcrumb, BubbleChart, Button, ButtonGroup, CALLOUT, CARD, CARD_CSS_VARS, CHART, CHECKBOX_CSS_VARS, CLASS_PREFIX, CODE_SNIPPET, COMPONENT_CSS_VARS, COUNTDOWN, Callout, CandlestickChart, Card, Chart, ChartRenderer, Checkbox, ColorModeToggle, Container, Countdown, DATA_TABLE_CLASSES, DATA_TABLE_SELECTORS, DATEPICKER, DEFAULT_ATOMIX_FONTS, DOTS, DROPDOWN, DROPDOWN_CSS_VARS, DataTable, DatePicker, DesignTokensCustomizer, DonutChart, Dropdown, EDGE_PANEL, EdgePanel, ElevationCard, FOOTER, FORM, FORM_GROUP, Footer, FooterLink, FooterSection, FooterSocialLink, Form, FormGroup, FunnelChart, GaugeChart, Grid, GridCol, HERO, HeatmapChart, Hero, INPUT, INPUT_CSS_VARS, Icon, Input, LIST, LIST_GROUP, LineChart, List, ListGroup, MESSAGES, MODAL, MODAL_CSS_VARS, MasonryGrid, MasonryGridItem, MegaMenu, MegaMenuColumn, MegaMenuLink, Menu, MenuDivider, MenuItem, Messages, Modal, MultiAxisChart, NAV, NAVBAR, Nav, NavDropdown, NavItem, Navbar, PAGINATION_DEFAULTS, PHOTOVIEWER, POPOVER, PROGRESS, PROGRESS_CSS_VARS, Pagination, PhotoViewer, PieChart, Popover, ProductReview, Progress, RADIO, RADIO_CSS_VARS, RATING, RIVER, RTLManager, RadarChart, Radio, Rating, River, Row, SECTION_INTRO, SELECT, SIDE_MENU, SIZES, SLIDER, SPINNER, STEPS, ScatterChart, SectionIntro, Select, SideMenu, SideMenuItem, SideMenuList, Slider, Spinner, Steps, TAB, TABS_CSS_VARS, TESTIMONIAL, TEXTAREA, THEME_COLORS, THEME_NAMING, TODO, TOGGLE, TOOLTIP, TOOLTIP_CSS_VARS, Tabs, Testimonial, Textarea, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeValidator, Todo, Toggle, Tooltip, TreemapChart, UPLOAD, Upload, VIDEO_PLAYER, VideoPlayer, WaterfallChart, applyCSSVariables, applyCSSVarsToStyle, applyComponentTheme, applyPartStyles, applyTheme, camelToKebab, clearThemes, composables, constants, createCSSVarStyle, createDarkVariant, createDebugAttrs, createFontPreloadLink, createPartProps, createSlotComponent, createSlotProps, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, deepMerge, atomix as default, defaultTokens, defineConfig, designTokensToCSSVars, exportTheme, extendTheme, extractComponentName, extractYouTubeId, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, generateFontPreloadTags, generateUUID, getAllThemes, getCSSVariable, getComponentCSSVars, getComponentThemeValue, getPartStyles, getTheme, getThemeApplicator, getThemeCount, getThemeIds, getThemeMetadata, hasCustomization, hasTheme, importTheme, injectCSS$1 as injectCSS, injectTheme, isCSSInjected, isDesignTokens, isSlot, isValidCSSVariableName, isYouTubeUrl, mapSCSSTokensToCSSVars, mergeCSSVars, mergeClassNames, mergeComponentProps, mergePartStyles, mergeSlots, mergeTheme, normalizeThemeTokens, preloadFonts, quickTheme, registerTheme, removeCSS, removeCSSVariables, removeTheme, renderSlot, sliderConstants, supportsDarkMode, theme, themePropertyToCSSVar, themeToCSS, types, unregisterTheme, useAccordion, useAtomixGlass, useBadge, useBarChart, useBlock, useBreadcrumb, useCard, useChartData, useChartInteraction, useChartScale, useComponentCustomization, useComponentDefaultProps, useComponentTheme, useDataTable, useEdgePanel, useForm, useFormGroup, useHero, useHistory, useInput, useLineChart, useMergedProps, useModal$1 as useModal, useNav, useNavDropdown, useNavItem, useNavbar, usePagination, usePieChart, useRadio, useRiver, useSelect, useSideMenu, useSideMenuItem, useSlider, useSlot, useSpinner, useTextarea, useTheme, useThemeTokens, useTodo, utils, validateTheme };
26513
+ export { ACCORDION, ATOMIX_GLASS, AVATAR, AVATAR_GROUP, Accordion, AreaChart, AtomixGlass, AtomixLogo, Avatar, AvatarGroup, BADGE, BADGE_CSS_VARS, BALANCED_PRESET, BLOCK, BREADCRUMB, BUTTON, BUTTON_CSS_VARS, BUTTON_GROUP, Badge, BarChart, Block, Breadcrumb, BubbleChart, Button, ButtonGroup, CALLOUT, CARD, CARD_CSS_VARS, CHART, CHECKBOX_CSS_VARS, CLASS_PREFIX, CODE_SNIPPET, COMPONENT_CSS_VARS, COUNTDOWN, Callout, CandlestickChart, Card, Chart, ChartRenderer, Checkbox, ColorModeToggle, Container, Countdown, DATA_TABLE_CLASSES, DATA_TABLE_SELECTORS, DATEPICKER, DEFAULT_ATOMIX_FONTS, DEFAULT_BREAKPOINTS, DROPDOWN, DROPDOWN_CSS_VARS, DataTable, DatePicker, DesignTokensCustomizer, DeviceDetector, DonutChart, Dropdown, EDGE_PANEL, EdgePanel, ElevationCard, FOOTER, FORM, FORM_GROUP, Footer, FooterLink, FooterSection, FooterSocialLink, Form, FormGroup, FunnelChart, GaugeChart, Grid, GridCol, HERO, HeatmapChart, Hero, INPUT, INPUT_CSS_VARS, Icon, Input, LIST, LIST_GROUP, LineChart, List, ListGroup, MESSAGES, MOBILE_OPTIMIZED_BREAKPOINTS, MODAL, MODAL_CSS_VARS, MasonryGrid, MasonryGridItem, MegaMenu, MegaMenuColumn, MegaMenuLink, Menu, MenuDivider, MenuItem, Messages, Modal, MultiAxisChart, NAV, NAVBAR, Nav, NavDropdown, NavItem, Navbar, PAGINATION_DEFAULTS, PERFORMANCE_PRESET, PHOTOVIEWER, POPOVER, PROGRESS, PROGRESS_CSS_VARS, Pagination, PerformanceOverlay, PhotoViewer, PieChart, Popover, ProductReview, Progress, QUALITY_PRESET, RADIO, RADIO_CSS_VARS, RATING, RIVER, RTLManager, RadarChart, Radio, Rating, River, Row, SECTION_INTRO, SELECT, SIDE_MENU, SIZES, SLIDER, SPINNER, STEPS, ScatterChart, SectionIntro, Select, SideMenu, SideMenuItem, SideMenuList, Slider, Spinner, Steps, TAB, TABS_CSS_VARS, TESTIMONIAL, TEXTAREA, THEME_COLORS, THEME_NAMING, TODO, TOGGLE, TOOLTIP, TOOLTIP_CSS_VARS, TYPEDBUTTON, Tabs, Testimonial, Textarea, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeValidator, Todo, Toggle, Tooltip, TreemapChart, UPLOAD, Upload, VIDEO_PLAYER, VideoPlayer, WaterfallChart, applyCSSVariables, applyCSSVarsToStyle, applyComponentTheme, applyPartStyles, applyTheme, camelToKebab, clearThemes, composables, constants, createBreakpoints$1 as createBreakpoints, createCSSVarStyle, createDarkVariant, createDebugAttrs, createFontPreloadLink, createPartProps, createSlotComponent, createSlotProps, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, deepMerge, atomix as default, defaultTokens, defineConfig, designTokensToCSSVars, exportTheme, extendTheme, extractComponentName, extractYouTubeId, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, generateFontPreloadTags, generateUUID, getAllThemes, getCSSVariable, getComponentCSSVars, getComponentThemeValue, getDefaultBreakpoints, getDevicePreset, getMobileOptimizedParams, getPartStyles, getQualityMultipliers, getTheme, getThemeApplicator, getThemeCount, getThemeIds, getThemeMetadata, hasCustomization, hasTheme, importTheme, injectCSS$1 as injectCSS, injectTheme, isCSSInjected, isDesignTokens, isSlot, isValidCSSVariableName, isYouTubeUrl, mapSCSSTokensToCSSVars, mergeCSSVars, mergeClassNames, mergeComponentProps, mergePartStyles, mergeSlots, mergeTheme, normalizeThemeTokens, preloadFonts, quickTheme, registerTheme, removeCSS, removeCSSVariables, removeTheme, renderSlot, sliderConstants, supportsDarkMode, theme, themePropertyToCSSVar, themeToCSS, types, unregisterTheme, useAccordion, useAtomixGlass, useBadge, useBarChart, useBlock, useChartData, useChartInteraction, useChartScale, useComponentCustomization, useComponentDefaultProps, useComponentTheme, useEdgePanel, useForm, useFormGroup, useHero, useHistory, useInput, useLineChart, useMergedProps, useNav, useNavDropdown, useNavItem, useNavbar, usePerformanceMonitor, usePieChart, useRadio, useResponsiveGlass, useRiver, useSelect, useSideMenu, useSideMenuItem, useSlot, useSpinner, useTextarea, useTheme, useThemeTokens, useTodo, utils, validateTheme };
25247
26514
  //# sourceMappingURL=index.esm.js.map