@shohojdhara/atomix 0.5.0 → 0.5.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/atomix.config.ts +12 -0
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +230 -83
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +24 -23
- package/dist/charts.js +271 -369
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +624 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +3 -2
- package/dist/core.js +342 -382
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +4 -6
- package/dist/forms.js +233 -334
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +11 -2
- package/dist/heavy.js +406 -445
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +109 -65
- package/dist/index.esm.js +654 -748
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +621 -717
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.js +4 -4
- package/dist/theme.js.map +1 -1
- package/package.json +24 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +1 -133
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
- package/src/components/AtomixGlass/glass-utils.ts +51 -1
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
- package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
- package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
- package/src/components/AtomixGlass/stories/types.ts +3 -3
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +111 -74
- package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +39 -0
- package/src/lib/constants/components.ts +1 -0
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
- package/src/lib/types/components.ts +1 -0
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +160 -99
- package/scripts/cli/__tests__/README.md +0 -81
- package/scripts/cli/__tests__/basic.test.js +0 -116
- package/scripts/cli/__tests__/clean.test.js +0 -278
- package/scripts/cli/__tests__/component-generator.test.js +0 -332
- package/scripts/cli/__tests__/component-validator.test.js +0 -433
- package/scripts/cli/__tests__/generator.test.js +0 -613
- package/scripts/cli/__tests__/glass-motion.test.js +0 -256
- package/scripts/cli/__tests__/integration.test.js +0 -938
- package/scripts/cli/__tests__/migrate.test.js +0 -74
- package/scripts/cli/__tests__/security.test.js +0 -206
- package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
- package/scripts/cli/__tests__/token-manager.test.js +0 -251
- package/scripts/cli/__tests__/token-provider.test.js +0 -361
- package/scripts/cli/__tests__/utils.test.js +0 -165
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
- package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
- package/src/components/TypedButton/TypedButton.tsx +0 -39
- package/src/components/TypedButton/index.ts +0 -2
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/composables/useTypedButton.ts +0 -66
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- package/src/styles/06-components/_components.typedbutton.scss +0 -212
package/dist/heavy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
|
|
3
|
-
import React, { memo, forwardRef, useMemo, useState, useRef, useEffect, useCallback, useImperativeHandle, Children, isValidElement } from "react";
|
|
3
|
+
import React, { memo, forwardRef, useId, useMemo, useState, useRef, useEffect, useCallback, useImperativeHandle, Children, isValidElement } from "react";
|
|
4
4
|
|
|
5
5
|
import * as PhosphorIcons from "@phosphor-icons/react";
|
|
6
6
|
|
|
@@ -33,6 +33,7 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
|
|
|
33
33
|
FILTER_OVERLAY_CLASS: "c-atomix-glass__filter-overlay",
|
|
34
34
|
FILTER_SHADOW_CLASS: "c-atomix-glass__filter-shadow",
|
|
35
35
|
CONTENT_CLASS: "c-atomix-glass__content",
|
|
36
|
+
BORDER_BACKDROP_CLASS: "c-atomix-glass__border-backdrop",
|
|
36
37
|
BORDER_1_CLASS: "c-atomix-glass__border-1",
|
|
37
38
|
BORDER_2_CLASS: "c-atomix-glass__border-2",
|
|
38
39
|
HOVER_1_CLASS: "c-atomix-glass__hover-1",
|
|
@@ -378,7 +379,7 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
|
|
|
378
379
|
default:
|
|
379
380
|
return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
|
|
380
381
|
}
|
|
381
|
-
}, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
|
|
382
|
+
}, sharedShaderCache = new Map, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
|
|
382
383
|
style: {
|
|
383
384
|
position: "absolute",
|
|
384
385
|
width: "100%",
|
|
@@ -517,24 +518,17 @@ import { Pause, Play, SkipBack, SkipForward, SpeakerX, SpeakerHigh, Gear, Downlo
|
|
|
517
518
|
GlassFilterComponent.displayName = "GlassFilter";
|
|
518
519
|
|
|
519
520
|
// Memoize component to prevent unnecessary re-renders
|
|
520
|
-
const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount))
|
|
521
|
-
|
|
522
|
-
// Module-level counter for deterministic ID generation
|
|
523
|
-
let idCounter = 0;
|
|
524
|
-
|
|
525
|
-
// Module-level shared shader cache with LRU eviction
|
|
526
|
-
const sharedShaderCache = new Map, AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
|
|
521
|
+
const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount)), AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
|
|
527
522
|
x: 0,
|
|
528
523
|
y: 0
|
|
529
524
|
}, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
|
|
530
525
|
width: 0,
|
|
531
526
|
height: 0
|
|
532
|
-
}, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1,
|
|
527
|
+
}, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1,
|
|
533
528
|
// Phase 1: Animation System props
|
|
534
529
|
shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
|
|
535
|
-
//
|
|
536
|
-
|
|
537
|
-
const filterId = useMemo((() => "atomix-glass-filter-" + ++idCounter), []), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
|
|
530
|
+
// React 18 useId — stable, unique, and SSR-safe (no module-level counter)
|
|
531
|
+
const rawId = useId(), filterId = useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
|
|
538
532
|
// Lazy load shader utilities only when shader mode is needed
|
|
539
533
|
useEffect((() => {
|
|
540
534
|
"shader" === mode ?
|
|
@@ -557,9 +551,7 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
|
|
|
557
551
|
// Create cache key from size and variant
|
|
558
552
|
const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`, cachedUrl = (key => {
|
|
559
553
|
const entry = sharedShaderCache.get(key);
|
|
560
|
-
return entry ? (
|
|
561
|
-
// Update access timestamp for LRU
|
|
562
|
-
entry.timestamp = Date.now(), entry.url) : null;
|
|
554
|
+
return entry ? (entry.timestamp = Date.now(), entry.url) : null;
|
|
563
555
|
})(cacheKey);
|
|
564
556
|
// Check shared cache first
|
|
565
557
|
if (cachedUrl) return void setShaderMapUrl(cachedUrl);
|
|
@@ -570,29 +562,24 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
|
|
|
570
562
|
if (shaderUtilsRef.current) try {
|
|
571
563
|
const {ShaderDisplacementGenerator: ShaderDisplacementGenerator, fragmentShaders: fragmentShaders} = shaderUtilsRef.current;
|
|
572
564
|
shaderGeneratorRef.current?.destroy();
|
|
573
|
-
const selectedShader = fragmentShaders[shaderVariant]
|
|
565
|
+
const selectedShader = fragmentShaders[shaderVariant] ?? fragmentShaders.liquidGlass;
|
|
574
566
|
shaderGeneratorRef.current = new ShaderDisplacementGenerator({
|
|
575
567
|
width: glassSize.width,
|
|
576
568
|
height: glassSize.height,
|
|
577
569
|
fragment: selectedShader
|
|
578
570
|
}), shaderUpdateTimeoutRef.current = setTimeout((() => {
|
|
579
|
-
const url = shaderGeneratorRef.current?.updateShader()
|
|
571
|
+
const url = shaderGeneratorRef.current?.updateShader() ?? "";
|
|
580
572
|
url && ((key, url) => {
|
|
581
|
-
// Evict oldest entries if at capacity
|
|
582
573
|
if (sharedShaderCache.size >= 15) {
|
|
583
574
|
const entries = Array.from(sharedShaderCache.entries());
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const oldestEntry = entries[0];
|
|
588
|
-
oldestEntry && sharedShaderCache.delete(oldestEntry[0]);
|
|
575
|
+
entries.sort(((a, b) => a[1].timestamp - b[1].timestamp));
|
|
576
|
+
const oldest = entries[0];
|
|
577
|
+
oldest && sharedShaderCache.delete(oldest[0]);
|
|
589
578
|
}
|
|
590
579
|
sharedShaderCache.set(key, {
|
|
591
580
|
url: url,
|
|
592
581
|
timestamp: Date.now()
|
|
593
|
-
}),
|
|
594
|
-
// Development mode: log cache size
|
|
595
|
-
"undefined" != typeof process && "production" === process.env?.NODE_ENV || sharedShaderCache.size;
|
|
582
|
+
}), "undefined" != typeof process && "production" === process.env?.NODE_ENV || sharedShaderCache.size;
|
|
596
583
|
})(cacheKey, url), setShaderMapUrl(url);
|
|
597
584
|
}), 100);
|
|
598
585
|
} catch (error) {
|
|
@@ -661,7 +648,6 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
|
|
|
661
648
|
console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
|
|
662
649
|
}
|
|
663
650
|
}), [ ref, glassSize ]);
|
|
664
|
-
// Pre-calculate static multipliers outside useMemo
|
|
665
651
|
const liquidBlur = useMemo((() => {
|
|
666
652
|
const defaultBlur = {
|
|
667
653
|
baseBlur: blurAmount,
|
|
@@ -730,7 +716,6 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
|
|
|
730
716
|
}), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
|
|
731
717
|
return jsx("div", {
|
|
732
718
|
ref: el => {
|
|
733
|
-
// Apply force no-transition
|
|
734
719
|
// Handle forwarded ref
|
|
735
720
|
"function" == typeof ref ? ref(el) : ref && (ref.current = el);
|
|
736
721
|
},
|
|
@@ -772,6 +757,7 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
|
|
|
772
757
|
});
|
|
773
758
|
}));
|
|
774
759
|
|
|
760
|
+
// ─── Blur multiplier constants (module-level, never change at runtime) ────────
|
|
775
761
|
AtomixGlassContainer.displayName = "AtomixGlassContainer";
|
|
776
762
|
|
|
777
763
|
// Singleton instance
|
|
@@ -972,8 +958,6 @@ class {
|
|
|
972
958
|
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})`;
|
|
973
959
|
// Container variables
|
|
974
960
|
const style = containerElement.style;
|
|
975
|
-
style.setProperty("--atomix-glass-container-width", isFixedOrSticky ? `${glassSize.width}` : "100%"),
|
|
976
|
-
style.setProperty("--atomix-glass-container-height", isFixedOrSticky ? `${glassSize.height}` : "100%"),
|
|
977
961
|
style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
|
|
978
962
|
style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
|
|
979
963
|
// Shadows
|
|
@@ -1434,47 +1418,55 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
|
|
|
1434
1418
|
return "undefined" == typeof process || process.env, finalConfig;
|
|
1435
1419
|
}
|
|
1436
1420
|
return "undefined" == typeof process || process.env, baseConfig;
|
|
1437
|
-
}), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null),
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
if (!container) return;
|
|
1442
|
-
// Use cached rect if available, otherwise get new one
|
|
1443
|
-
let rect = cachedRectRef.current;
|
|
1444
|
-
if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
|
|
1445
|
-
cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
|
|
1446
|
-
const center = calculateElementCenter(rect);
|
|
1447
|
-
// Write raw target — the lerp loop will smoothly pursue it
|
|
1448
|
-
targetMouseOffsetRef.current = {
|
|
1449
|
-
x: (globalPos.x - center.x) / rect.width * 100,
|
|
1450
|
-
y: (globalPos.y - center.y) / rect.height * 100
|
|
1451
|
-
}, targetGlobalMousePositionRef.current = globalPos;
|
|
1452
|
-
}), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects ]), startLerpLoop = useCallback((() => {
|
|
1421
|
+
}), [ overLight, getEffectiveOverLight, isHovered, isActive, validateConfigValue, debugOverLight ]), transformStyle = useMemo((() => effectiveWithoutEffects || isActive && Boolean(onClick) ? "scale(0.98)" : "scale(1)"), [ effectiveWithoutEffects, isActive, onClick ]), updateRectRef = useRef(null), stopLerpLoop = useCallback((() => {
|
|
1422
|
+
lerpActiveRef.current = !1, null !== lerpRafRef.current && (cancelAnimationFrame(lerpRafRef.current),
|
|
1423
|
+
lerpRafRef.current = null);
|
|
1424
|
+
}), []), startLerpLoop = useCallback((() => {
|
|
1453
1425
|
if (lerpActiveRef.current) return;
|
|
1454
1426
|
lerpActiveRef.current = !0;
|
|
1455
1427
|
const LERP_T = CONSTANTS.LERP_FACTOR, tick = () => {
|
|
1456
1428
|
if (!lerpActiveRef.current) return;
|
|
1457
|
-
|
|
1458
|
-
if (!glassRef.current || !wrapperRef?.current) return void (lerpActiveRef.current = !1);
|
|
1429
|
+
if (!glassRef.current) return void (lerpActiveRef.current = !1);
|
|
1459
1430
|
const cur = internalMouseOffsetRef.current, tgt = targetMouseOffsetRef.current, dx = tgt.x - cur.x, dy = tgt.y - cur.y;
|
|
1460
1431
|
// If we're close enough, snap and park
|
|
1461
|
-
if (Math.abs(dx) < .
|
|
1432
|
+
if (Math.abs(dx) < .01 && Math.abs(dy) < .01) return internalMouseOffsetRef.current = {
|
|
1462
1433
|
...tgt
|
|
1463
1434
|
}, internalGlobalMousePositionRef.current = {
|
|
1464
1435
|
...targetGlobalMousePositionRef.current
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1436
|
+
},
|
|
1437
|
+
// Final update and stop
|
|
1438
|
+
updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
|
|
1439
|
+
mouseOffset: internalMouseOffsetRef.current,
|
|
1440
|
+
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
1441
|
+
glassSize: glassSize,
|
|
1442
|
+
isHovered: isHovered,
|
|
1443
|
+
isActive: isActive,
|
|
1444
|
+
isOverLight: overLightConfig.isOverLight,
|
|
1445
|
+
baseOverLightConfig: overLightConfig,
|
|
1446
|
+
effectiveBorderRadius: effectiveBorderRadius,
|
|
1447
|
+
effectiveWithoutEffects: effectiveWithoutEffects,
|
|
1448
|
+
effectiveReducedMotion: effectiveReducedMotion,
|
|
1449
|
+
elasticity: elasticity,
|
|
1450
|
+
directionalScale: isActive && Boolean(onClick) ? "scale(0.96)" : "scale(1)",
|
|
1451
|
+
onClick: onClick,
|
|
1452
|
+
withLiquidBlur: withLiquidBlur,
|
|
1453
|
+
blurAmount: blurAmount,
|
|
1454
|
+
saturation: saturation,
|
|
1455
|
+
padding: padding,
|
|
1456
|
+
isFixedOrSticky: isFixedOrSticky
|
|
1457
|
+
}), void stopLerpLoop();
|
|
1458
|
+
// Smooth step
|
|
1459
|
+
internalMouseOffsetRef.current = {
|
|
1460
|
+
x: lerp$1(cur.x, tgt.x, LERP_T),
|
|
1461
|
+
y: lerp$1(cur.y, tgt.y, LERP_T)
|
|
1462
|
+
};
|
|
1463
|
+
const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
|
|
1464
|
+
internalGlobalMousePositionRef.current = {
|
|
1465
|
+
x: lerp$1(curG.x, tgtG.x, LERP_T),
|
|
1466
|
+
y: lerp$1(curG.y, tgtG.y, LERP_T)
|
|
1467
|
+
},
|
|
1468
|
+
// Imperative style update
|
|
1469
|
+
updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
|
|
1478
1470
|
mouseOffset: internalMouseOffsetRef.current,
|
|
1479
1471
|
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
1480
1472
|
glassSize: glassSize,
|
|
@@ -1497,10 +1489,24 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
|
|
|
1497
1489
|
};
|
|
1498
1490
|
// 0.08 – lower = more viscous
|
|
1499
1491
|
lerpRafRef.current = requestAnimationFrame(tick);
|
|
1500
|
-
}), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky ]),
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1492
|
+
}), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
|
|
1493
|
+
if (externalGlobalMousePosition && externalMouseOffset) return;
|
|
1494
|
+
if (effectiveWithoutEffects) return;
|
|
1495
|
+
const container = mouseContainer?.current || glassRef.current;
|
|
1496
|
+
if (!container) return;
|
|
1497
|
+
// Use cached rect if available, otherwise get new one
|
|
1498
|
+
let rect = cachedRectRef.current;
|
|
1499
|
+
if (rect && 0 !== rect.width && 0 !== rect.height || (rect = container.getBoundingClientRect(),
|
|
1500
|
+
cachedRectRef.current = rect), 0 === rect.width || 0 === rect.height) return;
|
|
1501
|
+
const center = calculateElementCenter(rect);
|
|
1502
|
+
// Write raw target — the lerp loop will smoothly pursue it
|
|
1503
|
+
targetMouseOffsetRef.current = {
|
|
1504
|
+
x: (globalPos.x - center.x) / rect.width * 100,
|
|
1505
|
+
y: (globalPos.y - center.y) / rect.height * 100
|
|
1506
|
+
}, targetGlobalMousePositionRef.current = globalPos,
|
|
1507
|
+
// Ensure the lerp loop is running to smoothly chase the new target
|
|
1508
|
+
lerpActiveRef.current || startLerpLoop();
|
|
1509
|
+
}), [ mouseContainer, glassRef, externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects, startLerpLoop ]);
|
|
1504
1510
|
/**
|
|
1505
1511
|
* Validate and clamp a numeric config value
|
|
1506
1512
|
*/
|
|
@@ -1509,7 +1515,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
|
|
|
1509
1515
|
if (externalGlobalMousePosition && externalMouseOffset) return;
|
|
1510
1516
|
if (effectiveWithoutEffects) return;
|
|
1511
1517
|
const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
|
|
1512
|
-
//
|
|
1518
|
+
// Initial start
|
|
1513
1519
|
startLerpLoop();
|
|
1514
1520
|
const container = mouseContainer?.current || glassRef.current;
|
|
1515
1521
|
let resizeObserver = null;
|
|
@@ -1522,7 +1528,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
|
|
|
1522
1528
|
unsubscribe(), stopLerpLoop(), null !== updateRectRef.current && (cancelAnimationFrame(updateRectRef.current),
|
|
1523
1529
|
updateRectRef.current = null), resizeObserver && resizeObserver.disconnect();
|
|
1524
1530
|
};
|
|
1525
|
-
}), [
|
|
1531
|
+
}), [ externalGlobalMousePosition, externalMouseOffset, effectiveWithoutEffects, handleGlobalMousePosition, startLerpLoop, stopLerpLoop, mouseContainer, glassRef ]),
|
|
1526
1532
|
// Also call updateStyles on other state changes (hover, active, etc)
|
|
1527
1533
|
useEffect((() => {
|
|
1528
1534
|
updateAtomixGlassStyles(wrapperRef?.current || null, glassRef.current, {
|
|
@@ -1998,184 +2004,147 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
1998
2004
|
/**
|
|
1999
2005
|
* Get GPU memory info if available (Chrome DevTools only)
|
|
2000
2006
|
*/
|
|
2007
|
+
/** Map an FPS value to a semantic color token string. */
|
|
2008
|
+
const getQualityColor = quality => {
|
|
2009
|
+
switch (quality) {
|
|
2010
|
+
case "high":
|
|
2011
|
+
return "var(--atomix-color-success, #4ade80)";
|
|
2012
|
+
|
|
2013
|
+
case "medium":
|
|
2014
|
+
return "var(--atomix-color-warning, #fbbf24)";
|
|
2015
|
+
|
|
2016
|
+
case "low":
|
|
2017
|
+
return "var(--atomix-color-danger, #ef4444)";
|
|
2018
|
+
|
|
2019
|
+
default:
|
|
2020
|
+
return "#9ca3af";
|
|
2021
|
+
}
|
|
2022
|
+
}, getFpsLabel = fps => fps >= 58 ? "Optimal" : fps >= 45 ? "Warning" : "Critical";
|
|
2023
|
+
|
|
2024
|
+
/** Map a quality level string to a semantic color token string. */
|
|
2025
|
+
// Inject keyframes once
|
|
2026
|
+
if ("undefined" != typeof document) {
|
|
2027
|
+
const styleId = "perf-dashboard-keyframes";
|
|
2028
|
+
if (!document.getElementById(styleId)) {
|
|
2029
|
+
const styleEl = document.createElement("style");
|
|
2030
|
+
styleEl.id = styleId, styleEl.textContent = "\n@keyframes perf-dashboard-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n",
|
|
2031
|
+
document.head.appendChild(styleEl);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2001
2035
|
/**
|
|
2002
|
-
* PerformanceDashboard - Real-time performance monitoring overlay
|
|
2036
|
+
* PerformanceDashboard - Real-time performance monitoring overlay.
|
|
2003
2037
|
*
|
|
2004
|
-
* Displays
|
|
2005
|
-
*
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
borderRadius: "8px",
|
|
2022
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
2023
|
-
fontFamily: "monospace",
|
|
2024
|
-
fontSize: "12px",
|
|
2025
|
-
color: "#fff",
|
|
2026
|
-
zIndex: 9999,
|
|
2027
|
-
minWidth: "200px",
|
|
2028
|
-
backdropFilter: "blur(8px)",
|
|
2029
|
-
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2030
|
-
transition: "opacity 0.3s ease",
|
|
2031
|
-
opacity: isVisible ? 1 : 0,
|
|
2032
|
-
pointerEvents: isVisible ? "auto" : "none"
|
|
2033
|
-
})), [ isVisible ]), headerStyle = useMemo((() => ({
|
|
2034
|
-
display: "flex",
|
|
2035
|
-
justifyContent: "space-between",
|
|
2036
|
-
alignItems: "center",
|
|
2037
|
-
marginBottom: "8px",
|
|
2038
|
-
paddingBottom: "8px",
|
|
2039
|
-
borderBottom: "1px solid rgba(255, 255, 255, 0.1)"
|
|
2040
|
-
})), []), titleStyle = useMemo((() => ({
|
|
2041
|
-
fontWeight: "bold",
|
|
2042
|
-
fontSize: "13px",
|
|
2043
|
-
color: "#fff"
|
|
2044
|
-
})), []), closeButtonStyle = useMemo((() => ({
|
|
2045
|
-
background: "transparent",
|
|
2046
|
-
border: "none",
|
|
2047
|
-
color: "#9ca3af",
|
|
2048
|
-
cursor: "pointer",
|
|
2049
|
-
fontSize: "16px",
|
|
2050
|
-
padding: "0",
|
|
2051
|
-
lineHeight: "1"
|
|
2052
|
-
})), []), metricRowStyle = useMemo((() => ({
|
|
2053
|
-
display: "flex",
|
|
2054
|
-
justifyContent: "space-between",
|
|
2055
|
-
alignItems: "center",
|
|
2056
|
-
marginBottom: "6px"
|
|
2057
|
-
})), []), labelStyle = useMemo((() => ({
|
|
2058
|
-
color: "#9ca3af",
|
|
2059
|
-
marginRight: "12px"
|
|
2060
|
-
})), []), valueStyle = useMemo((() => ({
|
|
2061
|
-
fontWeight: "bold"
|
|
2062
|
-
})), []);
|
|
2063
|
-
// Get quality level badge color
|
|
2064
|
-
return isVisible ? jsxs("div", {
|
|
2065
|
-
style: dashboardStyle,
|
|
2038
|
+
* Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
|
|
2039
|
+
* Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
|
|
2040
|
+
*/ const PerformanceDashboard = memo((({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
|
|
2041
|
+
if (!isVisible) return null;
|
|
2042
|
+
const fpsColor = (fps = metrics.fps) >= 58 ? "var(--atomix-color-success, #4ade80)" : fps >= 45 ? "var(--atomix-color-warning, #fbbf24)" : "var(--atomix-color-danger, #ef4444)";
|
|
2043
|
+
var fps;
|
|
2044
|
+
const isCritical = metrics.fps < 45;
|
|
2045
|
+
return jsxs("div", {
|
|
2046
|
+
className: "c-perf-dashboard u-position-fixed u-top-4 u-end-4 u-p-3 u-px-4 u-text-xs u-font-mono u-text-white u-rounded-md u-border u-border-white-alpha-10 u-shadow-lg",
|
|
2047
|
+
style: {
|
|
2048
|
+
zIndex: 9999,
|
|
2049
|
+
minWidth: "12.5rem",
|
|
2050
|
+
// 200px
|
|
2051
|
+
backgroundColor: "rgba(17, 24, 39, 0.95)",
|
|
2052
|
+
backdropFilter: "blur(8px)",
|
|
2053
|
+
transition: "opacity 0.3s ease"
|
|
2054
|
+
},
|
|
2066
2055
|
children: [ jsxs("div", {
|
|
2067
|
-
|
|
2056
|
+
className: "u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10",
|
|
2068
2057
|
children: [ jsx("span", {
|
|
2069
|
-
|
|
2058
|
+
className: "u-text-sm u-font-bold u-text-white",
|
|
2070
2059
|
children: "Performance Monitor"
|
|
2071
2060
|
}), onClose && jsx("button", {
|
|
2072
|
-
|
|
2061
|
+
className: "u-bg-transparent u-border-none u-p-0 u-line-height-1 u-text-base u-text-gray-400 u-cursor-pointer hover:u-text-white",
|
|
2073
2062
|
onClick: onClose,
|
|
2074
2063
|
"aria-label": "Close performance dashboard",
|
|
2064
|
+
style: {
|
|
2065
|
+
transition: "color 0.2s ease"
|
|
2066
|
+
},
|
|
2075
2067
|
children: "×"
|
|
2076
2068
|
}) ]
|
|
2077
2069
|
}), jsxs("div", {
|
|
2078
|
-
|
|
2070
|
+
className: "u-flex u-items-center u-justify-between u-mb-1-5",
|
|
2079
2071
|
children: [ jsx("span", {
|
|
2080
|
-
|
|
2072
|
+
className: "u-text-gray-400 u-me-3",
|
|
2081
2073
|
children: "FPS"
|
|
2082
2074
|
}), jsx("span", {
|
|
2075
|
+
className: "u-font-bold",
|
|
2083
2076
|
style: {
|
|
2084
|
-
|
|
2085
|
-
color: getFpsColor(metrics.fps)
|
|
2077
|
+
color: fpsColor
|
|
2086
2078
|
},
|
|
2087
2079
|
children: Math.round(metrics.fps)
|
|
2088
2080
|
}) ]
|
|
2089
2081
|
}), jsxs("div", {
|
|
2090
|
-
|
|
2082
|
+
className: "u-flex u-items-center u-justify-between u-mb-1-5",
|
|
2091
2083
|
children: [ jsx("span", {
|
|
2092
|
-
|
|
2084
|
+
className: "u-text-gray-400 u-me-3",
|
|
2093
2085
|
children: "Frame Time"
|
|
2094
2086
|
}), jsxs("span", {
|
|
2095
|
-
|
|
2087
|
+
className: "u-font-bold",
|
|
2096
2088
|
children: [ metrics.frameTime.toFixed(2), "ms" ]
|
|
2097
2089
|
}) ]
|
|
2098
2090
|
}), jsxs("div", {
|
|
2099
|
-
|
|
2091
|
+
className: "u-flex u-items-center u-justify-between u-mb-1-5",
|
|
2100
2092
|
children: [ jsx("span", {
|
|
2101
|
-
|
|
2093
|
+
className: "u-text-gray-400 u-me-3",
|
|
2102
2094
|
children: "Quality"
|
|
2103
2095
|
}), jsx("span", {
|
|
2096
|
+
className: "u-font-bold u-text-uppercase",
|
|
2104
2097
|
style: {
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
case "high":
|
|
2109
|
-
return "#4ade80";
|
|
2110
|
-
|
|
2111
|
-
case "medium":
|
|
2112
|
-
return "#fbbf24";
|
|
2113
|
-
|
|
2114
|
-
case "low":
|
|
2115
|
-
return "#ef4444";
|
|
2116
|
-
|
|
2117
|
-
default:
|
|
2118
|
-
return "#9ca3af";
|
|
2119
|
-
}
|
|
2120
|
-
})(metrics.qualityLevel),
|
|
2121
|
-
textTransform: "uppercase",
|
|
2122
|
-
fontSize: "11px"
|
|
2098
|
+
fontSize: "0.6875rem",
|
|
2099
|
+
// 11px
|
|
2100
|
+
color: getQualityColor(metrics.qualityLevel)
|
|
2123
2101
|
},
|
|
2124
2102
|
children: metrics.qualityLevel
|
|
2125
2103
|
}) ]
|
|
2126
2104
|
}), metrics.gpuMemory && jsxs("div", {
|
|
2127
|
-
|
|
2105
|
+
className: "u-flex u-items-center u-justify-between u-mb-1-5",
|
|
2128
2106
|
children: [ jsx("span", {
|
|
2129
|
-
|
|
2107
|
+
className: "u-text-gray-400 u-me-3",
|
|
2130
2108
|
children: "GPU Memory"
|
|
2131
2109
|
}), jsxs("span", {
|
|
2132
|
-
|
|
2110
|
+
className: "u-font-bold",
|
|
2133
2111
|
children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
|
|
2134
2112
|
}) ]
|
|
2135
2113
|
}), metrics.isAutoScaling && jsx("div", {
|
|
2114
|
+
className: "u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center",
|
|
2136
2115
|
style: {
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
fontSize: "10px",
|
|
2141
|
-
color: "#6b7280",
|
|
2142
|
-
textAlign: "center"
|
|
2116
|
+
fontSize: "0.625rem",
|
|
2117
|
+
// 10px
|
|
2118
|
+
color: "#6b7280"
|
|
2143
2119
|
},
|
|
2144
2120
|
children: "Auto-scaling active"
|
|
2145
2121
|
}), jsxs("div", {
|
|
2146
|
-
|
|
2147
|
-
marginTop: "8px",
|
|
2148
|
-
paddingTop: "8px",
|
|
2149
|
-
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
|
|
2150
|
-
display: "flex",
|
|
2151
|
-
alignItems: "center",
|
|
2152
|
-
gap: "6px"
|
|
2153
|
-
},
|
|
2122
|
+
className: "u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10",
|
|
2154
2123
|
children: [ jsx("div", {
|
|
2124
|
+
className: "u-rounded-full",
|
|
2155
2125
|
style: {
|
|
2156
|
-
width: "
|
|
2157
|
-
height: "
|
|
2158
|
-
|
|
2159
|
-
backgroundColor:
|
|
2160
|
-
|
|
2126
|
+
width: "0.5rem",
|
|
2127
|
+
height: "0.5rem",
|
|
2128
|
+
flexShrink: 0,
|
|
2129
|
+
backgroundColor: fpsColor,
|
|
2130
|
+
...isCritical && {
|
|
2131
|
+
animation: "perf-dashboard-pulse 1s infinite"
|
|
2132
|
+
}
|
|
2161
2133
|
}
|
|
2162
2134
|
}), jsx("span", {
|
|
2135
|
+
className: "u-text-xs",
|
|
2163
2136
|
style: {
|
|
2164
|
-
fontSize: "
|
|
2165
|
-
|
|
2137
|
+
fontSize: "0.625rem",
|
|
2138
|
+
// 10px
|
|
2139
|
+
color: fpsColor
|
|
2166
2140
|
},
|
|
2167
|
-
children: metrics.fps
|
|
2141
|
+
children: getFpsLabel(metrics.fps)
|
|
2168
2142
|
}) ]
|
|
2169
2143
|
}) ]
|
|
2170
|
-
})
|
|
2171
|
-
};
|
|
2144
|
+
});
|
|
2145
|
+
}));
|
|
2172
2146
|
|
|
2173
|
-
|
|
2174
|
-
if ("undefined" != typeof document) {
|
|
2175
|
-
const styleSheet = document.createElement("style");
|
|
2176
|
-
styleSheet.textContent = "\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n ",
|
|
2177
|
-
document.head.appendChild(styleSheet);
|
|
2178
|
-
}
|
|
2147
|
+
PerformanceDashboard.displayName = "PerformanceDashboard";
|
|
2179
2148
|
|
|
2180
2149
|
/**
|
|
2181
2150
|
* Mobile optimization presets
|
|
@@ -2186,7 +2155,8 @@ if ("undefined" != typeof document) {
|
|
|
2186
2155
|
/**
|
|
2187
2156
|
* Performance preset - Maximum FPS, reduced quality
|
|
2188
2157
|
* Best for low-end devices or when battery saving is priority
|
|
2189
|
-
*/
|
|
2158
|
+
*/
|
|
2159
|
+
const PERFORMANCE_PRESET = {
|
|
2190
2160
|
distortionOctaves: 2,
|
|
2191
2161
|
// Minimal FBM layers
|
|
2192
2162
|
displacementScale: 50,
|
|
@@ -2295,95 +2265,21 @@ if ("undefined" != typeof document) {
|
|
|
2295
2265
|
saturation: 70
|
|
2296
2266
|
}
|
|
2297
2267
|
}
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
* - Dynamic border-radius extraction from children CSS properties
|
|
2311
|
-
* - Automatic light/dark theme detection via overLight prop
|
|
2312
|
-
* - Accessibility and performance optimizations
|
|
2313
|
-
* - Multiple displacement modes (standard, polar, prominent, shader)
|
|
2314
|
-
* - Design token integration for consistent theming
|
|
2315
|
-
* - Focus ring support for keyboard navigation
|
|
2316
|
-
* - Responsive breakpoints for mobile optimization
|
|
2317
|
-
* - Enhanced ARIA attributes for screen readers
|
|
2318
|
-
* - Time-based animation system with FBM distortion
|
|
2319
|
-
* - Device preset optimization for performance/quality balance
|
|
2320
|
-
*
|
|
2321
|
-
* Design System Compliance:
|
|
2322
|
-
* - Uses design tokens for opacity, spacing, and colors
|
|
2323
|
-
* - Follows BEM methodology for class naming
|
|
2324
|
-
* - Implements focus-ring mixin for accessibility
|
|
2325
|
-
* - Supports reduced motion and high contrast preferences
|
|
2326
|
-
*
|
|
2327
|
-
* @example
|
|
2328
|
-
* // Basic usage with dynamic border-radius extraction
|
|
2329
|
-
* <AtomixGlass>
|
|
2330
|
-
* <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
|
|
2331
|
-
* </AtomixGlass>
|
|
2332
|
-
*
|
|
2333
|
-
* @example
|
|
2334
|
-
* // Manual border-radius override
|
|
2335
|
-
* <AtomixGlass borderRadius={20}>
|
|
2336
|
-
* <div>Content with 20px glass radius</div>
|
|
2337
|
-
* </AtomixGlass>
|
|
2338
|
-
*
|
|
2339
|
-
* @example
|
|
2340
|
-
* // Interactive glass with click handler
|
|
2341
|
-
* <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
|
|
2342
|
-
* <div>Clickable content</div>
|
|
2343
|
-
* </AtomixGlass>
|
|
2344
|
-
*
|
|
2345
|
-
* @example
|
|
2346
|
-
* // OverLight - Boolean mode (explicit control)
|
|
2347
|
-
* <AtomixGlass overLight={true}>
|
|
2348
|
-
* <div>Content on light background</div>
|
|
2349
|
-
* </AtomixGlass>
|
|
2350
|
-
*
|
|
2351
|
-
* @example
|
|
2352
|
-
* // OverLight - Auto-detection mode
|
|
2353
|
-
* <AtomixGlass overLight="auto">
|
|
2354
|
-
* <div>Content with auto-detected background</div>
|
|
2355
|
-
* </AtomixGlass>
|
|
2356
|
-
*
|
|
2357
|
-
* @example
|
|
2358
|
-
* // OverLight - Object config with custom settings
|
|
2359
|
-
* <AtomixGlass
|
|
2360
|
-
* overLight={{
|
|
2361
|
-
* threshold: 0.8,
|
|
2362
|
-
* opacity: 0.6,
|
|
2363
|
-
* contrast: 1.8,
|
|
2364
|
-
* brightness: 1.0,
|
|
2365
|
-
* saturationBoost: 1.5
|
|
2366
|
-
* }}
|
|
2367
|
-
* >
|
|
2368
|
-
* <div>Content with custom overLight config</div>
|
|
2369
|
-
* </AtomixGlass>
|
|
2370
|
-
*
|
|
2371
|
-
* @example
|
|
2372
|
-
* // Debug mode for overLight detection
|
|
2373
|
-
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
2374
|
-
* <div>Content with debug logging enabled</div>
|
|
2375
|
-
* </AtomixGlass>
|
|
2376
|
-
*
|
|
2377
|
-
* @example
|
|
2378
|
-
* // Performance-optimized for mobile devices
|
|
2379
|
-
* <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
|
|
2380
|
-
* <div>Mobile-optimized glass effect</div>
|
|
2381
|
-
* </AtomixGlass>
|
|
2382
|
-
*/
|
|
2383
|
-
function AtomixGlass({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, ...rest}) {
|
|
2384
|
-
const glassRef = useRef(null), contentRef = useRef(null), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, applyTimeBasedDistortion: applyTimeBasedDistortion, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
|
|
2268
|
+
}, AtomixGlassInner = forwardRef((function({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, isFixedOrSticky: propsIsFixedOrSticky, ...rest}, ref) {
|
|
2269
|
+
const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useMemo((() =>
|
|
2270
|
+
// Helper to merge refs
|
|
2271
|
+
function(...refs) {
|
|
2272
|
+
return node => {
|
|
2273
|
+
refs.forEach((ref => {
|
|
2274
|
+
"function" == typeof ref ? ref(node) : null != ref && (ref.current = node);
|
|
2275
|
+
}));
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
// Internal implementation with forwardRef
|
|
2279
|
+
(ref, internalWrapperRef)), [ ref ]), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = propsIsFixedOrSticky || "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
|
|
2385
2280
|
glassRef: glassRef,
|
|
2386
2281
|
contentRef: contentRef,
|
|
2282
|
+
wrapperRef: internalWrapperRef,
|
|
2387
2283
|
borderRadius: borderRadius,
|
|
2388
2284
|
globalMousePosition: externalGlobalMousePosition,
|
|
2389
2285
|
mouseOffset: externalMouseOffset,
|
|
@@ -2412,7 +2308,6 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2412
2308
|
distortionGain: distortionGain,
|
|
2413
2309
|
distortionQuality: distortionQuality
|
|
2414
2310
|
});
|
|
2415
|
-
// Re-calculate only when devicePreset changes
|
|
2416
2311
|
// Responsive breakpoint system - automatically adjusts parameters based on viewport
|
|
2417
2312
|
!
|
|
2418
2313
|
/**
|
|
@@ -2602,11 +2497,10 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2602
2497
|
},
|
|
2603
2498
|
breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
|
|
2604
2499
|
enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
|
|
2605
|
-
// Enable unless disabled
|
|
2606
2500
|
debug: !1
|
|
2607
2501
|
});
|
|
2608
2502
|
// Performance monitoring - tracks FPS, frame time, memory usage
|
|
2609
|
-
const {metrics: performanceMetrics,
|
|
2503
|
+
const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} =
|
|
2610
2504
|
/**
|
|
2611
2505
|
* Performance Monitor Hook
|
|
2612
2506
|
*
|
|
@@ -2770,17 +2664,17 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2770
2664
|
toggleMonitoring: toggleMonitoring
|
|
2771
2665
|
};
|
|
2772
2666
|
}({
|
|
2773
|
-
enabled:
|
|
2774
|
-
//
|
|
2667
|
+
enabled: debugPerformance,
|
|
2668
|
+
// Enable when debugPerformance is true
|
|
2775
2669
|
debug: !1,
|
|
2776
2670
|
showOverlay: !1
|
|
2777
2671
|
});
|
|
2778
|
-
// Auto-start performance monitoring
|
|
2672
|
+
// Auto-start performance monitoring when debugPerformance is enabled
|
|
2779
2673
|
React.useEffect((() => {
|
|
2780
|
-
|
|
2674
|
+
debugPerformance && toggleMonitoring();
|
|
2781
2675
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2782
|
-
}), []);
|
|
2783
|
-
//
|
|
2676
|
+
}), [ debugPerformance ]);
|
|
2677
|
+
// Re-run when debugPerformance changes
|
|
2784
2678
|
const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
|
|
2785
2679
|
if (!isFixedOrSticky) return {};
|
|
2786
2680
|
const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
|
|
@@ -2808,34 +2702,30 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2808
2702
|
if (isFixedOrSticky) {
|
|
2809
2703
|
const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
|
|
2810
2704
|
return {
|
|
2811
|
-
...visualStyle
|
|
2812
|
-
...!effectiveWithoutEffects && {
|
|
2813
|
-
transform: transformStyle
|
|
2814
|
-
}
|
|
2705
|
+
...visualStyle
|
|
2815
2706
|
};
|
|
2816
2707
|
}
|
|
2817
2708
|
return {
|
|
2818
|
-
...restStyle
|
|
2819
|
-
...!effectiveWithoutEffects && {
|
|
2820
|
-
transform: transformStyle
|
|
2821
|
-
}
|
|
2709
|
+
...restStyle
|
|
2822
2710
|
};
|
|
2823
|
-
}), [ isFixedOrSticky, restStyle
|
|
2711
|
+
}), [ isFixedOrSticky, restStyle ]);
|
|
2824
2712
|
// Build className with state modifiers
|
|
2825
2713
|
const componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
|
|
2826
2714
|
position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
|
|
2827
|
-
top: isFixedOrSticky ?
|
|
2828
|
-
left: isFixedOrSticky ?
|
|
2829
|
-
|
|
2715
|
+
top: isFixedOrSticky ? restStyle.top ?? 0 : 0,
|
|
2716
|
+
left: isFixedOrSticky ? restStyle.left ?? 0 : 0,
|
|
2717
|
+
right: isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
|
|
2718
|
+
bottom: isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto"
|
|
2719
|
+
})), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), adjustedSize = useMemo((() => {
|
|
2830
2720
|
// Keep a reference to positionStyles to avoid unused-variable lint,
|
|
2831
2721
|
// but sizing is driven by explicit width/height or measured size.
|
|
2832
2722
|
positionStyles.position;
|
|
2833
|
-
const resolveLength = (value, measured) => void 0 !== value ? "number" == typeof value ? `${value}px` : value : measured > 0 ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
|
|
2723
|
+
const resolveLength = (value, measured) => void 0 !== value && isFixedOrSticky ? "number" == typeof value ? `${value}px` : value : measured > 0 && isFixedOrSticky ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
|
|
2834
2724
|
return {
|
|
2835
2725
|
width: resolveLength(effectiveWidth, glassSize.width),
|
|
2836
2726
|
height: resolveLength(effectiveHeight, glassSize.height)
|
|
2837
2727
|
};
|
|
2838
|
-
}), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height ]), gradientValues = useMemo((() => {
|
|
2728
|
+
}), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height, isFixedOrSticky ]), gradientValues = useMemo((() => {
|
|
2839
2729
|
const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
|
|
2840
2730
|
return {
|
|
2841
2731
|
borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
|
|
@@ -2865,33 +2755,32 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2865
2755
|
absMx: absMx,
|
|
2866
2756
|
absMy: absMy
|
|
2867
2757
|
};
|
|
2868
|
-
}), [ mouseOffset.x, mouseOffset.y ]), opacityValues = useMemo((() => {
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
};
|
|
2877
|
-
}), [ isHovered, isActive, isOverLight, overLightConfig.opacity ]), glassVars = useMemo((() => {
|
|
2878
|
-
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;
|
|
2758
|
+
}), [ mouseOffset.x, mouseOffset.y ]), clampedOverLightOpacity = Math.max(0, Math.min(1, overLightConfig?.opacity ?? .4)), clampedBorderOpacity = Math.max(0, Math.min(1, overLightConfig?.borderOpacity ?? 1)), opacityValues = useMemo((() => ({
|
|
2759
|
+
hover1: isHovered || isActive ? .5 : 0,
|
|
2760
|
+
hover2: isActive ? .5 : 0,
|
|
2761
|
+
hover3: isHovered ? .4 : isActive ? .8 : 0,
|
|
2762
|
+
base: isOverLight ? clampedOverLightOpacity || .4 : 0,
|
|
2763
|
+
over: isOverLight ? 1.1 * (clampedOverLightOpacity || .4) : 0
|
|
2764
|
+
})), [ isHovered, isActive, isOverLight, clampedOverLightOpacity ]), glassVars = useMemo((() => {
|
|
2765
|
+
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;
|
|
2879
2766
|
return {
|
|
2880
2767
|
...void 0 !== customZIndex && {
|
|
2881
2768
|
"--atomix-glass-base-z-index": customZIndex
|
|
2882
2769
|
},
|
|
2883
2770
|
"--atomix-glass-radius": `${effectiveBorderRadius}px`,
|
|
2884
2771
|
"--atomix-glass-transform": transformStyle || "none",
|
|
2885
|
-
|
|
2886
|
-
"--atomix-glass-position": rootLayoutStyle.position
|
|
2887
|
-
"--atomix-glass-top": `${isFixedOrSticky ?
|
|
2888
|
-
"--atomix-glass-left": `${isFixedOrSticky ?
|
|
2772
|
+
"--atomix-glass-container-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
|
|
2773
|
+
"--atomix-glass-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
|
|
2774
|
+
"--atomix-glass-top": `${isFixedOrSticky ? restStyle.top ?? 0 : 0}px`,
|
|
2775
|
+
"--atomix-glass-left": `${isFixedOrSticky ? restStyle.left ?? 0 : 0}px`,
|
|
2776
|
+
"--atomix-glass-right": isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
|
|
2777
|
+
"--atomix-glass-bottom": isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto",
|
|
2889
2778
|
"--atomix-glass-width": adjustedSize.width,
|
|
2890
2779
|
"--atomix-glass-height": adjustedSize.height,
|
|
2891
|
-
"--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.
|
|
2780
|
+
"--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.125rem)",
|
|
2892
2781
|
"--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
|
|
2893
|
-
"--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) *
|
|
2894
|
-
"--atomix-glass-border-gradient-2": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) *
|
|
2782
|
+
"--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
|
|
2783
|
+
"--atomix-glass-border-gradient-2": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
|
|
2895
2784
|
"--atomix-glass-hover-1-opacity": opacityValues.hover1,
|
|
2896
2785
|
"--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}%)`,
|
|
2897
2786
|
"--atomix-glass-hover-2-opacity": opacityValues.hover2,
|
|
@@ -2905,13 +2794,14 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2905
2794
|
"--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
|
|
2906
2795
|
"--atomix-glass-overlay-highlight-bg": `radial-gradient(circle at ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_X}% ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_Y}%, rgba(255, 255, 255, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.WHITE_OPACITY}) 0%, transparent ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.STOP}%)`
|
|
2907
2796
|
};
|
|
2908
|
-
}), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight,
|
|
2797
|
+
}), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, clampedBorderOpacity, customZIndex, isFixedOrSticky, positionStyles.position, rootLayoutStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), renderBackgroundLayer = layerType => jsx("div", {
|
|
2909
2798
|
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(" ")
|
|
2910
2799
|
});
|
|
2911
2800
|
// Calculate position and size styles for internal layers
|
|
2912
2801
|
// When root is fixed/sticky, internal layers use absolute (relative to root)
|
|
2913
2802
|
return jsxs("div", {
|
|
2914
2803
|
...rest,
|
|
2804
|
+
ref: mergedRef,
|
|
2915
2805
|
className: componentClassName,
|
|
2916
2806
|
style: {
|
|
2917
2807
|
...glassVars
|
|
@@ -2921,17 +2811,14 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2921
2811
|
"aria-label": ariaLabel,
|
|
2922
2812
|
"aria-describedby": ariaDescribedBy,
|
|
2923
2813
|
"aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
|
|
2924
|
-
"aria-pressed":
|
|
2814
|
+
"aria-pressed": void 0,
|
|
2925
2815
|
onKeyDown: onClick ? handleKeyDown : void 0,
|
|
2926
2816
|
children: [ jsx(AtomixGlassContainer, {
|
|
2927
2817
|
ref: glassRef,
|
|
2928
2818
|
contentRef: contentRef,
|
|
2929
2819
|
className: className,
|
|
2930
2820
|
style: {
|
|
2931
|
-
...restStyle
|
|
2932
|
-
...!isFixedOrSticky && {
|
|
2933
|
-
position: "relative"
|
|
2934
|
-
}
|
|
2821
|
+
...restStyle
|
|
2935
2822
|
},
|
|
2936
2823
|
borderRadius: effectiveBorderRadius,
|
|
2937
2824
|
displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
|
|
@@ -2967,6 +2854,7 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2967
2854
|
effectiveReducedMotion: effectiveReducedMotion,
|
|
2968
2855
|
shaderVariant: shaderVariant,
|
|
2969
2856
|
withLiquidBlur: withLiquidBlur,
|
|
2857
|
+
isFixedOrSticky: isFixedOrSticky,
|
|
2970
2858
|
// Phase 1: Animation System props
|
|
2971
2859
|
shaderTime: getShaderTime(),
|
|
2972
2860
|
withTimeAnimation: withTimeAnimation,
|
|
@@ -2995,6 +2883,8 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
2995
2883
|
}) ]
|
|
2996
2884
|
}), withBorder && jsxs(Fragment, {
|
|
2997
2885
|
children: [ jsx("span", {
|
|
2886
|
+
className: ATOMIX_GLASS.BORDER_BACKDROP_CLASS
|
|
2887
|
+
}), jsx("span", {
|
|
2998
2888
|
className: ATOMIX_GLASS.BORDER_1_CLASS
|
|
2999
2889
|
}), jsx("span", {
|
|
3000
2890
|
className: ATOMIX_GLASS.BORDER_2_CLASS
|
|
@@ -3005,11 +2895,18 @@ function AtomixGlass({children: children, displacementScale: displacementScale =
|
|
|
3005
2895
|
onClose: () => {}
|
|
3006
2896
|
}) ]
|
|
3007
2897
|
});
|
|
3008
|
-
}
|
|
2898
|
+
}));
|
|
3009
2899
|
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
2900
|
+
/**
|
|
2901
|
+
* Balanced preset - Good quality with reasonable performance
|
|
2902
|
+
* Default preset for most mobile devices
|
|
2903
|
+
*/ AtomixGlassInner.displayName = "AtomixGlass";
|
|
2904
|
+
|
|
2905
|
+
/**
|
|
2906
|
+
* AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
|
|
2907
|
+
* Ref is forwarded to the root `<div>` element.
|
|
2908
|
+
*/
|
|
2909
|
+
const AtomixGlass = memo(AtomixGlassInner), smoothStep = (a, b, t) => {
|
|
3013
2910
|
// Add input validation
|
|
3014
2911
|
if ("number" != typeof a || "number" != typeof b || "number" != typeof t) return 0;
|
|
3015
2912
|
const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
|
|
@@ -3304,6 +3201,8 @@ const smoothStep = (a, b, t) => {
|
|
|
3304
3201
|
value: "Module"
|
|
3305
3202
|
}));
|
|
3306
3203
|
|
|
3204
|
+
// Adapted from https://github.com/shuding/liquid-glass
|
|
3205
|
+
// Constants
|
|
3307
3206
|
/**
|
|
3308
3207
|
* Component Utilities
|
|
3309
3208
|
*
|
|
@@ -3676,11 +3575,13 @@ const VideoPlayer = forwardRef((({src: src, type: type = "video", youtubeId: yo
|
|
|
3676
3575
|
detectBorderRadius();
|
|
3677
3576
|
// Create ResizeObserver to watch for style changes
|
|
3678
3577
|
let resizeObserver = null;
|
|
3679
|
-
|
|
3578
|
+
"undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
|
|
3680
3579
|
resizeObserver.observe(containerRef.current)),
|
|
3681
3580
|
// Also listen for window resize (in case styles change)
|
|
3682
|
-
window.addEventListener("resize", detectBorderRadius)
|
|
3683
|
-
|
|
3581
|
+
window.addEventListener("resize", detectBorderRadius);
|
|
3582
|
+
const currentContainer = containerRef.current;
|
|
3583
|
+
return () => {
|
|
3584
|
+
window.removeEventListener("resize", detectBorderRadius), resizeObserver && currentContainer && (resizeObserver.unobserve(currentContainer),
|
|
3684
3585
|
resizeObserver.disconnect());
|
|
3685
3586
|
};
|
|
3686
3587
|
}), []);
|
|
@@ -4015,7 +3916,7 @@ VideoPlayer.displayName = "VideoPlayer";
|
|
|
4015
3916
|
*/
|
|
4016
3917
|
const MasonryGrid = forwardRef((({children: children, className: className = "", xs: xs = 1, sm: sm, md: md, lg: lg, xl: xl, xxl: xxl, gap: gap = 16, animate: animate = !0, imagesLoaded: imagesLoaded = !0, onLayoutComplete: onLayoutComplete, onImageLoad: onImageLoad, ...props}, ref) => {
|
|
4017
3918
|
// === REFS & STATE ===
|
|
4018
|
-
const [columns, setColumns] = useState(xs), [positions, setPositions] = useState([]), [
|
|
3919
|
+
const [columns, setColumns] = useState(xs), [positions, setPositions] = useState([]), [, setLayoutComplete] = useState(!1), [loadingImages, setLoadingImages] = useState(!1), containerRef = useRef(null), columnHeights = useRef([]), imagesLoadedCount = useRef(0), totalImagesCount = useRef(0), imageElements = useRef(new Map);
|
|
4019
3920
|
useEffect((() => {
|
|
4020
3921
|
setLoadingImages(!!imagesLoaded);
|
|
4021
3922
|
}), [ columns, imagesLoaded ]),
|
|
@@ -4044,34 +3945,45 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
|
|
|
4044
3945
|
});
|
|
4045
3946
|
})), setItems(newItems);
|
|
4046
3947
|
}), [ children ]);
|
|
4047
|
-
// ===
|
|
4048
|
-
const
|
|
4049
|
-
if (!
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
3948
|
+
// === MANAGE ITEM LAYOUT ===
|
|
3949
|
+
const calculateLayout = useCallback((() => {
|
|
3950
|
+
if (!containerRef.current || 0 === items.length) return;
|
|
3951
|
+
const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
|
|
3952
|
+
columnHeights.current = Array(columns).fill(0);
|
|
3953
|
+
const newPositions = [];
|
|
3954
|
+
items.forEach(((item, index) => {
|
|
3955
|
+
if (item.ref.current) {
|
|
3956
|
+
// Find the shortest column
|
|
3957
|
+
const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
|
|
3958
|
+
columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
|
|
3959
|
+
left: left,
|
|
3960
|
+
top: top,
|
|
3961
|
+
width: colWidth,
|
|
3962
|
+
height: height
|
|
3963
|
+
};
|
|
4055
3964
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
setLoadingImages(!1), // This ensures the loading class is removed *immediately* after images load
|
|
4065
|
-
// Force a double requestAnimationFrame for final layout calculation after all images are loaded (guarantees DOM paint)
|
|
4066
|
-
requestAnimationFrame((() => {
|
|
4067
|
-
requestAnimationFrame((() => {
|
|
4068
|
-
calculateLayout(),
|
|
4069
|
-
// As a failsafe, if still present for some render lag, force another setLoadingImages(false)
|
|
4070
|
-
setLoadingImages(!1);
|
|
4071
|
-
}));
|
|
4072
|
-
})), onLayoutComplete?.());
|
|
3965
|
+
})), setPositions(newPositions);
|
|
3966
|
+
}), [ items, columns, gap ]), handleImageLoad = useCallback((img => {
|
|
3967
|
+
if (imageElements.current.get(img)) return;
|
|
3968
|
+
// Add loaded class for animation
|
|
3969
|
+
if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
|
|
3970
|
+
const itemElement = img.closest(".o-masonry-grid > div");
|
|
3971
|
+
itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
|
|
3972
|
+
itemElement.classList.remove("o-masonry-grid__item-loading"));
|
|
4073
3973
|
}
|
|
4074
|
-
|
|
3974
|
+
// Schedule layout recalculation after next paint to prevent overlap
|
|
3975
|
+
const scheduleLayoutUpdate = () => {
|
|
3976
|
+
const frameId = requestAnimationFrame((() => {
|
|
3977
|
+
onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current), calculateLayout();
|
|
3978
|
+
}));
|
|
3979
|
+
return () => cancelAnimationFrame(frameId);
|
|
3980
|
+
}, cleanup = scheduleLayoutUpdate();
|
|
3981
|
+
// Clean up previous scheduled updates
|
|
3982
|
+
// If all images have loaded, update loading state and complete layout
|
|
3983
|
+
imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
|
|
3984
|
+
setLoadingImages(!1), setTimeout((() => cleanup()), 0), // Clean up after current execution
|
|
3985
|
+
scheduleLayoutUpdate(), onLayoutComplete?.());
|
|
3986
|
+
}), [ onImageLoad, onLayoutComplete, imagesLoaded, calculateLayout ]), trackImages = useCallback((() => {
|
|
4075
3987
|
if (!imagesLoaded || !containerRef.current) return;
|
|
4076
3988
|
imageElements.current.clear(), imagesLoadedCount.current = 0;
|
|
4077
3989
|
const images = containerRef.current.querySelectorAll("img");
|
|
@@ -4090,30 +4002,21 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
|
|
|
4090
4002
|
img.removeEventListener("error", masonryImg._masonryLoadHandler), delete masonryImg._masonryLoadHandler);
|
|
4091
4003
|
}));
|
|
4092
4004
|
});
|
|
4093
|
-
}), [ imagesLoaded, handleImageLoad, onLayoutComplete ])
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
columnHeights.current = Array(columns).fill(0);
|
|
4097
|
-
const newPositions = [];
|
|
4098
|
-
items.forEach(((item, index) => {
|
|
4099
|
-
if (item.ref.current) {
|
|
4100
|
-
// Find the shortest column
|
|
4101
|
-
const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
|
|
4102
|
-
columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
|
|
4103
|
-
left: left,
|
|
4104
|
-
top: top,
|
|
4105
|
-
width: colWidth,
|
|
4106
|
-
height: height
|
|
4107
|
-
};
|
|
4108
|
-
}
|
|
4109
|
-
})), setPositions(newPositions);
|
|
4110
|
-
}), [ items, columns, gap ]);
|
|
4111
|
-
// === OBSERVE CONTAINER RESIZE ===
|
|
4005
|
+
}), [ imagesLoaded, handleImageLoad, onLayoutComplete ]);
|
|
4006
|
+
// === TRACK & MANAGE IMAGES ===
|
|
4007
|
+
// === OBSERVE CONTAINER RESIZE ===
|
|
4112
4008
|
useEffect((() => {
|
|
4113
4009
|
if (!containerRef.current) return;
|
|
4114
|
-
let animationFrame = null;
|
|
4115
|
-
const observer = new ResizeObserver((
|
|
4116
|
-
|
|
4010
|
+
let animationFrame = null, lastWidth = 0;
|
|
4011
|
+
const observer = new ResizeObserver((entries => {
|
|
4012
|
+
const entry = entries[0];
|
|
4013
|
+
if (!entry) return;
|
|
4014
|
+
const currentWidth = entry.contentRect.width;
|
|
4015
|
+
// Only recalculate if width actually changed (prevents excessive calculations)
|
|
4016
|
+
Math.abs(currentWidth - lastWidth) > 1 && (animationFrame && cancelAnimationFrame(animationFrame),
|
|
4017
|
+
animationFrame = requestAnimationFrame((() => {
|
|
4018
|
+
calculateLayout(), lastWidth = currentWidth;
|
|
4019
|
+
})));
|
|
4117
4020
|
}));
|
|
4118
4021
|
return observer.observe(containerRef.current), () => {
|
|
4119
4022
|
observer.disconnect(), animationFrame && cancelAnimationFrame(animationFrame);
|
|
@@ -4124,24 +4027,21 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
|
|
|
4124
4027
|
setLayoutComplete(!0), void setLoadingImages(!1))
|
|
4125
4028
|
// Only reset layoutComplete when items or columns change
|
|
4126
4029
|
), [ items, columns, calculateLayout, imagesLoaded, trackImages ]),
|
|
4127
|
-
// ===
|
|
4030
|
+
// === ADD RESIZEOBSERVERS TO GRID ITEMS FOR DYNAMIC CONTENT MEASUREMENT ===
|
|
4128
4031
|
React.useEffect((() => {
|
|
4129
|
-
// Clean up old observers if items ever change
|
|
4130
4032
|
const observers = [];
|
|
4033
|
+
let animationFrame = null;
|
|
4034
|
+
// Debounced layout calculation for item resize events
|
|
4035
|
+
const debouncedCalculateLayout = () => {
|
|
4036
|
+
animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame(calculateLayout);
|
|
4037
|
+
};
|
|
4131
4038
|
return items.forEach((item => {
|
|
4132
4039
|
if (item.ref.current) {
|
|
4133
|
-
const obs = new ResizeObserver(
|
|
4134
|
-
// Double rAF: ensures layout only runs after DOM/paint/async renders
|
|
4135
|
-
requestAnimationFrame((() => {
|
|
4136
|
-
requestAnimationFrame((() => {
|
|
4137
|
-
calculateLayout();
|
|
4138
|
-
}));
|
|
4139
|
-
}));
|
|
4140
|
-
}));
|
|
4040
|
+
const obs = new ResizeObserver(debouncedCalculateLayout);
|
|
4141
4041
|
obs.observe(item.ref.current), observers.push(obs);
|
|
4142
4042
|
}
|
|
4143
4043
|
})), () => {
|
|
4144
|
-
observers.forEach((obs => obs.disconnect()));
|
|
4044
|
+
observers.forEach((obs => obs.disconnect())), animationFrame && cancelAnimationFrame(animationFrame);
|
|
4145
4045
|
};
|
|
4146
4046
|
}), [ items, calculateLayout ]);
|
|
4147
4047
|
// Ensure loadingImages state resets when items/columns/imagesLoaded change
|
|
@@ -4452,9 +4352,48 @@ class ThemeNaming {
|
|
|
4452
4352
|
}
|
|
4453
4353
|
}
|
|
4454
4354
|
|
|
4355
|
+
/**
|
|
4356
|
+
* Render a slot with the given props
|
|
4357
|
+
*
|
|
4358
|
+
* Priority order:
|
|
4359
|
+
* 1. render function
|
|
4360
|
+
* 2. component
|
|
4361
|
+
* 3. children
|
|
4362
|
+
* 4. fallback
|
|
4363
|
+
*
|
|
4364
|
+
* @example
|
|
4365
|
+
* renderSlot(
|
|
4366
|
+
* { render: (props) => <CustomButton {...props} /> },
|
|
4367
|
+
* { onClick: handleClick, children: 'Click me' }
|
|
4368
|
+
* )
|
|
4369
|
+
*/
|
|
4370
|
+
function renderSlot(slot, props, fallback) {
|
|
4371
|
+
// No slot provided, use fallback
|
|
4372
|
+
if (!slot) return fallback;
|
|
4373
|
+
// Slot is a plain React node
|
|
4374
|
+
if ( React.isValidElement(slot) || "string" == typeof slot || "number" == typeof slot) return slot;
|
|
4375
|
+
// Slot is an object with rendering options
|
|
4376
|
+
if ("object" == typeof slot && null !== slot) {
|
|
4377
|
+
const slotObj = slot;
|
|
4378
|
+
// Priority 1: render function
|
|
4379
|
+
if (slotObj.render && "function" == typeof slotObj.render) return slotObj.render(props);
|
|
4380
|
+
// Priority 2: component
|
|
4381
|
+
if (slotObj.component) {
|
|
4382
|
+
const Component = slotObj.component;
|
|
4383
|
+
return jsx(Component, {
|
|
4384
|
+
...props
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
// Priority 3: children
|
|
4388
|
+
if (void 0 !== slotObj.children) return slotObj.children;
|
|
4389
|
+
}
|
|
4390
|
+
// Fallback
|
|
4391
|
+
return fallback;
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4455
4394
|
ThemeNaming.prefix = "atomix";
|
|
4456
4395
|
|
|
4457
|
-
const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, ...props}, ref) => {
|
|
4396
|
+
const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, slots: slots, ...props}, ref) => {
|
|
4458
4397
|
const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsx(Icon, {
|
|
4459
4398
|
name: iconName,
|
|
4460
4399
|
size: iconSize
|
|
@@ -4470,17 +4409,28 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
|
|
|
4470
4409
|
children: [ loading && jsx("span", {
|
|
4471
4410
|
className: ThemeNaming.bemClass("btn", "spinner"),
|
|
4472
4411
|
"aria-hidden": "true",
|
|
4473
|
-
children:
|
|
4412
|
+
children: renderSlot(slots?.spinner, {
|
|
4413
|
+
className: ThemeNaming.bemClass("btn", "spinner"),
|
|
4474
4414
|
size: spinnerSize,
|
|
4475
4415
|
variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
|
|
4476
|
-
}
|
|
4416
|
+
}, jsx(Spinner, {
|
|
4417
|
+
size: spinnerSize,
|
|
4418
|
+
variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
|
|
4419
|
+
}))
|
|
4477
4420
|
}), iconElement && !loading && jsx("span", {
|
|
4478
4421
|
className: ThemeNaming.bemClass("btn", "icon"),
|
|
4479
4422
|
"aria-hidden": "true",
|
|
4480
|
-
children:
|
|
4423
|
+
children: renderSlot(slots?.icon, {
|
|
4424
|
+
className: ThemeNaming.bemClass("btn", "icon"),
|
|
4425
|
+
children: iconElement,
|
|
4426
|
+
size: iconSize
|
|
4427
|
+
}, iconElement)
|
|
4481
4428
|
}), !iconOnly && buttonText && jsx("span", {
|
|
4482
4429
|
className: ThemeNaming.bemClass("btn", "label"),
|
|
4483
|
-
children:
|
|
4430
|
+
children: renderSlot(slots?.label, {
|
|
4431
|
+
className: ThemeNaming.bemClass("btn", "label"),
|
|
4432
|
+
children: buttonText
|
|
4433
|
+
}, buttonText)
|
|
4484
4434
|
}) ]
|
|
4485
4435
|
}), buttonProps = {
|
|
4486
4436
|
className: buttonClass,
|
|
@@ -4497,48 +4447,59 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
|
|
|
4497
4447
|
tabIndex: void 0 !== tabIndex ? tabIndex : isDisabled ? -1 : 0,
|
|
4498
4448
|
style: style,
|
|
4499
4449
|
...props
|
|
4500
|
-
}
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4450
|
+
}, buttonChildren = renderSlot(slots?.root, {
|
|
4451
|
+
className: buttonClass,
|
|
4452
|
+
children: buttonContent,
|
|
4453
|
+
disabled: isDisabled,
|
|
4454
|
+
loading: loading,
|
|
4455
|
+
onClick: handleClickEvent,
|
|
4456
|
+
type: type,
|
|
4457
|
+
"aria-label": safeAriaLabel,
|
|
4458
|
+
"aria-disabled": isDisabled,
|
|
4459
|
+
"aria-busy": loading
|
|
4460
|
+
}, (() => {
|
|
4461
|
+
// Render as anchor if href is provided
|
|
4462
|
+
if (shouldRenderAsLink) {
|
|
4463
|
+
// Use custom linkComponent if provided (e.g., Next.js Link)
|
|
4464
|
+
if (linkComponent) {
|
|
4465
|
+
const LinkComp = linkComponent, linkProps = {
|
|
4466
|
+
...buttonProps,
|
|
4467
|
+
ref: ref,
|
|
4468
|
+
// linkComponent usually forwards ref to anchor
|
|
4469
|
+
href: isDisabled ? void 0 : href,
|
|
4470
|
+
to: isDisabled ? void 0 : href,
|
|
4471
|
+
target: target,
|
|
4472
|
+
rel: "_blank" === target ? "noopener noreferrer" : void 0
|
|
4473
|
+
};
|
|
4474
|
+
return jsx(LinkComp, {
|
|
4475
|
+
...linkProps,
|
|
4476
|
+
children: buttonContent
|
|
4477
|
+
});
|
|
4478
|
+
}
|
|
4479
|
+
// Fallback to regular anchor tag
|
|
4480
|
+
return jsx("a", {
|
|
4481
|
+
...buttonProps,
|
|
4482
|
+
ref: ref,
|
|
4483
|
+
href: isDisabled ? void 0 : href,
|
|
4484
|
+
target: target,
|
|
4485
|
+
rel: "_blank" === target ? "noopener noreferrer" : void 0,
|
|
4486
|
+
children: buttonContent
|
|
4487
|
+
});
|
|
4488
|
+
}
|
|
4489
|
+
// Default button rendering
|
|
4490
|
+
return jsx(Component, {
|
|
4511
4491
|
...buttonProps,
|
|
4512
4492
|
ref: ref,
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
to: isDisabled ? void 0 : href,
|
|
4516
|
-
target: target,
|
|
4517
|
-
rel: "_blank" === target ? "noopener noreferrer" : void 0
|
|
4518
|
-
};
|
|
4519
|
-
content = jsx(LinkComp, {
|
|
4520
|
-
...linkProps,
|
|
4493
|
+
type: "button" === Component ? type : void 0,
|
|
4494
|
+
disabled: isDisabled,
|
|
4521
4495
|
children: buttonContent
|
|
4522
4496
|
});
|
|
4523
|
-
}
|
|
4524
|
-
//
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
target: target,
|
|
4530
|
-
rel: "_blank" === target ? "noopener noreferrer" : void 0,
|
|
4531
|
-
children: buttonContent
|
|
4532
|
-
}); else
|
|
4533
|
-
// Default button rendering
|
|
4534
|
-
content = jsx(Component, {
|
|
4535
|
-
...buttonProps,
|
|
4536
|
-
ref: ref,
|
|
4537
|
-
type: "button" === Component ? type : void 0,
|
|
4538
|
-
disabled: isDisabled,
|
|
4539
|
-
children: buttonContent
|
|
4540
|
-
});
|
|
4541
|
-
if (glass) {
|
|
4497
|
+
})());
|
|
4498
|
+
// Determine if we should render as a link
|
|
4499
|
+
// If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
|
|
4500
|
+
// The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
|
|
4501
|
+
// This is a safe fallback for disabled links.
|
|
4502
|
+
if (glass) {
|
|
4542
4503
|
// Default glass props
|
|
4543
4504
|
const defaultGlassProps = {
|
|
4544
4505
|
displacementScale: 20,
|
|
@@ -4551,10 +4512,10 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
|
|
|
4551
4512
|
};
|
|
4552
4513
|
return jsx(AtomixGlass, {
|
|
4553
4514
|
...glassProps,
|
|
4554
|
-
children:
|
|
4515
|
+
children: buttonChildren
|
|
4555
4516
|
});
|
|
4556
4517
|
}
|
|
4557
|
-
return
|
|
4518
|
+
return buttonChildren;
|
|
4558
4519
|
})));
|
|
4559
4520
|
|
|
4560
4521
|
Button.displayName = "Button";
|
|
@@ -5093,7 +5054,7 @@ useEffect((() => {
|
|
|
5093
5054
|
}
|
|
5094
5055
|
};
|
|
5095
5056
|
}));
|
|
5096
|
-
}), [
|
|
5057
|
+
}), [ currentIndex, calculateBounds, constrainPosition ]), setImagePosition = useCallback((position => {
|
|
5097
5058
|
setImageStates((prev => {
|
|
5098
5059
|
const currentState = prev[currentIndex] || {
|
|
5099
5060
|
zoomLevel: 1,
|
|
@@ -5143,7 +5104,7 @@ useEffect((() => {
|
|
|
5143
5104
|
}
|
|
5144
5105
|
};
|
|
5145
5106
|
}));
|
|
5146
|
-
}), [
|
|
5107
|
+
}), [ currentIndex, calculateBounds, constrainPosition ]), handleWheel = useCallback((event => {
|
|
5147
5108
|
var _context;
|
|
5148
5109
|
if (!isMounted || !event || !event.currentTarget) return;
|
|
5149
5110
|
// Additional safety check for the target element
|
|
@@ -5500,7 +5461,7 @@ useEffect((() => {
|
|
|
5500
5461
|
}
|
|
5501
5462
|
return prev;
|
|
5502
5463
|
}));
|
|
5503
|
-
}), [
|
|
5464
|
+
}), [ enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = useCallback((() => {
|
|
5504
5465
|
setIsDragging(!1), lastDistanceRef.current = null, lastMidpointRef.current = null;
|
|
5505
5466
|
}), []), currentState = imageStates[currentIndex] || {
|
|
5506
5467
|
zoomLevel: 1,
|