aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
269 lines (266 loc) • 10.2 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import { Minus, ArrowDownIcon, ArrowUpIcon } from 'lucide-react';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import { MotionFramer } from '../../primitives/motion/MotionFramer.js';
import { CardHeader, CardTitle, CardContent } from '../card/index.js';
import { GlassCard } from '../card/GlassCard.js';
/**
* GlassKPICard component
* A glassmorphism KPI card for displaying key performance indicators
*/
const GlassKPICard = ({
// TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance
title = "KPI",
value = "--",
unit = "",
previousValue,
trend = "none",
trendPercentage,
description,
icon,
variant = "default",
size = "md",
showTrend = true,
trendLabel,
formatValue,
children,
loading = false,
onClick,
className,
...props
}) => {
// Size configurations
const sizeConfigs = {
sm: {
cardClass: "glass-p-4",
titleClass: "glass-text-sm font-medium",
valueClass: "glass-text-2xl font-bold",
iconSize: "w-6 h-6",
trendIconSize: "w-4 h-4",
trendTextSize: "glass-text-xs"
},
md: {
cardClass: "glass-p-6",
titleClass: "glass-text-base font-medium",
valueClass: "text-3xl font-bold",
iconSize: "w-8 h-8",
trendIconSize: "w-5 h-5",
trendTextSize: "glass-text-sm"
},
lg: {
cardClass: "p-8",
titleClass: "glass-text-lg font-semibold",
valueClass: "text-4xl font-bold",
iconSize: "w-10 h-10",
trendIconSize: "w-6 h-6",
trendTextSize: "glass-text-base"
},
xl: {
cardClass: "glass-p-10",
titleClass: "glass-text-xl font-semibold",
valueClass: "text-5xl font-bold",
iconSize: "w-12 h-12",
trendIconSize: "w-7 h-7",
trendTextSize: "glass-text-lg"
}
};
const config = sizeConfigs[size] ?? sizeConfigs.md;
// Variant color configurations
const variantConfigs = {
default: {
iconColor: "glass-text-primary/70",
trendUpColor: "text-green-400",
trendDownColor: "text-red-400",
trendNeutralColor: "text-yellow-400",
valueColor: "glass-text-primary"
},
success: {
iconColor: "text-green-400",
trendUpColor: "text-green-300",
trendDownColor: "text-red-400",
trendNeutralColor: "text-yellow-400",
valueColor: "text-green-200"
},
warning: {
iconColor: "text-yellow-400",
trendUpColor: "text-green-400",
trendDownColor: "text-red-400",
trendNeutralColor: "text-yellow-300",
valueColor: "text-yellow-200"
},
error: {
iconColor: "text-red-400",
trendUpColor: "text-red-400",
trendDownColor: "text-red-300",
trendNeutralColor: "text-yellow-400",
valueColor: "text-red-200"
},
info: {
iconColor: "text-blue-400",
trendUpColor: "text-green-400",
trendDownColor: "text-red-400",
trendNeutralColor: "text-yellow-400",
valueColor: "text-blue-200"
},
primary: {
iconColor: "text-primary",
trendUpColor: "text-green-400",
trendDownColor: "text-red-400",
trendNeutralColor: "text-yellow-400",
valueColor: "text-primary-foreground"
}
};
const variantConfig = variantConfigs[variant] ?? variantConfigs.default;
// Format the display value
const displayValue = formatValue ? formatValue(value ?? "--") : String(value ?? "--");
// Calculate trend information
const getTrendInfo = () => {
if (trend === "none" || !showTrend) return null;
let trendIcon;
let trendColor;
let trendText = "";
switch (trend) {
case "up":
trendIcon = jsx(ArrowUpIcon, {
className: cn(config.trendIconSize, variantConfig.trendUpColor)
});
trendColor = variantConfig.trendUpColor;
break;
case "down":
trendIcon = jsx(ArrowDownIcon, {
className: cn(config.trendIconSize, variantConfig.trendDownColor)
});
trendColor = variantConfig.trendDownColor;
break;
case "neutral":
trendIcon = jsx(Minus, {
className: cn(config.trendIconSize, variantConfig.trendNeutralColor)
});
trendColor = variantConfig.trendNeutralColor;
break;
}
if (trendPercentage !== undefined) {
trendText = `${trend === "up" ? "+" : trend === "down" ? "-" : ""}${Math.abs(trendPercentage)}%`;
} else if (trendLabel) {
trendText = trendLabel;
}
return {
trendIcon,
trendColor,
trendText
};
};
const trendInfo = getTrendInfo();
// Loading skeleton
if (loading) {
return jsx(GlassCard, {
"data-glass-component": true,
className: cn("animate-pulse", config.cardClass, className),
children: jsxs("div", {
className: "glass-gap-4",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("div", {
className: 'h-4 glass-surface-subtle/20 glass-radius-md w-24'
}), jsx("div", {
className: 'w-8 h-8 glass-surface-subtle/20 glass-radius-md'
})]
}), jsxs("div", {
className: "glass-gap-2",
children: [jsx("div", {
className: 'h-8 glass-surface-subtle/20 glass-radius-md w-32'
}), jsx("div", {
className: 'h-4 glass-surface-subtle/20 glass-radius-md w-20'
})]
})]
})
});
}
return jsx(MotionFramer, {
preset: "fadeIn",
className: "glass-w-full glass-kpi-card",
children: jsxs(GlassCard, {
elevation: "level2",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
hoverable: !!onClick,
clickable: !!onClick,
onClick: onClick,
className: cn(config.cardClass, "group relative overflow-hidden", onClick && ["cursor-pointer", "hover:shadow-2xl hover:shadow-purple-500/20", "transition-all duration-500 ease-out"], className),
...props,
children: [jsxs(CardHeader, {
className: 'pb-2',
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsxs(CardTitle, {
className: cn(config.titleClass, "glass-text-primary/90 flex items-center glass-gap-2"),
children: [icon && jsxs("div", {
className: cn("inline-flex items-center justify-center glass-radius-lg glass-p-2 mr-3", "bg-gradient-to-br from-white/10 to-white/5 border border-white/20", "group-hover:from-white/15 group-hover:to-white/8", "group-hover:border-white/30 group-hover:shadow-lg", "group-hover:scale-110 group-hover:rotate-2", "transition-all duration-300 ease-out", variantConfig.iconColor),
children: [jsx("div", {
className: 'absolute inset-0 glass-gradient-primary glass-gradient-primary glass-gradient-primary glass-radius-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300'
}), jsx("span", {
className: cn(config.iconSize, "relative z-10 transition-transform duration-300 group-hover:scale-110"),
children: icon
})]
}), title]
}), trendInfo && trendInfo.trendIcon && jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-1 trend-indicator',
children: [jsx("div", {
className: 'glass-p-1 glass-radius-md glass-surface-subtle/10 group-hover:glass-surface-subtle/20 transition-colors duration-300',
children: jsx("span", {
"data-icon": true,
className: 'transition-all duration-300',
children: trendInfo.trendIcon
})
}), trendInfo.trendText && jsx("span", {
className: cn(config.trendTextSize, trendInfo.trendColor, "font-medium transition-all duration-300", "group-hover:scale-105 group-hover:brightness-110"),
children: trendInfo.trendText
})]
})]
}), description && jsx("p", {
className: 'glass-text-sm text-primary/60 glass-mt-1',
children: description
})]
}), jsxs(CardContent, {
className: 'pt-0',
children: [jsxs("div", {
className: 'glass-flex items-baseline glass-gap-2 relative',
children: [jsxs("div", {
className: 'relative premium-glow',
children: [jsx("div", {
className: 'absolute inset-0 glass-gradient-primary glass-gradient-primary via-purple-400/30 glass-gradient-primary glass-blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 -z-10 scale-150'
}), jsx("span", {
"data-value": true,
className: cn(config.valueClass, variantConfig.valueColor, "relative z-10 font-bold tracking-tight", "group-hover:glass-text-primary transition-all duration-300", "drop-shadow-sm group-hover:drop-shadow-lg"),
children: displayValue
})]
}), unit && jsx("span", {
className: cn("glass-text-lg glass-text-primary/70 font-medium", "group-hover:glass-text-primary/90 transition-colors duration-300"),
children: unit
})]
}), previousValue && jsx("div", {
className: "glass-mt-2",
children: jsxs("span", {
className: 'glass-text-sm text-primary/50',
children: ["Previous:", " ", formatValue ? formatValue(previousValue) : String(previousValue), unit]
})
}), children && jsx("div", {
className: "glass-mt-4",
children: children
})]
})]
})
});
};
export { GlassKPICard, GlassKPICard as default };
//# sourceMappingURL=GlassKPICard.js.map