@shohojdhara/atomix 0.4.1 → 0.4.3

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 (131) hide show
  1. package/dist/atomix.css +9351 -9259
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +12 -19
  6. package/dist/charts.js +555 -358
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +21 -24
  9. package/dist/core.js +435 -265
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +11 -18
  12. package/dist/forms.js +411 -257
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +14 -21
  15. package/dist/heavy.js +409 -254
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +38 -41
  18. package/dist/index.esm.js +731 -487
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +733 -492
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +1 -1
  25. package/scripts/atomix-cli.js +34 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
  28. package/src/components/AtomixGlass/README.md +5 -5
  29. package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
  31. package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
  32. package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
  33. package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
  34. package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
  35. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
  36. package/src/components/Badge/Badge.stories.tsx +1 -1
  37. package/src/components/Badge/Badge.tsx +1 -1
  38. package/src/components/Breadcrumb/Breadcrumb.tsx +90 -77
  39. package/src/components/Breadcrumb/index.ts +2 -2
  40. package/src/components/Button/Button.stories.tsx +1 -1
  41. package/src/components/Button/Button.tsx +2 -1
  42. package/src/components/Button/README.md +2 -2
  43. package/src/components/Callout/Callout.test.tsx +3 -3
  44. package/src/components/Callout/Callout.tsx +2 -2
  45. package/src/components/Callout/README.md +2 -2
  46. package/src/components/Card/Card.tsx +31 -11
  47. package/src/components/Chart/Chart.stories.tsx +1 -1
  48. package/src/components/Chart/Chart.tsx +5 -5
  49. package/src/components/Chart/TreemapChart.tsx +37 -29
  50. package/src/components/DatePicker/readme.md +3 -3
  51. package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
  52. package/src/components/Dropdown/Dropdown.tsx +276 -273
  53. package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
  54. package/src/components/Footer/FooterLink.tsx +2 -2
  55. package/src/components/Form/Checkbox.stories.tsx +1 -1
  56. package/src/components/Form/Checkbox.tsx +1 -1
  57. package/src/components/Form/Input.stories.tsx +1 -1
  58. package/src/components/Form/Input.tsx +1 -1
  59. package/src/components/Form/Radio.stories.tsx +1 -1
  60. package/src/components/Form/Radio.tsx +1 -1
  61. package/src/components/Form/Select.stories.tsx +1 -1
  62. package/src/components/Form/Select.tsx +1 -1
  63. package/src/components/Form/Textarea.stories.tsx +1 -1
  64. package/src/components/Form/Textarea.tsx +1 -1
  65. package/src/components/Hero/Hero.stories.tsx +2 -2
  66. package/src/components/Hero/Hero.tsx +2 -2
  67. package/src/components/Messages/Messages.stories.tsx +1 -1
  68. package/src/components/Messages/Messages.tsx +2 -2
  69. package/src/components/Modal/Modal.stories.tsx +1 -1
  70. package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
  71. package/src/components/Navigation/Nav/Nav.tsx +1 -1
  72. package/src/components/Navigation/Nav/NavItem.tsx +6 -3
  73. package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
  74. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  75. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
  76. package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
  77. package/src/components/Pagination/Pagination.stories.tsx +1 -1
  78. package/src/components/Pagination/Pagination.tsx +1 -1
  79. package/src/components/Popover/Popover.stories.tsx +1 -1
  80. package/src/components/Popover/Popover.tsx +1 -1
  81. package/src/components/Progress/Progress.tsx +1 -1
  82. package/src/components/Rating/Rating.stories.tsx +1 -1
  83. package/src/components/Rating/Rating.test.tsx +73 -0
  84. package/src/components/Rating/Rating.tsx +25 -37
  85. package/src/components/Spinner/Spinner.tsx +1 -1
  86. package/src/components/Steps/Steps.stories.tsx +1 -1
  87. package/src/components/Steps/Steps.tsx +2 -2
  88. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  89. package/src/components/Tabs/Tabs.tsx +1 -1
  90. package/src/components/Todo/Todo.tsx +0 -1
  91. package/src/components/Toggle/Toggle.stories.tsx +1 -1
  92. package/src/components/Toggle/Toggle.tsx +1 -1
  93. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  94. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
  95. package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
  96. package/src/lib/composables/__tests__/useChart.test.ts +50 -0
  97. package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
  98. package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
  99. package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
  100. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
  101. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
  102. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
  103. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
  104. package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
  105. package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
  106. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
  107. package/src/lib/composables/glass-styles.ts +302 -0
  108. package/src/lib/composables/index.ts +0 -4
  109. package/src/lib/composables/useAtomixGlass.ts +331 -522
  110. package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
  111. package/src/lib/composables/useBarChart.ts +1 -1
  112. package/src/lib/composables/useBreadcrumb.ts +6 -6
  113. package/src/lib/composables/useChart.ts +104 -21
  114. package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
  115. package/src/lib/composables/useSlider.ts +66 -34
  116. package/src/lib/theme/devtools/CLI.ts +1 -1
  117. package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
  118. package/src/lib/types/components.ts +18 -21
  119. package/src/lib/utils/__tests__/dom.test.ts +100 -0
  120. package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
  121. package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
  122. package/src/styles/02-tools/_tools.utility-api.scss +6 -6
  123. package/src/styles/06-components/_components.accordion.scss +0 -2
  124. package/src/styles/06-components/_components.chart.scss +0 -1
  125. package/src/styles/06-components/_components.dropdown.scss +0 -1
  126. package/src/styles/06-components/_components.edge-panel.scss +0 -2
  127. package/src/styles/06-components/_components.photoviewer.scss +0 -1
  128. package/src/styles/06-components/_components.river.scss +0 -1
  129. package/src/styles/06-components/_components.slider.scss +0 -3
  130. package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
  131. package/src/styles/99-utilities/_utilities.text.scss +1 -0
