style(TrackStatsDisplay): elevate visual fidelity to premium standards
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d444fb3896
commit
8e640400a1
2 changed files with 98 additions and 15 deletions
|
|
@ -4,7 +4,15 @@ import { TrackStatsDisplay } from './TrackStatsDisplay';
|
|||
const meta = {
|
||||
title: 'Components/Features/Tracks/TrackStatsDisplay',
|
||||
component: TrackStatsDisplay,
|
||||
parameters: { layout: 'centered' },
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="p-4 bg-background border border-border rounded-[var(--radius-xl)] min-h-layout-story">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} satisfies Meta<typeof TrackStatsDisplay>;
|
||||
|
||||
export default meta;
|
||||
|
|
@ -17,9 +25,26 @@ export const Horizontal: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
export const HorizontalWithLabels: Story = {
|
||||
args: {
|
||||
trackId: 1,
|
||||
variant: 'horizontal',
|
||||
showLabels: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Grid: Story = {
|
||||
args: {
|
||||
trackId: 1,
|
||||
variant: 'grid',
|
||||
},
|
||||
};
|
||||
|
||||
/** Grid + labels to validate chart tokens and transitions. */
|
||||
export const VisualStressTest: Story = {
|
||||
args: {
|
||||
trackId: 1,
|
||||
variant: 'grid',
|
||||
showLabels: true,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,27 +53,63 @@ export function TrackStatsDisplay({
|
|||
return `${secs}s`;
|
||||
};
|
||||
|
||||
if (loading) return <div className="flex h-10 items-center gap-2 text-xs font-mono text-primary animate-pulse"><Loader2 className="w-3 h-3 animate-spin" /> SCANNING...</div>;
|
||||
if (loading) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-10 items-center gap-2 text-xs font-mono text-muted-foreground/90 tracking-tight',
|
||||
'animate-pulse transition-opacity duration-[var(--duration-normal)]',
|
||||
)}
|
||||
>
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
SCANNING...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (error || !stats) return null;
|
||||
|
||||
const statsItems = [
|
||||
{ icon: Eye, value: formatNumber(stats.views), label: 'Views', color: 'text-cyan-500', bg: 'bg-cyan-500/10' },
|
||||
{ icon: Heart, value: formatNumber(stats.likes), label: 'Likes', color: 'text-magenta-500', bg: 'bg-magenta-500/10' },
|
||||
{ icon: MessageCircle, value: formatNumber(stats.comments), label: 'Comms', color: 'text-lime-500', bg: 'bg-lime-500/10' },
|
||||
{ icon: Download, value: formatNumber(stats.downloads), label: 'Data', color: 'text-gold-500', bg: 'bg-gold-500/10' },
|
||||
{ icon: Eye, value: formatNumber(stats.views), label: 'Views', color: 'text-chart-1', bg: 'bg-chart-1/10' },
|
||||
{ icon: Heart, value: formatNumber(stats.likes), label: 'Likes', color: 'text-chart-2', bg: 'bg-chart-2/10' },
|
||||
{ icon: MessageCircle, value: formatNumber(stats.comments), label: 'Comms', color: 'text-chart-3', bg: 'bg-chart-3/10' },
|
||||
{ icon: Download, value: formatNumber(stats.downloads), label: 'Data', color: 'text-chart-4', bg: 'bg-chart-4/10' },
|
||||
{ icon: Clock, value: formatDuration(stats.total_play_time), label: 'Pulse', color: 'text-primary', bg: 'bg-primary/10' },
|
||||
];
|
||||
|
||||
if (variant === 'grid') {
|
||||
return (
|
||||
<div className={cn("grid grid-cols-2 md:grid-cols-5 gap-3", className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'grid grid-cols-2 md:grid-cols-5 gap-3 transition-opacity duration-[var(--duration-normal)]',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{statsItems.map((item, i) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Card key={i} variant="glass" className="p-3 bg-black/40 border-white/5 flex flex-col items-center gap-1 group hover:border-white/20 transition-all">
|
||||
<div className={cn("p-2 rounded-lg mb-1 group-hover:scale-110 transition-transform", item.bg, item.color)}><Icon className="w-4 h-4" /></div>
|
||||
<div className="text-lg font-display font-bold text-white">{item.value}</div>
|
||||
<div className="text-[8px] font-mono text-muted-foreground uppercase tracking-widest">{item.label}</div>
|
||||
<Card
|
||||
key={i}
|
||||
variant="outline"
|
||||
className={cn(
|
||||
'p-3 border-border flex flex-col items-center gap-1',
|
||||
'hover:border-primary/30 hover:shadow-md transition-[border-color,box-shadow,transform] duration-[var(--duration-normal)]',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'p-2 rounded-[var(--radius-md)] mb-1 transition-transform duration-[var(--duration-normal)] hover:scale-105',
|
||||
item.bg,
|
||||
item.color,
|
||||
)}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="text-lg font-display font-bold text-foreground tabular-nums tracking-tight">
|
||||
{item.value}
|
||||
</div>
|
||||
<div className="text-xs font-mono text-muted-foreground/90 uppercase tracking-widest">
|
||||
{item.label}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
|
|
@ -82,15 +118,37 @@ export function TrackStatsDisplay({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-wrap items-center gap-6", className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-wrap items-center gap-6 transition-opacity duration-[var(--duration-normal)]',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{statsItems.map((item, i) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<div key={i} className="flex items-center gap-2 group cursor-default">
|
||||
<div className={cn("p-1.5 rounded bg-white/5 opacity-60 group-hover:opacity-100 transition-all", item.color)}><Icon className="w-3.5 h-3.5" /></div>
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-2 group cursor-default"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'p-1.5 rounded-[var(--radius-md)] opacity-80 group-hover:opacity-100 transition-[opacity,transform] duration-[var(--duration-normal)]',
|
||||
item.bg,
|
||||
item.color,
|
||||
)}
|
||||
>
|
||||
<Icon className="w-3.5 h-3.5" aria-hidden />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-bold text-white group-hover:text-primary transition-colors">{item.value}</span>
|
||||
{showLabels && <span className="text-[8px] font-mono text-muted-foreground uppercase tracking-wider">{item.label}</span>}
|
||||
<span className="text-sm font-bold text-foreground tabular-nums tracking-tight group-hover:text-primary transition-colors duration-[var(--duration-normal)]">
|
||||
{item.value}
|
||||
</span>
|
||||
{showLabels && (
|
||||
<span className="text-xs font-mono text-muted-foreground/90 uppercase tracking-wider">
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue