aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
760 lines (757 loc) • 27.9 kB
JavaScript
'use client';
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import React, { memo, useState, useRef, useCallback, useEffect } from 'react';
import { useMotionAwareAnimation, useAnimationDuration } from '../../hooks/useMotionPreference.js';
import { cn } from '../../lib/utilsComprehensive.js';
import { useMotionValue, useSpring, useTransform, motion } from 'framer-motion';
// Memoized icons for handle indicators to prevent unnecessary re-renders
const ChevronLeftIcon = /*#__PURE__*/memo(() => jsx("svg", {
className: 'w-3 h-3',
fill: "currentColor",
viewBox: "0 0 20 20",
children: jsx("path", {
fillRule: "evenodd",
d: "M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z",
clipRule: "evenodd"
})
}));
const ChevronRightIcon = /*#__PURE__*/memo(() => jsx("svg", {
className: 'w-3 h-3',
fill: "currentColor",
viewBox: "0 0 20 20",
children: jsx("path", {
fillRule: "evenodd",
d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
clipRule: "evenodd"
})
}));
const ChevronUpIcon = /*#__PURE__*/memo(() => jsx("svg", {
className: 'w-3 h-3',
fill: "currentColor",
viewBox: "0 0 20 20",
children: jsx("path", {
fillRule: "evenodd",
d: "M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z",
clipRule: "evenodd"
})
}));
const ChevronDownIcon = /*#__PURE__*/memo(() => jsx("svg", {
className: 'w-3 h-3',
fill: "currentColor",
viewBox: "0 0 20 20",
children: jsx("path", {
fillRule: "evenodd",
d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",
clipRule: "evenodd"
})
}));
// Preset positions for common comparisons
const SLIDER_PRESETS = {
BEFORE_ONLY: 0,
QUARTER: 25,
HALF: 50,
THREE_QUARTER: 75,
AFTER_ONLY: 100
};
const GlassWipeSliderComponent = ({
beforeContent,
afterContent,
className,
initialPosition = 50,
orientation = "horizontal",
enableSnapping = true,
snapThreshold = 5,
enableMomentum = true,
momentumMultiplier = 0.3,
debounceMs = 16,
handleSize = "md",
showLabels = true,
showProgress = true,
showMetrics = false,
labels = {
before: "Before",
after: "After"
},
metrics = [],
trackStyle = "default",
gradientOverlay = true,
height = "24rem",
// h-96
minHeight,
onPositionChange,
onSnapToPreset,
onDragStart,
onDragEnd
}) => {
// Motion-aware hooks
const {
getAnimationProps,
getTransition
} = useMotionAwareAnimation();
const {
duration
} = useAnimationDuration(200);
// State management
const [position, setPosition] = useState(initialPosition);
const [isDragging, setIsDragging] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [velocity, setVelocity] = useState(0);
// Refs
const containerRef = useRef(null);
const handleRef = useRef(null);
const lastUpdateTime = useRef(Date.now());
const lastPosition = useRef(initialPosition);
const animationFrame = useRef(null);
const debounceTimeout = useRef(null);
// Memoized motion configuration to prevent unnecessary spring recreations
const springConfig = {
stiffness: 300,
damping: 30
};
// Motion values for smooth animations
const motionX = useMotionValue(initialPosition);
const motionY = useMotionValue(initialPosition);
const springX = useSpring(motionX, springConfig);
const springY = useSpring(motionY, springConfig);
useTransform(orientation === "horizontal" ? springX : springY, [0, 100], ["translateX(-50%) translateY(-50%)", "translateX(-50%) translateY(-50%)"]);
// Memoized preset values to prevent recreation on every render
const presetValues = React.useMemo(() => Object.values(SLIDER_PRESETS), []);
// Optimized snap to preset positions
const snapToNearestPreset = useCallback(currentPos => {
if (!enableSnapping) return currentPos;
const nearest = presetValues.reduce((prev, curr) => Math.abs(curr - currentPos) < Math.abs(prev - currentPos) ? curr : prev);
if (Math.abs(nearest - currentPos) <= snapThreshold) {
onSnapToPreset?.(nearest);
return nearest;
}
return currentPos;
}, [enableSnapping, snapThreshold, onSnapToPreset, presetValues]);
// Apply momentum when drag ends
const applyMomentum = useCallback(() => {
if (!enableMomentum || Math.abs(velocity) < 0.1) return;
const startPos = position;
const targetPos = Math.max(0, Math.min(100, position + velocity * momentumMultiplier * 10));
const snappedPos = snapToNearestPreset(targetPos);
// Animate to final position
const startTime = Date.now();
const duration = Math.min(800, Math.abs(snappedPos - startPos) * 20);
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing function (ease-out cubic)
const easedProgress = 1 - Math.pow(1 - progress, 3);
const currentPos = startPos + (snappedPos - startPos) * easedProgress;
setPosition(currentPos);
motionX.set(currentPos);
motionY.set(currentPos);
if (progress < 1) {
animationFrame.current = requestAnimationFrame(animate);
} else {
setVelocity(0);
}
};
animationFrame.current = requestAnimationFrame(animate);
}, [position, velocity, enableMomentum, momentumMultiplier, snapToNearestPreset]);
const handleMouseDown = e => {
e.preventDefault();
setIsDragging(true);
onDragStart?.();
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
}
const clientPos = orientation === "horizontal" ? e.clientX : e.clientY;
handleMove(clientPos);
// Reset velocity tracking
lastUpdateTime.current = Date.now();
lastPosition.current = position;
};
// Optimized handle move with better performance
const handleMove = useCallback(clientPos => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const size = orientation === "horizontal" ? rect.width : rect.height;
const offset = orientation === "horizontal" ? clientPos - rect.left : clientPos - rect.top;
const newPosition = offset / size * 100;
const clampedPosition = Math.max(0, Math.min(100, newPosition));
// Calculate velocity for momentum (optimized)
const now = Date.now();
const timeDelta = now - lastUpdateTime.current;
if (timeDelta > 0) {
const positionDelta = clampedPosition - lastPosition.current;
setVelocity(positionDelta / timeDelta);
lastUpdateTime.current = now;
lastPosition.current = clampedPosition;
}
setPosition(clampedPosition);
motionX.set(clampedPosition);
motionY.set(clampedPosition);
// Debounced callback with cleanup
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
debounceTimeout.current = setTimeout(() => {
onPositionChange?.(clampedPosition);
}, debounceMs);
}, [orientation, onPositionChange, debounceMs]);
useEffect(() => {
const handleMouseMove = e => {
if (isDragging) {
const clientPos = orientation === "horizontal" ? e.clientX : e.clientY;
handleMove(clientPos);
}
};
const handleMouseUp = () => {
if (isDragging) {
setIsDragging(false);
onDragEnd?.();
// Apply momentum and snapping
setTimeout(() => {
applyMomentum();
}, 0);
}
};
const handleTouchMove = e => {
if (isDragging && e.touches?.[0]) {
e.preventDefault();
const touch = e.touches?.[0];
const clientPos = orientation === "horizontal" ? touch.clientX : touch.clientY;
handleMove(clientPos);
}
};
if (isDragging) {
document.addEventListener("mousemove", handleMouseMove, {
passive: false
});
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener("touchmove", handleTouchMove, {
passive: false
});
document.addEventListener("touchend", handleMouseUp);
}
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
document.removeEventListener("touchmove", handleTouchMove);
document.removeEventListener("touchend", handleMouseUp);
};
}, [isDragging, handleMove, orientation, applyMomentum, onDragEnd]);
const handleTouchStart = e => {
e.preventDefault();
setIsDragging(true);
onDragStart?.();
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
}
if (e.touches?.[0]) {
const touch = e.touches?.[0];
const clientPos = orientation === "horizontal" ? touch.clientX : touch.clientY;
handleMove(clientPos);
// Reset velocity tracking
lastUpdateTime.current = Date.now();
lastPosition.current = position;
}
};
const handleKeyDown = e => {
const step = e.shiftKey ? 10 : e.ctrlKey || e.metaKey ? 25 : 1;
let newPosition = position;
let shouldSnap = false;
switch (e.key) {
case "ArrowLeft":
case "ArrowUp":
e.preventDefault();
newPosition = Math.max(0, position - step);
break;
case "ArrowRight":
case "ArrowDown":
e.preventDefault();
newPosition = Math.min(100, position + step);
break;
case "Home":
e.preventDefault();
newPosition = SLIDER_PRESETS.BEFORE_ONLY;
shouldSnap = true;
break;
case "End":
e.preventDefault();
newPosition = SLIDER_PRESETS.AFTER_ONLY;
shouldSnap = true;
break;
case "PageUp":
e.preventDefault();
newPosition = SLIDER_PRESETS.QUARTER;
shouldSnap = true;
break;
case "PageDown":
e.preventDefault();
newPosition = SLIDER_PRESETS.THREE_QUARTER;
shouldSnap = true;
break;
case " ":
case "Enter":
e.preventDefault();
newPosition = SLIDER_PRESETS.HALF;
shouldSnap = true;
break;
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
e.preventDefault();
const presetIndex = parseInt(e.key);
const presetValues = Object.values(SLIDER_PRESETS);
if (presetIndex < (presetValues?.length || 0)) {
newPosition = presetValues[presetIndex];
shouldSnap = true;
}
break;
}
if (newPosition !== position) {
setPosition(newPosition);
motionX.set(newPosition);
motionY.set(newPosition);
onPositionChange?.(newPosition);
if (shouldSnap) {
onSnapToPreset?.(newPosition);
}
}
};
// Memoized handle size variants to prevent recreation
const handleSizes = React.useMemo(() => ({
sm: "w-10 h-10",
md: "w-12 h-12",
lg: "w-14 h-14"
}), []);
// Memoized dynamic styles based on props
const containerStyles = React.useMemo(() => ({
height: typeof height === "string" ? height : `${height}px`,
minHeight: minHeight ? typeof minHeight === "string" ? minHeight : `${minHeight}px` : undefined
}), [height, minHeight]);
// Clean up on unmount
useEffect(() => {
return () => {
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
}
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
};
}, []);
// Memoized computed values for better performance
const computedValues = React.useMemo(() => {
const isVertical = orientation === "vertical";
const cursorClass = isVertical ? "cursor-row-resize" : "cursor-col-resize";
const clipPath = isVertical ? `inset(${100 - position}% 0 0 0)` : `inset(0 ${100 - position}% 0 0)`;
return {
isVertical,
cursorClass,
clipPath
};
}, [orientation, position]);
const {
isVertical,
cursorClass,
clipPath
} = computedValues;
return jsxs("div", {
ref: containerRef,
className: cn("relative w-full overflow-hidden glass-radius-lg select-none glass-card-motion-aware", cursorClass, {
"h-96": !height || height === "24rem"
}, className),
style: containerStyles,
role: "slider",
"aria-valuemin": 0,
"aria-valuemax": 100,
"aria-valuenow": Math.round(position),
"aria-label": `${labels.before} vs ${labels.after} comparison slider. Use arrow keys, Home, End, or number keys 0-5 for presets.`,
"aria-describedby": showMetrics && (metrics?.length || 0) > 0 ? "slider-metrics" : undefined,
tabIndex: 0,
onKeyDown: handleKeyDown,
onMouseEnter: () => setIsHovered(true),
onMouseLeave: () => setIsHovered(false),
onFocus: () => setIsFocused(true),
onBlur: () => setIsFocused(false),
children: [jsx("div", {
className: 'absolute inset-0 glass-w-full glass-h-full',
children: beforeContent
}), jsx("div", {
className: 'absolute inset-0 overflow-hidden transition-all duration-75 ease-out',
style: {
clipPath,
...(isVertical ? {
width: "100%",
height: "100%"
} : {
width: "100%",
height: "100%"
})
},
children: afterContent
}), gradientOverlay && jsx("div", {
className: 'absolute pointer-events-none transition-opacity duration-300',
style: {
...(isVertical ? {
top: `${position}%`,
left: 0,
right: 0,
height: "20px",
transform: "translateY(-50%)",
background: '/* Use createGlassStyle({ intent: "primary", elevation: "level2" }) */'
} : {
left: `${position}%`,
top: 0,
bottom: 0,
width: "20px",
transform: "translateX(-50%)",
background: '/* Use createGlassStyle({ intent: "primary", elevation: "level2" }) */'
}),
opacity: isHovered || isDragging ? 0.5 : 0.2
}
}), jsxs("div", {
className: cn("absolute glass-backdrop-blur-md shadow-lg transition-all duration-200 ease-out", {
// Horizontal orientation
"top-0 bottom-0 bg-gradient-to-b from-white/40 via-white/60 to-white/40": !isVertical,
"w-0.5": !isVertical && trackStyle === "minimal",
"w-1": !isVertical && trackStyle === "default",
"w-2": !isVertical && trackStyle === "bold",
// Vertical orientation
"left-0 right-0 bg-gradient-to-r from-white/40 via-white/60 to-white/40": isVertical,
"h-0.5": isVertical && trackStyle === "minimal",
"h-1": isVertical && trackStyle === "default",
"h-2": isVertical && trackStyle === "bold"
}),
style: {
...(isVertical ? {
top: `${position}%`,
transform: "translateY(-50%)"
} : {
left: `${position}%`,
transform: "translateX(-50%)"
}),
boxShadow: isDragging || isHovered ? '0 0 20px ${glassStyles.borderColor || "var(--glass-color-primary, 0.6)"}' : '0 0 10px ${glassStyles.borderColor || "var(--glass-bg-hover)"}'
},
children: [jsx("div", {
className: cn("absolute inset-0", isVertical ? "bg-gradient-to-t from-transparent via-white/20 to-transparent" : "bg-gradient-to-r from-transparent via-white/20 to-transparent")
}), jsx("div", {
className: cn("absolute inset-0 transition-opacity duration-300", isVertical ? "bg-gradient-to-t from-cyan-500/20 via-blue-500/30 to-cyan-500/20" : "bg-gradient-to-r from-cyan-500/20 via-blue-500/30 to-cyan-500/20"),
style: {
opacity: isDragging || isHovered ? 0.8 : 0
}
})]
}), jsx(motion.div, {
ref: handleRef,
className: cn("absolute cursor-grab active:cursor-grabbing touch-target z-20", handleSizes?.[handleSize], {
"top-1/2 -translate-y-1/2": !isVertical,
"left-1/2 -translate-x-1/2": isVertical
}),
style: {
...(isVertical ? {
top: `${position}%`,
transform: "translateY(-50%) translateX(-50%)"
} : {
left: `${position}%`,
transform: "translateX(-50%) translateY(-50%)"
})
},
onMouseDown: handleMouseDown,
onTouchStart: handleTouchStart,
...getAnimationProps({
whileHover: {
scale: 1.1
},
whileTap: {
scale: 0.95
},
animate: {
boxShadow: isDragging || isFocused ? '0 0 30px ${glassStyles.borderColor || "var(--glass-color-primary, 0.6)"}' : isHovered ? "0 0 25px var(--glass-border-default)" : '0 0 20px ${glassStyles.borderColor || "var(--glass-bg-hover)"}'
}
}),
transition: getTransition(duration / 1000),
children: jsxs("div", {
className: 'glass-w-full glass-h-full glass-radius-full glass-foundation-complete bg-glass-gradient-strong glass-glass-backdrop-blur-md-medium glass-border glass-border-white/30 glass-flex glass-items-center glass-justify-center group relative overflow-hidden glass-contrast-guard',
children: [jsx("div", {
className: cn("flex items-center glass-gap-1 glass-text-primary/70 group-hover:glass-text-primary/90 transition-colors duration-200 relative z-10", isVertical ? "flex-col" : "flex-row"),
children: isVertical ? jsxs(Fragment, {
children: [jsx(ChevronUpIcon, {}), jsx("div", {
className: 'w-1 h-1 glass-radius-full bg-transparent opacity-50'
}), jsx(ChevronDownIcon, {})]
}) : jsxs(Fragment, {
children: [jsx(ChevronLeftIcon, {}), jsx("div", {
className: 'w-1 h-1 glass-radius-full bg-transparent opacity-50'
}), jsx(ChevronRightIcon, {})]
})
}), jsx("div", {
className: 'absolute inset-0 glass-radius-full glass-gradient-primary glass-gradient-primary glass-gradient-primary opacity-0 group-hover:opacity-100 transition-opacity duration-300'
}), jsx("div", {
className: 'absolute inset-0 glass-radius-full glass-gradient-primary glass-gradient-primary glass-gradient-primary transition-opacity duration-200',
style: {
opacity: isDragging ? 0.7 : 0
}
}), isFocused && jsx("div", {
className: 'absolute -inset-1 glass-radius-full glass-border-2 glass-border-blue/50 animate-pulse'
}), jsx("div", {
className: 'absolute inset-0 glass-radius-full glass-surface-subtle/20 scale-0 transition-transform duration-200',
style: {
transform: isDragging ? "scale(1.5)" : "scale(0)",
opacity: isDragging ? 0.3 : 0
}
})]
})
}), showLabels && jsxs(Fragment, {
children: [jsxs(motion.div, {
className: cn("absolute chip chip-muted glass-text-sm pointer-events-none z-10", isVertical ? "top-4 left-1/2 transform -translate-x-1/2" : "top-4 left-4"),
...getAnimationProps({
initial: {
opacity: 0,
scale: 0.9
},
animate: {
opacity: 1,
scale: 1
}
}),
transition: getTransition(0.3, "ease-out"),
children: [jsx("div", {
className: 'font-medium',
children: labels.after
}), labels.afterDescription && jsx("div", {
className: 'glass-text-xs text-primary/60 glass-mt-0-5',
children: labels.afterDescription
})]
}), jsxs(motion.div, {
className: cn("absolute chip chip-muted glass-text-sm pointer-events-none z-10", isVertical ? "bottom-4 left-1/2 transform -translate-x-1/2" : "top-4 right-4"),
...getAnimationProps({
initial: {
opacity: 0,
scale: 0.9
},
animate: {
opacity: 1,
scale: 1
}
}),
transition: getTransition(0.3, "ease-out"),
children: [jsx("div", {
className: 'font-medium',
children: labels.before
}), labels.beforeDescription && jsx("div", {
className: 'glass-text-xs text-primary/60 glass-mt-0-5',
children: labels.beforeDescription
})]
})]
}), showProgress && jsx(motion.div, {
className: cn("absolute chip chip-muted glass-text-xs pointer-events-none z-10", isVertical ? "bottom-4 right-4" : "bottom-4 left-1/2 transform -translate-x-1/2"),
...getAnimationProps({
initial: {
opacity: 0,
y: 10
},
animate: {
opacity: 1,
y: 0
}
}),
transition: getTransition(0.3, "ease-out"),
children: jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("div", {
className: 'font-mono font-medium',
children: [Math.round(position), "%"]
}), enableSnapping && jsx("div", {
className: 'w-1 h-1 glass-radius-full glass-surface-subtle/40'
})]
})
}), enableSnapping && jsx("div", {
className: 'absolute inset-0 pointer-events-none z-5',
children: Object.entries(SLIDER_PRESETS).map(([key, value]) => jsx("div", {
className: cn("absolute w-2 h-2 glass-radius-full bg-white/30 glass-backdrop-blur-md border border-white/20 transition-all duration-200", Math.abs(position - value) <= snapThreshold ? "glass-surface-primary/60 scale-125" : ""),
style: {
...(isVertical ? {
top: `${value}%`,
left: "50%",
transform: "translateX(-50%) translateY(-50%)"
} : {
left: `${value}%`,
top: "50%",
transform: "translateX(-50%) translateY(-50%)"
})
}
}, key))
}), showMetrics && (metrics?.length || 0) > 0 && jsx("div", {
id: "slider-metrics",
className: cn("absolute glass-foundation-complete bg-glass-gradient-strong glass-backdrop-blur-md-medium border border-white/20 glass-radius-lg glass-p-4 z-10 max-w-sm", isVertical ? "top-1/2 right-4 transform -translate-y-1/2" : "top-4 left-1/2 transform -translate-x-1/2"),
style: {
opacity: isHovered || isDragging || isFocused ? 0.95 : 0.7
},
children: jsx("div", {
className: "glass-auto-gap glass-auto-gap-sm",
children: metrics.map((metric, index) => jsxs("div", {
className: cn("flex items-center justify-between glass-text-sm", metric?.highlight ? "glass-text-primary font-semibold" : "glass-text-primary/80"),
children: [jsx("span", {
className: 'font-medium',
children: metric?.label
}), jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 font-mono',
children: [jsxs("span", {
className: "glass-text-danger",
children: [metric?.beforeValue, metric?.unit]
}), jsx("span", {
className: 'text-primary/50',
children: "\u2192"
}), jsxs("span", {
className: "glass-text-success",
children: [metric?.afterValue, metric?.unit]
})]
})]
}, index))
})
}), jsx("div", {
className: 'sr-only',
children: "Use arrow keys to adjust the comparison. Press Home for 0%, End for 100%, Space for 50%. Number keys 0-5 jump to preset positions. Shift+arrows for larger steps."
})]
});
};
// Export optimized memoized component
const GlassWipeSlider = /*#__PURE__*/memo(GlassWipeSliderComponent);
// Comparison content components for common use cases
function ComparisonImage({
src,
alt,
className,
loading = "lazy"
}) {
return jsx("img", {
src: src,
alt: alt,
width: 1200,
height: 800,
className: cn("w-full h-full object-cover transition-all duration-300", className),
loading: loading
});
}
function ComparisonContent({
children,
className,
background = "gradient"
}) {
const backgroundClasses = {
gradient: "bg-gradient-to-br from-slate-900 via-blue-900 to-slate-900",
solid: "glass-surface-primary",
glass: "glass-foundation-complete bg-glass-gradient-subtle glass-backdrop-blur-md-medium",
transparent: "bg-transparent"
};
return jsx("div", {
className: cn("w-full h-full flex items-center justify-center p-8 transition-all duration-300", backgroundClasses?.[background], className),
children: children
});
}
// Pre-built comparison layouts
function FeatureComparison({
beforeFeatures,
afterFeatures,
title,
className
}) {
return jsx(ComparisonContent, {
className: className,
background: "glass",
children: jsxs("div", {
className: 'glass-w-full max-w-md glass-auto-gap glass-auto-gap-lg',
children: [title && jsx("h3", {
className: 'glass-text-lg font-semibold text-primary text-center mb-6',
children: title
}), jsx("div", {
className: "glass-auto-gap glass-auto-gap-sm",
children: beforeFeatures.map((feature, index) => {
const afterFeature = afterFeatures?.[index];
return jsxs("div", {
className: 'glass-flex glass-items-center glass-justify-between glass-py-2 glass-px-3 surface-1',
children: [jsx("span", {
className: cn("glass-text-sm", feature.highlight ? "glass-text-primary font-medium" : "glass-text-primary/90"),
children: feature.name
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-3",
children: [jsx("div", {
className: cn("w-2 h-2 glass-radius-full", feature.available ? "glass-surface-success" : "glass-surface-danger")
}), afterFeature && jsxs(Fragment, {
children: [jsx("span", {
className: 'text-primary/40',
children: "\u2192"
}), jsx("div", {
className: cn("w-2 h-2 glass-radius-full", afterFeature.available ? "glass-surface-success" : "glass-surface-danger")
})]
})]
})]
}, feature.name);
})
})]
})
});
}
// Usage examples for AuraOne vs competitors
const AURAONE_COMPARISON_EXAMPLES = {
SCALE_AI: {
labels: {
before: "Scale AI",
after: "AuraOne",
beforeDescription: "Traditional approach",
afterDescription: "Next-gen platform"
},
metrics: [{
label: "Model Training Speed",
beforeValue: "2-4",
afterValue: "< 1",
unit: " weeks",
highlight: true
}, {
label: "Accuracy Improvement",
beforeValue: "85",
afterValue: "97",
unit: "%",
highlight: true
}, {
label: "Infrastructure Cost",
beforeValue: "$10K",
afterValue: "$2K",
unit: "/month",
highlight: true
}, {
label: "Setup Complexity",
beforeValue: "High",
afterValue: "Low",
unit: ""
}]
},
OPENAI_GYM: {
labels: {
before: "OpenAI Gym",
after: "AuraOne",
beforeDescription: "Research-focused",
afterDescription: "Production-ready"
},
metrics: [{
label: "Environment Setup",
beforeValue: "Hours",
afterValue: "Minutes",
unit: "",
highlight: true
}, {
label: "Scalability",
beforeValue: "Limited",
afterValue: "Unlimited",
unit: "",
highlight: true
}, {
label: "Production Features",
beforeValue: "Basic",
afterValue: "Enterprise",
unit: "",
highlight: true
}]
}
};
export { AURAONE_COMPARISON_EXAMPLES, ComparisonContent, ComparisonImage, FeatureComparison, GlassWipeSlider, SLIDER_PRESETS };
//# sourceMappingURL=GlassWipeSlider.js.map