UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

267 lines (264 loc) 8.68 kB
'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