@shohojdhara/atomix 0.4.8 → 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 (165) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +148 -120
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +1 -1
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1227 -122
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1052 -41
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2086 -1035
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1620 -600
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +441 -270
  19. package/dist/index.esm.js +1900 -638
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1935 -670
  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 +148 -4
  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 +4 -1
  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 +135 -14
  44. package/scripts/cli/commands/init.js +45 -18
  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/component-validator.js +443 -0
  52. package/scripts/cli/internal/config-loader.js +162 -0
  53. package/scripts/cli/internal/filesystem.js +102 -2
  54. package/scripts/cli/internal/generator.js +359 -39
  55. package/scripts/cli/internal/glass-generator.js +398 -0
  56. package/scripts/cli/internal/hook-generator.js +369 -0
  57. package/scripts/cli/internal/hooks.js +61 -0
  58. package/scripts/cli/internal/itcss-generator.js +565 -0
  59. package/scripts/cli/internal/motion-generator.js +679 -0
  60. package/scripts/cli/internal/template-engine.js +301 -0
  61. package/scripts/cli/internal/theme-bridge.js +664 -0
  62. package/scripts/cli/internal/tokens/engine.js +122 -0
  63. package/scripts/cli/internal/tokens/provider.js +34 -0
  64. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  65. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  66. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  67. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  68. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  69. package/scripts/cli/internal/validator.js +276 -0
  70. package/scripts/cli/internal/wizard.js +60 -6
  71. package/scripts/cli/mappings.js +23 -0
  72. package/scripts/cli/migration-tools.js +164 -94
  73. package/scripts/cli/plugins/style-dictionary.js +46 -0
  74. package/scripts/cli/templates/README.md +525 -95
  75. package/scripts/cli/templates/common-templates.js +40 -14
  76. package/scripts/cli/templates/components/react-component.ts +282 -0
  77. package/scripts/cli/templates/config/project-config.ts +112 -0
  78. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  79. package/scripts/cli/templates/index.js +19 -4
  80. package/scripts/cli/templates/index.ts +171 -0
  81. package/scripts/cli/templates/next-templates.js +72 -0
  82. package/scripts/cli/templates/react-templates.js +70 -126
  83. package/scripts/cli/templates/scss-templates.js +35 -35
  84. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  85. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  86. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  87. package/scripts/cli/templates/token-templates.js +337 -1
  88. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  89. package/scripts/cli/templates/types/component-types.ts +145 -0
  90. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  91. package/scripts/cli/templates/vanilla-templates.js +39 -0
  92. package/scripts/cli/token-manager.js +8 -2
  93. package/scripts/cli/utils/cache-manager.js +240 -0
  94. package/scripts/cli/utils/detector.js +46 -0
  95. package/scripts/cli/utils/diagnostics.js +289 -0
  96. package/scripts/cli/utils/error.js +45 -3
  97. package/scripts/cli/utils/helpers.js +24 -0
  98. package/scripts/cli/utils/logger.js +1 -1
  99. package/scripts/cli/utils/security.js +302 -0
  100. package/scripts/cli/utils/telemetry.js +115 -0
  101. package/scripts/cli/utils/validation.js +4 -38
  102. package/scripts/cli/utils.js +46 -0
  103. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  104. package/src/components/Accordion/Accordion.test.tsx +0 -17
  105. package/src/components/Accordion/Accordion.tsx +0 -4
  106. package/src/components/AtomixGlass/AtomixGlass.tsx +102 -2
  107. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +125 -12
  108. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  109. package/src/components/AtomixGlass/README.md +25 -10
  110. package/src/components/AtomixGlass/animation-system.ts +578 -0
  111. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  112. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  113. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  114. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  115. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  116. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  117. package/src/components/Avatar/Avatar.tsx +1 -1
  118. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  119. package/src/components/Button/Button.stories.tsx +10 -0
  120. package/src/components/Button/Button.test.tsx +16 -11
  121. package/src/components/Button/Button.tsx +4 -4
  122. package/src/components/Card/Card.tsx +1 -1
  123. package/src/components/Dropdown/Dropdown.tsx +12 -12
  124. package/src/components/Form/Select.tsx +62 -3
  125. package/src/components/Modal/Modal.tsx +14 -3
  126. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  127. package/src/components/Slider/Slider.stories.tsx +3 -3
  128. package/src/components/Slider/Slider.tsx +38 -0
  129. package/src/components/Steps/Steps.tsx +3 -3
  130. package/src/components/Tabs/Tabs.tsx +77 -8
  131. package/src/components/Testimonial/Testimonial.tsx +1 -1
  132. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  133. package/src/components/TypedButton/TypedButton.tsx +39 -0
  134. package/src/components/TypedButton/index.ts +2 -0
  135. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  136. package/src/lib/composables/index.ts +4 -7
  137. package/src/lib/composables/types.ts +45 -0
  138. package/src/lib/composables/useAccordion.ts +0 -7
  139. package/src/lib/composables/useAtomixGlass.ts +144 -5
  140. package/src/lib/composables/useChartExport.ts +3 -13
  141. package/src/lib/composables/useDropdown.ts +66 -0
  142. package/src/lib/composables/useFocusTrap.ts +80 -0
  143. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  144. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  145. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  146. package/src/lib/composables/useTooltip.ts +16 -0
  147. package/src/lib/composables/useTypedButton.ts +66 -0
  148. package/src/lib/config/index.ts +62 -5
  149. package/src/lib/constants/components.ts +55 -0
  150. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  151. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  152. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  153. package/src/lib/types/components.ts +37 -11
  154. package/src/lib/types/glass.ts +35 -0
  155. package/src/lib/types/index.ts +1 -0
  156. package/src/lib/utils/displacement-generator.ts +1 -1
  157. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  158. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  159. package/src/styles/06-components/_components.testbutton.scss +212 -0
  160. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  161. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  162. package/src/styles/99-utilities/_index.scss +1 -0
  163. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  164. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  165. 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",
@@ -1727,7 +1735,16 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
1727
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
1750
  ACTIVATION_ZONE: 200,
@@ -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;
@@ -2197,14 +2250,16 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2197
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 = {
2198
2251
  width: 0,
2199
2252
  height: 0
2200
- }, 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) => {
2201
2256
  // Generate a stable, deterministic ID for SSR compatibility
2202
2257
  // Use a module-level counter that's consistent across server and client
2203
- 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);
2204
2259
  // Lazy load shader utilities only when shader mode is needed
