veza/apps/web/src/components/player/audio-player/AudioPlayerTrackInfo.tsx

68 lines
2 KiB
TypeScript
Raw Normal View History

import { useRef, useState, useEffect } from 'react';
import { cn } from '@/lib/utils';
interface Track {
title?: string;
artist?: string;
artistId?: string;
cover?: string;
}
interface AudioPlayerTrackInfoProps {
track: Track;
onArtistClick?: (artistId: string) => void;
}
export function AudioPlayerTrackInfo({ track, onArtistClick }: AudioPlayerTrackInfoProps) {
const titleRef = useRef<HTMLParagraphElement>(null);
const [isOverflowing, setIsOverflowing] = useState(false);
useEffect(() => {
const el = titleRef.current;
if (el) {
setIsOverflowing(el.scrollWidth > el.clientWidth);
}
}, [track.title]);
return (
<div className="flex items-center gap-3 min-w-0 group/track">
{/* Album art with ambient glow */}
{track.cover && (
<div className="relative shrink-0">
{/* Ambient glow */}
<div className="absolute inset-0 rounded-lg bg-primary/20 blur-xl scale-150 opacity-0 group-hover/track:opacity-100 transition-opacity duration-500" />
<img
src={track.cover}
alt={track.title ?? ''}
className="relative z-10 w-10 h-10 rounded object-cover"
/>
</div>
)}
<div className="min-w-0 flex-1">
<div className="overflow-hidden">
<p
ref={titleRef}
className={cn(
'text-sm font-medium text-foreground whitespace-nowrap',
isOverflowing
? 'animate-marquee'
: 'truncate',
)}
>
{track.title}
</p>
</div>
{track.artist && (
<button
type="button"
className="appearance-none bg-transparent border-0 p-0 text-xs text-muted-foreground truncate hover:text-foreground hover:underline transition-colors cursor-pointer text-left"
onClick={() => onArtistClick?.(track.artistId ?? '')}
>
{track.artist}
</button>
)}
</div>
</div>
);
}