@shohojdhara/atomix 0.4.0 → 0.4.2
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 +0 -14
- 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 -359
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +98 -28
- package/dist/core.js +1082 -733
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +26 -21
- package/dist/forms.js +937 -350
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +14 -21
- package/dist/heavy.js +409 -256
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +518 -284
- package/dist/index.esm.js +1993 -1237
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1994 -1237
- 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 +2 -2
- package/scripts/atomix-cli.js +43 -1
- package/scripts/cli/__tests__/utils.test.js +6 -2
- package/scripts/cli/migration-tools.js +2 -2
- package/scripts/cli/theme-bridge.js +7 -9
- package/scripts/cli/utils.js +2 -1
- package/src/components/Accordion/Accordion.stories.tsx +40 -0
- package/src/components/Accordion/Accordion.tsx +174 -56
- package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
- 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 +185 -65
- package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
- package/src/components/Breadcrumb/index.ts +2 -2
- package/src/components/Button/Button.stories.tsx +1 -1
- package/src/components/Button/README.md +2 -2
- package/src/components/Callout/Callout.stories.tsx +166 -1011
- package/src/components/Callout/Callout.test.tsx +3 -3
- package/src/components/Callout/Callout.tsx +196 -84
- package/src/components/Callout/CalloutCompound.test.tsx +72 -0
- package/src/components/Callout/README.md +2 -2
- 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 +133 -20
- package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
- package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
- package/src/components/EdgePanel/EdgePanel.tsx +164 -112
- package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
- 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 +24 -1
- package/src/components/Form/Select.test.tsx +99 -0
- package/src/components/Form/Select.tsx +145 -94
- package/src/components/Form/SelectOption.tsx +88 -0
- package/src/components/Form/Textarea.stories.tsx +1 -1
- package/src/components/Form/Textarea.tsx +1 -1
- package/src/components/Hero/Hero.stories.tsx +39 -2
- package/src/components/Hero/Hero.test.tsx +142 -0
- package/src/components/Hero/Hero.tsx +143 -4
- package/src/components/List/List.test.tsx +62 -0
- package/src/components/List/List.tsx +16 -5
- package/src/components/List/ListItem.tsx +20 -0
- package/src/components/Messages/Messages.stories.tsx +1 -1
- package/src/components/Messages/Messages.tsx +2 -2
- package/src/components/Modal/Modal.stories.tsx +66 -2
- package/src/components/Modal/Modal.tsx +115 -35
- package/src/components/Modal/ModalCompound.test.tsx +94 -0
- package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.tsx +1 -1
- 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 +125 -22
- package/src/components/Steps/StepsCompound.test.tsx +81 -0
- package/src/components/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +198 -45
- package/src/components/Tabs/TabsCompound.test.tsx +64 -0
- 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 -8
- package/src/lib/composables/useAtomixGlass.ts +331 -537
- 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 +2 -10
- package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
- package/src/lib/types/components.ts +21 -23
- package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
- package/src/lib/utils/__tests__/dom.test.ts +100 -0
- package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
- package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
- package/src/lib/utils/themeNaming.ts +1 -1
- 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/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,65 +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
|
-
x: (globalPos.x - center.x) / rect.width * 100,
|
|
2293
|
-
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
|
|
2294
2475
|
};
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
return;
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
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))
|
|
2319
2499
|
};
|
|
2320
|
-
}), [
|
|
2321
|
-
|
|
2322
|
-
const calculateDirectionalScale = useCallback((() => {
|
|
2323
|
-
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)";
|
|
2324
2502
|
if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return "scale(1)";
|
|
2325
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({
|
|
2326
2504
|
x: edgeDistanceX,
|
|
@@ -2334,7 +2512,7 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2334
2512
|
if (0 === centerDistance) return "scale(1)";
|
|
2335
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;
|
|
2336
2514
|
return `scaleX(${Math.max(.8, scaleX)}) scaleY(${Math.max(.8, scaleY)})`;
|
|
2337
|
-
}), [ globalMousePosition, elasticity, glassSize, glassRef,
|
|
2515
|
+
}), [ globalMousePosition, elasticity, glassSize, glassRef, baseOverLightConfig ]), calculateFadeInFactor = useCallback((() => {
|
|
2338
2516
|
if (!(globalMousePosition.x && globalMousePosition.y && glassRef.current && validateGlassSize(glassSize))) return 0;
|
|
2339
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({
|
|
2340
2518
|
x: edgeDistanceX,
|
|
@@ -2354,122 +2532,110 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2354
2532
|
x: (globalMousePosition.x - center.x) * elasticity * .1 * fadeInFactor,
|
|
2355
2533
|
y: (globalMousePosition.y - center.y) * elasticity * .1 * fadeInFactor
|
|
2356
2534
|
};
|
|
2357
|
-
}), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() =>
|
|
2535
|
+
}), [ globalMousePosition, elasticity, calculateFadeInFactor, glassRef ]), elasticTranslation = useMemo((() => effectiveWithoutEffects ? {
|
|
2358
2536
|
x: 0,
|
|
2359
2537
|
y: 0
|
|
2360
|
-
} : calculateElasticTranslation()), [ calculateElasticTranslation,
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
const
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
if (rect.width <= 0 || rect.height <= 0) return void (rafId = null);
|
|
2373
|
-
// Measure actual rendered size without artificial offsets to avoid feedback loops
|
|
2374
|
-
const newSize = {
|
|
2375
|
-
width: Math.round(rect.width),
|
|
2376
|
-
height: Math.round(rect.height)
|
|
2377
|
-
}, cornerRadiusChanged = lastCornerRadius !== effectiveCornerRadius, dimensionsChanged = Math.abs(newSize.width - lastSize.width) > 1 || Math.abs(newSize.height - lastSize.height) > 1;
|
|
2378
|
-
var size;
|
|
2379
|
-
(forceUpdate || cornerRadiusChanged || dimensionsChanged) && validateGlassSize(size = newSize) && size.width <= CONSTANTS.MAX_SIZE && size.height <= CONSTANTS.MAX_SIZE && (lastSize = newSize,
|
|
2380
|
-
lastCornerRadius = effectiveCornerRadius, setGlassSize(newSize)), rafId = null;
|
|
2381
|
-
}));
|
|
2382
|
-
};
|
|
2383
|
-
let resizeTimeoutId = null;
|
|
2384
|
-
const debouncedResizeHandler = () => {
|
|
2385
|
-
resizeTimeoutId && clearTimeout(resizeTimeoutId), resizeTimeoutId = setTimeout((() => updateGlassSize(!1)), 16);
|
|
2386
|
-
}, initialTimeoutId = setTimeout((() => updateGlassSize(!0)), 0);
|
|
2387
|
-
let resizeObserver = null, resizeDebounceTimeout = null;
|
|
2388
|
-
// ResizeObserver has 98%+ browser support, no need for fallback
|
|
2389
|
-
if ("undefined" != typeof ResizeObserver && isValidElement(glassRef.current)) try {
|
|
2390
|
-
resizeObserver = new ResizeObserver((entries => {
|
|
2391
|
-
for (const entry of entries) if (entry.target === glassRef.current) {
|
|
2392
|
-
// Update cached rect when size changes
|
|
2393
|
-
glassRef.current && (cachedRectRef.current = glassRef.current.getBoundingClientRect()),
|
|
2394
|
-
// Debounce resize updates to match RAF timing (16ms)
|
|
2395
|
-
resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), resizeDebounceTimeout = setTimeout((() => updateGlassSize(!1)), 16);
|
|
2396
|
-
break;
|
|
2397
|
-
}
|
|
2398
|
-
})), resizeObserver.observe(glassRef.current);
|
|
2399
|
-
} catch (error) {
|
|
2400
|
-
console.warn("AtomixGlass: ResizeObserver not available, using window resize only", error);
|
|
2401
|
-
}
|
|
2402
|
-
return window.addEventListener("resize", debouncedResizeHandler, {
|
|
2403
|
-
passive: !0
|
|
2404
|
-
}), () => {
|
|
2405
|
-
clearTimeout(initialTimeoutId), null !== rafId && cancelAnimationFrame(rafId), resizeTimeoutId && clearTimeout(resizeTimeoutId),
|
|
2406
|
-
resizeDebounceTimeout && clearTimeout(resizeDebounceTimeout), window.removeEventListener("resize", debouncedResizeHandler),
|
|
2407
|
-
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
|
|
2408
2550
|
};
|
|
2409
|
-
|
|
2410
|
-
|
|
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 ]);
|
|
2411
2576
|
/**
|
|
2412
|
-
*
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
const
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
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();
|
|
2428
2593
|
};
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
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 => {
|
|
2444
2619
|
!onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
|
|
2445
|
-
}), [ onClick ])
|
|
2446
|
-
|
|
2447
|
-
* Validate and clamp a numeric config value
|
|
2448
|
-
* @param value - The value to validate
|
|
2449
|
-
* @param min - Minimum allowed value
|
|
2450
|
-
* @param max - Maximum allowed value
|
|
2451
|
-
* @param defaultValue - Default value if validation fails
|
|
2452
|
-
* @returns Validated and clamped value
|
|
2453
|
-
*/ return {
|
|
2454
|
-
// State
|
|
2620
|
+
}), [ onClick ]);
|
|
2621
|
+
return {
|
|
2455
2622
|
isHovered: isHovered,
|
|
2456
2623
|
isActive: isActive,
|
|
2457
2624
|
glassSize: glassSize,
|
|
2458
|
-
|
|
2459
|
-
|
|
2625
|
+
dynamicBorderRadius: dynamicBorderRadius,
|
|
2626
|
+
effectiveBorderRadius: effectiveBorderRadius,
|
|
2460
2627
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2461
2628
|
effectiveHighContrast: effectiveHighContrast,
|
|
2462
|
-
|
|
2629
|
+
effectiveWithoutEffects: effectiveWithoutEffects,
|
|
2463
2630
|
detectedOverLight: detectedOverLight,
|
|
2464
2631
|
globalMousePosition: globalMousePosition,
|
|
2632
|
+
// This is now static (refs or props) unless prop changes
|
|
2465
2633
|
mouseOffset: mouseOffset,
|
|
2466
|
-
//
|
|
2634
|
+
// This is now static (refs or props) unless prop changes
|
|
2467
2635
|
overLightConfig: overLightConfig,
|
|
2468
|
-
// Transform calculations
|
|
2469
2636
|
elasticTranslation: elasticTranslation,
|
|
2470
2637
|
directionalScale: directionalScale,
|
|
2471
2638
|
transformStyle: transformStyle,
|
|
2472
|
-
// Event handlers
|
|
2473
2639
|
handleMouseEnter: handleMouseEnter,
|
|
2474
2640
|
handleMouseLeave: handleMouseLeave,
|
|
2475
2641
|
handleMouseDown: handleMouseDown,
|
|
@@ -2508,7 +2674,7 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2508
2674
|
*
|
|
2509
2675
|
* @example
|
|
2510
2676
|
* // Manual border-radius override
|
|
2511
|
-
* <AtomixGlass
|
|
2677
|
+
* <AtomixGlass borderRadius={20}>
|
|
2512
2678
|
* <div>Content with 20px glass radius</div>
|
|
2513
2679
|
* </AtomixGlass>
|
|
2514
2680
|
*
|
|
@@ -2549,37 +2715,48 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2549
2715
|
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
2550
2716
|
* <div>Content with debug logging enabled</div>
|
|
2551
2717
|
* </AtomixGlass>
|
|
2552
|
-
*/ 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,
|
|
2553
|
-
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({
|
|
2554
2720
|
glassRef: glassRef,
|
|
2555
2721
|
contentRef: contentRef,
|
|
2556
|
-
|
|
2722
|
+
borderRadius: borderRadius,
|
|
2557
2723
|
globalMousePosition: externalGlobalMousePosition,
|
|
2558
2724
|
mouseOffset: externalMouseOffset,
|
|
2559
2725
|
mouseContainer: mouseContainer,
|
|
2560
2726
|
overLight: overLight,
|
|
2561
2727
|
reducedMotion: reducedMotion,
|
|
2562
2728
|
highContrast: highContrast,
|
|
2563
|
-
|
|
2729
|
+
withoutEffects: withoutEffects,
|
|
2564
2730
|
elasticity: elasticity,
|
|
2565
2731
|
onClick: onClick,
|
|
2566
|
-
|
|
2732
|
+
debugBorderRadius: debugBorderRadius,
|
|
2567
2733
|
debugOverLight: debugOverLight,
|
|
2568
|
-
|
|
2569
|
-
children: children
|
|
2570
|
-
|
|
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 = {
|
|
2571
2742
|
...style,
|
|
2572
|
-
...!
|
|
2743
|
+
...!effectiveWithoutEffects && {
|
|
2573
2744
|
transform: transformStyle
|
|
2574
2745
|
}
|
|
2575
|
-
}, 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((() => ({
|
|
2576
2747
|
position: style.position || "absolute",
|
|
2577
2748
|
top: style.top || 0,
|
|
2578
2749
|
left: style.left || 0
|
|
2579
|
-
})), [ style.position, style.top, style.left ]), adjustedSize = useMemo((() =>
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
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((() => {
|
|
2583
2760
|
const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
|
|
2584
2761
|
return {
|
|
2585
2762
|
borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
|
|
@@ -2621,13 +2798,13 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2621
2798
|
}), [ isHovered, isActive, isOverLight, overLightConfig.opacity ]), glassVars = useMemo((() => {
|
|
2622
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;
|
|
2623
2800
|
return {
|
|
2624
|
-
"--atomix-glass-radius": `${
|
|
2801
|
+
"--atomix-glass-radius": `${effectiveBorderRadius}px`,
|
|
2625
2802
|
"--atomix-glass-transform": transformStyle || "none",
|
|
2626
2803
|
"--atomix-glass-position": positionStyles.position,
|
|
2627
2804
|
"--atomix-glass-top": "fixed" !== positionStyles.top ? `${positionStyles.top}px` : "0",
|
|
2628
2805
|
"--atomix-glass-left": "fixed" !== positionStyles.left ? `${positionStyles.left}px` : "0",
|
|
2629
|
-
"--atomix-glass-width":
|
|
2630
|
-
"--atomix-glass-height":
|
|
2806
|
+
"--atomix-glass-width": adjustedSize.width,
|
|
2807
|
+
"--atomix-glass-height": adjustedSize.height,
|
|
2631
2808
|
"--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.09375rem)",
|
|
2632
2809
|
"--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
|
|
2633
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%)`,
|
|
@@ -2643,24 +2820,25 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2643
2820
|
"--atomix-glass-overlay-opacity": opacityValues.over,
|
|
2644
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})`
|
|
2645
2822
|
};
|
|
2646
|
-
}), [ gradientValues, opacityValues,
|
|
2823
|
+
}), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, positionStyles, adjustedSize, isOverLight, overLightConfig.borderOpacity ]), renderBackgroundLayer = layerType => jsx("div", {
|
|
2647
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(" "),
|
|
2648
2825
|
style: {
|
|
2649
2826
|
...positionStyles,
|
|
2650
2827
|
height: adjustedSize.height,
|
|
2651
2828
|
width: adjustedSize.width,
|
|
2652
|
-
borderRadius: `${
|
|
2829
|
+
borderRadius: `${effectiveBorderRadius}px`,
|
|
2653
2830
|
transform: baseStyle.transform
|
|
2654
2831
|
}
|
|
2655
2832
|
});
|
|
2656
2833
|
return jsxs("div", {
|
|
2834
|
+
...rest,
|
|
2657
2835
|
className: componentClassName,
|
|
2658
2836
|
style: glassVars,
|
|
2659
2837
|
role: role || (onClick ? "button" : void 0),
|
|
2660
2838
|
tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
|
|
2661
2839
|
"aria-label": ariaLabel,
|
|
2662
2840
|
"aria-describedby": ariaDescribedBy,
|
|
2663
|
-
"aria-disabled": !(!onClick || !
|
|
2841
|
+
"aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
|
|
2664
2842
|
"aria-pressed": !(!onClick || !isActive) || !onClick && void 0,
|
|
2665
2843
|
onKeyDown: onClick ? handleKeyDown : void 0,
|
|
2666
2844
|
children: [ jsx(AtomixGlassContainer, {
|
|
@@ -2668,18 +2846,18 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2668
2846
|
contentRef: contentRef,
|
|
2669
2847
|
className: className,
|
|
2670
2848
|
style: baseStyle,
|
|
2671
|
-
|
|
2672
|
-
displacementScale:
|
|
2673
|
-
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,
|
|
2674
2852
|
saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
|
|
2675
|
-
aberrationIntensity:
|
|
2853
|
+
aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
|
|
2676
2854
|
glassSize: glassSize,
|
|
2677
2855
|
padding: padding,
|
|
2678
|
-
mouseOffset:
|
|
2856
|
+
mouseOffset: effectiveWithoutEffects ? {
|
|
2679
2857
|
x: 0,
|
|
2680
2858
|
y: 0
|
|
2681
2859
|
} : mouseOffset,
|
|
2682
|
-
globalMousePosition:
|
|
2860
|
+
globalMousePosition: effectiveWithoutEffects ? {
|
|
2683
2861
|
x: 0,
|
|
2684
2862
|
y: 0
|
|
2685
2863
|
} : globalMousePosition,
|
|
@@ -2699,11 +2877,11 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2699
2877
|
onClick: onClick,
|
|
2700
2878
|
mode: mode,
|
|
2701
2879
|
transform: baseStyle.transform,
|
|
2702
|
-
|
|
2880
|
+
effectiveWithoutEffects: effectiveWithoutEffects,
|
|
2703
2881
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2704
2882
|
shaderVariant: shaderVariant,
|
|
2705
2883
|
elasticity: elasticity,
|
|
2706
|
-
|
|
2884
|
+
withLiquidBlur: withLiquidBlur,
|
|
2707
2885
|
children: children
|
|
2708
2886
|
}), Boolean(onClick) && jsxs(Fragment, {
|
|
2709
2887
|
children: [ jsx("div", {
|
|
@@ -2725,11 +2903,19 @@ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, cornerRadiu
|
|
|
2725
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}%)`
|
|
2726
2904
|
}
|
|
2727
2905
|
}) ]
|
|
2728
|
-
}),
|
|
2906
|
+
}), withBorder && jsxs(Fragment, {
|
|
2729
2907
|
children: [ jsx("span", {
|
|
2730
|
-
className: ATOMIX_GLASS.BORDER_1_CLASS
|
|
2908
|
+
className: ATOMIX_GLASS.BORDER_1_CLASS,
|
|
2909
|
+
style: {
|
|
2910
|
+
width: glassSize.width,
|
|
2911
|
+
height: glassSize.height
|
|
2912
|
+
}
|
|
2731
2913
|
}), jsx("span", {
|
|
2732
|
-
className: ATOMIX_GLASS.BORDER_2_CLASS
|
|
2914
|
+
className: ATOMIX_GLASS.BORDER_2_CLASS,
|
|
2915
|
+
style: {
|
|
2916
|
+
width: glassSize.width,
|
|
2917
|
+
height: glassSize.height
|
|
2918
|
+
}
|
|
2733
2919
|
}) ]
|
|
2734
2920
|
}) ]
|
|
2735
2921
|
});
|
|
@@ -2768,11 +2954,7 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
|
|
|
2768
2954
|
onZoomReset: handleZoomReset,
|
|
2769
2955
|
onPanToggle: handlePanToggle,
|
|
2770
2956
|
onReset: handleReset
|
|
2771
|
-
})), [ onRefresh, onExport, handleFullscreenChange, handleZoomIn, handleZoomOut, handleZoomReset, handlePanToggle, handleReset ]), {state: toolbarState, handlers: toolbarHandlers, toolbarGroups: toolbarGroups} =
|
|
2772
|
-
/**
|
|
2773
|
-
* Hook for managing chart toolbar state and generating chart-specific configurations
|
|
2774
|
-
*/
|
|
2775
|
-
function(chartType, config = {}, handlers = {}) {
|
|
2957
|
+
})), [ onRefresh, onExport, handleFullscreenChange, handleZoomIn, handleZoomOut, handleZoomReset, handlePanToggle, handleReset ]), {state: toolbarState, handlers: toolbarHandlers, toolbarGroups: toolbarGroups} = function(chartType, config = {}, handlers = {}) {
|
|
2776
2958
|
const [state, setState] = useState({
|
|
2777
2959
|
isFullscreen: !1,
|
|
2778
2960
|
isExporting: !1,
|
|
@@ -3157,16 +3339,16 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
|
|
|
3157
3339
|
aberrationIntensity: 1.5,
|
|
3158
3340
|
elasticity: 0,
|
|
3159
3341
|
// No elastic effect for charts
|
|
3160
|
-
|
|
3342
|
+
withLiquidBlur: !1,
|
|
3161
3343
|
// Keep it simple
|
|
3162
|
-
|
|
3344
|
+
withBorder: !0,
|
|
3163
3345
|
mode: "standard",
|
|
3164
3346
|
mouseContainer: chartContainerRef,
|
|
3165
3347
|
reducedMotion: !1
|
|
3166
3348
|
})), []), glassProps = useMemo((() => glass ? !0 === glass ? defaultChartGlassProps : {
|
|
3167
3349
|
...defaultChartGlassProps,
|
|
3168
3350
|
...glass
|
|
3169
|
-
} : null), [ glass, defaultChartGlassProps ]), chartBorderRadius = useMemo((() => glassProps?.
|
|
3351
|
+
} : null), [ glass, defaultChartGlassProps ]), chartBorderRadius = useMemo((() => glassProps?.borderRadius || void 0), [ glassProps?.borderRadius ]), chartContextValue = useMemo((() => ({
|
|
3170
3352
|
zoomLevel: zoomLevel,
|
|
3171
3353
|
panOffset: panOffset,
|
|
3172
3354
|
panEnabled: panEnabled,
|
|
@@ -3297,7 +3479,7 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
|
|
|
3297
3479
|
}) ]
|
|
3298
3480
|
}), wrappedChart = glassProps ? jsx(AtomixGlass, {
|
|
3299
3481
|
...glassProps,
|
|
3300
|
-
|
|
3482
|
+
borderRadius: chartBorderRadius,
|
|
3301
3483
|
style: {
|
|
3302
3484
|
width: "100%",
|
|
3303
3485
|
height: "100%",
|
|
@@ -3350,7 +3532,13 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
|
|
|
3350
3532
|
// Get chart context (zoom/pan state from toolbar) - optional
|
|
3351
3533
|
// Always call useContext to maintain consistent hook order
|
|
3352
3534
|
const chartContext = useContext(ChartContext), {calculateScales: calculateScales, getChartColors: getChartColors} = useChart(), {processedData: processedData, isProcessing: isProcessing} = function(datasets, options) {
|
|
3353
|
-
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) => {
|
|
3354
3542
|
if (!enableDecimation || !data.length) return data;
|
|
3355
3543
|
const dataLength = data[0]?.data?.length || 0;
|
|
3356
3544
|
if (dataLength <= maxPoints) return data;
|
|
@@ -3370,10 +3558,17 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
|
|
|
3370
3558
|
}), []), calculateTrendLine = useCallback((values => {
|
|
3371
3559
|
const n = values.length;
|
|
3372
3560
|
if (n < 2) return values.map((() => null));
|
|
3373
|
-
|
|
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;
|
|
3374
3568
|
return values.map(((_, i) => slope * i + intercept));
|
|
3375
3569
|
}), []);
|
|
3376
|
-
//
|
|
3570
|
+
// Moving average calculation
|
|
3571
|
+
// Process data when datasets change
|
|
3377
3572
|
return useEffect((() => {
|
|
3378
3573
|
setIsProcessing(!0), (async () => {
|
|
3379
3574
|
let processed = [ ...datasets ];
|
|
@@ -3385,11 +3580,12 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
|
|
|
3385
3580
|
useEffect((() => {
|
|
3386
3581
|
if (!enableRealTime) return;
|
|
3387
3582
|
const interval = setInterval((() => {
|
|
3388
|
-
setProcessedData((prev =>
|
|
3389
|
-
|
|
3390
|
-
|
|
3583
|
+
setProcessedData((prev =>
|
|
3584
|
+
// Only trigger update if signature changed
|
|
3585
|
+
getDatasetSignature(prev) === lastDataSignature.current ? prev : [ ...prev ]));
|
|
3586
|
+
}), realTimeInterval);
|
|
3391
3587
|
return () => clearInterval(interval);
|
|
3392
|
-
}), [ enableRealTime, realTimeInterval ]), {
|
|
3588
|
+
}), [ enableRealTime, realTimeInterval, getDatasetSignature ]), {
|
|
3393
3589
|
processedData: processedData,
|
|
3394
3590
|
isProcessing: isProcessing,
|
|
3395
3591
|
decimateData: decimateData,
|
|
@@ -3410,12 +3606,12 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
|
|
|
3410
3606
|
*/
|
|
3411
3607
|
function(datasets, options) {
|
|
3412
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 => {
|
|
3413
|
-
const
|
|
3609
|
+
const {min: min, max: max, hasValid: hasValid} = getDatasetBounds(dataset.data);
|
|
3414
3610
|
return {
|
|
3415
3611
|
label: dataset.label,
|
|
3416
3612
|
dataLength: dataset.data?.length || 0,
|
|
3417
|
-
minValue:
|
|
3418
|
-
maxValue:
|
|
3613
|
+
minValue: hasValid ? min : 0,
|
|
3614
|
+
maxValue: hasValid ? max : 0
|
|
3419
3615
|
};
|
|
3420
3616
|
})) : null), [ datasets, enableMemoization ]), debouncedUpdate = useCallback((callback => {
|
|
3421
3617
|
debounceRef.current && clearTimeout(debounceRef.current), debounceRef.current = setTimeout((() => {
|
|
@@ -3514,8 +3710,8 @@ const ChartRenderer = memo( forwardRef((({datasets: datasets = [], config: conf
|
|
|
3514
3710
|
const getAccessibleDescription = useCallback((() => {
|
|
3515
3711
|
if (!datasets.length) return "Empty chart";
|
|
3516
3712
|
const datasetDescriptions = datasets.map(((dataset, i) => {
|
|
3517
|
-
const dataCount = dataset.data?.length || 0,
|
|
3518
|
-
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}`;
|
|
3519
3715
|
})).join(". ");
|
|
3520
3716
|
return `Chart with ${datasets.length} datasets. ${datasetDescriptions}`;
|
|
3521
3717
|
}), [ datasets ]);
|
|
@@ -5758,14 +5954,14 @@ const ScatterChart = memo( forwardRef((({datasets: datasets = [], config: confi
|
|
|
5758
5954
|
|
|
5759
5955
|
ScatterChart.displayName = "ScatterChart";
|
|
5760
5956
|
|
|
5761
|
-
const
|
|
5957
|
+
const DEFAULT_COLOR_CONFIG = {
|
|
5762
5958
|
scheme: "category"
|
|
5763
|
-
},
|
|
5959
|
+
}, DEFAULT_LABEL_CONFIG = {
|
|
5764
5960
|
showLabels: !0,
|
|
5765
5961
|
minSize: 1e3,
|
|
5766
5962
|
fontSize: 12,
|
|
5767
5963
|
textColor: "white"
|
|
5768
|
-
}, 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) => {
|
|
5769
5965
|
const [hoveredNode, setHoveredNode] = useState(null), [selectedNode, setSelectedNode] = useState(null);
|
|
5770
5966
|
useState({
|
|
5771
5967
|
x: 0,
|
|
@@ -5877,87 +6073,87 @@ const TreemapChart = memo( forwardRef((({data: data = [], algorithm: algorithm
|
|
|
5877
6073
|
remainingHeight -= rowHeight), currentRow = [];
|
|
5878
6074
|
}
|
|
5879
6075
|
}
|
|
5880
|
-
}), [])
|
|
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 ]);
|
|
5881
6150
|
// Squarified treemap algorithm
|
|
5882
6151
|
return jsx(BaseChart, {
|
|
5883
6152
|
ref: ref,
|
|
5884
6153
|
type: "treemap",
|
|
5885
|
-
datasets:
|
|
5886
|
-
label: "Treemap Data",
|
|
5887
|
-
data: data
|
|
5888
|
-
} ],
|
|
6154
|
+
datasets: datasets,
|
|
5889
6155
|
config: config,
|
|
5890
|
-
renderContent:
|
|
5891
|
-
if (!data.length) return null;
|
|
5892
|
-
// Calculate available space with padding
|
|
5893
|
-
const availableWidth = scales.width - 40, availableHeight = scales.height - 40, leafNodes = data.filter((item => !item.children || 0 === item.children.length));
|
|
5894
|
-
if (!leafNodes.length) return null;
|
|
5895
|
-
const totalValue = _reduceInstanceProperty(leafNodes).call(leafNodes, ((sum, node) => sum + node.value), 0), treemapNodes = leafNodes.map(((item, index) => ({
|
|
5896
|
-
id: item.id,
|
|
5897
|
-
label: item.label,
|
|
5898
|
-
value: item.value,
|
|
5899
|
-
color: generateColor(item, 0, index) || "transparent",
|
|
5900
|
-
x: 0,
|
|
5901
|
-
// Will be calculated by squarify
|
|
5902
|
-
y: 0,
|
|
5903
|
-
// Will be calculated by squarify
|
|
5904
|
-
width: 0,
|
|
5905
|
-
// Will be calculated by squarify
|
|
5906
|
-
height: 0,
|
|
5907
|
-
// Will be calculated by squarify
|
|
5908
|
-
depth: 0,
|
|
5909
|
-
children: [],
|
|
5910
|
-
originalData: item
|
|
5911
|
-
})));
|
|
5912
|
-
// Create treemap nodes with proper dimensions
|
|
5913
|
-
// Apply squarified algorithm to layout nodes proportionally by value
|
|
5914
|
-
if ("squarified" === algorithm && totalValue > 0) squarify(treemapNodes, 20, 20, availableWidth, availableHeight); else {
|
|
5915
|
-
// Fallback: simple grid layout (equal sizes)
|
|
5916
|
-
const cols = Math.ceil(Math.sqrt(leafNodes.length)), rows = Math.ceil(leafNodes.length / cols), nodeWidth = availableWidth / cols, nodeHeight = availableHeight / rows;
|
|
5917
|
-
treemapNodes.forEach(((node, index) => {
|
|
5918
|
-
const col = index % cols, row = Math.floor(index / cols);
|
|
5919
|
-
node.x = 20 + col * nodeWidth, node.y = 20 + row * nodeHeight, node.width = nodeWidth,
|
|
5920
|
-
node.height = nodeHeight;
|
|
5921
|
-
}));
|
|
5922
|
-
}
|
|
5923
|
-
return jsx(Fragment, {
|
|
5924
|
-
children: treemapNodes.map((node => {
|
|
5925
|
-
const isHovered = hoveredNode === node, isSelected = selectedNode === node, area = node.width * node.height, showLabel = labelConfig.showLabels && area >= (labelConfig.minSize || 1e3);
|
|
5926
|
-
return jsxs("g", {
|
|
5927
|
-
children: [ jsx("rect", {
|
|
5928
|
-
x: node.x,
|
|
5929
|
-
y: node.y,
|
|
5930
|
-
width: node.width,
|
|
5931
|
-
height: node.height,
|
|
5932
|
-
fill: node.color,
|
|
5933
|
-
className: `c-chart__treemap-node ${isHovered ? "c-chart__treemap-node--hovered" : ""} ${isSelected ? "c-chart__treemap-node--selected" : ""}`,
|
|
5934
|
-
onClick: () => {
|
|
5935
|
-
setSelectedNode(node), handlers.onDataPointClick?.(node.originalData, 0, 0);
|
|
5936
|
-
},
|
|
5937
|
-
onMouseEnter: e => {
|
|
5938
|
-
setHoveredNode(node);
|
|
5939
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
5940
|
-
handlers.onPointHover(0, 0, node.x, node.y, rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
5941
|
-
},
|
|
5942
|
-
onMouseLeave: () => {
|
|
5943
|
-
setHoveredNode(null), handlers.onPointLeave();
|
|
5944
|
-
}
|
|
5945
|
-
}), showLabel && jsx("text", {
|
|
5946
|
-
x: node.x + node.width / 2,
|
|
5947
|
-
y: node.y + node.height / 2,
|
|
5948
|
-
textAnchor: "middle",
|
|
5949
|
-
dominantBaseline: "middle",
|
|
5950
|
-
className: "c-chart__treemap-label",
|
|
5951
|
-
style: {
|
|
5952
|
-
fontSize: labelConfig.fontSize,
|
|
5953
|
-
fill: labelConfig.textColor
|
|
5954
|
-
},
|
|
5955
|
-
children: node.label
|
|
5956
|
-
}) ]
|
|
5957
|
-
}, node.id);
|
|
5958
|
-
}))
|
|
5959
|
-
});
|
|
5960
|
-
},
|
|
6156
|
+
renderContent: renderContent,
|
|
5961
6157
|
onDataPointClick: onDataPointClick,
|
|
5962
6158
|
interactive: !0,
|
|
5963
6159
|
...props
|