package/dist/charts.js CHANGED
@@ -1014,12 +1014,22 @@ function useChart(initialProps) {
1014
1014
  left: 40
1015
1015
  }, config) => {
1016
1016
  if (!datasets || 0 === datasets.length) return null;
1017
- // Flatten all data points to find min/max values
1018
- const allDataPoints = datasets.flatMap((dataset => dataset.data));
1019
- if (0 === allDataPoints.length) return null;
1020
- const minValue = Math.min(...allDataPoints.map((point => point.value))), maxValue = Math.max(...allDataPoints.map((point => point.value))), valueRange = maxValue - minValue || 1, innerWidth = width - padding.left - padding.right, innerHeight = height - padding.top - padding.bottom;
1021
- return {
1022
- xScale: (index, dataLength = allDataPoints.length) => dataLength <= 1 ? padding.left + innerWidth / 2 : padding.left + index / (dataLength - 1) * innerWidth,
1017
+ // Calculate total points and min/max values efficiently avoiding spread operator
1018
+ let totalPoints = 0, minValue = 1 / 0, maxValue = -1 / 0, hasValidData = !1;
1019
+ for (const dataset of datasets) if (dataset.data) {
1020
+ totalPoints += dataset.data.length;
1021
+ const {min: min, max: max, hasValid: hasValid} = getDatasetBounds(dataset.data);
1022
+ hasValid && (min < minValue && (minValue = min), max > maxValue && (maxValue = max),
1023
+ hasValidData = !0);
1024
+ }
1025
+ if (0 === totalPoints) return null;
1026
+ // Handle case with no valid numeric data
1027
+ hasValidData || (minValue = 0, maxValue = 0);
1028
+ const valueRange = maxValue - minValue || 1, innerWidth = width - padding.left - padding.right, innerHeight = height - padding.top - padding.bottom;
1029
+ // Avoid division by zero
1030
+ // Apply padding
1031
+ return {
1032
+ xScale: (index, dataLength = totalPoints) => dataLength <= 1 ? padding.left + innerWidth / 2 : padding.left + index / (dataLength - 1) * innerWidth,
1023
1033
  yScale: value => padding.top + innerHeight - (value - minValue) / valueRange * innerHeight,
1024
1034
  minValue: minValue,
1025
1035
  maxValue: maxValue,
@@ -1075,6 +1085,29 @@ function useChart(initialProps) {
1075
1085
  /**
1076
1086
  * Hook for chart data processing and transformation
1077
1087
  */
1088
+ /**
1089
+ * Helper to calculate min/max values from a dataset efficiently
1090
+ * avoiding spread operator which can cause stack overflow on large arrays
1091
+ */
1092
+ function getDatasetBounds(data) {
1093
+ let min = 1 / 0, max = -1 / 0, hasValid = !1;
1094
+ if (data && data.length > 0) for (let i = 0; i < data.length; i++) {
1095
+ const point = data[i];
1096
+ if (point && "number" == typeof point.value) {
1097
+ const val = point.value;
1098
+ val < min && (min = val), val > max && (max = val), hasValid = !0;
1099
+ }
1100
+ }
1101
+ return {
1102
+ min: min,
1103
+ max: max,
1104
+ hasValid: hasValid
1105
+ };
1106
+ }
1107
+
1108
+ /**
1109
+ * Hook for managing chart toolbar state and generating chart-specific configurations
1110
+ */
1078
1111
  // Map string sizes to pixel values
1079
1112
  const sizeMap = {
1080
1113
  xs: 16,
@@ -1491,7 +1524,7 @@ const ChartToolbar = memo( forwardRef((({chartType: chartType = "line", groups:
1491
1524
 
1492
1525
  ChartToolbar.displayName = "ChartToolbar";
1493
1526
 
1494
- const {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
1527
+ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateDistance = (pos1, pos2) => {
1495
1528
  if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
1496
1529
  const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
1497
1530
  return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
@@ -1504,36 +1537,36 @@ const {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2)
1504
1537
  }, calculateMouseInfluence = mouseOffset => {
1505
1538
  if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
1506
1539
  // Bounded calculation — keeps the glass effect subtle and stable
1507
- const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$1.MOUSE_INFLUENCE_DIVISOR;
1540
+ const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$2.MOUSE_INFLUENCE_DIVISOR;
1508
1541
  return Math.min(.8, influence);
1509
1542
  // Tighter cap to prevent blur/filter blow-out
1510
- }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$1.MIN_BLUR : Math.max(CONSTANTS$1.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$1.MAX_SIZE && size.height <= CONSTANTS$1.MAX_SIZE, parseBorderRadiusValue = value => {
1543
+ }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$2.MIN_BLUR : Math.max(CONSTANTS$2.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$2.MAX_SIZE && size.height <= CONSTANTS$2.MAX_SIZE, parseBorderRadiusValue = value => {
1511
1544
  if ("number" == typeof value) return Math.max(0, value);
1512
- if ("string" != typeof value || !value.trim()) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
1545
+ if ("string" != typeof value || !value.trim()) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1513
1546
  const trimmedValue = value.trim();
1514
1547
  // Handle px values
1515
1548
  if (trimmedValue.endsWith("px")) {
1516
1549
  const parsed = parseFloat(trimmedValue);
1517
- return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
1550
+ return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
1518
1551
  }
1519
1552
  // Handle rem values (assume 16px = 1rem)
1520
1553
  if (trimmedValue.endsWith("rem")) {
1521
1554
  const parsed = parseFloat(trimmedValue);
1522
- return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1555
+ return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1523
1556
  }
1524
1557
  // Handle em values (assume 16px = 1em for simplicity)
1525
1558
  if (trimmedValue.endsWith("em")) {
1526
1559
  const parsed = parseFloat(trimmedValue);
1527
- return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1560
+ return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1528
1561
  }
1529
1562
  // Handle percentage (convert to approximate px value, assuming 200px container)
1530
1563
  if (trimmedValue.endsWith("%")) {
1531
1564
  const parsed = parseFloat(trimmedValue);
1532
- return isNaN(parsed) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
1565
+ return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
1533
1566
  }
1534
1567
  // Handle unitless numbers
1535
1568
  const numValue = parseFloat(trimmedValue);
1536
- return isNaN(numValue) ? CONSTANTS$1.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
1569
+ return isNaN(numValue) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
1537
1570
  }, extractBorderRadiusFromElement = element => {
1538
1571
  if (!element || !element.props) return null;
1539
1572
  // Check inline styles first (highest priority)
@@ -1549,11 +1582,11 @@ const {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2)
1549
1582
  // If element has children, recursively check them
1550
1583
  if (element.props.children) {
1551
1584
  const childRadius = extractBorderRadiusFromChildren(element.props.children);
1552
- if (childRadius > 0 && childRadius !== CONSTANTS$1.DEFAULT_CORNER_RADIUS) return childRadius;
1585
+ if (childRadius > 0 && childRadius !== CONSTANTS$2.DEFAULT_CORNER_RADIUS) return childRadius;
1553
1586
  }
1554
1587
  return null;
1555
1588
  }, extractBorderRadiusFromChildren = children => {
1556
- if (!children) return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
1589
+ if (!children) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1557
1590
  try {
1558
1591
  const childArray = React.Children.toArray(children);
1559
1592
  for (let i = 0; i < childArray.length; i++) {
@@ -1566,7 +1599,7 @@ const {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS, calculateDistance = (pos1, pos2)
1566
1599
  } catch (error) {
1567
1600
  // Silently handle errors
1568
1601
  }
1569
- return CONSTANTS$1.DEFAULT_CORNER_RADIUS;
1602
+ return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1570
1603
  }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
1571
1604
  switch (mode) {
1572
1605
  case "standard":
@@ -1737,10 +1770,10 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1737
1770
  }, globalMousePosition: globalMousePosition = {
1738
1771
  x: 0,
1739
1772
  y: 0
1740
- }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isHovered: isHovered = !1, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, cornerRadius: cornerRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
1773
+ }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isHovered: isHovered = !1, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
1741
1774
  width: 0,
1742
1775
  height: 0
1743
- }, onClick: onClick, mode: mode = "standard", effectiveDisableEffects: effectiveDisableEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", enableLiquidBlur: enableLiquidBlur = !1, elasticity: elasticity = 0, contentRef: contentRef}, ref) => {
1776
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, elasticity: elasticity = 0, contentRef: contentRef}, ref) => {
1744
1777
  // Generate a stable, deterministic ID for SSR compatibility
1745
1778
  // Use a module-level counter that's consistent across server and client
1746
1779
  const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null);
@@ -1850,7 +1883,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1850
1883
  flowBlur: 1.2 * blurAmount
1851
1884
  };
1852
1885
  // Enhanced validation for liquid blur
1853
- if (!enableLiquidBlur || !rectCache || !mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y || isNaN(mouseOffset.x) || isNaN(mouseOffset.y)) return defaultBlur;
1886
+ if (!withLiquidBlur || !rectCache || !mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y || isNaN(mouseOffset.x) || isNaN(mouseOffset.y)) return defaultBlur;
1854
1887
  try {
1855
1888
  const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = 2 * blurAmount, baseBlur = Math.min(maxBlur, blurAmount + mouseInfluence * blurAmount * .15), edgeIntensity = .15 * mouseInfluence, edgeBlur = Math.min(maxBlur, baseBlur * (.8 + .4 * edgeIntensity)), centerIntensity = .1 * mouseInfluence, centerBlur = Math.min(maxBlur, baseBlur * (.3 + .3 * centerIntensity)), flowBlur = Math.min(maxBlur, 1.2 * baseBlur);
1856
1889
  // NOTE: hover/active multipliers intentionally omitted here —
@@ -1865,14 +1898,14 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1865
1898
  return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
1866
1899
  defaultBlur;
1867
1900
  }
1868
- }), [ enableLiquidBlur, blurAmount, mouseOffset, rectCache ]), backdropStyle = useMemo((() => {
1901
+ }), [ withLiquidBlur, blurAmount, mouseOffset, rectCache ]), backdropStyle = useMemo((() => {
1869
1902
  try {
1870
1903
  const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), validatedBaseBlur = "number" != typeof liquidBlur.baseBlur || isNaN(liquidBlur.baseBlur) ? 0 : liquidBlur.baseBlur, validatedEdgeBlur = "number" != typeof liquidBlur.edgeBlur || isNaN(liquidBlur.edgeBlur) ? 0 : liquidBlur.edgeBlur, validatedCenterBlur = "number" != typeof liquidBlur.centerBlur || isNaN(liquidBlur.centerBlur) ? 0 : liquidBlur.centerBlur, validatedFlowBlur = "number" != typeof liquidBlur.flowBlur || isNaN(liquidBlur.flowBlur) ? 0 : liquidBlur.flowBlur, area = rectCache ? rectCache.width * rectCache.height : 0;
1871
1904
  // Validate blur values before using them
1872
- return !enableLiquidBlur || effectiveReducedMotion || effectiveDisableEffects || area > 18e4 ? {
1905
+ return !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? {
1873
1906
  backdropFilter: `blur(${clampBlur(Math.max(validatedBaseBlur, .8 * validatedEdgeBlur, 1.1 * validatedCenterBlur, .9 * validatedFlowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`
1874
1907
  } : {
1875
- backdropFilter: `blur(${clampBlur(.4 * validatedBaseBlur + .25 * validatedEdgeBlur + .15 * validatedCenterBlur + .2 * validatedFlowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`
1908
+ backdropFilter: `blur(${clampBlur(.4 * validatedBaseBlur + .25 * validatedEdgeBlur + .15 * validatedCenterBlur + .2 * validatedFlowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1}) brightness(${overLightConfig?.brightness || 1})`
1876
1909
  };
1877
1910
  // Single-pass fallback: stronger radius to match perceived blur of multi-pass
1878
1911
  } catch (error) {
@@ -1881,7 +1914,7 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1881
1914
  backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
1882
1915
  };
1883
1916
  }
1884
- }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveDisableEffects, enableLiquidBlur ]), containerVars = useMemo((() => {
1917
+ }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur ]), containerVars = useMemo((() => {
1885
1918
  try {
1886
1919
  // Safe extraction of mouse offset values
1887
1920
  const mx = mouseOffset && "number" == typeof mouseOffset.x && !isNaN(mouseOffset.x) ? mouseOffset.x : 0, my = mouseOffset && "number" == typeof mouseOffset.y && !isNaN(mouseOffset.y) ? mouseOffset.y : 0;
@@ -1889,10 +1922,10 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1889
1922
  "--atomix-glass-container-width": `${glassSize?.width}`,
1890
1923
  "--atomix-glass-container-height": `${glassSize?.height}`,
1891
1924
  "--atomix-glass-container-padding": padding || "0 0",
1892
- "--atomix-glass-container-radius": `${"number" != typeof cornerRadius || isNaN(cornerRadius) ? 0 : cornerRadius}px`,
1925
+ "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`,
1893
1926
  "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
1894
1927
  "--atomix-glass-container-shadow": overLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig?.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset",
1895
- "--atomix-glass-container-shadow-opacity": effectiveDisableEffects ? 0 : 1,
1928
+ "--atomix-glass-container-shadow-opacity": effectiveWithoutEffects ? 0 : 1,
1896
1929
  // Background and shadow values use design token-aligned RGB values
1897
1930
  "--atomix-glass-container-bg": overLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none",
1898
1931
  "--atomix-glass-container-text-shadow": overLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
@@ -1910,14 +1943,10 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1910
1943
  "--atomix-glass-container-text-shadow": "none"
1911
1944
  };
1912
1945
  }
1913
- }), [ glassSize, padding, cornerRadius, backdropStyle, mouseOffset, overLight, effectiveDisableEffects ]), setForceNoTransition = el => {
1914
- el && (el.style.setProperty("transition-duration", "0s", "important"), el.style.setProperty("animation-duration", "0s", "important"),
1915
- el.style.setProperty("transition-delay", "0s", "important"));
1916
- };
1946
+ }), [ glassSize, padding, borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects ]);
1917
1947
  return jsx("div", {
1918
1948
  ref: el => {
1919
1949
  // Apply force no-transition
1920
- setForceNoTransition(el),
1921
1950
  // Handle forwarded ref
1922
1951
  "function" == typeof ref ? ref(el) : ref && (ref.current = el);
1923
1952
  },
@@ -1952,7 +1981,10 @@ const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({childre
1952
1981
  aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
1953
1982
  shaderMapUrl: shaderMapUrl
1954
1983
  }), jsx("div", {
1955
- ref: setForceNoTransition,
1984
+ ref: el => {
1985
+ el && (el.style.setProperty("transition-duration", "0s", "important"), el.style.setProperty("animation-duration", "0s", "important"),
1986
+ el.style.setProperty("transition-delay", "0s", "important"));
1987
+ },
1956
1988
  className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
1957
1989
  style: {
1958
1990
  filter: `url(#${filterId})`,
@@ -2069,7 +2101,128 @@ class {
2069
2101
  */ getSubscriberCount() {
2070
2102
  return this.listeners.size;
2071
2103
  }
2072
- }, {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
2104
+ }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
2105
+ if (!wrapperElement && !containerElement) return;
2106
+ const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, directionalScale: directionalScale, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
2107
+ opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
2108
+ contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
2109
+ brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
2110
+ shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
2111
+ borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
2112
+ saturationBoost: baseOverLightConfig.saturationBoost
2113
+ };
2114
+ // Calculate mouse influence
2115
+ // Calculate elastic translation
2116
+ let elasticTranslation = {
2117
+ x: 0,
2118
+ y: 0
2119
+ };
2120
+ if (!effectiveWithoutEffects && wrapperElement) {
2121
+ const rect = wrapperElement.getBoundingClientRect(), center = calculateElementCenter(rect);
2122
+ // Calculate fade in factor
2123
+ let fadeInFactor = 0;
2124
+ if (globalMousePosition.x && globalMousePosition.y && validateGlassSize(glassSize)) {
2125
+ const edgeDistanceX = Math.max(0, Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2), edgeDistance = calculateDistance({
2126
+ x: edgeDistanceX,
2127
+ y: edgeDistanceY
2128
+ }, {
2129
+ x: 0,
2130
+ y: 0
2131
+ });
2132
+ fadeInFactor = edgeDistance > ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE;
2133
+ }
2134
+ elasticTranslation = {
2135
+ x: (globalMousePosition.x - center.x) * elasticity * .1 * fadeInFactor,
2136
+ y: (globalMousePosition.y - center.y) * elasticity * .1 * fadeInFactor
2137
+ };
2138
+ }
2139
+ const transformStyle = effectiveWithoutEffects ? isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)" : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? "scale(0.96)" : directionalScale}`;
2140
+ // Update Wrapper Styles (glassVars)
2141
+ if (wrapperElement) {
2142
+ const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), borderOpacities = [ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH ], configBorderOpacity = overLightConfig.borderOpacity, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
2143
+ hover1: {
2144
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
2145
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
2146
+ },
2147
+ hover2: {
2148
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
2149
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2
2150
+ },
2151
+ hover3: {
2152
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
2153
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3
2154
+ }
2155
+ }, basePosition = {
2156
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
2157
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
2158
+ }, opacityValues = {
2159
+ hover1: isHovered || isActive ? .5 : 0,
2160
+ hover2: isActive ? .5 : 0,
2161
+ hover3: isHovered ? .4 : isActive ? .8 : 0,
2162
+ base: isOverLight ? overLightConfig.opacity : 0,
2163
+ over: isOverLight ? 1.1 * overLightConfig.opacity : 0
2164
+ }, style = wrapperElement.style;
2165
+ style.setProperty("--atomix-glass-transform", transformStyle || "none"),
2166
+ // Gradients
2167
+ style.setProperty("--atomix-glass-border-gradient-1", `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
2168
+ style.setProperty("--atomix-glass-border-gradient-2", `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
2169
+ // Hover gradients
2170
+ style.setProperty("--atomix-glass-hover-1-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`),
2171
+ style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`),
2172
+ style.setProperty("--atomix-glass-hover-3-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`),
2173
+ style.setProperty("--atomix-glass-base-gradient", isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`),
2174
+ style.setProperty("--atomix-glass-overlay-gradient", isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`),
2175
+ // Opacities
2176
+ style.setProperty("--atomix-glass-hover-1-opacity", opacityValues.hover1.toString()),
2177
+ style.setProperty("--atomix-glass-hover-2-opacity", opacityValues.hover2.toString()),
2178
+ style.setProperty("--atomix-glass-hover-3-opacity", opacityValues.hover3.toString()),
2179
+ style.setProperty("--atomix-glass-base-opacity", opacityValues.base.toString()),
2180
+ style.setProperty("--atomix-glass-overlay-opacity", opacityValues.over.toString()),
2181
+ style.setProperty("--atomix-glass-overlay-highlight-opacity", (opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER).toString()),
2182
+ // Other
2183
+ style.setProperty("--atomix-glass-blend-mode", isOverLight ? "multiply" : "overlay"),
2184
+ style.setProperty("--atomix-glass-radius", `${effectiveBorderRadius}px`);
2185
+ }
2186
+ // Update Container Styles (containerVars)
2187
+ if (containerElement) {
2188
+ const mx = mouseOffset.x, my = mouseOffset.y, EDGE_BLUR_MULTIPLIER = 1.25, CENTER_BLUR_MULTIPLIER = 1.1, FLOW_BLUR_MULTIPLIER = 1.2, MOUSE_INFLUENCE_BLUR_FACTOR = .15, EDGE_INTENSITY_MOUSE_FACTOR = .15, CENTER_INTENSITY_MOUSE_FACTOR = .1, MAX_BLUR_RELATIVE = 2, rect = containerElement.getBoundingClientRect();
2189
+ let liquidBlur = {
2190
+ baseBlur: blurAmount,
2191
+ edgeBlur: blurAmount * EDGE_BLUR_MULTIPLIER,
2192
+ centerBlur: blurAmount * CENTER_BLUR_MULTIPLIER,
2193
+ flowBlur: blurAmount * FLOW_BLUR_MULTIPLIER
2194
+ };
2195
+ if (withLiquidBlur && rect) {
2196
+ const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = blurAmount * MAX_BLUR_RELATIVE, baseBlur = Math.min(maxBlur, blurAmount + mouseInfluence * blurAmount * MOUSE_INFLUENCE_BLUR_FACTOR), edgeIntensity = mouseInfluence * EDGE_INTENSITY_MOUSE_FACTOR, edgeBlur = Math.min(maxBlur, baseBlur * (.8 + .4 * edgeIntensity)), centerIntensity = mouseInfluence * CENTER_INTENSITY_MOUSE_FACTOR, centerBlur = Math.min(maxBlur, baseBlur * (.3 + .3 * centerIntensity)), flowBlur = Math.min(maxBlur, baseBlur * FLOW_BLUR_MULTIPLIER);
2197
+ liquidBlur = {
2198
+ baseBlur: clampBlur(baseBlur),
2199
+ edgeBlur: clampBlur(edgeBlur),
2200
+ centerBlur: clampBlur(centerBlur),
2201
+ flowBlur: clampBlur(flowBlur)
2202
+ };
2203
+ }
2204
+ // Backdrop filter
2205
+ let backdropFilterString = `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`;
2206
+ const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), area = rect ? rect.width * rect.height : 0;
2207
+ backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
2208
+ // Container variables
2209
+ const style = containerElement.style;
2210
+ style.setProperty("--atomix-glass-container-width", `${glassSize.width}`), style.setProperty("--atomix-glass-container-height", `${glassSize.height}`),
2211
+ style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
2212
+ style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
2213
+ // Shadows
2214
+ style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset"),
2215
+ style.setProperty("--atomix-glass-container-shadow-opacity", effectiveWithoutEffects ? "0" : "1"),
2216
+ style.setProperty("--atomix-glass-container-bg", isOverLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none"),
2217
+ style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 2px 12px rgba(0, 0, 0, 0)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
2218
+ 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)");
2219
+ }
2220
+ }, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
2221
+
2222
+ /**
2223
+ * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2224
+ * to avoid React re-renders on mouse movement.
2225
+ */ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
2073
2226
  parentElement && backgroundDetectionCache.set(parentElement, {
2074
2227
  result: result,
2075
2228
  timestamp: Date.now(),
@@ -2082,18 +2235,74 @@ class {
2082
2235
  * Composable hook for AtomixGlass component logic
2083
2236
  * Manages all state, calculations, and event handlers
2084
2237
  */
2085
- function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadius: cornerRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, disableEffects: disableEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugCornerRadius: debugCornerRadius = !1, debugOverLight: debugOverLight = !1, children: children}) {
2238
+ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = .05, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, debugPerformance: debugPerformance = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur}) {
2086
2239
  // State
2087
- const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), [glassSize, setGlassSize] = useState({
2088
- width: 270,
2089
- height: 69
2090
- }), [internalGlobalMousePosition, setInternalGlobalMousePosition] = useState({
2240
+ const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
2091
2241
  x: 0,
2092
2242
  y: 0
2093
- }), [internalMouseOffset, setInternalMouseOffset] = useState({
2243
+ }), internalMouseOffsetRef = useRef({
2094
2244
  x: 0,
2095
2245
  y: 0
2096
- }), [dynamicCornerRadius, setDynamicCornerRadius] = useState(CONSTANTS.DEFAULT_CORNER_RADIUS), [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = useState(!1), [detectedOverLight, setDetectedOverLight] = useState(!1), effectiveCornerRadius = useMemo((() => void 0 !== cornerRadius ? Math.max(0, cornerRadius) : Math.max(0, dynamicCornerRadius)), [ cornerRadius, dynamicCornerRadius ]), effectiveReducedMotion = useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveHighContrast = useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveDisableEffects = useMemo((() => disableEffects || effectiveReducedMotion), [ disableEffects, effectiveReducedMotion ]), globalMousePosition = useMemo((() => externalGlobalMousePosition || internalGlobalMousePosition), [ externalGlobalMousePosition, internalGlobalMousePosition ]), mouseOffset = useMemo((() => externalMouseOffset || internalMouseOffset), [ externalMouseOffset, internalMouseOffset ]);
2246
+ }), [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}) {
2247
+ const [glassSize, setGlassSize] = useState({
2248
+ width: 270,
2249
+ height: 69
2250
+ });
2251
+ return useEffect((() => {
2252
+ const isValidElement = element => null !== element && element instanceof HTMLElement && element.isConnected;
2253
+ let rafId = null, lastSize = {
2254
+ width: 0,
2255
+ height: 0
2256
+ }, lastCornerRadius = effectiveBorderRadius;
2257
+ const updateGlassSize = (forceUpdate = !1) => {
2258
+ null !== rafId && cancelAnimationFrame(rafId), rafId = requestAnimationFrame((() => {
2259
+ if (!isValidElement(glassRef.current)) return void (rafId = null);
2260
+ const rect = glassRef.current.getBoundingClientRect();
2261
+ if (rect.width <= 0 || rect.height <= 0) return void (rafId = null);
2262
+ // Measure actual rendered size without artificial offsets to avoid feedback loops
2263
+ const newSize = {
2264
+ width: Math.round(rect.width),
2265
+ height: Math.round(rect.height)
2266
+ }, cornerRadiusChanged = lastCornerRadius !== effectiveBorderRadius, dimensionsChanged = Math.abs(newSize.width - lastSize.width) > 1 || Math.abs(newSize.height - lastSize.height) > 1;
2267
+ var size;
2268
+ (forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateGlassSize(size = newSize) && size.width <= CONSTANTS$1.MAX_SIZE && size.height <= CONSTANTS$1.MAX_SIZE && (lastSize = newSize,
2269
+ lastCornerRadius = effectiveBorderRadius, setGlassSize(newSize)), rafId = null;
2270
+ }));
2271
+ };
2272
+ let resizeTimeoutId = null;
2273
+ const debouncedResizeHandler = () => {
2274
+ resizeTimeoutId && clearTimeout(resizeTimeoutId), resizeTimeoutId = setTimeout((() => updateGlassSize(!1)), 16);
2275
+ }, initialTimeoutId = setTimeout((() => updateGlassSize(!0)), 0);
2276
+ let resizeObserver = null, resizeDebounceTimeout = null;
2277
+ // ResizeObserver has 98%+ browser support, no need for fallback
2278
+ if ("undefined" != typeof ResizeObserver && isValidElement(glassRef.current)) try {
2279
+ resizeObserver = new ResizeObserver((entries => {
2280
+ for (const entry of entries) if (entry.target === glassRef.current) {
2281
+ // Update cached rect when size changes
2282
+ glassRef.current && cachedRectRef && (cachedRectRef.current = glassRef.current.getBoundingClientRect()),
2283
+ // Debounce resize updates to match RAF timing (16ms)
2284
+ resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), resizeDebounceTimeout = setTimeout((() => updateGlassSize(!1)), 16);
2285
+ break;
2286
+ }
2287
+ })), resizeObserver.observe(glassRef.current);
2288
+ } catch (error) {
2289
+ console.warn("AtomixGlass: ResizeObserver not available, using window resize only", error);
2290
+ }
2291
+ return window.addEventListener("resize", debouncedResizeHandler, {
2292
+ passive: !0
2293
+ }), () => {
2294
+ clearTimeout(initialTimeoutId), null !== rafId && cancelAnimationFrame(rafId), resizeTimeoutId && clearTimeout(resizeTimeoutId),
2295
+ resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), window.removeEventListener("resize", debouncedResizeHandler),
2296
+ resizeObserver?.disconnect();
2297
+ };
2298
+ }), [ effectiveBorderRadius, glassRef, cachedRectRef ]), {
2299
+ glassSize: glassSize
2300
+ };
2301
+ }({
2302
+ glassRef: glassRef,
2303
+ effectiveBorderRadius: effectiveBorderRadius,
2304
+ cachedRectRef: cachedRectRef
2305
+ }), 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;
2097
2306
  // Extract border-radius from children
