104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
import { ScrollArea } from '@/components/ui/scroll-area'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import { Music, Play } from 'lucide-react'
|
|
import { type PlayerPlaylist } from '@/lib/api/services/player'
|
|
import { filesService } from '@/lib/api/services/files'
|
|
import { cn } from '@/lib/utils'
|
|
import { formatDuration } from '@/utils/format-duration'
|
|
|
|
interface PlaylistProps {
|
|
playlist: PlayerPlaylist
|
|
currentIndex?: number
|
|
onTrackSelect: (index: number) => void
|
|
variant?: 'normal' | 'maximized'
|
|
}
|
|
|
|
export function Playlist({
|
|
playlist,
|
|
currentIndex,
|
|
onTrackSelect,
|
|
variant = 'normal'
|
|
}: PlaylistProps) {
|
|
return (
|
|
<div className="w-full">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h4 className="font-medium text-sm truncate">
|
|
{playlist.name}
|
|
</h4>
|
|
<Badge variant="secondary" className="text-xs">
|
|
{playlist.sounds.length} tracks
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Track List */}
|
|
<ScrollArea className={variant === 'maximized' ? 'h-[calc(100vh-230px)]' : 'h-60'}>
|
|
<div className="w-full">
|
|
{playlist.sounds.map((sound, index) => (
|
|
<div
|
|
key={sound.id}
|
|
className={cn(
|
|
'grid grid-cols-10 gap-2 items-center py-1.5 px-2 rounded hover:bg-muted/50 cursor-pointer text-xs',
|
|
currentIndex === index && 'bg-primary/10 text-primary'
|
|
)}
|
|
onClick={() => onTrackSelect(index)}
|
|
>
|
|
{/* Track number/play icon - 1 column */}
|
|
<div className="col-span-1 flex justify-center">
|
|
{currentIndex === index ? (
|
|
<Play className="h-3 w-3" />
|
|
) : (
|
|
<span className="text-muted-foreground">{index + 1}</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Thumbnail - 1 column */}
|
|
<div className="col-span-1">
|
|
<div className={cn(
|
|
'bg-muted rounded flex items-center justify-center overflow-hidden',
|
|
variant === 'maximized' ? 'w-6 h-6' : 'w-5 h-5'
|
|
)}>
|
|
{sound.thumbnail ? (
|
|
<img
|
|
src={filesService.getThumbnailUrl(sound.id)}
|
|
alt=""
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<Music className="h-3 w-3 text-muted-foreground" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Track name - 6 columns (takes most space) */}
|
|
<div className="col-span-6">
|
|
<span className={cn(
|
|
'font-medium truncate block',
|
|
variant === 'maximized' ? 'text-sm' : 'text-xs',
|
|
currentIndex === index ? 'text-primary' : 'text-foreground'
|
|
)}>
|
|
{sound.name}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Duration - 2 columns */}
|
|
<div className="col-span-2 text-right">
|
|
<span className="text-muted-foreground text-xs whitespace-nowrap">
|
|
{formatDuration(sound.duration)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</ScrollArea>
|
|
|
|
{/* Footer Stats */}
|
|
<div className="pt-2 mt-2 border-t">
|
|
<div className="flex justify-between text-xs text-muted-foreground">
|
|
<span>{playlist.sounds.length} tracks</span>
|
|
<span>{formatDuration(playlist.duration)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |