aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
267 lines (264 loc) • 8.68 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { forwardRef, useState, useEffect } from 'react';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import '../../primitives/motion/MotionFramer.js';
import { cn } from '../../lib/utilsComprehensive.js';
import { useA11yId } from '../../utils/a11y.js';
import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js';
/**
* GlassProgress component
* A glassmorphism progress bar with various styles and animations
*/
const GlassProgress = /*#__PURE__*/forwardRef(({
// TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance
value = 0,
max = 100,
variant = "default",
size = "md",
shape = "glass-radius-md",
showValue = false,
formatValue,
indeterminate = false,
animated = true,
animationDuration = 500,
striped = false,
label,
labelPosition = "top",
respectMotionPreference = true,
className,
...props
}, ref) => {
const progressId = useA11yId("progress");
const {
prefersReducedMotion
} = useMotionPreferenceContext();
const [animatedValue, setAnimatedValue] = useState(0);
const [mounted, setMounted] = useState(false);
const shouldAnimate = animated && (!respectMotionPreference || !prefersReducedMotion);
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
if (!mounted || indeterminate) return;
if (shouldAnimate) {
const timer = setTimeout(() => {
setAnimatedValue(value);
}, 100);
return () => clearTimeout(timer);
} else {
setAnimatedValue(value);
}
}, [value, animated, mounted, indeterminate]);
const percentage = indeterminate ? 0 : Math.min(Math.max(animatedValue / max * 100, 0), 100);
const sizeClasses = {
xs: "h-1",
sm: "h-2",
md: "h-3",
lg: "h-4",
xl: "h-6"
};
const shapeClasses = {
"glass-radius-md": "glass-radius-md",
pill: "glass-radius-full",
square: "rounded-none"
};
const variantClasses = {
default: "glass-surface-primary",
success: "glass-surface-success",
warning: "glass-surface-warning",
error: "glass-surface-danger",
gradient: "bg-gradient-to-r from-primary via-secondary to-accent",
primary: "glass-surface-primary"
};
const trackClasses = cn("relative w-full overflow-hidden", "bg-muted/30 glass-backdrop-blur-md", sizeClasses[size], shapeClasses[shape]);
const fillClasses = cn("h-full transition-all ease-out relative", variantClasses[variant], {
"bg-stripes": striped,
"animate-pulse": indeterminate,
"animate-progress-indeterminate": indeterminate
});
const formatDisplayValue = () => {
if (formatValue) {
return formatValue(value, max);
}
return `${Math.round(percentage)}%`;
};
const getLabelContent = () => {
if (label) return label;
if (showValue) return formatDisplayValue();
return null;
};
const labelContent = getLabelContent();
return jsxs("div", {
ref: ref,
className: cn("w-full", className),
...props,
children: [labelContent && labelPosition === "top" && jsxs("div", {
id: `${progressId}-label`,
className: 'glass-flex glass-justify-between glass-items-center mb-2',
children: [jsx("span", {
className: 'glass-text-sm font-medium text-primary',
children: label
}), showValue && jsx("span", {
className: "glass-text-sm glass-text-secondary",
children: formatDisplayValue()
})]
}), jsx(OptimizedGlassCore, {
elevation: undefined,
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
id: progressId,
className: trackClasses,
role: "progressbar",
"aria-valuenow": indeterminate ? undefined : value,
"aria-valuemin": 0,
"aria-valuemax": max,
"aria-label": label || "Progress",
"aria-describedby": labelContent ? `${progressId}-label` : undefined,
children: jsxs("div", {
className: fillClasses,
style: {
width: indeterminate ? "100%" : `${percentage}%`,
transitionDuration: shouldAnimate ? `${animationDuration}ms` : "0ms"
},
children: [jsx("div", {
className: 'pointer-events-none absolute inset-0 glass-sheen'
}), labelContent && labelPosition === "inline" && jsx("div", {
className: "glass-flex glass-items-center glass-justify-center glass-h-full glass-px-2",
children: jsx("span", {
className: 'glass-text-xs font-medium text-primary mix-blend-difference',
children: showValue ? formatDisplayValue() : label
})
}), striped && jsx("div", {
className: 'absolute inset-0 bg-stripes opacity-20'
})]
})
}), labelContent && labelPosition === "bottom" && jsxs("div", {
id: `${progressId}-label`,
className: "glass-flex glass-justify-between glass-items-center glass-mt-2",
children: [jsx("span", {
className: 'glass-text-sm font-medium text-primary',
children: label
}), showValue && jsx("span", {
className: "glass-text-sm glass-text-secondary",
children: formatDisplayValue()
})]
})]
});
});
GlassProgress.displayName = "GlassProgress";
const CircularProgress = /*#__PURE__*/forwardRef(({
value = 0,
max = 100,
variant = "default",
size = "md",
strokeWidth,
indeterminate = false,
showValue = false,
children,
className,
...props
}, ref) => {
const [animatedValue, setAnimatedValue] = useState(0);
useEffect(() => {
if (indeterminate) return;
const timer = setTimeout(() => {
setAnimatedValue(value);
}, 100);
return () => clearTimeout(timer);
}, [value, indeterminate]);
const sizeConfig = {
sm: {
size: 40,
stroke: strokeWidth || 3
},
md: {
size: 60,
stroke: strokeWidth || 4
},
lg: {
size: 80,
stroke: strokeWidth || 5
},
xl: {
size: 120,
stroke: strokeWidth || 6
}
};
const config = sizeConfig[size];
const radius = (config.size - config.stroke) / 2;
const circumference = radius * 2 * Math.PI;
const percentage = Math.min(Math.max(animatedValue / max * 100, 0), 100);
const strokeDashoffset = circumference - percentage / 100 * circumference;
const variantColors = {
default: "stroke-primary",
success: "stroke-success",
warning: "stroke-warning",
error: "stroke-destructive"
};
return jsxs("div", {
ref: ref,
className: cn("relative inline-flex items-center justify-center", className),
style: {
width: config.size,
height: config.size
},
...props,
children: [jsxs("svg", {
className: 'transform -rotate-90',
width: config.size,
height: config.size,
children: [jsx("circle", {
cx: config.size / 2,
cy: config.size / 2,
r: radius,
stroke: "currentColor",
strokeWidth: config.stroke,
fill: "transparent",
className: 'text-muted/20'
}), jsx("circle", {
cx: config.size / 2,
cy: config.size / 2,
r: radius,
stroke: "currentColor",
strokeWidth: config.stroke,
fill: "transparent",
strokeDasharray: circumference,
strokeDashoffset: indeterminate ? circumference * 0.25 : strokeDashoffset,
strokeLinecap: "round",
className: cn("transition-all duration-500 ease-out", variantColors[variant], {
"animate-spin": indeterminate
}),
style: {
transformOrigin: "50% 50%"
}
}), !indeterminate && jsx("circle", {
cx: config.size / 2,
cy: config.size / 2,
r: radius,
strokeWidth: config.stroke,
fill: "transparent",
strokeDasharray: circumference,
strokeDashoffset: strokeDashoffset,
className: 'stroke-white/10'
})]
}), jsx("div", {
className: 'absolute inset-0 glass-flex glass-items-center glass-justify-center',
children: children || showValue && jsxs("span", {
className: 'glass-text-sm font-medium text-primary',
children: [Math.round(percentage), "%"]
})
})]
});
});
CircularProgress.displayName = "CircularProgress";
export { CircularProgress, GlassProgress };
//# sourceMappingURL=GlassProgress.js.map