2098
2307
  useEffect((() => {
2099
2308
  const extractRadius = () => {
@@ -2124,13 +2333,13 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2124
2333
  }
2125
2334
  null !== extractedRadius && extractedRadius > 0 && setDynamicCornerRadius(extractedRadius);
2126
2335
  } catch (error) {
2127
- "undefined" != typeof process && "production" === process.env?.NODE_ENV || !debugCornerRadius || console.error("[AtomixGlass] Error extracting corner radius:", error);
2336
+ "undefined" != typeof process && "production" === process.env?.NODE_ENV || !debugBorderRadius || console.error("[AtomixGlass] Error extracting corner radius:", error);
2128
2337
  }
2129
2338
  };
2130
2339
  extractRadius();
2131
2340
  const timeoutId = setTimeout(extractRadius, 100);
2132
2341
  return () => clearTimeout(timeoutId);
2133
- }), [ children, debugCornerRadius, contentRef ]),
2342
+ }), [ children, debugBorderRadius, contentRef ]),
2134
2343
  // Media query handlers and background detection
2135
2344
  useEffect((() => {
2136
2345
  if (("auto" === overLight || "object" == typeof overLight && null !== overLight) && glassRef.current) {
@@ -2186,72 +2395,52 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2186
2395
  const rgb = bgColor.match(/\d+/g);
2187
2396
  if (rgb && rgb.length >= 3) {
2188
2397
  const r = Number(rgb[0]), g = Number(rgb[1]), b = Number(rgb[2]);
2189
- // Validate RGB values are valid numbers
2190
- if (!isNaN(r) && !isNaN(g) && !isNaN(b) && isFinite(r) && isFinite(g) && isFinite(b) && r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && (r > 10 || g > 10 || b > 10)) {
2398
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b) && (r > 10 || g > 10 || b > 10)) {
2191
2399
  const luminance = (.299 * r + .587 * g + .114 * b) / 255;
2192
2400
  !isNaN(luminance) && isFinite(luminance) && (totalLuminance += luminance, validSamples++,
2193
2401
  hasValidBackground = !0);
2194
2402
  }
2195
2403
  }
2196
2404
  }
2197
- // Check for image backgrounds
2198
- bgImage && "none" !== bgImage && "initial" !== bgImage && (
2199
- // For image backgrounds, assume medium luminance
2200
- totalLuminance += .5, validSamples++, hasValidBackground = !0);
2405
+ bgImage && "none" !== bgImage && "initial" !== bgImage && (totalLuminance += .5,
2406
+ validSamples++, hasValidBackground = !0);
2201
2407
  } catch (styleError) {
2202
- "undefined" == typeof process || process.env;
2408
+ // Silently continue
2203
2409
  }
2204
- // Move to parent element for next iteration
2205
- if (!currentElement) break;
2206
- // Exit loop if currentElement becomes null
2207
- currentElement = currentElement.parentElement, depth++;
2410
+ if (!currentElement) break;
2411
+ currentElement = currentElement.parentElement, depth++;
2208
2412
  }
