@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.
- package/dist/atomix.css +9351 -9259
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +4 -4
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +12 -19
- package/dist/charts.js +555 -358
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +21 -24
- package/dist/core.js +435 -265
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +11 -18
- package/dist/forms.js +411 -257
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +14 -21
- package/dist/heavy.js +409 -254
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +38 -41
- package/dist/index.esm.js +731 -487
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +733 -492
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
- package/scripts/atomix-cli.js +34 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
- package/src/components/AtomixGlass/README.md +5 -5
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
- package/src/components/Badge/Badge.stories.tsx +1 -1
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Breadcrumb/Breadcrumb.tsx +90 -77
- package/src/components/Breadcrumb/index.ts +2 -2
- package/src/components/Button/Button.stories.tsx +1 -1
- package/src/components/Button/Button.tsx +2 -1
- package/src/components/Button/README.md +2 -2
- package/src/components/Callout/Callout.test.tsx +3 -3
- package/src/components/Callout/Callout.tsx +2 -2
- package/src/components/Callout/README.md +2 -2
- package/src/components/Card/Card.tsx +31 -11
- package/src/components/Chart/Chart.stories.tsx +1 -1
- package/src/components/Chart/Chart.tsx +5 -5
- package/src/components/Chart/TreemapChart.tsx +37 -29
- package/src/components/DatePicker/readme.md +3 -3
- package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +276 -273
- package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
- package/src/components/Footer/FooterLink.tsx +2 -2
- package/src/components/Form/Checkbox.stories.tsx +1 -1
- package/src/components/Form/Checkbox.tsx +1 -1
- package/src/components/Form/Input.stories.tsx +1 -1
- package/src/components/Form/Input.tsx +1 -1
- package/src/components/Form/Radio.stories.tsx +1 -1
- package/src/components/Form/Radio.tsx +1 -1
- package/src/components/Form/Select.stories.tsx +1 -1
- package/src/components/Form/Select.tsx +1 -1
- package/src/components/Form/Textarea.stories.tsx +1 -1
- package/src/components/Form/Textarea.tsx +1 -1
- package/src/components/Hero/Hero.stories.tsx +2 -2
- package/src/components/Hero/Hero.tsx +2 -2
- package/src/components/Messages/Messages.stories.tsx +1 -1
- package/src/components/Messages/Messages.tsx +2 -2
- package/src/components/Modal/Modal.stories.tsx +1 -1
- package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.tsx +1 -1
- package/src/components/Navigation/Nav/NavItem.tsx +6 -3
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
- package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.tsx +1 -1
- package/src/components/Popover/Popover.stories.tsx +1 -1
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/Rating/Rating.stories.tsx +1 -1
- package/src/components/Rating/Rating.test.tsx +73 -0
- package/src/components/Rating/Rating.tsx +25 -37
- package/src/components/Spinner/Spinner.tsx +1 -1
- package/src/components/Steps/Steps.stories.tsx +1 -1
- package/src/components/Steps/Steps.tsx +2 -2
- package/src/components/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Todo/Todo.tsx +0 -1
- package/src/components/Toggle/Toggle.stories.tsx +1 -1
- package/src/components/Toggle/Toggle.tsx +1 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
- package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
- package/src/lib/composables/__tests__/useChart.test.ts +50 -0
- package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
- package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
- package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
- package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
- package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
- package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
- package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
- package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
- package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
- package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
- package/src/lib/composables/glass-styles.ts +302 -0
- package/src/lib/composables/index.ts +0 -4
- package/src/lib/composables/useAtomixGlass.ts +331 -522
- package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
- package/src/lib/composables/useBarChart.ts +1 -1
- package/src/lib/composables/useBreadcrumb.ts +6 -6
- package/src/lib/composables/useChart.ts +104 -21
- package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
- package/src/lib/composables/useSlider.ts +66 -34
- package/src/lib/theme/devtools/CLI.ts +1 -1
- package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
- package/src/lib/types/components.ts +18 -21
- package/src/lib/utils/__tests__/dom.test.ts +100 -0
- package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
- package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
- package/src/styles/02-tools/_tools.utility-api.scss +6 -6
- package/src/styles/06-components/_components.accordion.scss +0 -2
- package/src/styles/06-components/_components.chart.scss +0 -1
- package/src/styles/06-components/_components.dropdown.scss +0 -1
- package/src/styles/06-components/_components.edge-panel.scss +0 -2
- package/src/styles/06-components/_components.photoviewer.scss +0 -1
- package/src/styles/06-components/_components.river.scss +0 -1
- package/src/styles/06-components/_components.slider.scss +0 -3
- package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
- 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
|
-
//
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 = {},
|
|
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",
|
|
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 (!
|
|
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
|
-
}), [
|
|
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 !
|
|
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
|
|
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,
|
|
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
|
|
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":
|
|
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,
|
|
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:
|
|
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
|
-
},
|
|
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,
|
|
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),
|
|
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
|
-
}),
|
|
2243
|
+
}), internalMouseOffsetRef = useRef({
|
|
2094
2244
|
x: 0,
|
|
2095
2245
|
y: 0
|
|
2096
|
-
}), [
|
|
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 || !
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
2198
|
-
|
|
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
|
-
|
|
2408
|
+
// Silently continue
|
|
2203
2409
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
// Exit loop if currentElement becomes null
|
|
2207
|
-
currentElement = currentElement.parentElement, depth++;
|
|
2410
|
+
if (!currentElement) break;
|
|
2411
|
+
currentElement = currentElement.parentElement, depth++;
|
|
2208
2412
|
}
|
|
2209
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2457
|
+
return;
|
|
2275
2458
|
}
|
|
2276
2459
|
}), [ overLight, glassRef, debugOverLight ]);
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
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
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
return;
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
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
|
-
}), [
|
|
2320
|
-
|
|
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,
|
|
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((() =>
|
|
2535
|
+
}), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveWithoutEffects ? {
|
|
2357
2536
|
x: 0,
|
|
2358
2537
|
y: 0
|
|
2359
|
-
} : calculateElasticTranslation()), [ calculateElasticTranslation,
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
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
|
-
|
|
2409
|
-
|
|
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
|
-
*
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
const
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
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
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
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 ])
|
|
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
|
-
|
|
2458
|
-
|
|
2625
|
+
dynamicBorderRadius: dynamicBorderRadius,
|
|
2626
|
+
effectiveBorderRadius: effectiveBorderRadius,
|
|
2459
2627
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2460
2628
|
effectiveHighContrast: effectiveHighContrast,
|
|
2461
|
-
|
|
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
|
-
//
|
|
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
|
|
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,
|
|
2552
|
-
const glassRef = useRef(null), contentRef = useRef(null), {isHovered: isHovered, isActive: isActive, glassSize: glassSize,
|
|
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
|
-
|
|
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
|
-
|
|
2729
|
+
withoutEffects: withoutEffects,
|
|
2563
2730
|
elasticity: elasticity,
|
|
2564
2731
|
onClick: onClick,
|
|
2565
|
-
|
|
2732
|
+
debugBorderRadius: debugBorderRadius,
|
|
2566
2733
|
debugOverLight: debugOverLight,
|
|
2567
|
-
|
|
2568
|
-
children: children
|
|
2569
|
-
|
|
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
|
-
...!
|
|
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`,
|
|
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
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
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": `${
|
|
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":
|
|
2629
|
-
"--atomix-glass-height":
|
|
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,
|
|
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: `${
|
|
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 || !
|
|
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
|
-
|
|
2671
|
-
displacementScale:
|
|
2672
|
-
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:
|
|
2853
|
+
aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
|
|
2675
2854
|
glassSize: glassSize,
|
|
2676
2855
|
padding: padding,
|
|
2677
|
-
mouseOffset:
|
|
2856
|
+
mouseOffset: effectiveWithoutEffects ? {
|
|
2678
2857
|
x: 0,
|
|
2679
2858
|
y: 0
|
|
2680
2859
|
} : mouseOffset,
|
|
2681
|
-
globalMousePosition:
|
|
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
|
-
|
|
2880
|
+
effectiveWithoutEffects: effectiveWithoutEffects,
|
|
2702
2881
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2703
2882
|
shaderVariant: shaderVariant,
|
|
2704
2883
|
elasticity: elasticity,
|
|
2705
|
-
|
|
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
|
-
}),
|
|
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
|
-
|
|
3342
|
+
withLiquidBlur: !1,
|
|
3160
3343
|
// Keep it simple
|
|
3161
|
-
|
|
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?.
|
|
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
|
-
|
|
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 || {},
|
|
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
|
-
|
|
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
|
-
//
|
|
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 =>
|
|
3388
|
-
|
|
3389
|
-
|
|
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
|
|
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:
|
|
3417
|
-
maxValue:
|
|
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,
|
|
3517
|
-
return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${
|
|
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
|
|
5957
|
+
const DEFAULT_COLOR_CONFIG = {
|
|
5761
5958
|
scheme: "category"
|
|
5762
|
-
},
|
|
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 =
|
|
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:
|
|
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
|