2205
2260
  useEffect((() => {
2206
2261
  "shader" === mode ?
2207
- // Dynamic import shader utilities
2262
+ // Dynamic import shader utilities with animation support
2208
2263
  Promise.resolve().then((() => shaderUtils)).then((shaderUtils => {
2209
2264
  shaderUtilsRef.current = {
2210
2265
  ShaderDisplacementGenerator: shaderUtils.ShaderDisplacementGenerator,
@@ -2219,7 +2274,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2219
2274
  // Generate shader map with debouncing and caching
2220
2275
  useEffect((() => {
2221
2276
  // Enhanced validation for shader mode
2222
- if ("shader" === mode && glassSize && validateGlassSize(glassSize) && shaderUtilsRef.current) {
2277
+ if ("shader" === mode && glassSize && validateGlassSize(glassSize)) {
2223
2278
  // Create cache key from size and variant
2224
2279
  const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
2225
2280
  const entry = sharedShaderCache.get(key);
@@ -2241,11 +2296,9 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2241
2296
  width: glassSize.width,
2242
2297
  height: glassSize.height,
2243
2298
  fragment: selectedShader
2244
- }),
2245
- // Defer shader generation with longer delay to avoid blocking
2246
- setTimeout((() => {
2299
+ }), shaderUpdateTimeoutRef.current = setTimeout((() => {
2247
2300
  const url = shaderGeneratorRef.current?.updateShader() || "";
2248
- ((key, url) => {
2301
+ url && ((key, url) => {
2249
2302
  // Evict oldest entries if at capacity
2250
2303
  if (sharedShaderCache.size >= 15) {
2251
2304
  const entries = Array.from(sharedShaderCache.entries());
@@ -2277,7 +2330,8 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2277
2330
  // Cleanup function with error handling
2278
2331
  return () => {
2279
2332
  shaderDebounceTimeoutRef.current && (clearTimeout(shaderDebounceTimeoutRef.current),
2280
- shaderDebounceTimeoutRef.current = null);
2333
+ shaderDebounceTimeoutRef.current = null), shaderUpdateTimeoutRef.current && (clearTimeout(shaderUpdateTimeoutRef.current),
2334
+ shaderUpdateTimeoutRef.current = null);
2281
2335
  try {
2282
2336
  shaderGeneratorRef.current?.destroy();
2283
2337
  } catch (error) {
@@ -2286,7 +2340,37 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
2286
2340
  shaderGeneratorRef.current = null;
2287
2341
  }
2288
2342
  };
2289
- }), [ 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 ]);
2290
2374
  // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
2291
2375
  const [rectCache, setRectCache] = useState(null);
2292
2376
  useEffect((() => {
@@ -2620,12 +2704,144 @@ class {
2620
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)"),
2621
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)");
2622
2706
  }
2623
- }, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
2707
+ };
2624
2708
 
2625
2709
  /**
2626
2710
  * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2627
2711
  * to avoid React re-renders on mouse movement.
2628
- */ 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) => {
2629
2845
  parentElement && backgroundDetectionCache.set(parentElement, {
2630
2846
  result: result,
2631
2847
  timestamp: Date.now(),
@@ -2638,7 +2854,9 @@ class {
2638
2854
  * Composable hook for AtomixGlass component logic
2639
2855
  * Manages all state, calculations, and event handlers
2640
2856
  */
2641
- 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}) {
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}) {
2642
2860
  // State
2643
2861
  const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
2644
2862
  x: 0,
@@ -2652,7 +2870,47 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2652
2870
  }), targetGlobalMousePositionRef = useRef({
2653
2871
  x: 0,
2654
2872
  y: 0
2655
- }), 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}) {
2656
2914
  const [glassSize, setGlassSize] = useState({
2657
2915
  width: 270,
2658
2916
  height: 69
@@ -2711,7 +2969,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2711
2969
  glassRef: glassRef,
2712
2970
  effectiveBorderRadius: effectiveBorderRadius,
2713
2971
  cachedRectRef: cachedRectRef
2714
- }), 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
+ */
2715
2976
  // Extract border-radius from children
2716
2977
  useEffect((() => {
2717
2978
  const extractRadius = () => {
@@ -2914,6 +3175,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2914
3175
  lerpActiveRef.current = !0;
2915
3176
  const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
2916
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);
2917
3180
  const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
2918
3181
  // If we're close enough, snap and park
2919
3182
  if (Math.abs(dx) < .05 && Math.abs(dy) < .05) internalMouseOffsetRef.current = {
@@ -2922,17 +3185,17 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
2922
3185
  ...targetGlobalMousePositionRef.current
2923
3186
  }; else {
2924
3187
  internalMouseOffsetRef.current = {
2925
- x: lerp(cur.x, tgt.x, LERP_T),
2926
- 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)
2927
3190
  };
2928
3191
  const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
2929
3192
  internalGlobalMousePositionRef.current = {
2930
- x: lerp(curG.x, tgtG.x, LERP_T),
2931
- 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)
2932
3195
  };
2933
3196
  }
2934
3197
  // Imperative style update with the smoothed values
2935
- updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
3198
+ updateAtomixGlassStyles(wrapperRef.current, glassRef.current, {
2936
3199
  mouseOffset: internalMouseOffsetRef.current,
2937
3200
  globalMousePosition: internalGlobalMousePositionRef.current,
2938
3201
  glassSize: glassSize,
@@ -3023,6 +3286,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3023
3286
  // This is now static (refs or props) unless prop changes
3024
3287
  overLightConfig: overLightConfig,
3025
3288
  transformStyle: transformStyle,
3289
+ getShaderTime: getShaderTime,
3290
+ applyTimeBasedDistortion: applyTimeBasedDistortion,
3026
3291
  handleMouseEnter: handleMouseEnter,
3027
3292
  handleMouseLeave: handleMouseLeave,
3028
3293
  handleMouseDown: handleMouseDown,
@@ -3031,6 +3296,800 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3031
3296
  };
3032
3297
  }
3033
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
+
3034
4093
  /**
3035
4094
  * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
3036
4095
  *
@@ -3045,6 +4104,8 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3045
4104
  * - Focus ring support for keyboard navigation
3046
4105
  * - Responsive breakpoints for mobile optimization
3047
4106
  * - Enhanced ARIA attributes for screen readers
4107
+ * - Time-based animation system with FBM distortion
4108
+ * - Device preset optimization for performance/quality balance
3048
4109
  *
3049
4110
  * Design System Compliance:
3050
4111
  * - Uses design tokens for opacity, spacing, and colors
@@ -3101,8 +4162,14 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3101
4162
  * <AtomixGlass overLight="auto" debugOverLight={true}>
3102
4163
  * <div>Content with debug logging enabled</div>
3103
4164
  * </AtomixGlass>
3104
- */ 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}) {
3105
- 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, 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({
3106
4173
  glassRef: glassRef,
3107
4174
  contentRef: contentRef,
3108
4175
  borderRadius: borderRadius,
@@ -3115,7 +4182,6 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3115
4182
  withoutEffects: withoutEffects,
3116
4183
  elasticity: elasticity,
3117
4184
  onClick: onClick,
3118
- debugBorderRadius: debugBorderRadius,
3119
4185
  debugOverLight: debugOverLight,
3120
4186
  debugPerformance: debugPerformance,
3121
4187
  children: children,
@@ -3124,8 +4190,48 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3124
4190
  withLiquidBlur: withLiquidBlur,
3125
4191
  padding: padding,
3126
4192
  style: style,
3127
- isFixedOrSticky: isFixedOrSticky
3128
- }), isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
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((() => {
3129
4235
  if (!isFixedOrSticky) return {};
3130
4236
  const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
3131
4237
  return {
@@ -3311,6 +4417,15 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3311
4417
  effectiveReducedMotion: effectiveReducedMotion,
3312
4418
  shaderVariant: shaderVariant,
3313
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,
3314
4429
  children: children
3315
4430
  }), Boolean(onClick) && jsxs(Fragment, {
3316
4431
  children: [ jsx("div", {
@@ -3334,6 +4449,10 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef:
3334
4449
  }), jsx("span", {
3335
4450
  className: ATOMIX_GLASS.BORDER_2_CLASS
3336
4451
  }) ]
4452
+ }), debugPerformance && performanceMetrics && jsx(PerformanceDashboard, {
4453
+ metrics: performanceMetrics,
4454
+ isVisible: !0,
4455
+ onClose: () => {}
3337
4456
  }) ]
3338
4457
  });
3339
4458
  }
@@ -3400,16 +4519,14 @@ const AccordionBody = forwardRef((({children: children, className: className =
3400
4519
 
3401
4520
  AccordionBody.displayName = "AccordionBody";
3402
4521
 
3403
- 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}) => {
3404
4523
  // Generate unique IDs for accessibility
3405
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({
3406
4525
  defaultOpen: defaultOpen,
3407
4526
  disabled: disabled,
3408
4527
  iconPosition: iconPosition,
3409
4528
  isOpen: controlledOpen,
3410
- onOpenChange: onOpenChange,
3411
- onOpen: onOpen,
3412
- onClose: onClose
4529
+ onOpenChange: onOpenChange
3413
4530
  }), headerClassNames = generateHeaderClassNames(), panelClassNames = ACCORDION.SELECTORS.PANEL.replace(".", ""), hasCompoundComponents = React.Children.toArray(children).some((child => {
3414
4531
  var _context;
3415
4532
 
@@ -3790,7 +4907,9 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
3790
4907
  return this.canvasDPI;
3791
4908
  }
3792
4909
  },
3793
- fragmentShaders: fragmentShaders
4910
+ createFBMEngine: createFBMEngine,
4911
+ fragmentShaders: fragmentShaders,
4912
+ liquidGlassWithTime: liquidGlassWithTime
3794
4913
  }, Symbol.toStringTag, {
3795
4914
  value: "Module"
3796
4915
  })), sizeMap = {
@@ -3819,7 +4938,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
3819
4938
 
3820
4939
  Icon.displayName = "Icon";
3821
4940
 
3822
- 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}) => {
3823
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(" ");
3824
4943
  return jsx("div", {
3825
4944
  className: avatarClasses,
@@ -4292,7 +5411,7 @@ class ThemeNaming {
4292
5411
  ThemeNaming.prefix = "atomix";
4293
5412
 
4294
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) => {
4295
- const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href && !isDisabled), iconElement = iconName ? jsx(Icon, {
5414
+ const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsx(Icon, {
4296
5415
  name: iconName,
4297
5416
  size: iconSize
4298
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 => {
@@ -4348,8 +5467,8 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4348
5467
  ...buttonProps,
4349
5468
  ref: ref,
4350
5469
  // linkComponent usually forwards ref to anchor
4351
- href: href,
4352
- to: href,
5470
+ href: isDisabled ? void 0 : href,
5471
+ to: isDisabled ? void 0 : href,
4353
5472
  target: target,
4354
5473
  rel: "_blank" === target ? "noopener noreferrer" : void 0
4355
5474
  };
@@ -4362,7 +5481,7 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4362
5481
  content = jsx("a", {
4363
5482
  ...buttonProps,
4364
5483
  ref: ref,
4365
- href: href,
5484
+ href: isDisabled ? void 0 : href,
4366
5485
  target: target,
4367
5486
  rel: "_blank" === target ? "noopener noreferrer" : void 0,
4368
5487
  children: buttonContent
@@ -4641,7 +5760,7 @@ row: row = !1, flat: flat = !1,
4641
5760
  // States
4642
5761
  active: active = !1, disabled: disabled = !1, loading: loading = !1, selected: selected = !1, interactive: interactive = !1,
4643
5762
  // Content
4644
- 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,
4645
5764
  // Interaction
4646
5765
  onClick: onClick, onHover: onHover, onFocus: onFocus, href: href, target: target,
4647
5766
  // Custom Link
@@ -4832,50 +5951,50 @@ Card.Header = CardHeader, Card.Body = CardBody, Card.Footer = CardFooter;
4832
5951
  * @param options - Configuration options for the card
4833
5952
  * @returns Card state and handlers
4834
5953
  */
4835
- const useCard = (options = {}) => {
4836
- 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 => {
4837
- flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)), onClick && onClick(event);
4838
- }), [ flipEffect, flipTrigger, onClick ]), handleKeyDown = useCallback((event => {
4839
- "Enter" !== event.key && " " !== event.key || (event.preventDefault(), flipEffect && "click" === flipTrigger && setIsFlipped((prev => !prev)),
4840
- onClick && onClick(event));
4841
- }), [ flipEffect, flipTrigger, onClick ]), handleMouseEnter = useCallback((() => {
4842
- setIsHovered(!0), elevationEffect && setIsElevated(!0), flipEffect && "hover" === flipTrigger && setIsFlipped(!0);
4843
- }), [ elevationEffect, flipEffect, flipTrigger ]), handleMouseLeave = useCallback((() => {
4844
- setIsHovered(!1), elevationEffect && setIsElevated(!1), flipEffect && "hover" === flipTrigger && setIsFlipped(!1);
4845
- }), [ elevationEffect, flipEffect, flipTrigger ]), handleFocus = useCallback((() => {
4846
- setIsFocused(!0);
4847
- }), []), handleBlur = useCallback((() => {
4848
- setIsFocused(!1);
4849
- }), []), getCardProps = useCallback((() => ({
4850
- className: [ CARD.CLASSES.BASE, isElevated ? elevationClass : "", isFlipped ? CARD.CLASSES.FLIPPED : "", isFocused && focusEffect ? CARD.CLASSES.FOCUSED : "", clickable ? CARD.CLASSES.CLICKABLE : "" ].filter(Boolean).join(" "),
4851
- ref: cardRef,
4852
- tabxwIndex: clickable || flipEffect ? 0 : -1,
4853
- role: clickable ? "button" : void 0,
4854
- onMouseEnter: handleMouseEnter,
4855
- onMouseLeave: handleMouseLeave,
4856
- onFocus: handleFocus,
4857
- onBlur: handleBlur,
4858
- onClick: handleClick,
4859
- onKeyDown: handleKeyDown
4860
- })), [ isElevated, isFlipped, isFocused, elevationClass, focusEffect, clickable, handleMouseEnter, handleMouseLeave, handleFocus, handleBlur, handleClick, handleKeyDown, flipEffect ]);
4861
- return {
4862
- cardRef: cardRef,
4863
- frontRef: frontRef,
4864
- backRef: backRef,
4865
- isFlipped: isFlipped,
4866
- isElevated: isElevated,
4867
- isFocused: isFocused,
4868
- isHovered: isHovered,
4869
- handleClick: handleClick,
4870
- handleKeyDown: handleKeyDown,
4871
- handleMouseEnter: handleMouseEnter,
4872
- handleMouseLeave: handleMouseLeave,
4873
- handleFocus: handleFocus,
4874
- handleBlur: handleBlur,
4875
- getCardProps: getCardProps
4876
- };
4877
- }, ElevationCard = ({elevationClass: elevationClass = "is-elevated", className: className = "", style: style, children: children, onClick: onClick, ...props}) => {
4878
- 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
+ })({
4879
5998
  elevationEffect: !0,
4880
5999
  elevationClass: elevationClass,
4881
6000
  clickable: Boolean(onClick),
@@ -9256,216 +10375,13 @@ function getRowId$1(row, rowKey) {
9256
10375
 
9257
10376
  /**
9258
10377
  * Hook for managing DataTable state and behavior
9259
- */ 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}) {
9260
- // Sort state
9261
- 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((() => {
9262
- const visibility = {};
9263
- return columns.forEach((col => {
9264
- visibility[col.key] = !1 !== col.visible;
9265
- })), visibility;
9266
- })), [columnFilterValues, setColumnFilterValues] = useState({});
9267
- // Pagination state
9268
- // Update column order when columns prop changes
9269
- useEffect((() => {
9270
- const newOrder = columns.map((col => col.key)), currentOrderSet = new Set(columnOrder), newOrderSet = new Set(newOrder);
9271
- // Only update if there are actual differences
9272
- newOrder.length === columnOrder.length && newOrder.every((key => currentOrderSet.has(key))) && columnOrder.every((key => newOrderSet.has(key))) || setColumnOrder(newOrder);
9273
- }), [ columns ]),
9274
- // Update column visibility when columns prop changes
9275
- useEffect((() => {
9276
- setColumnVisibility((prev => {
9277
- const updated = {
9278
- ...prev
9279
- };
9280
- return columns.forEach((col => {
9281
- col.key in updated || (updated[col.key] = !1 !== col.visible);
9282
- })), updated;
9283
- }));
9284
- }), [ columns ]);
9285
- // Visible columns based on order and visibility
9286
- 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 => {
9287
- if (!sortable) return;
9288
- let direction = "asc";
9289
- sortConfig && sortConfig.key === key && "asc" === sortConfig.direction && (direction = "desc");
9290
- const newSortConfig = {
9291
- key: key,
9292
- direction: direction
9293
- };
9294
- setSortConfig(newSortConfig), onSort && onSort(newSortConfig);
9295
- }), [ sortable, sortConfig, onSort ]), handlePageChange = useCallback((page => {
9296
- page < 1 || setCurrentPage(page);
9297
- }), []), handleSearch = useCallback((query => {
9298
- setSearchQuery(query), setCurrentPage(1);
9299
- } // Reset to first page when searching
9300
- ), []), handleColumnFilterChange = useCallback(((columnKey, value) => {
9301
- setColumnFilterValues((prev => ({
9302
- ...prev,
9303
- [columnKey]: value
9304
- }))), setCurrentPage(1);
9305
- } // Reset to first page when filtering
9306
- ), []), clearColumnFilters = useCallback((() => {
9307
- setColumnFilterValues({}), setCurrentPage(1);
9308
- }), []), activeColumnFilters = useMemo((() => columnFilters ? Object.entries(columnFilterValues).filter((([, value]) => null != value && "" !== value)).map((([columnKey, value]) => {
9309
- const column = columns.find((col => col.key === columnKey));
9310
- return column && column.filterable ? {
9311
- key: columnKey,
9312
- value: value,
9313
- lowercaseValue: "string" == typeof value ? value.toLowerCase() : String(value).toLowerCase(),
9314
- column: column
9315
- } : null;
9316
- })).filter((f => null !== f)) : []), [ columnFilters, columnFilterValues, columns ]), filteredData = useMemo((() => {
9317
- if (!searchQuery && 0 === activeColumnFilters.length) return data;
9318
- const lowercaseQuery = searchQuery ? searchQuery.toLowerCase() : "";
9319
- return data.filter((row => {
9320
- // Apply global search
9321
- if (searchQuery && !visibleColumns.some((column => {
9322
- var _context;
9323
- const value = row[column.key];
9324
- return null != value && _includesInstanceProperty(_context = String(value).toLowerCase()).call(_context, lowercaseQuery);
9325
- }))) return !1;
9326
- // Apply column-specific filters
9327
- for (let i = 0; i < activeColumnFilters.length; i++) {
9328
- const filter = activeColumnFilters[i];
9329
- if (!filter) continue;
9330
- const {key: key, value: value, lowercaseValue: lowercaseValue, column: column} = filter, cellValue = row[key];
9331
- if (null == cellValue) return !1;
9332
- // Use custom filter function if provided
9333
- var _context2;
9334
- // Default text filter
9335
- if (column.filterFunction) {
9336
- if (!column.filterFunction(cellValue, value)) return !1;
9337
- } else if (!_includesInstanceProperty(_context2 = String(cellValue).toLowerCase()).call(_context2, lowercaseValue)) return !1;
9338
- }
9339
- return !0;
9340
- }));
9341
- }), [ data, visibleColumns, searchQuery, activeColumnFilters ]), sortedData = useMemo((() => sortConfig && sortable ? [ ...filteredData ].sort(((a, b) => {
9342
- const aValue = a[sortConfig.key], bValue = b[sortConfig.key];
9343
- 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;
9344
- })) : filteredData), [ filteredData, sortConfig, sortable ]), paginatedData = useMemo((() => {
9345
- if (!paginated) return sortedData;
9346
- const startIndex = (currentPage - 1) * pageSize;
9347
- return sortedData.slice(startIndex, startIndex + pageSize);
9348
- }), [ 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) => {
9349
- if ("none" === selectionMode) return;
9350
- let newSelectedIds;
9351
- if (newSelectedIds = "single" === selectionMode ? selected ? [ rowId ] : [] :
9352
- // multiple
9353
- selected ? [ ...selectedRowIds, rowId ] : selectedRowIds.filter((id => id !== rowId)),
9354
- controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
9355
- const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
9356
- onSelectionChange(selectedRowsData, newSelectedIds);
9357
- }
9358
- }), [ selectionMode, selectedRowIds, controlledSelectedRowIds, onSelectionChange, sortedData, rowKey ]), handleSelectAll = useCallback((selected => {
9359
- if ("multiple" !== selectionMode) return;
9360
- const newSelectedIds = selected ? paginatedData.map((row => getRowId$1(row, rowKey))) : [];
9361
- if (controlledSelectedRowIds || setInternalSelectedRowIds(newSelectedIds), onSelectionChange) {
9362
- const selectedRowsData = sortedData.filter((row => _includesInstanceProperty(newSelectedIds).call(newSelectedIds, getRowId$1(row, rowKey))));
9363
- onSelectionChange(selectedRowsData, newSelectedIds);
9364
- }
9365
- }), [ 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((() => {
9366
- if ("multiple" !== selectionMode || 0 === paginatedData.length) return !1;
9367
- const selectedCount = paginatedData.filter((row => _includesInstanceProperty(selectedRowIds).call(selectedRowIds, getRowId$1(row, rowKey)))).length;
9368
- return selectedCount > 0 && selectedCount < paginatedData.length;
9369
- }), [ selectionMode, paginatedData, selectedRowIds, rowKey ]), handleColumnVisibilityToggle = useCallback((columnKey => {
9370
- setColumnVisibility((prev => {
9371
- const updated = {
9372
- ...prev,
9373
- [columnKey]: !prev[columnKey]
9374
- };
9375
- if (onColumnVisibilityChange) {
9376
- const visibleKeys = Object.entries(updated).filter((([, visible]) => visible)).map((([key]) => key));
9377
- onColumnVisibilityChange(visibleKeys);
9378
- }
9379
- return updated;
9380
- }));
9381
- }), [ onColumnVisibilityChange ]);
9382
- // Handle sorting
9383
- // Handle column reorder
9384
- return useCallback(((fromIndex, toIndex) => {
9385
- const newOrder = [ ...columnOrder ], [removed] = newOrder.splice(fromIndex, 1);
9386
- removed && (newOrder.splice(toIndex, 0, removed), setColumnOrder(newOrder), onColumnReorder && onColumnReorder(newOrder));
9387
- }), [ columnOrder, onColumnReorder ]),
9388
- // Reset to first page when data changes
9389
- useEffect((() => {
9390
- setCurrentPage(1);
9391
- }), [ data ]),
9392
- // Reset current page if it's out of bounds
9393
- useEffect((() => {
9394
- currentPage > totalPages && totalPages > 0 && setCurrentPage(Math.max(1, totalPages));
9395
- }), [ currentPage, totalPages ]), {
9396
- displayData: paginatedData,
9397
- sortConfig: sortConfig,
9398
- currentPage: currentPage,
9399
- totalPages: totalPages,
9400
- handleSort: handleSort,
9401
- handlePageChange: handlePageChange,
9402
- handleSearch: handleSearch,
9403
- selectedRowIds: selectedRowIds,
9404
- selectedRows: selectedRows,
9405
- handleRowSelect: handleRowSelect,
9406
- handleSelectAll: handleSelectAll,
9407
- isAllSelected: isAllSelected,
9408
- isIndeterminate: isIndeterminate,
9409
- columnOrder: columnOrder,
9410
- visibleColumns: visibleColumns,
9411
- columnVisibility: columnVisibility,
9412
- handleColumnVisibilityToggle: handleColumnVisibilityToggle,
9413
- columnFilterValues: columnFilterValues,
9414
- handleColumnFilterChange: handleColumnFilterChange,
9415
- clearColumnFilters: clearColumnFilters
9416
- };
9417
- }
10378
+ */ Countdown.displayName = "Countdown";
9418
10379
 
9419
- Countdown.displayName = "Countdown";
9420
-
9421
- const DOTS = "...", range = (start, end) => {
10380
+ const range = (start, end) => {
9422
10381
  const length = end - start + 1;
9423
10382
  return Array.from({
9424
10383
  length: length
9425
10384
  }, ((_, idx) => idx + start));
9426
- }, usePagination = ({currentPage: currentPage, totalPages: totalPages, siblingCount: siblingCount = 1, onPageChange: onPageChange}) => {
9427
- const paginationRange = useMemo((() => {
9428
- // siblingCount + firstPage + lastPage + currentPage + 2*DOTS
9429
- /*
9430
- Case 1: If the number of pages is less than the page numbers we want to show in our
9431
- paginationComponent, we return the range [1..totalPages]
9432
- */
9433
- if (siblingCount + 5 >= totalPages) return range(1, totalPages);
9434
- const leftSiblingIndex = Math.max(currentPage - siblingCount, 1), rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages), shouldShowLeftDots = leftSiblingIndex > 2, shouldShowRightDots = rightSiblingIndex < totalPages - 2, lastPageIndex = totalPages;
9435
- /*
9436
- Case 2: No left dots to show, but rights dots to be shown
9437
- */
9438
- return !shouldShowLeftDots && shouldShowRightDots ? [ ...range(1, 3 + 2 * siblingCount), "...", totalPages ] :
9439
- /*
9440
- Case 3: No right dots to show, but left dots to be shown
9441
- */
9442
- shouldShowLeftDots && !shouldShowRightDots ? [ 1, "...", ...range(totalPages - (3 + 2 * siblingCount) + 1, totalPages) ] :
9443
- /*
9444
- Case 4: Both left and right dots to be shown
9445
- */
9446
- shouldShowLeftDots && shouldShowRightDots ? [ 1, "...", ...range(leftSiblingIndex, rightSiblingIndex), "...", lastPageIndex ] : [];
9447
- }), [ totalPages, siblingCount, currentPage ]), goToPage = page => {
9448
- page >= 1 && page <= totalPages && page !== currentPage && onPageChange(page);
9449
- };
9450
- return {
9451
- paginationRange: paginationRange,
9452
- currentPage: currentPage,
9453
- totalPages: totalPages,
9454
- goToPage: goToPage,
9455
- nextPage: () => {
9456
- goToPage(currentPage + 1);
9457
- },
9458
- prevPage: () => {
9459
- goToPage(currentPage - 1);
9460
- },
9461
- firstPage: () => {
9462
- goToPage(1);
9463
- },
9464
- lastPage: () => {
9465
- goToPage(totalPages);
9466
- },
9467
- DOTS: "..."
9468
- };
9469
10385
  }, PaginationNavButton = memo((({type: type, onClick: onClick, disabled: disabled, label: label, iconName: iconName}) => jsx("li", {
9470
10386
  className: `c-pagination__item c-pagination__item--${type} ${disabled ? "is-disabled" : ""}`,
9471
10387
  "aria-disabled": disabled,
@@ -9482,7 +10398,50 @@ const DOTS = "...", range = (start, end) => {
9482
10398
  })
9483
10399
  })
9484
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}) => {
9485
- 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
+ })({
9486
10445
  currentPage: currentPage,
9487
10446
  totalPages: totalPages,
9488
10447
  siblingCount: siblingCount,
@@ -9694,14 +10653,13 @@ const DropdownContext = createContext({
9694
10653
  id: "",
9695
10654
  trigger: "click"
9696
10655
  }), DropdownMenu = forwardRef((({children: children, className: className = "", ...props}, ref) => {
9697
- const {glass: glass} = useContext(DropdownStyleContext);
9698
- // We need to access glass prop here?
9699
- // Wait, the original code wrapped <ul> in Context Provider.
9700
- // And applied glass wrapper around <ul>.
9701
- // If we use Compound Component, DropdownMenu should be the list.
9702
- return jsx("ul", {
10656
+ const {glass: glass} = useContext(DropdownStyleContext), {id: id} = useContext(DropdownContext);
10657
+ return jsx("ul", {
9703
10658
  ref: ref,
10659
+ id: `${id}-menu`,
9704
10660
  className: `c-dropdown__menu ${glass ? "c-dropdown__menu--glass" : ""} ${className}`.trim(),
10661
+ role: "menu",
10662
+ "aria-labelledby": `${id}-trigger`,
9705
10663
  ...props,
9706
10664
  children: children
9707
10665
  });
@@ -9710,14 +10668,23 @@ const DropdownContext = createContext({
9710
10668
  // Compound Components
9711
10669
  DropdownMenu.displayName = "DropdownMenu";
9712
10670
 
9713
- const DropdownTrigger = forwardRef((({children: children, className: className = "", onClick: onClick, onKeyDown: onKeyDown, ...props}, ref) => jsx("div", {
9714
- ref: ref,
9715
- className: `c-dropdown__toggle ${className}`.trim(),
9716
- onClick: onClick,
9717
- onKeyDown: onKeyDown,
9718
- ...props,
9719
- children: children
9720
- })));
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
+ }));
9721
10688
 
9722
10689
  DropdownTrigger.displayName = "DropdownTrigger";
9723
10690
 
@@ -9957,7 +10924,165 @@ Dropdown.Item = DropdownItem, Dropdown.Divider = DropdownDivider, Dropdown.Heade
9957
10924
  * ```
9958
10925
  */
9959
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}) => {
9960
- 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
+ }({
9961
11086
  data: data,
9962
11087
  columns: columns,
9963
11088
  sortable: sortable,
@@ -12311,297 +13436,6 @@ function useTodo(initialProps) {
12311
13436
  };
12312
13437
  }
12313
13438
 
12314
- /**
12315
- * Breadcrumb state and functionality
12316
- * @param initialOptions - Initial breadcrumb options
12317
- * @returns Breadcrumb state and methods
12318
- */ function useBreadcrumb(initialOptions) {
12319
- return {
12320
- defaultOptions: {
12321
- items: [],
12322
- divider: BREADCRUMB.DEFAULTS.DIVIDER,
12323
- className: "",
12324
- "aria-label": "Breadcrumb",
12325
- ...initialOptions
12326
- },
12327
- generateBreadcrumbClass: options => {
12328
- const {className: className = ""} = options;
12329
- return [ BREADCRUMB.CLASSES.BASE, className ].filter(Boolean).join(" ").trim();
12330
- },
12331
- generateItemClass: (item, isLast) => [ BREADCRUMB.CLASSES.ITEM, item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : "" ].filter(Boolean).join(" ").trim(),
12332
- isItemLink: (item, isLast) => Boolean(item.href && !item.active && !isLast),
12333
- parseItemsFromJson: jsonString => {
12334
- try {
12335
- return JSON.parse(jsonString);
12336
- } catch (error) {
12337
- return console.error("Error parsing breadcrumb items:", error), [];
12338
- }
12339
- }
12340
- };
12341
- }
12342
-
12343
- /**
12344
- * Hook for managing modal state
12345
- */ function useModal$1({isOpen: isOpenProp, onOpenChange: onOpenChange, onOpen: onOpen, onClose: onClose} = {}) {
12346
- // For uncontrolled usage
12347
- const [isOpenState, setIsOpenState] = useState(!1), isControlled = void 0 !== isOpenProp, isOpen = isControlled ? !!isOpenProp : isOpenState;
12348
- // Determine if we're in controlled or uncontrolled mode
12349
- // Update internal state when prop changes (for controlled mode)
12350
- useEffect((() => {
12351
- isControlled && setIsOpenState(!!isOpenProp);
12352
- }), [ isOpenProp, isControlled ]);
12353
- const updateOpen = useCallback((nextIsOpen => {
12354
- // For uncontrolled mode, update internal state
12355
- isControlled || setIsOpenState(nextIsOpen),
12356
- // Call the change handler in either mode
12357
- onOpenChange && onOpenChange(nextIsOpen),
12358
- // Call the specific handler
12359
- nextIsOpen && onOpen ? onOpen() : !nextIsOpen && onClose && onClose();
12360
- }), [ isControlled, onOpenChange, onOpen, onClose ]), open = useCallback((() => {
12361
- updateOpen(!0);
12362
- }), [ updateOpen ]), close = useCallback((() => {
12363
- updateOpen(!1);
12364
- }), [ updateOpen ]), toggle = useCallback((() => {
12365
- updateOpen(!isOpen);
12366
- }), [ isOpen, updateOpen ]);
12367
- return {
12368
- isOpen: isOpen,
12369
- open: open,
12370
- close: close,
12371
- toggle: toggle
12372
- };
12373
- }
12374
-
12375
- function useSlider(options) {
12376
- 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({
12377
- isTransitioning: !1,
12378
- loop: loop,
12379
- slides: slides,
12380
- slidesToShow: slidesToShow,
12381
- speed: speed,
12382
- onSlideChange: onSlideChange
12383
- }), [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) => ({
12384
- ...slide,
12385
- id: `set1-${slide.id || i}`
12386
- }))), ...slides.map(((slide, i) => ({
12387
- ...slide,
12388
- id: `set2-${slide.id || i}`
12389
- }))), ...slides.map(((slide, i) => ({
12390
- ...slide,
12391
- id: `set3-${slide.id || i}`
12392
- }))) ] : slides), [ slides, loop ]), loopedSlides = slides.length, translateValue = useMemo((() => 0 === slideWidth ? 0 : -internalIndex * slideWidth + dragOffset), [ slideWidth, internalIndex, dragOffset ]);
12393
- // Update the ref whenever the relevant state/props change
12394
- useEffect((() => {
12395
- sliderStateRef.current = {
12396
- isTransitioning: isTransitioning,
12397
- loop: loop,
12398
- slides: slides,
12399
- slidesToShow: slidesToShow,
12400
- speed: speed,
12401
- onSlideChange: onSlideChange
12402
- };
12403
- }), [ isTransitioning, loop, slides, slidesToShow, speed, onSlideChange ]),
12404
- // Autoplay effect
12405
- useEffect((() => {
12406
- if (!autoplay) return autoplayRef.current && (clearInterval(autoplayRef.current),
12407
- autoplayRef.current = null), void setAutoplayRunning(!1);
12408
- const autoplayParams = "boolean" == typeof autoplay ? {
12409
- delay: 3e3
12410
- } : autoplay, {delay: delay = 3e3, pauseOnMouseEnter: pauseOnMouseEnter = !1, disableOnInteraction: disableOnInteraction = !1, reverseDirection: reverseDirection = !1} = autoplayParams;
12411
- // Clear any existing interval
12412
- autoplayRef.current && clearInterval(autoplayRef.current),
12413
- // Create new interval
12414
- autoplayRef.current = setInterval((() => {
12415
- // Use ref to get the latest state without resetting the interval
12416
- const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
12417
- // We need to use a functional update to get the latest values
12418
- setRealIndex((prevRealIndex => {
12419
- if (currentIsTransitioning) return prevRealIndex;
12420
- // Stop autoplay on interaction if disableOnInteraction is true
12421
- let nextIndex;
12422
- // Trigger the slide change
12423
- if (disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12424
- autoplayRef.current = null, setAutoplayRunning(!1)), nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
12425
- reverseDirection) {
12426
- // For reverse direction, we would go to previous slide
12427
- const prevIndex = currentLoop ? 0 === prevRealIndex ? currentSlides.length - 1 : prevRealIndex - 1 : Math.max(prevRealIndex - 1, 0);
12428
- return setInternalIndex(currentLoop ? currentSlides.length + prevIndex : prevIndex),
12429
- setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
12430
- setIsTransitioning(!1), currentOnSlideChange?.(prevIndex);
12431
- }), currentSpeed), prevIndex;
12432
- }
12433
- // Normal direction
12434
- return setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex),
12435
- setIsTransitioning(!0), setDragOffset(0), setTimeout((() => {
12436
- setIsTransitioning(!1), currentOnSlideChange?.(nextIndex),
12437
- // Reposition after transition ends for looped sliders
12438
- currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
12439
- setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
12440
- repositioningRef.current = !1;
12441
- }), 0));
12442
- }), currentSpeed), nextIndex;
12443
- }));
12444
- }), delay), setAutoplayRunning(!0);
12445
- // Handle pause on mouse enter/leave if enabled
12446
- let containerElement = null;
12447
- const handleMouseEnter = () => {
12448
- autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null,
12449
- setAutoplayRunning(!1));
12450
- }, handleMouseLeave = () => {
12451
- // Restart autoplay
12452
- autoplayRef.current && clearInterval(autoplayRef.current), autoplayRef.current = setInterval((() => {
12453
- const {isTransitioning: currentIsTransitioning, loop: currentLoop, slides: currentSlides, slidesToShow: currentSlidesToShow, speed: currentSpeed, onSlideChange: currentOnSlideChange} = sliderStateRef.current;
12454
- setRealIndex((prevRealIndex => {
12455
- if (currentIsTransitioning) return prevRealIndex;
12456
- let nextIndex;
12457
- return nextIndex = currentLoop ? (prevRealIndex + 1) % currentSlides.length : Math.min(prevRealIndex + 1, currentSlides.length - currentSlidesToShow),
12458
- setInternalIndex(currentLoop ? currentSlides.length + nextIndex : nextIndex), setIsTransitioning(!0),
12459
- setDragOffset(0), setTimeout((() => {
12460
- setIsTransitioning(!1), currentOnSlideChange?.(nextIndex), currentLoop && nextIndex >= 2 * currentSlides.length && (repositioningRef.current = !0,
12461
- setInternalIndex(currentSlides.length + nextIndex), setTimeout((() => {
12462
- repositioningRef.current = !1;
12463
- }), 0));
12464
- }), currentSpeed), nextIndex;
12465
- }));
12466
- }), delay), setAutoplayRunning(!0);
12467
- };
12468
- // Cleanup
12469
- return pauseOnMouseEnter && containerRef.current && (containerElement = containerRef.current,
12470
- containerElement.addEventListener("mouseenter", handleMouseEnter), containerElement.addEventListener("mouseleave", handleMouseLeave)),
12471
- () => {
12472
- autoplayRef.current && (clearInterval(autoplayRef.current), autoplayRef.current = null),
12473
- containerElement && (containerElement.removeEventListener("mouseenter", handleMouseEnter),
12474
- containerElement.removeEventListener("mouseleave", handleMouseLeave)), setAutoplayRunning(!1);
12475
- };
12476
- }), [ autoplay, repositioningRef ]),
12477
- // Initialize
12478
- useEffect((() => {
12479
- setInternalIndex(loop ? slides.length + initialSlide : initialSlide);
12480
- }), [ loop, slides.length, initialSlide ]), useEffect((() => {
12481
- const updateSize = () => {
12482
- if (containerRef.current) {
12483
- const size = "horizontal" === direction ? containerRef.current.offsetWidth : containerRef.current.offsetHeight;
12484
- setContainerSize(size);
12485
- }
12486
- };
12487
- return updateSize(), window.addEventListener("resize", updateSize), () => window.removeEventListener("resize", updateSize);
12488
- }), [ direction ]);
12489
- const slideNext = useCallback((() => {
12490
- if (!isTransitioning) if (
12491
- // Stop autoplay on interaction if disableOnInteraction is true
12492
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12493
- autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
12494
- const nextRealIndex = (realIndex + 1) % slides.length, nextInternalIndex = internalIndex + 1;
12495
- setRealIndex(nextRealIndex), setInternalIndex(nextInternalIndex), setIsTransitioning(!0),
12496
- setDragOffset(0), setTimeout((() => {
12497
- setIsTransitioning(!1), onSlideChange?.(nextRealIndex),
12498
- // Reposition after transition ends
12499
- nextInternalIndex >= 2 * slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + nextRealIndex),
12500
- setTimeout((() => {
12501
- repositioningRef.current = !1;
12502
- }), 0));
12503
- }), speed);
12504
- } else {
12505
- const nextIndex = Math.min(realIndex + 1, slides.length - slidesToShow);
12506
- setRealIndex(nextIndex), setInternalIndex(nextIndex), setIsTransitioning(!0), setDragOffset(0),
12507
- setTimeout((() => {
12508
- setIsTransitioning(!1), onSlideChange?.(nextIndex);
12509
- }), speed);
12510
- }
12511
- }), [ realIndex, internalIndex, slides.length, slidesToShow, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), slidePrev = useCallback((() => {
12512
- if (!isTransitioning) if (
12513
- // Stop autoplay on interaction if disableOnInteraction is true
12514
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12515
- autoplayRef.current = null, setAutoplayRunning(!1)), loop) {
12516
- const prevRealIndex = 0 === realIndex ? slides.length - 1 : realIndex - 1, prevInternalIndex = internalIndex - 1;
12517
- setRealIndex(prevRealIndex), setInternalIndex(prevInternalIndex), setIsTransitioning(!0),
12518
- setDragOffset(0), setTimeout((() => {
12519
- setIsTransitioning(!1), onSlideChange?.(prevRealIndex),
12520
- // Reposition after transition ends
12521
- prevInternalIndex < slides.length && (repositioningRef.current = !0, setInternalIndex(slides.length + prevRealIndex),
12522
- setTimeout((() => {
12523
- repositioningRef.current = !1;
12524
- }), 0));
12525
- }), speed);
12526
- } else {
12527
- const prevIndex = Math.max(realIndex - 1, 0);
12528
- setRealIndex(prevIndex), setInternalIndex(prevIndex), setIsTransitioning(!0), setDragOffset(0),
12529
- setTimeout((() => {
12530
- setIsTransitioning(!1), onSlideChange?.(prevIndex);
12531
- }), speed);
12532
- }
12533
- }), [ realIndex, internalIndex, slides.length, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), goToSlide = useCallback((index => {
12534
- isTransitioning || index === realIndex || (
12535
- // Stop autoplay on interaction if disableOnInteraction is true
12536
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12537
- autoplayRef.current = null, setAutoplayRunning(!1)), setIsTransitioning(!0), setDragOffset(0),
12538
- setRealIndex(index), setInternalIndex(loop ? slides.length + index : index), setTimeout((() => {
12539
- setIsTransitioning(!1), onSlideChange?.(index);
12540
- }), speed));
12541
- }), [ realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay ]), handleTouchStart = useCallback((e => {
12542
- if (!allowTouchMove) return;
12543
- // Stop autoplay on interaction if disableOnInteraction is true
12544
- autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
12545
- autoplayRef.current = null, setAutoplayRunning(!1));
12546
- const client = "horizontal" === direction ? "touches" in e ? e.touches[0]?.clientX || 0 : e.clientX : "touches" in e ? e.touches[0]?.clientY || 0 : e.clientY;
12547
- setTouchStart(client), setTouching(!0), setDragOffset(0);
12548
- }), [ allowTouchMove, direction, autoplay ]), handleTouchMove = useCallback((e => {
12549
- if (!touching || !allowTouchMove) return;
12550
- 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;
12551
- Math.abs(diff) > 10 && (e.preventDefault(), setDragOffset(.5 * -diff));
12552
- }), [ touching, touchStart, allowTouchMove, direction ]), handleTouchEnd = useCallback((e => {
12553
- if (!touching || !allowTouchMove) return;
12554
- 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;
12555
- setTouching(!1), setDragOffset(0), Math.abs(diff) > threshold && (diff > 0 ? slideNext() : slidePrev());
12556
- }), [ touching, touchStart, threshold, slideNext, slidePrev, allowTouchMove, direction ]), canSlideNext = loop || realIndex < slides.length - slidesToShow, canSlidePrev = loop || realIndex > 0;
12557
- return {
12558
- activeIndex: realIndex,
12559
- realIndex: realIndex,
12560
- previousIndex: realIndex,
12561
- isBeginning: !loop && 0 === realIndex,
12562
- isEnd: !loop && realIndex >= slides.length - slidesToShow,
12563
- progress: slides.length > 0 ? realIndex / (slides.length - 1) : 0,
12564
- autoplayRunning: autoplayRunning,
12565
- transitioning: isTransitioning,
12566
- touching: touching,
12567
- translate: translateValue,
12568
- slidesPerView: slidesToShow,
12569
- slidesCount: slides.length,
12570
- isLocked: !1,
12571
- destroyed: !1,
12572
- size: containerSize,
12573
- touches: {
12574
- startX: 0,
12575
- startY: 0,
12576
- currentX: 0,
12577
- currentY: 0,
12578
- diff: 0
12579
- },
12580
- allowSlideNext: canSlideNext,
12581
- allowSlidePrev: canSlidePrev,
12582
- allowTouchMove: allowTouchMove,
12583
- animating: isTransitioning,
12584
- enabled: !0,
12585
- initialized: !0,
12586
- slideNext: slideNext,
12587
- slidePrev: slidePrev,
12588
- goToSlide: goToSlide,
12589
- canSlideNext: canSlideNext,
12590
- canSlidePrev: canSlidePrev,
12591
- containerRef: containerRef,
12592
- wrapperRef: wrapperRef,
12593
- handleTouchStart: handleTouchStart,
12594
- handleTouchMove: handleTouchMove,
12595
- handleTouchEnd: handleTouchEnd,
12596
- allSlides: allSlides,
12597
- translateValue: translateValue,
12598
- slideWidth: slideWidth,
12599
- currentSlidesToShow: slidesToShow,
12600
- loopedSlides: loopedSlides,
12601
- repositioningRef: repositioningRef
12602
- };
12603
- }
12604
-
12605
13439
  /**
12606
13440
  * Simplified hook for chart data processing
12607
13441
  */ function useChartData(data) {
@@ -12702,37 +13536,44 @@ function useSlider(options) {
12702
13536
  // Accordion composables
12703
13537
  const composablesImport = Object.freeze( Object.defineProperty({
12704
13538
  __proto__: null,
12705
- 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,
12706
13551
  useAccordion: useAccordion,
12707
13552
  useAtomixGlass: useAtomixGlass,
12708
13553
  useBadge: useBadge,
12709
13554
  useBarChart: useBarChart,
12710
13555
  useBlock: useBlock,
12711
- useBreadcrumb: useBreadcrumb,
12712
- useCard: useCard,
12713
13556
  useChartData: useChartData,
12714
13557
  useChartInteraction: useChartInteraction,
12715
13558
  useChartScale: useChartScale,
12716
- useDataTable: useDataTable,
12717
13559
  useEdgePanel: useEdgePanel,
12718
13560
  useForm: useForm,
12719
13561
  useFormGroup: useFormGroup,
12720
13562
  useHero: useHero,
12721
13563
  useInput: useInput,
12722
13564
  useLineChart: useLineChart,
12723
- useModal: useModal$1,
12724
13565
  useNav: useNav,
12725
13566
  useNavDropdown: useNavDropdown,
12726
13567
  useNavItem: useNavItem,
12727
13568
  useNavbar: useNavbar,
12728
- usePagination: usePagination,
13569
+ usePerformanceMonitor: usePerformanceMonitor,
12729
13570
  usePieChart: usePieChart,
12730
13571
  useRadio: useRadio,
13572
+ useResponsiveGlass: useResponsiveGlass,
12731
13573
  useRiver: useRiver,
12732
13574
  useSelect: useSelect,
12733
13575
  useSideMenu: useSideMenu,
12734
13576
  useSideMenuItem: useSideMenuItem,
12735
- useSlider: useSlider,
12736
13577
  useSpinner: useSpinner,
12737
13578
  useTextarea: useTextarea,
12738
13579
  useTodo: useTodo
@@ -12823,7 +13664,10 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12823
13664
  };
12824
13665
  }), []);
12825
13666
  // Toggle dropdown
12826
- 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 => {
12827
13671
  if (setSelectedLabel(option.label), setIsOpen(!1), bodyRef.current && (bodyRef.current.style.height = "0px"),
12828
13672
  nativeSelectRef.current && (nativeSelectRef.current.value = option.value), onChange) {
12829
13673
  // Create a synthetic event
@@ -12867,7 +13711,15 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12867
13711
  "aria-describedby": ariaDescribedBy,
12868
13712
  "aria-invalid": invalid,
12869
13713
  style: {
12870
- 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"
12871
13723
  },
12872
13724
  children: [ placeholder && jsx("option", {
12873
13725
  value: "",
@@ -12880,11 +13732,29 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12880
13732
  }, option.value))) ]
12881
13733
  }), jsx("div", {
12882
13734
  className: SELECT.CLASSES.SELECTED,
12883
- onClick: () => {
12884
- disabled || (!isOpen && bodyRef.current && panelRef.current ? bodyRef.current.style.height = `${panelRef.current.clientHeight}px` : bodyRef.current && (bodyRef.current.style.height = "0px"),
12885
- 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
+ }
12886
13751
  },
12887
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,
12888
13758
  children: selectedLabel
12889
13759
  }), jsx("i", {
12890
13760
  className: `${SELECT.CLASSES.ICON_CARET} ${SELECT.CLASSES.TOGGLE_ICON}`
@@ -12899,10 +13769,16 @@ const Select = memo((({options: options, value: value, onChange: onChange, onBl
12899
13769
  ref: panelRef,
12900
13770
  children: jsx("ul", {
12901
13771
  className: SELECT.CLASSES.SELECT_ITEMS,
13772
+ role: "listbox",
13773
+ id: id ? `${id}-listbox` : void 0,
13774
+ "aria-labelledby": id,
12902
13775
  children: hasOptionsProp ? options.map(((option, index) => jsx("li", {
12903
13776
  className: SELECT.CLASSES.SELECT_ITEM,
12904
13777
  "data-value": option.value,
12905
13778
  onClick: () => !option.disabled && handleItemClick(option),
13779
+ role: "option",
13780
+ "aria-selected": value === option.value,
13781
+ "aria-disabled": option.disabled,
12906
13782
  children: jsxs("label", {
12907
13783
  htmlFor: `SelectItem${index}`,
12908
13784
  className: "c-checkbox",
@@ -14438,7 +15314,45 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14438
15314
  onOpenChange: onOpenChange,
14439
15315
  onClose: onClose,
14440
15316
  onOpen: onOpen
14441
- });
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}`;
14442
15356
  // Handle keyboard events for Escape key
14443
15357
  useEffect((() => {
14444
15358
  if (!keyboard) return;
@@ -14456,15 +15370,19 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14456
15370
  return React.isValidElement(child) && _includesInstanceProperty(_context = [ "ModalHeader", "ModalBody", "ModalFooter" ]).call(_context, child.type.displayName);
14457
15371
  })), modalContent = jsx("div", {
14458
15372
  className: "c-modal__content",
15373
+ ref: contentRef,
14459
15374
  children: hasCompoundComponents ? React.Children.map(children, (child => React.isValidElement(child) && "ModalHeader" === child.type.displayName ? React.cloneElement(child, {
14460
- onClose: child.props.onClose || close
15375
+ onClose: child.props.onClose || close,
15376
+ id: titleId
14461
15377
  }) : child)) : jsxs(Fragment, {
14462
15378
  children: [ (title || closeButton) && jsx(ModalHeader, {
15379
+ id: titleId,
14463
15380
  title: title,
14464
15381
  subtitle: subtitle,
14465
15382
  closeButton: closeButton,
14466
15383
  onClose: close
14467
15384
  }), jsx(ModalBody, {
15385
+ id: descId,
14468
15386
  children: children
14469
15387
  }), footer && jsx(ModalFooter, {
14470
15388
  children: footer
@@ -14482,6 +15400,8 @@ const ModalImpl = memo((({children: children, isOpen: isOpen = !1, onOpenChange
14482
15400
  role: "dialog",
14483
15401
  "aria-modal": "true",
14484
15402
  "aria-hidden": !isOpenState,
15403
+ "aria-labelledby": titleId,
15404
+ "aria-describedby": descId,
14485
15405
  ...props,
14486
15406
  children: [ jsx("div", {
14487
15407
  ref: backdropRef,
@@ -14728,7 +15648,27 @@ const Navbar = forwardRef((({brand: brand, children: children, variant: variant
14728
15648
  return window.addEventListener("resize", handleResize), () => {
14729
15649
  window.removeEventListener("resize", handleResize);
14730
15650
  };
14731
- }), [ 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 ]);
14732
15672
  // Generate the navbar class
14733
15673
  const navbarClass = generateNavbarClass({
14734
15674
  position: position,
@@ -17320,7 +18260,235 @@ const SectionIntro = ({title: title, label: label, text: text, actions: actions,
17320
18260
  SectionIntro.displayName = "SectionIntro";
17321
18261
 
17322
18262
  const Slider = forwardRef(((props, ref) => {
17323
- 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
+ }({
17324
18492
  slides: validSlides,
17325
18493
  slidesToShow: slidesToShow,
17326
18494
  spaceBetween: spaceBetween,
@@ -17347,7 +18515,8 @@ const Slider = forwardRef(((props, ref) => {
17347
18515
  })
17348
18516
  });
17349
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(" ");
17350
- return jsxs("div", {
18518
+ // Keyboard navigation
18519
+ return jsxs("div", {
17351
18520
  ref: ref || containerRef,
17352
18521
  className: containerClasses,
17353
18522
  style: {
@@ -17365,9 +18534,32 @@ const Slider = forwardRef(((props, ref) => {
17365
18534
  onMouseMove: handleTouchMove,
17366
18535
  onMouseUp: handleTouchEnd,
17367
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,
17368
18559
  children: [ jsx("div", {
17369
18560
  ref: wrapperRef,
17370
18561
  className: "c-slider__wrapper",
18562
+ "aria-live": autoplay ? "off" : "polite",
17371
18563
  style: {
17372
18564
  display: "flex",
17373
18565
  flexDirection: "vertical" === direction ? "column" : "row",
@@ -17545,15 +18737,24 @@ Steps.displayName = "Steps", Steps.Item = StepsItem, Steps.Step = StepsItem;
17545
18737
  // Context for compound usage
17546
18738
  const TabsContext = createContext({
17547
18739
  currentTab: 0,
17548
- handleTabClick: () => {}
17549
- }), TabsList = forwardRef((({children: children, className: className = "", ...props}, ref) => jsx("ul", {
17550
- ref: ref,
17551
- className: `c-tabs__nav ${className}`.trim(),
17552
- ...props,
17553
- children: React.Children.map(children, ((child, index) => React.isValidElement(child) ? React.cloneElement(child, {
17554
- index: index
17555
- }) : child))
17556
- })));
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
+ }));
17557
18758
 
17558
18759
  // Compound components
17559
18760
  TabsList.displayName = "TabsList";
@@ -17565,8 +18766,10 @@ const TabsTrigger = forwardRef((({children: children, className: className = ""
17565
18766
  const isActive = void 0 !== index && currentTab === index;
17566
18767
  return jsx("li", {
17567
18768
  className: "c-tabs__nav-item",
18769
+ role: "presentation",
17568
18770
  children: jsx("button", {
17569
18771
  ref: ref,
18772
+ id: `tab-nav-${index}`,
17570
18773
  className: `c-tabs__nav-btn ${isActive ? TAB.CLASSES.ACTIVE : ""} ${className}`.trim(),
17571
18774
  onClick: e => {
17572
18775
  void 0 !== index && handleTabClick(index), onClick?.(e);
@@ -17575,6 +18778,7 @@ const TabsTrigger = forwardRef((({children: children, className: className = ""
17575
18778
  role: "tab",
17576
18779
  "aria-selected": isActive,
17577
18780
  "aria-controls": `tab-panel-${index}`,
18781
+ tabIndex: isActive ? 0 : -1,
17578
18782
  type: "button",
17579
18783
  ...props,
17580
18784
  children: children
@@ -17624,24 +18828,59 @@ TabsPanel.displayName = "TabsPanel";
17624
18828
  const Tabs = memo((({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTIVE_INDEX, onTabChange: onTabChange, className: className = "", style: style, glass: glass, children: children}) => {
17625
18829
  const [currentTab, setCurrentTab] = useState(activeIndex), handleTabClick = index => {
17626
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);
17627
18859
  };
17628
18860
  // Handle tab change
17629
18861
  // Determine content based on mode (legacy items vs compound children)
17630
18862
  let content;
17631
18863
  // Use items prop if provided
17632
- // Legacy mode
17633
- content = items && items.length > 0 ? jsxs(Fragment, {
18864
+ if (items && items.length > 0)
18865
+ // Legacy mode
18866
+ content = jsxs(Fragment, {
17634
18867
  children: [ jsx("ul", {
17635
18868
  className: "c-tabs__nav",
18869
+ role: "tablist",
18870
+ onKeyDown: e => handleKeyDown(e, items.length),
17636
18871
  children: items.map(((item, index) => jsx("li", {
17637
18872
  className: "c-tabs__nav-item",
18873
+ role: "presentation",
17638
18874
  children: jsx("button", {
18875
+ id: `tab-nav-${index}`,
17639
18876
  className: `c-tabs__nav-btn ${index === currentTab ? TAB.CLASSES.ACTIVE : ""}`,
17640
18877
  onClick: () => handleTabClick(index),
17641
18878
  "data-tabindex": index,
17642
18879
  role: "tab",
17643
18880
  "aria-selected": index === currentTab,
17644
18881
  "aria-controls": `tab-panel-${index}`,
18882
+ tabIndex: index === currentTab ? 0 : -1,
18883
+ type: "button",
17645
18884
  children: item.label
17646
18885
  })
17647
18886
  }, `tab-nav-${index}`)))
@@ -17665,13 +18904,19 @@ const Tabs = memo((({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTI
17665
18904
  })
17666
18905
  }, `tab-panel-${index}`)))
17667
18906
  }) ]
17668
- }) : jsx(TabsContext.Provider, {
17669
- value: {
17670
- currentTab: currentTab,
17671
- handleTabClick: handleTabClick
17672
- },
17673
- children: children
17674
- });
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
+ }
17675
18920
  const wrapper = jsx("div", {
17676
18921
  className: `c-tabs js-atomix-tab ${className}`,
17677
18922
  style: style,
@@ -17762,7 +19007,7 @@ const Testimonial = ({quote: quote, author: author, size: size = "", skeleton: s
17762
19007
  className: "c-testimonial__author",
17763
19008
  children: [ author.avatarSrc && jsx("img", {
17764
19009
  src: author.avatarSrc,
17765
- alt: author.avatarAlt || "",
19010
+ alt: author.avatarAlt || `${author.name}'s avatar`,
17766
19011
  className: "c-testimonial__author-avatar c-avatar c-avatar--xxl c-avatar--circle"
17767
19012
  }), jsxs("div", {
17768
19013
  className: "c-testimonial__info",
@@ -18038,7 +19283,17 @@ const Tooltip = memo((({content: content, children: children, position: positio
18038
19283
  cancelAnimationFrame(rafId), window.removeEventListener("resize", handleUpdate),
18039
19284
  window.removeEventListener("scroll", handleUpdate, !0);
18040
19285
  };
18041
- }), [ 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 ]);
18042
19297
  // Setup trigger props
18043
19298
  const triggerProps = {
18044
19299
  "aria-describedby": isVisible ? tooltipId : void 0
@@ -18727,9 +19982,15 @@ const VideoPlayer = forwardRef((({src: src, type: type = "video", youtubeId: yo
18727
19982
  const rect = e.currentTarget.getBoundingClientRect(), percent = (e.clientX - rect.left) / rect.width;
18728
19983
  setVolume(percent);
18729
19984
  }), [ setVolume ]), handleDownload = useCallback((() => {
18730
- if (src) {
18731
- const a = document.createElement("a");
18732
- 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
18733
19994
  }
18734
19995
  }), [ src ]), handleShare = useCallback((async () => {
18735
19996
  if (navigator.share) try {
@@ -19306,6 +20567,7 @@ const components = Object.freeze( Object.defineProperty({
19306
20567
  TODO: TODO,
19307
20568
  TOGGLE: TOGGLE,
19308
20569
  TOOLTIP: TOOLTIP,
20570
+ TYPEDBUTTON: TYPEDBUTTON,
19309
20571
  UPLOAD: UPLOAD,
19310
20572
  VIDEO_PLAYER: VIDEO_PLAYER,
19311
20573
  sliderConstants: sliderConstants
@@ -25248,5 +26510,5 @@ const atomix = {
25248
26510
  types: types
25249
26511
  };
25250
26512
 
25251
- 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 };
25252
26514
  //# sourceMappingURL=index.esm.js.map