2209
- // More conservative detection with better error handling
2210
- if (hasValidBackground && validSamples > 0) {
2413
+ if (hasValidBackground && validSamples > 0) {
2211
2414
  const avgLuminance = totalLuminance / validSamples;
2212
2415
  if (!isNaN(avgLuminance) && isFinite(avgLuminance)) {
2213
2416
  let threshold = .7;
2214
- // Conservative threshold for overlight
2215
- // If overLight is an object, use its threshold property with validation
2216
- if ("object" == typeof overLight && null !== overLight) {
2417
+ if ("object" == typeof overLight && null !== overLight) {
2217
2418
  const objConfig = overLight;
2218
2419
  if (void 0 !== objConfig.threshold) {
2219
- const configThreshold = "number" == typeof objConfig.threshold && !isNaN(objConfig.threshold) && isFinite(objConfig.threshold) ? objConfig.threshold : .7;
2420
+ const configThreshold = "number" != typeof objConfig.threshold || isNaN(objConfig.threshold) ? .7 : objConfig.threshold;
2220
2421
  threshold = Math.min(.9, Math.max(.1, configThreshold));
2221
2422
  }
2222
2423
  }
2223
2424
  const isOverLightDetected = avgLuminance > threshold;
2224
- // Cache the result in shared cache
2225
- setCachedBackgroundDetection(element.parentElement, overLight, isOverLightDetected, threshold),
2425
+ setCachedBackgroundDetection(element.parentElement, overLight, isOverLightDetected, threshold),
2226
2426
  setDetectedOverLight(isOverLightDetected);
2227
2427
  } else {
2228
- // Invalid luminance calculation, default to false
2229
2428
  const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
2230
2429
  setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
2231
2430
  setDetectedOverLight(result);
2232
2431
  }
2233
2432
  } else {
2234
- // Default to false if no valid background found
2235
2433
  const result = !1, threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
2236
2434
  setCachedBackgroundDetection(element.parentElement, overLight, result, threshold),
2237
2435
  setDetectedOverLight(result);
2238
2436
  }
2239
2437
  } catch (error) {
2240
- // Enhanced error logging with context
2241
- "undefined" != typeof process && "development" !== process.env?.NODE_ENV || console.warn("AtomixGlass: Error detecting background brightness:", error);
2242
- const result = !1;
2243
- if (element && element.parentElement) {
2244
- const threshold = "object" == typeof overLight && null !== overLight && overLight.threshold || .7;
2245
- setCachedBackgroundDetection(element.parentElement, overLight, result, threshold);
2246
- }
2247
- setDetectedOverLight(result);
2438
+ setDetectedOverLight(!1);
2248
2439
  }
2249
2440
  }), 150);
2250
2441
  return () => clearTimeout(timeoutId);
2251
2442
  }
2252
- if ("boolean" == typeof overLight &&
2253
- // For boolean values, disable auto-detection
2254
- setDetectedOverLight(!1), "function" == typeof window.matchMedia) try {
2443
+ if ("boolean" == typeof overLight && setDetectedOverLight(!1), "function" == typeof window.matchMedia) try {
2255
2444
  const mediaQueryReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)"), mediaQueryHighContrast = window.matchMedia("(prefers-contrast: high)");
2256
2445
  setUserPrefersReducedMotion(mediaQueryReducedMotion.matches), setUserPrefersHighContrast(mediaQueryHighContrast.matches);
2257
2446
  const handleReducedMotionChange = e => {
@@ -2262,64 +2451,54 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2262
2451
  return mediaQueryReducedMotion.addEventListener ? (mediaQueryReducedMotion.addEventListener("change", handleReducedMotionChange),
2263
2452
  mediaQueryHighContrast.addEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.addListener && (mediaQueryReducedMotion.addListener(handleReducedMotionChange),
2264
2453
  mediaQueryHighContrast.addListener(handleHighContrastChange)), () => {
2265
- try {
2266
- mediaQueryReducedMotion.removeEventListener ? (mediaQueryReducedMotion.removeEventListener("change", handleReducedMotionChange),
2267
- mediaQueryHighContrast.removeEventListener("change", handleHighContrastChange)) : mediaQueryReducedMotion.removeListener && (mediaQueryReducedMotion.removeListener(handleReducedMotionChange),
2268
- mediaQueryHighContrast.removeListener(handleHighContrastChange));
2269
- } catch (cleanupError) {
2270
- console.error("AtomixGlass: Error cleaning up media query listeners:", cleanupError);
2271
- }
2454
+ // ignore
2272
2455
  };
2273
2456
  } catch (error) {
2274
- return void console.error("AtomixGlass: Error setting up media queries:", error);
2457
+ return;
2275
2458
  }
2276
2459
  }), [ overLight, glassRef, debugOverLight ]);
