refactor: improve layout and styling of Playlist component for better readability
This commit is contained in:
@@ -22,9 +22,10 @@ export function Playlist({
|
|||||||
const maxHeight = variant === 'maximized' ? 'h-[calc(100vh-200px)]' : 'h-60'
|
const maxHeight = variant === 'maximized' ? 'h-[calc(100vh-200px)]' : 'h-60'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="w-full">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<h4 className="font-medium text-sm">
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h4 className="font-medium text-sm truncate">
|
||||||
{playlist.name}
|
{playlist.name}
|
||||||
</h4>
|
</h4>
|
||||||
<Badge variant="secondary" className="text-xs">
|
<Badge variant="secondary" className="text-xs">
|
||||||
@@ -32,98 +33,69 @@ export function Playlist({
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ScrollArea className={cn('w-full', maxHeight)}>
|
{/* Track List */}
|
||||||
<div className="space-y-1">
|
<ScrollArea className={maxHeight}>
|
||||||
|
<div className="w-full">
|
||||||
{playlist.sounds.map((sound, index) => (
|
{playlist.sounds.map((sound, index) => (
|
||||||
<div
|
<div
|
||||||
key={sound.id}
|
key={sound.id}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors',
|
'grid grid-cols-10 gap-2 items-center py-1.5 px-2 rounded hover:bg-muted/50 cursor-pointer text-xs',
|
||||||
'hover:bg-muted/50',
|
currentIndex === index && 'bg-primary/10 text-primary'
|
||||||
currentIndex === index && 'bg-primary/10 border border-primary/20'
|
|
||||||
)}
|
)}
|
||||||
onClick={() => onTrackSelect(index)}
|
onClick={() => onTrackSelect(index)}
|
||||||
>
|
>
|
||||||
{/* Track Number or Play Icon */}
|
{/* Track number/play icon - 1 column */}
|
||||||
<div className="w-6 h-6 flex items-center justify-center flex-shrink-0">
|
<div className="col-span-1 flex justify-center">
|
||||||
{currentIndex === index ? (
|
{currentIndex === index ? (
|
||||||
<Play className="h-3 w-3 text-primary" />
|
<Play className="h-3 w-3" />
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-muted-foreground">{index + 1}</span>
|
||||||
{index + 1}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Thumbnail */}
|
{/* Thumbnail - 1 column */}
|
||||||
|
<div className="col-span-1">
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
'flex-shrink-0 bg-muted rounded flex items-center justify-center overflow-hidden',
|
'bg-muted rounded flex items-center justify-center overflow-hidden',
|
||||||
variant === 'maximized' ? 'w-10 h-10' : 'w-8 h-8'
|
variant === 'maximized' ? 'w-6 h-6' : 'w-5 h-5'
|
||||||
)}>
|
)}>
|
||||||
{sound.thumbnail ? (
|
{sound.thumbnail ? (
|
||||||
<img
|
<img
|
||||||
src={filesService.getThumbnailUrl(sound.id)}
|
src={filesService.getThumbnailUrl(sound.id)}
|
||||||
alt={sound.name}
|
alt=""
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
onError={(e) => {
|
|
||||||
// Hide image and show music icon if thumbnail fails to load
|
|
||||||
const target = e.target as HTMLImageElement
|
|
||||||
target.style.display = 'none'
|
|
||||||
const musicIcon = target.nextElementSibling as HTMLElement
|
|
||||||
if (musicIcon) musicIcon.style.display = 'block'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : (
|
||||||
<Music
|
<Music className="h-3 w-3 text-muted-foreground" />
|
||||||
className={cn(
|
|
||||||
'text-muted-foreground',
|
|
||||||
variant === 'maximized' ? 'h-5 w-5' : 'h-4 w-4',
|
|
||||||
sound.thumbnail ? 'hidden' : 'block'
|
|
||||||
)}
|
)}
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Track Info */}
|
{/* Track name - 6 columns (takes most space) */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="col-span-6">
|
||||||
<p className={cn(
|
<span className={cn(
|
||||||
'font-medium truncate',
|
'font-medium truncate block',
|
||||||
variant === 'maximized' ? 'text-sm' : 'text-xs',
|
variant === 'maximized' ? 'text-sm' : 'text-xs',
|
||||||
currentIndex === index && 'text-primary'
|
currentIndex === index ? 'text-primary' : 'text-foreground'
|
||||||
)}>
|
)}>
|
||||||
{sound.name}
|
{sound.name}
|
||||||
</p>
|
</span>
|
||||||
{/* <p className={cn(
|
|
||||||
'text-muted-foreground truncate',
|
|
||||||
variant === 'maximized' ? 'text-xs' : 'text-[10px]'
|
|
||||||
)}>
|
|
||||||
{sound.filename}
|
|
||||||
</p> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Duration and Type */}
|
{/* Duration - 2 columns */}
|
||||||
{/* <div className="flex flex-col items-end gap-1 flex-shrink-0">
|
<div className="col-span-2 text-right">
|
||||||
<span className={cn(
|
<span className="text-muted-foreground text-[10px] whitespace-nowrap">
|
||||||
'text-muted-foreground',
|
|
||||||
variant === 'maximized' ? 'text-xs' : 'text-[10px]'
|
|
||||||
)}>
|
|
||||||
{formatDuration(sound.duration)}
|
{formatDuration(sound.duration)}
|
||||||
</span>
|
</span>
|
||||||
<Badge
|
</div>
|
||||||
variant="outline"
|
|
||||||
className={cn(
|
|
||||||
variant === 'maximized' ? 'text-[10px] px-1' : 'text-[8px] px-1 py-0'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{sound.type}
|
|
||||||
</Badge>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
||||||
{/* Playlist Stats */}
|
{/* Footer Stats */}
|
||||||
<div className="pt-2 border-t">
|
<div className="pt-2 mt-2 border-t">
|
||||||
<div className="flex justify-between text-xs text-muted-foreground">
|
<div className="flex justify-between text-xs text-muted-foreground">
|
||||||
<span>{playlist.sounds.length} tracks</span>
|
<span>{playlist.sounds.length} tracks</span>
|
||||||
<span>{formatDuration(playlist.duration)}</span>
|
<span>{formatDuration(playlist.duration)}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user