2277
- // Mouse tracking using shared global tracker
2278
- // Cache bounding rect to avoid repeated getBoundingClientRect calls
2279
- const cachedRectRef = useRef(null), updateRectRef = useRef(null), handleGlobalMousePosition = useCallback((globalPos => {
2280
- if (externalGlobalMousePosition && externalMouseOffset)
2281
- // External mouse position provided, skip internal tracking
2282
- return;
2283
- if (effectiveDisableEffects) return;
2284
- const container = mouseContainer?.current || glassRef.current;
2285
- if (!container) return;
2286
- // Use cached rect if available, otherwise get new one
2287
- let rect = cachedRectRef.current;
2288
- if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
2289
- cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
2290
- const center = calculateElementCenter(rect), newOffset = {
2291
- x: (globalPos.x - center.x) / rect.width * 100,
2292
- y: (globalPos.y - center.y) / rect.height * 100
2460
+ /**
2461
+ * Get effective overLight value based on configuration
2462
+ */
2463
+ const getEffectiveOverLight = useCallback((() => "boolean" == typeof overLight ? overLight : ("auto" === overLight || "object" == typeof overLight && null !== overLight) && detectedOverLight), [ overLight, detectedOverLight ]), validateConfigValue = useCallback(((value, min, max, defaultValue) => "number" != typeof value || isNaN(value) || !isFinite(value) ? defaultValue : Math.min(max, Math.max(min, value))), []), baseOverLightConfig = useMemo((() => {
2464
+ const isOverLight = getEffectiveOverLight(), baseConfig = {
2465
+ isOverLight: isOverLight,
2466
+ threshold: .7,
2467
+ opacity: isOverLight ? Math.min(.6, Math.max(.2, .5)) : 0,
2468
+ contrast: 1,
2469
+ // Base contrast
2470
+ brightness: 1,
2471
+ // Base brightness
2472
+ saturationBoost: 1.3,
2473
+ shadowIntensity: .9,
2474
+ borderOpacity: .7
2293
2475
  };
2294
- // Calculate offset relative to this container
2295
- // React 18 automatically batches these updates
2296
- setInternalMouseOffset(newOffset), setInternalGlobalMousePosition(globalPos);
2297
- }), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects ]);
2298
- // Subscribe to shared mouse tracker
2299
- useEffect((() => {
2300
- if (externalGlobalMousePosition && externalMouseOffset)
2301
- // External mouse position provided, don't subscribe
2302
- return;
2303
- if (effectiveDisableEffects)
2304
- // Effects disabled, don't subscribe
2305
- return;
2306
- // Subscribe to shared tracker
2307
- const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition), container = mouseContainer?.current || glassRef.current;
2308
- // Update cached rect when container size changes
2309
- let resizeObserver = null;
2310
- return container && "undefined" != typeof ResizeObserver && (resizeObserver = new ResizeObserver((() => {
2311
- null !== updateRectRef.current && cancelAnimationFrame(updateRectRef.current), updateRectRef.current = requestAnimationFrame((() => {
2312
- const container = mouseContainer?.current || glassRef.current;
2313
- container && (cachedRectRef.current = container.getBoundingClientRect()), updateRectRef.current = null;
2314
- }));
2315
- })), resizeObserver.observe(container)), () => {
2316
- unsubscribe(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
2317
- updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
2476
+ if ("object" == typeof overLight && null !== overLight) {
2477
+ const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost);
2478
+ return {
2479
+ ...baseConfig,
2480
+ threshold: validatedThreshold,
2481
+ opacity: validatedOpacity,
2482
+ contrast: validatedContrast,
2483
+ brightness: validatedBrightness,
2484
+ saturationBoost: validatedSaturationBoost
2485
+ };
2486
+ }
2487
+ return baseConfig;
2488
+ }), [ overLight, getEffectiveOverLight, validateConfigValue ]), overLightConfig = useMemo((() => {
2489
+ const mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1;
2490
+ return {
2491
+ isOverLight: baseOverLightConfig.isOverLight,
2492
+ threshold: baseOverLightConfig.threshold,
2493
+ opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
2494
+ contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
2495
+ brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
2496
+ saturationBoost: baseOverLightConfig.saturationBoost,
2497
+ shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
2498
+ borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence))
2318
2499
  };
2319
- }), [ handleGlobalMousePosition, mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveDisableEffects ]);
2320
- // Transform calculations
2321
- const calculateDirectionalScale = useCallback((() => {
2322
- if (!0 === overLight || "auto" === overLight && detectedOverLight || "object" == typeof overLight && null !== overLight && detectedOverLight) return "scale(1)";
2500
+ }), [ baseOverLightConfig, mouseOffset, isHovered, isActive ]), updateRectRef = useRef(null), calculateDirectionalScale = useCallback((() => {
2501
+ if (baseOverLightConfig.isOverLight) return "scale(1)";
2323
2502
  if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return "scale(1)";
2324
2503
  const rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), deltaX = globalMousePosition.x - center.x, deltaY = globalMousePosition.y - center.y, edgeDistanceX = Math.max(0, Math.abs(deltaX) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(deltaY) - glassSize.height / 2), edgeDistance = calculateDistance({
2325
2504
  x: edgeDistanceX,
@@ -2333,7 +2512,7 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2333
2512
  if (0 === centerDistance) return "scale(1)";
2334
2513
  const normalizedX = deltaX / centerDistance, normalizedY = deltaY / centerDistance, stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor, scaleX = 1 + Math.abs(normalizedX) * stretchIntensity * .3 - Math.abs(normalizedY) * stretchIntensity * .15, scaleY = 1 + Math.abs(normalizedY) * stretchIntensity * .3 - Math.abs(normalizedX) * stretchIntensity * .15;
2335
2514
  return `scaleX(${Math.max(.8, scaleX)}) scaleY(${Math.max(.8, scaleY)})`;
2336
- }), [ globalMousePosition, elasticity, glassSize, glassRef, overLight, detectedOverLight ]), calculateFadeInFactor = useCallback((() => {
2515
+ }), [ globalMousePosition, elasticity, glassSize, glassRef, baseOverLightConfig ]), calculateFadeInFactor = useCallback((() => {
2337
2516
  if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return 0;
2338
2517
  const rect = glassRef.current.getBoundingClientRect(), center = calculateElementCenter(rect), edgeDistanceX = Math.max(0, Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2), edgeDistanceY = Math.max(0, Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2), edgeDistance = calculateDistance({
2339
2518
  x: edgeDistanceX,
@@ -2353,122 +2532,110 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2353
2532
  x: (globalMousePosition.x - center.x) * elasticity * .1 * fadeInFactor,
2354
2533
  y: (globalMousePosition.y - center.y) * elasticity * .1 * fadeInFactor
2355
2534
  };
2356
- }), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveDisableEffects ? {
2535
+ }), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveWithoutEffects ? {
2357
2536
  x: 0,
2358
2537
  y: 0
2359
- } : calculateElasticTranslation()), [ calculateElasticTranslation, effectiveDisableEffects ]), directionalScale = useMemo((() => effectiveDisableEffects ? "scale(1)" : calculateDirectionalScale()), [ calculateDirectionalScale, effectiveDisableEffects ]), transformStyle = useMemo((() => effectiveDisableEffects ? isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)" : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? "scale(0.96)" : directionalScale}`), [ elasticTranslation, isActive, onClick, directionalScale, effectiveDisableEffects ]);
2360
- // Size management
2361
- useEffect((() => {
2362
- const isValidElement = element => null !== element && element instanceof HTMLElement && element.isConnected;
2363
- let rafId = null, lastSize = {
2364
- width: 0,
2365
- height: 0
2366
- }, lastCornerRadius = effectiveCornerRadius;
2367
- const updateGlassSize = (forceUpdate = !1) => {
2368
- null !== rafId && cancelAnimationFrame(rafId), rafId = requestAnimationFrame((() => {
2369
- if (!isValidElement(glassRef.current)) return void (rafId = null);
2370
- const rect = glassRef.current.getBoundingClientRect();
2371
- if (rect.width <= 0 || rect.height <= 0) return void (rafId = null);
2372
- // Measure actual rendered size without artificial offsets to avoid feedback loops
2373
- const newSize = {
2374
- width: Math.round(rect.width),
2375
- height: Math.round(rect.height)
2376
- }, cornerRadiusChanged = lastCornerRadius !== effectiveCornerRadius, dimensionsChanged = Math.abs(newSize.width - lastSize.width) > 1 || Math.abs(newSize.height - lastSize.height) > 1;
2377
- var size;
2378
- (forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateGlassSize(size = newSize) && size.width <= CONSTANTS.MAX_SIZE && size.height <= CONSTANTS.MAX_SIZE && (lastSize = newSize,
2379
- lastCornerRadius = effectiveCornerRadius, setGlassSize(newSize)), rafId = null;
2380
- }));
2381
- };
2382
- let resizeTimeoutId = null;
2383
- const debouncedResizeHandler = () => {
2384
- resizeTimeoutId && clearTimeout(resizeTimeoutId), resizeTimeoutId = setTimeout((() => updateGlassSize(!1)), 16);
2385
- }, initialTimeoutId = setTimeout((() => updateGlassSize(!0)), 0);
2386
- let resizeObserver = null, resizeDebounceTimeout = null;
2387
- // ResizeObserver has 98%+ browser support, no need for fallback
2388
- if ("undefined" != typeof ResizeObserver && isValidElement(glassRef.current)) try {
2389
- resizeObserver = new ResizeObserver((entries => {
2390
- for (const entry of entries) if (entry.target === glassRef.current) {
2391
- // Update cached rect when size changes
2392
- glassRef.current && (cachedRectRef.current = glassRef.current.getBoundingClientRect()),
2393
- // Debounce resize updates to match RAF timing (16ms)
2394
- resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), resizeDebounceTimeout = setTimeout((() => updateGlassSize(!1)), 16);
2395
- break;
2396
- }
2397
- })), resizeObserver.observe(glassRef.current);
2398
- } catch (error) {
2399
- console.warn("AtomixGlass: ResizeObserver not available, using window resize only", error);
2400
- }
2401
- return window.addEventListener("resize", debouncedResizeHandler, {
2402
- passive: !0
2403
- }), () => {
2404
- clearTimeout(initialTimeoutId), null !== rafId && cancelAnimationFrame(rafId), resizeTimeoutId && clearTimeout(resizeTimeoutId),
2405
- resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), window.removeEventListener("resize", debouncedResizeHandler),
2406
- resizeObserver?.disconnect();
2538
+ } : calculateElasticTranslation()), [ calculateElasticTranslation, effectiveWithoutEffects ]), directionalScale = useMemo((() => effectiveWithoutEffects ? "scale(1)" : calculateDirectionalScale()), [ calculateDirectionalScale, effectiveWithoutEffects ]), transformStyle = useMemo((() => effectiveWithoutEffects ? isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)" : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? "scale(0.96)" : directionalScale}`), [ elasticTranslation, isActive, onClick, directionalScale, effectiveWithoutEffects ]), handleGlobalMousePosition = useCallback((globalPos => {
2539
+ if (externalGlobalMousePosition && externalMouseOffset) return;
2540
+ if (effectiveWithoutEffects) return;
2541
+ const container = mouseContainer?.current || glassRef.current;
2542
+ if (!container) return;
2543
+ // Use cached rect if available, otherwise get new one
2544
+ let rect = cachedRectRef.current;
2545
+ if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
2546
+ cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
2547
+ const center = calculateElementCenter(rect), newOffset = {
2548
+ x: (globalPos.x - center.x) / rect.width * 100,
2549
+ y: (globalPos.y - center.y) / rect.height * 100
2407
2550
  };
2408
- }), [ effectiveCornerRadius, glassRef ]);
2409
- // OverLight config
2551
+ // Calculate offset relative to this container
2552
+ // Store in refs instead of state
2553
+ internalMouseOffsetRef.current = newOffset, internalGlobalMousePositionRef.current = globalPos,
2554
+ // Imperative style update
2555
+ updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
2556
+ mouseOffset: newOffset,
2557
+ globalMousePosition: globalPos,
2558
+ glassSize: glassSize,
2559
+ isHovered: isHovered,
2560
+ isActive: isActive,
2561
+ isOverLight: baseOverLightConfig.isOverLight,
2562
+ baseOverLightConfig: baseOverLightConfig,
2563
+ effectiveBorderRadius: effectiveBorderRadius,
2564
+ effectiveWithoutEffects: effectiveWithoutEffects,
2565
+ effectiveReducedMotion: effectiveReducedMotion,
2566
+ elasticity: elasticity,
2567
+ directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
2568
+ // Simplified directional scale for fast path
2569
+ onClick: onClick,
2570
+ withLiquidBlur: withLiquidBlur,
2571
+ blurAmount: blurAmount,
2572
+ saturation: saturation,
2573
+ padding: padding
2574
+ });
2575
+ }), [ mouseContainer, glassRef, wrapperRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects, glassSize, isHovered, isActive, baseOverLightConfig, effectiveBorderRadius, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding ]);
2410
2576
  /**
2411
- * Get effective overLight value based on configuration
2412
- * - boolean: returns the boolean value directly
2413
- * - 'auto': returns detectedOverLight (auto-detected from background)
2414
- * - object: returns detectedOverLight (auto-detected, but config object provides customization)
2415
- */
2416
- const getEffectiveOverLight = useCallback((() => "boolean" == typeof overLight ? overLight : ("auto" === overLight || "object" == typeof overLight && null !== overLight) && detectedOverLight), [ overLight, detectedOverLight ]), validateConfigValue = useCallback(((value, min, max, defaultValue) => "number" != typeof value || isNaN(value) || !isFinite(value) ? defaultValue : Math.min(max, Math.max(min, value))), []), overLightConfig = useMemo((() => {
2417
- const isOverLight = getEffectiveOverLight(), mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, baseConfig = {
2418
- isOverLight: isOverLight,
2419
- threshold: .7,
2420
- opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
2421
- contrast: Math.min(1.6, Math.max(1, 1.4 + .1 * mouseInfluence)),
2422
- brightness: Math.min(1.1, Math.max(.8, .9 + .05 * mouseInfluence)),
2423
- saturationBoost: 1.3,
2424
- // Fixed value dynamic saturation amplifies perceived displacement
2425
- shadowIntensity: Math.min(1.2, Math.max(.5, .9 + .2 * mouseInfluence)),
2426
- borderOpacity: Math.min(1, Math.max(.3, .7 + .1 * mouseInfluence))
2577
+ * Validate and clamp a numeric config value
2578
+ */
2579
+ // Subscribe to shared mouse tracker
2580
+ useEffect((() => {
2581
+ if (externalGlobalMousePosition && externalMouseOffset) return;
2582
+ if (effectiveWithoutEffects) return;
2583
+ const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition), container = mouseContainer?.current || glassRef.current;
2584
+ let resizeObserver = null;
2585
+ return container && "undefined" != typeof ResizeObserver && (resizeObserver = new ResizeObserver((() => {
2586
+ null !== updateRectRef.current && cancelAnimationFrame(updateRectRef.current), updateRectRef.current = requestAnimationFrame((() => {
2587
+ const container = mouseContainer?.current || glassRef.current;
2588
+ container && (cachedRectRef.current = container.getBoundingClientRect()), updateRectRef.current = null;
2589
+ }));
2590
+ })), resizeObserver.observe(container)), () => {
2591
+ unsubscribe(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
2592
+ updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
2427
2593
  };
2428
- if ("object" == typeof overLight && null !== overLight) {
2429
- const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), finalConfig = {
2430
- ...baseConfig,
2431
- threshold: validatedThreshold,
2432
- opacity: validatedOpacity * hoverIntensity * activeIntensity,
2433
- contrast: Math.min(1.6, validatedContrast + .1 * mouseInfluence),
2434
- brightness: Math.min(1.1, validatedBrightness + .05 * mouseInfluence),
2435
- saturationBoost: validatedSaturationBoost
2436
- };
2437
- // Validate and apply object config values with proper clamping
2438
- return "undefined" == typeof process || process.env, finalConfig;
2439
- }
2440
- // Debug logging for non-object configs
2441
- return "undefined" == typeof process || process.env, baseConfig;
2442
- }), [ overLight, getEffectiveOverLight, mouseOffset, isHovered, isActive, validateConfigValue, debugOverLight ]), handleMouseEnter = useCallback((() => setIsHovered(!0)), []), handleMouseLeave = useCallback((() => setIsHovered(!1)), []), handleMouseDown = useCallback((() => setIsActive(!0)), []), handleMouseUp = useCallback((() => setIsActive(!1)), []), handleKeyDown = useCallback((e => {
2594
+ }), [ handleGlobalMousePosition, mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects ]),
2595
+ // Also call updateStyles on other state changes (hover, active, etc)
2596
+ useEffect((() => {
2597
+ updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
2598
+ mouseOffset: externalMouseOffset || internalMouseOffsetRef.current,
2599
+ globalMousePosition: externalGlobalMousePosition || internalGlobalMousePositionRef.current,
2600
+ glassSize: glassSize,
2601
+ isHovered: isHovered,
2602
+ isActive: isActive,
2603
+ isOverLight: baseOverLightConfig.isOverLight,
2604
+ baseOverLightConfig: baseOverLightConfig,
2605
+ effectiveBorderRadius: effectiveBorderRadius,
2606
+ effectiveWithoutEffects: effectiveWithoutEffects,
2607
+ effectiveReducedMotion: effectiveReducedMotion,
2608
+ elasticity: elasticity,
2609
+ directionalScale: directionalScale,
2610
+ onClick: onClick,
2611
+ withLiquidBlur: withLiquidBlur,
2612
+ blurAmount: blurAmount,
2613
+ saturation: saturation,
2614
+ padding: padding
2615
+ });
2616
+ }), [ isHovered, isActive, glassSize, baseOverLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, directionalScale, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, padding, onClick ]);
2617
+ // Event handlers
2618
+ const handleMouseEnter = useCallback((() => setIsHovered(!0)), []), handleMouseLeave = useCallback((() => setIsHovered(!1)), []), handleMouseDown = useCallback((() => setIsActive(!0)), []), handleMouseUp = useCallback((() => setIsActive(!1)), []), handleMouseMove = useCallback((_e => {}), []), handleKeyDown = useCallback((e => {
2443
2619
  !onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
2444
- }), [ onClick ]), handleMouseMove = useCallback((_e => {}), []);
2445
- /**
2446
- * Validate and clamp a numeric config value
2447
- * @param value - The value to validate
2448
- * @param min - Minimum allowed value
2449
- * @param max - Maximum allowed value
2450
- * @param defaultValue - Default value if validation fails
2451
- * @returns Validated and clamped value
2452
- */ return {
2453
- // State
2620
+ }), [ onClick ]);
2621
+ return {
2454
2622
  isHovered: isHovered,
2455
2623
  isActive: isActive,
2456
2624
  glassSize: glassSize,
2457
- dynamicCornerRadius: dynamicCornerRadius,
2458
- effectiveCornerRadius: effectiveCornerRadius,
2625
+ dynamicBorderRadius: dynamicBorderRadius,
2626
+ effectiveBorderRadius: effectiveBorderRadius,
2459
2627
  effectiveReducedMotion: effectiveReducedMotion,
2460
2628
  effectiveHighContrast: effectiveHighContrast,
2461
- effectiveDisableEffects: effectiveDisableEffects,
2629
+ effectiveWithoutEffects: effectiveWithoutEffects,
2462
2630
  detectedOverLight: detectedOverLight,
2463
2631
  globalMousePosition: globalMousePosition,
2632
+ // This is now static (refs or props) unless prop changes
2464
2633
  mouseOffset: mouseOffset,
2465
- // OverLight config
2634
+ // This is now static (refs or props) unless prop changes
2466
2635
  overLightConfig: overLightConfig,
2467
- // Transform calculations
2468
2636
  elasticTranslation: elasticTranslation,
2469
2637
  directionalScale: directionalScale,
2470
2638
  transformStyle: transformStyle,
2471
- // Event handlers
2472
2639
  handleMouseEnter: handleMouseEnter,
2473
2640
  handleMouseLeave: handleMouseLeave,
2474
2641
  handleMouseDown: handleMouseDown,
@@ -2507,7 +2674,7 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2507
2674
  *
2508
2675
  * @example
2509
2676
  * // Manual border-radius override
2510
- * <AtomixGlass cornerRadius={20}>
2677
+ * <AtomixGlass borderRadius={20}>
2511
2678
  * <div>Content with 20px glass radius</div>
2512
2679
  * </AtomixGlass>
2513
2680
  *
@@ -2548,37 +2715,48 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2548
2715
  * <AtomixGlass overLight="auto" debugOverLight={true}>
2549
2716
  * <div>Content with debug logging enabled</div>
2550
2717
  * </AtomixGlass>
2551
- */ function AtomixGlass({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, cornerRadius: cornerRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, disableEffects: disableEffects = !1, enableLiquidBlur: enableLiquidBlur = !1, enableBorderEffect: enableBorderEffect = !0, enableOverLightLayers: enableOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, enablePerformanceMonitoring: enablePerformanceMonitoring = !1, debugCornerRadius: debugCornerRadius = !1, debugOverLight: debugOverLight = !1}) {
2552
- const glassRef = useRef(null), contentRef = useRef(null), {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveCornerRadius: effectiveCornerRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveDisableEffects: effectiveDisableEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
2718
+ */ 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}) {
2719
+ const glassRef = useRef(null), contentRef = useRef(null), {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
2553
2720
  glassRef: glassRef,
2554
2721
  contentRef: contentRef,
2555
- cornerRadius: cornerRadius,
2722
+ borderRadius: borderRadius,
2556
2723
  globalMousePosition: externalGlobalMousePosition,
2557
2724
  mouseOffset: externalMouseOffset,
2558
2725
  mouseContainer: mouseContainer,
2559
2726
  overLight: overLight,
2560
2727
  reducedMotion: reducedMotion,
2561
2728
  highContrast: highContrast,
2562
- disableEffects: disableEffects,
2729
+ withoutEffects: withoutEffects,
2563
2730
  elasticity: elasticity,
2564
2731
  onClick: onClick,
2565
- debugCornerRadius: debugCornerRadius,
2732
+ debugBorderRadius: debugBorderRadius,
2566
2733
  debugOverLight: debugOverLight,
2567
- enablePerformanceMonitoring: enablePerformanceMonitoring,
2568
- children: children
2569
- }), isOverLight = useMemo((() => overLightConfig?.isOverLight), [ overLight ]), shouldRenderOverLightLayers = enableOverLightLayers && isOverLight, baseStyle = {
2734
+ debugPerformance: debugPerformance,
2735
+ children: children,
2736
+ blurAmount: blurAmount,
2737
+ saturation: saturation,
2738
+ withLiquidBlur: withLiquidBlur,
2739
+ padding: padding,
2740
+ style: style
2741
+ }), isOverLight = useMemo((() => overLightConfig?.isOverLight), [ overLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, baseStyle = {
2570
2742
  ...style,
2571
- ...!effectiveDisableEffects && {
2743
+ ...!effectiveWithoutEffects && {
2572
2744
  transform: transformStyle
2573
2745
  }
2574
- }, componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveDisableEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
2746
+ }, componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
2575
2747
  position: style.position || "absolute",
2576
2748
  top: style.top || 0,
2577
2749
  left: style.left || 0
2578
- })), [ style.position, style.top, style.left ]), adjustedSize = useMemo((() => ({
2579
- width: "fixed" !== style.position ? "100%" : style.width ? style.width : Math.max(glassSize.width, 0),
2580
- height: "fixed" !== style.position ? "100%" : style.height ? style.height : Math.max(glassSize.height, 0)
2581
- })), [ style.position, style.width, style.height, glassSize.width, glassSize.height ]), gradientValues = useMemo((() => {
2750
+ })), [ style.position, style.top, style.left ]), adjustedSize = useMemo((() => {
2751
+ const resolveSize = (propValue, styleValue, measuredSize) => {
2752
+ const explicitSize = propValue ?? styleValue;
2753
+ return void 0 !== explicitSize ? "number" == typeof explicitSize ? `${explicitSize}px` : explicitSize : "fixed" === positionStyles.position ? `${Math.max(measuredSize, 0)}px` : "100%";
2754
+ };
2755
+ return {
2756
+ width: resolveSize(width, style.width, glassSize.width),
2757
+ height: resolveSize(height, style.height, glassSize.height)
2758
+ };
2759
+ }), [ width, height, style.width, style.height, positionStyles.position, glassSize.width, glassSize.height ]), gradientValues = useMemo((() => {
2582
2760
  const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
2583
2761
  return {
2584
2762
  borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
@@ -2620,13 +2798,13 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2620
2798
  }), [ isHovered, isActive, isOverLight, overLightConfig.opacity ]), glassVars = useMemo((() => {
2621
2799
  const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, {borderGradientAngle: borderGradientAngle, borderStop1: borderStop1, borderStop2: borderStop2, borderOpacities: borderOpacities, hoverPositions: hoverPositions, basePosition: basePosition, mx: mx, my: my, absMx: absMx, absMy: absMy} = gradientValues, configBorderOpacity = overLightConfig?.borderOpacity ?? 1;
2622
2800
  return {
2623
- "--atomix-glass-radius": `${effectiveCornerRadius}px`,
2801
+ "--atomix-glass-radius": `${effectiveBorderRadius}px`,
2624
2802
  "--atomix-glass-transform": transformStyle || "none",
2625
2803
  "--atomix-glass-position": positionStyles.position,
2626
2804
  "--atomix-glass-top": "fixed" !== positionStyles.top ? `${positionStyles.top}px` : "0",
2627
2805
  "--atomix-glass-left": "fixed" !== positionStyles.left ? `${positionStyles.left}px` : "0",
2628
- "--atomix-glass-width": "fixed" !== style.position ? adjustedSize.width : `${adjustedSize.width}px`,
2629
- "--atomix-glass-height": "fixed" !== style.position ? adjustedSize.height : `${adjustedSize.height}px`,
2806
+ "--atomix-glass-width": adjustedSize.width,
2807
+ "--atomix-glass-height": adjustedSize.height,
2630
2808
  "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
2631
2809
  "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
2632
2810
  "--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
@@ -2642,24 +2820,25 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2642
2820
  "--atomix-glass-overlay-opacity": opacityValues.over,
2643
2821
  "--atomix-glass-overlay-gradient": isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`
2644
2822
  };
2645
- }), [ gradientValues, opacityValues, effectiveCornerRadius, transformStyle, positionStyles, adjustedSize, style.position, isOverLight, overLightConfig.borderOpacity ]), renderBackgroundLayer = layerType => jsx("div", {
2823
+ }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, positionStyles, adjustedSize, isOverLight, overLightConfig.borderOpacity ]), renderBackgroundLayer = layerType => jsx("div", {
2646
2824
  className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, "dark" === layerType ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" "),
2647
2825
  style: {
2648
2826
  ...positionStyles,
2649
2827
  height: adjustedSize.height,
2650
2828
  width: adjustedSize.width,
2651
- borderRadius: `${effectiveCornerRadius}px`,
2829
+ borderRadius: `${effectiveBorderRadius}px`,
2652
2830
  transform: baseStyle.transform
2653
2831
  }
2654
2832
  });
2655
2833
  return jsxs("div", {
2834
+ ...rest,
2656
2835
  className: componentClassName,
2657
2836
  style: glassVars,
2658
2837
  role: role || (onClick ? "button" : void 0),
2659
2838
  tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
2660
2839
  "aria-label": ariaLabel,
2661
2840
  "aria-describedby": ariaDescribedBy,
2662
- "aria-disabled": !(!onClick || !effectiveDisableEffects) || !onClick && void 0,
2841
+ "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
2663
2842
  "aria-pressed": !(!onClick || !isActive) || !onClick && void 0,
2664
2843
  onKeyDown: onClick ? handleKeyDown : void 0,
2665
2844
  children: [ jsx(AtomixGlassContainer, {
@@ -2667,18 +2846,18 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2667
2846
  contentRef: contentRef,
2668
2847
  className: className,
2669
2848
  style: baseStyle,
2670
- cornerRadius: effectiveCornerRadius,
2671
- displacementScale: effectiveDisableEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
2672
- blurAmount: effectiveDisableEffects ? 0 : blurAmount,
2849
+ borderRadius: effectiveBorderRadius,
2850
+ displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
2851
+ blurAmount: effectiveWithoutEffects ? 0 : blurAmount,
2673
2852
  saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
2674
- aberrationIntensity: effectiveDisableEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
2853
+ aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
2675
2854
  glassSize: glassSize,
2676
2855
  padding: padding,
2677
- mouseOffset: effectiveDisableEffects ? {
2856
+ mouseOffset: effectiveWithoutEffects ? {
2678
2857
  x: 0,
2679
2858
  y: 0
2680
2859
  } : mouseOffset,
2681
- globalMousePosition: effectiveDisableEffects ? {
2860
+ globalMousePosition: effectiveWithoutEffects ? {
2682
2861
  x: 0,
2683
2862
  y: 0
2684
2863
  } : globalMousePosition,
@@ -2698,11 +2877,11 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2698
2877
  onClick: onClick,
2699
2878
  mode: mode,
2700
2879
  transform: baseStyle.transform,
2701
- effectiveDisableEffects: effectiveDisableEffects,
2880
+ effectiveWithoutEffects: effectiveWithoutEffects,
2702
2881
  effectiveReducedMotion: effectiveReducedMotion,
2703
2882
  shaderVariant: shaderVariant,
2704
2883
  elasticity: elasticity,
2705
- enableLiquidBlur: enableLiquidBlur,
2884
+ withLiquidBlur: withLiquidBlur,
2706
2885
  children: children
2707
2886
  }), Boolean(onClick) && jsxs(Fragment, {
2708
2887
  children: [ jsx("div", {
@@ -2724,11 +2903,19 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
2724
2903
  background: `radial-gradient(circle at ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_X}% ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_Y}%, rgba(255, 255, 255, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.WHITE_OPACITY}) 0%, transparent ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.STOP}%)`
2725
2904
  }
2726
2905
  }) ]
2727
- }), enableBorderEffect && jsxs(Fragment, {
2906
+ }), withBorder && jsxs(Fragment, {
2728
2907
  children: [ jsx("span", {
2729
- className: ATOMIX_GLASS.BORDER_1_CLASS
2908
+ className: ATOMIX_GLASS.BORDER_1_CLASS,
2909
+ style: {
2910
+ width: glassSize.width,
2911
+ height: glassSize.height
2912
+ }
2730
2913
  }), jsx("span", {
2731
- className: ATOMIX_GLASS.BORDER_2_CLASS
2914
+ className: ATOMIX_GLASS.BORDER_2_CLASS,
2915
+ style: {
2916
+ width: glassSize.width,
2917
+ height: glassSize.height
2918
+ }
2732
2919
  }) ]
2733
2920
  }) ]
2734
2921
  });
@@ -2767,11 +2954,7 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
2767
2954
  onZoomReset: handleZoomReset,
2768
2955
  onPanToggle: handlePanToggle,
2769
2956
  onReset: handleReset
2770
- })), [ onRefresh, onExport, handleFullscreenChange, handleZoomIn, handleZoomOut, handleZoomReset, handlePanToggle, handleReset ]), {state: toolbarState, handlers: toolbarHandlers, toolbarGroups: toolbarGroups} =
2771
- /**
2772
- * Hook for managing chart toolbar state and generating chart-specific configurations
2773
- */
2774
- function(chartType, config = {}, handlers = {}) {
2957
+ })), [ onRefresh, onExport, handleFullscreenChange, handleZoomIn, handleZoomOut, handleZoomReset, handlePanToggle, handleReset ]), {state: toolbarState, handlers: toolbarHandlers, toolbarGroups: toolbarGroups} = function(chartType, config = {}, handlers = {}) {
2775
2958
  const [state, setState] = useState({
2776
2959
  isFullscreen: !1,
2777
2960
  isExporting: !1,
@@ -3156,16 +3339,16 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
3156
3339
  aberrationIntensity: 1.5,
3157
3340
  elasticity: 0,
3158
3341
  // No elastic effect for charts
3159
- enableLiquidBlur: !1,
3342
+ withLiquidBlur: !1,
3160
3343
  // Keep it simple
3161
- enableBorderEffect: !0,
3344
+ withBorder: !0,
3162
3345
  mode: "standard",
3163
3346
  mouseContainer: chartContainerRef,
3164
3347
  reducedMotion: !1
3165
3348
  })), []), glassProps = useMemo((() => glass ? !0 === glass ? defaultChartGlassProps : {
3166
3349
  ...defaultChartGlassProps,
3167
3350
  ...glass
3168
- } : null), [ glass, defaultChartGlassProps ]), chartBorderRadius = useMemo((() => glassProps?.cornerRadius || void 0), [ glassProps?.cornerRadius ]), chartContextValue = useMemo((() => ({
3351
+ } : null), [ glass, defaultChartGlassProps ]), chartBorderRadius = useMemo((() => glassProps?.borderRadius || void 0), [ glassProps?.borderRadius ]), chartContextValue = useMemo((() => ({
3169
3352
  zoomLevel: zoomLevel,
3170
3353
  panOffset: panOffset,
3171
3354
  panEnabled: panEnabled,
@@ -3296,7 +3479,7 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
3296
3479
  }) ]
3297
3480
  }), wrappedChart = glassProps ? jsx(AtomixGlass, {
3298
3481
  ...glassProps,
3299
- cornerRadius: chartBorderRadius,
3482
+ borderRadius: chartBorderRadius,
3300
3483
  style: {
3301
3484
  width: "100%",
3302
3485
  height: "100%",
@@ -3349,7 +3532,13 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
3349
3532
  // Get chart context (zoom/pan state from toolbar) - optional
3350
3533
  // Always call useContext to maintain consistent hook order
3351
3534
  const chartContext = useContext(ChartContext), {calculateScales: calculateScales, getChartColors: getChartColors} = useChart(), {processedData: processedData, isProcessing: isProcessing} = function(datasets, options) {
3352
- const [processedData, setProcessedData] = useState(datasets), [isProcessing, setIsProcessing] = useState(!1), {enableDecimation: enableDecimation = !1, maxDataPoints: maxDataPoints = 1e3, enableRealTime: enableRealTime = !1, realTimeInterval: realTimeInterval = 1e3} = options || {}, decimateData = useCallback(((data, maxPoints) => {
3535
+ const [processedData, setProcessedData] = useState(datasets), [isProcessing, setIsProcessing] = useState(!1), {enableDecimation: enableDecimation = !1, maxDataPoints: maxDataPoints = 1e3, enableRealTime: enableRealTime = !1, realTimeInterval: realTimeInterval = 1e3} = options || {}, lastDataSignature = useRef(""), getDatasetSignature = useCallback((data => data.map((d => `${d.label}:${JSON.stringify(d.data)}`)).join("|")), []);
3536
+ // Update signature when processedData changes (e.g. via props)
3537
+ useEffect((() => {
3538
+ lastDataSignature.current = getDatasetSignature(processedData);
3539
+ }), [ processedData, getDatasetSignature ]);
3540
+ // Data decimation for performance
3541
+ const decimateData = useCallback(((data, maxPoints) => {
3353
3542
  if (!enableDecimation || !data.length) return data;
3354
3543
  const dataLength = data[0]?.data?.length || 0;
3355
3544
  if (dataLength <= maxPoints) return data;
@@ -3369,10 +3558,17 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
3369
3558
  }), []), calculateTrendLine = useCallback((values => {
3370
3559
  const n = values.length;
3371
3560
  if (n < 2) return values.map((() => null));
3372
- const xSum = _reduceInstanceProperty(values).call(values, ((sum, _, i) => sum + i), 0), ySum = _reduceInstanceProperty(values).call(values, ((sum, val) => sum + val), 0), slope = (n * _reduceInstanceProperty(values).call(values, ((sum, val, i) => sum + i * val), 0) - xSum * ySum) / (n * _reduceInstanceProperty(values).call(values, ((sum, _, i) => sum + i * i), 0) - xSum * xSum), intercept = (ySum - slope * xSum) / n;
3561
+ let xSum = 0, ySum = 0, xySum = 0, x2Sum = 0;
3562
+ for (let i = 0; i < n; i++) {
3563
+ const val = values[i], safeVal = "number" == typeof val ? val : 0;
3564
+ // Treat null/undefined as 0 to match original reduce behavior
3565
+ xSum += i, ySum += safeVal, xySum += i * safeVal, x2Sum += i * i;
3566
+ }
3567
+ const slope = (n * xySum - xSum * ySum) / (n * x2Sum - xSum * xSum), intercept = (ySum - slope * xSum) / n;
3373
3568
  return values.map(((_, i) => slope * i + intercept));
3374
3569
  }), []);
3375
- // Process data when datasets change
3570
+ // Moving average calculation
3571
+ // Process data when datasets change
3376
3572
  return useEffect((() => {
3377
3573
  setIsProcessing(!0), (async () => {
3378
3574
  let processed = [ ...datasets ];
@@ -3384,11 +3580,12 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
3384
3580
  useEffect((() => {
3385
3581
  if (!enableRealTime) return;
3386
3582
  const interval = setInterval((() => {
3387
- setProcessedData((prev => [ ...prev ]));
3388
- // Trigger re-render for real-time updates
3389
- }), realTimeInterval);
3583
+ setProcessedData((prev =>
3584
+ // Only trigger update if signature changed
3585
+ getDatasetSignature(prev) === lastDataSignature.current ? prev : [ ...prev ]));
3586
+ }), realTimeInterval);
3390
3587
  return () => clearInterval(interval);
3391
- }), [ enableRealTime, realTimeInterval ]), {
3588
+ }), [ enableRealTime, realTimeInterval, getDatasetSignature ]), {
3392
3589
  processedData: processedData,
3393
3590
  isProcessing: isProcessing,
3394
3591
  decimateData: decimateData,
@@ -3409,12 +3606,12 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
3409
3606
  */
3410
3607
  function(datasets, options) {
3411
3608
  const {enableVirtualization: enableVirtualization = !1, enableMemoization: enableMemoization = !0, debounceMs: debounceMs = 100} = options || {}, [isOptimizing, setIsOptimizing] = useState(!1), debounceRef = useRef(null), memoizedScales = useMemo((() => enableMemoization ? datasets.map((dataset => {
3412
- const values = dataset.data?.map((d => d.value)).filter((v => "number" == typeof v)) || [], validValues = values.length > 0 ? values : [ 0 ];
3609
+ const {min: min, max: max, hasValid: hasValid} = getDatasetBounds(dataset.data);
3413
3610
  return {
3414
3611
  label: dataset.label,
3415
3612
  dataLength: dataset.data?.length || 0,
3416
- minValue: Math.min(...validValues),
3417
- maxValue: Math.max(...validValues)
3613
+ minValue: hasValid ? min : 0,
3614
+ maxValue: hasValid ? max : 0
3418
3615
  };
3419
3616
  })) : null), [ datasets, enableMemoization ]), debouncedUpdate = useCallback((callback => {
3420
3617
  debounceRef.current && clearTimeout(debounceRef.current), debounceRef.current = setTimeout((() => {
@@ -3513,8 +3710,8 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
3513
3710
  const getAccessibleDescription = useCallback((() => {
3514
3711
  if (!datasets.length) return "Empty chart";
3515
3712
  const datasetDescriptions = datasets.map(((dataset, i) => {
3516
- const dataCount = dataset.data?.length || 0, values = dataset.data?.map((d => d.value)).filter((v => "number" == typeof v)) || [], min = values.length > 0 ? Math.min(...values) : 0, max = values.length > 0 ? Math.max(...values) : 0;
3517
- return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${min} to ${max}`;
3713
+ const dataCount = dataset.data?.length || 0, {min: min, max: max, hasValid: hasValid} = getDatasetBounds(dataset.data), minVal = hasValid ? min : 0, maxVal = hasValid ? max : 0;
3714
+ return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${minVal} to ${maxVal}`;
3518
3715
  })).join(". ");
3519
3716
  return `Chart with ${datasets.length} datasets. ${datasetDescriptions}`;
3520
3717
  }), [ datasets ]);
@@ -5757,14 +5954,14 @@ const ScatterChart = memo( forwardRef((({datasets: datasets = [], config: confi
5757
5954
 
5758
5955
  ScatterChart.displayName = "ScatterChart";
5759
5956
 
5760
- const TreemapChart = memo( forwardRef((({data: data = [], algorithm: algorithm = "squarified", colorConfig: colorConfig = {
5957
+ const DEFAULT_COLOR_CONFIG = {
5761
5958
  scheme: "category"
5762
- }, labelConfig: labelConfig = {
5959
+ }, DEFAULT_LABEL_CONFIG = {
5763
5960
  showLabels: !0,
5764
5961
  minSize: 1e3,
5765
5962
  fontSize: 12,
5766
5963
  textColor: "white"
5767
- }, onDataPointClick: onDataPointClick, config: config = {}, ...props}, ref) => {
5964
+ }, DEFAULT_CONFIG = {}, TreemapChart = memo( forwardRef((({data: data = [], algorithm: algorithm = "squarified", colorConfig: colorConfig = DEFAULT_COLOR_CONFIG, labelConfig: labelConfig = DEFAULT_LABEL_CONFIG, onDataPointClick: onDataPointClick, config: config = DEFAULT_CONFIG, ...props}, ref) => {
5768
5965
  const [hoveredNode, setHoveredNode] = useState(null), [selectedNode, setSelectedNode] = useState(null);
5769
5966
  useState({
5770
5967
  x: 0,
@@ -5876,87 +6073,87 @@ const TreemapChart = memo( forwardRef((({data: data = [], algorithm: algorithm
5876
6073
  remainingHeight -= rowHeight), currentRow = [];
5877
6074
  }
5878
6075
  }
5879
- }), []);
6076
+ }), []), renderContent = useCallback((({scales: scales, colors: colors, datasets: renderedDatasets, handlers: handlers, hoveredPoint: hoveredPoint}) => {
6077
+ if (!data.length) return null;
6078
+ // Calculate available space with padding
6079
+ const availableWidth = scales.width - 40, availableHeight = scales.height - 40, leafNodes = data.filter((item => !item.children || 0 === item.children.length));
6080
+ if (!leafNodes.length) return null;
6081
+ const totalValue = _reduceInstanceProperty(leafNodes).call(leafNodes, ((sum, node) => sum + node.value), 0), treemapNodes = leafNodes.map(((item, index) => ({
6082
+ id: item.id,
6083
+ label: item.label,
6084
+ value: item.value,
6085
+ color: generateColor(item, 0, index) || "transparent",
6086
+ x: 0,
6087
+ // Will be calculated by squarify
6088
+ y: 0,
6089
+ // Will be calculated by squarify
6090
+ width: 0,
6091
+ // Will be calculated by squarify
6092
+ height: 0,
6093
+ // Will be calculated by squarify
6094
+ depth: 0,
6095
+ children: [],
6096
+ originalData: item
6097
+ })));
6098
+ // Create treemap nodes with proper dimensions
6099
+ // Apply squarified algorithm to layout nodes proportionally by value
6100
+ if ("squarified" === algorithm && totalValue > 0) squarify(treemapNodes, 20, 20, availableWidth, availableHeight); else {
6101
+ // Fallback: simple grid layout (equal sizes)
6102
+ const cols = Math.ceil(Math.sqrt(leafNodes.length)), rows = Math.ceil(leafNodes.length / cols), nodeWidth = availableWidth / cols, nodeHeight = availableHeight / rows;
6103
+ treemapNodes.forEach(((node, index) => {
6104
+ const col = index % cols, row = Math.floor(index / cols);
6105
+ node.x = 20 + col * nodeWidth, node.y = 20 + row * nodeHeight, node.width = nodeWidth,
6106
+ node.height = nodeHeight;
6107
+ }));
6108
+ }
6109
+ return jsx(Fragment, {
6110
+ children: treemapNodes.map((node => {
6111
+ const isHovered = hoveredNode === node, isSelected = selectedNode === node, area = node.width * node.height, showLabel = labelConfig.showLabels && area >= (labelConfig.minSize || 1e3);
6112
+ return jsxs("g", {
6113
+ children: [ jsx("rect", {
6114
+ x: node.x,
6115
+ y: node.y,
6116
+ width: node.width,
6117
+ height: node.height,
6118
+ fill: node.color,
6119
+ className: `c-chart__treemap-node ${isHovered ? "c-chart__treemap-node--hovered" : ""} ${isSelected ? "c-chart__treemap-node--selected" : ""}`,
6120
+ onClick: () => {
6121
+ setSelectedNode(node), handlers.onDataPointClick?.(node.originalData, 0, 0);
6122
+ },
6123
+ onMouseEnter: e => {
6124
+ setHoveredNode(node);
6125
+ const rect = e.currentTarget.getBoundingClientRect();
6126
+ handlers.onPointHover(0, 0, node.x, node.y, rect.left + rect.width / 2, rect.top + rect.height / 2);
6127
+ },
6128
+ onMouseLeave: () => {
6129
+ setHoveredNode(null), handlers.onPointLeave();
6130
+ }
6131
+ }), showLabel && jsx("text", {
6132
+ x: node.x + node.width / 2,
6133
+ y: node.y + node.height / 2,
6134
+ textAnchor: "middle",
6135
+ dominantBaseline: "middle",
6136
+ className: "c-chart__treemap-label",
6137
+ style: {
6138
+ fontSize: labelConfig.fontSize,
6139
+ fill: labelConfig.textColor
6140
+ },
6141
+ children: node.label
6142
+ }) ]
6143
+ }, node.id);
6144
+ }))
6145
+ });
6146
+ }), [ data, algorithm, generateColor, squarify, labelConfig, hoveredNode, selectedNode ]), datasets = useMemo((() => [ {
6147
+ label: "Treemap Data",
6148
+ data: data
6149
+ } ]), [ data ]);
5880
6150
  // Squarified treemap algorithm
5881
6151
  return jsx(BaseChart, {
5882
6152
  ref: ref,
5883
6153
  type: "treemap",
5884
- datasets: [ {
5885
- label: "Treemap Data",
5886
- data: data
5887
- } ],
6154
+ datasets: datasets,
5888
6155
  config: config,
5889
- renderContent: ({scales: scales, colors: colors, datasets: renderedDatasets, handlers: handlers, hoveredPoint: hoveredPoint}) => {
5890
- if (!data.length) return null;
5891
- // Calculate available space with padding
5892
- const availableWidth = scales.width - 40, availableHeight = scales.height - 40, leafNodes = data.filter((item => !item.children || 0 === item.children.length));
5893
- if (!leafNodes.length) return null;
5894
- const totalValue = _reduceInstanceProperty(leafNodes).call(leafNodes, ((sum, node) => sum + node.value), 0), treemapNodes = leafNodes.map(((item, index) => ({
5895
- id: item.id,
5896
- label: item.label,
5897
- value: item.value,
5898
- color: generateColor(item, 0, index) || "transparent",
5899
- x: 0,
5900
- // Will be calculated by squarify
5901
- y: 0,
5902
- // Will be calculated by squarify
5903
- width: 0,
5904
- // Will be calculated by squarify
5905
- height: 0,
5906
- // Will be calculated by squarify
5907
- depth: 0,
5908
- children: [],
5909
- originalData: item
5910
- })));
5911
- // Create treemap nodes with proper dimensions
5912
- // Apply squarified algorithm to layout nodes proportionally by value
5913
- if ("squarified" === algorithm && totalValue > 0) squarify(treemapNodes, 20, 20, availableWidth, availableHeight); else {
5914
- // Fallback: simple grid layout (equal sizes)
5915
- const cols = Math.ceil(Math.sqrt(leafNodes.length)), rows = Math.ceil(leafNodes.length / cols), nodeWidth = availableWidth / cols, nodeHeight = availableHeight / rows;
5916
- treemapNodes.forEach(((node, index) => {
5917
- const col = index % cols, row = Math.floor(index / cols);
5918
- node.x = 20 + col * nodeWidth, node.y = 20 + row * nodeHeight, node.width = nodeWidth,
5919
- node.height = nodeHeight;
5920
- }));
5921
- }
5922
- return jsx(Fragment, {
5923
- children: treemapNodes.map((node => {
5924
- const isHovered = hoveredNode === node, isSelected = selectedNode === node, area = node.width * node.height, showLabel = labelConfig.showLabels && area >= (labelConfig.minSize || 1e3);
5925
- return jsxs("g", {
5926
- children: [ jsx("rect", {
5927
- x: node.x,
5928
- y: node.y,
5929
- width: node.width,
5930
- height: node.height,
5931
- fill: node.color,
5932
- className: `c-chart__treemap-node ${isHovered ? "c-chart__treemap-node--hovered" : ""} ${isSelected ? "c-chart__treemap-node--selected" : ""}`,
5933
- onClick: () => {
5934
- setSelectedNode(node), handlers.onDataPointClick?.(node.originalData, 0, 0);
5935
- },
5936
- onMouseEnter: e => {
5937
- setHoveredNode(node);
5938
- const rect = e.currentTarget.getBoundingClientRect();
5939
- handlers.onPointHover(0, 0, node.x, node.y, rect.left + rect.width / 2, rect.top + rect.height / 2);
5940
- },
5941
- onMouseLeave: () => {
5942
- setHoveredNode(null), handlers.onPointLeave();
5943
- }
5944
- }), showLabel && jsx("text", {
5945
- x: node.x + node.width / 2,
5946
- y: node.y + node.height / 2,
5947
- textAnchor: "middle",
5948
- dominantBaseline: "middle",
5949
- className: "c-chart__treemap-label",
5950
- style: {
5951
- fontSize: labelConfig.fontSize,
5952
- fill: labelConfig.textColor
5953
- },
5954
- children: node.label
5955
- }) ]
5956
- }, node.id);
5957
- }))
5958
- });
5959
- },
6156
+ renderContent: renderContent,
5960
6157
  onDataPointClick: onDataPointClick,
5961
6158
  interactive: !0,
5962
6159
  ...props