61 lines
3.1 KiB
TypeScript
61 lines
3.1 KiB
TypeScript
import { ScrollArea, IconButton, Badge } from 'modern-sk';
|
|
import { useAppDispatch, useAppSelector } from '../../hooks/useAppDispatch';
|
|
import { goToIndex, removeFromQueue, clearQueue } from '../../store/slices/queue';
|
|
import { toggleQueue } from '../../store/slices/player';
|
|
import { formatDuration } from '../../lib/format';
|
|
|
|
export function QueuePanel() {
|
|
const dispatch = useAppDispatch();
|
|
const queue = useAppSelector((s) => s.queue);
|
|
const isOpen = useAppSelector((s) => s.player.isQueueOpen);
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div style={{ width: '20rem', borderLeft: '1px solid var(--color-border)', display: 'flex', flexDirection: 'column', background: 'var(--color-surface-1)', flexShrink: 0 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0.75rem 1rem', borderBottom: '1px solid var(--color-border)' }}>
|
|
<span style={{ fontWeight: 600, fontSize: '0.875rem' }}>Queue</span>
|
|
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
|
{queue.sourceName && <Badge variant="neutral">{queue.sourceName}</Badge>}
|
|
<IconButton variant="ghost" size="sm" onClick={() => dispatch(clearQueue())} aria-label="Clear queue">✕</IconButton>
|
|
<IconButton variant="ghost" size="sm" onClick={() => dispatch(toggleQueue())} aria-label="Close">✕</IconButton>
|
|
</div>
|
|
</div>
|
|
<ScrollArea style={{ flex: 1 }}>
|
|
{queue.entries.length === 0 ? (
|
|
<p style={{ padding: '2rem', textAlign: 'center', color: 'var(--color-text-3)', fontSize: '0.875rem' }}>Queue is empty</p>
|
|
) : (
|
|
queue.entries.map((entry, i) => (
|
|
<div
|
|
key={`${entry.trackId}-${i}`}
|
|
onDoubleClick={() => dispatch(goToIndex(i))}
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: '1fr auto',
|
|
padding: '0.5rem 1rem',
|
|
gap: '0.5rem',
|
|
alignItems: 'center',
|
|
background: i === queue.currentIndex ? 'var(--color-surface-2)' : undefined,
|
|
cursor: 'default',
|
|
}}
|
|
>
|
|
<div style={{ minWidth: 0 }}>
|
|
<div style={{ fontSize: '0.8125rem', fontWeight: i === queue.currentIndex ? 600 : 400, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: i === queue.currentIndex ? 'var(--color-accent)' : 'var(--color-text-1)' }}>
|
|
{entry.title}
|
|
</div>
|
|
<div style={{ fontSize: '0.75rem', color: 'var(--color-text-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
{entry.artistName}
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.25rem', flexShrink: 0 }}>
|
|
<span style={{ fontSize: '0.75rem', color: 'var(--color-text-3)' }}>{formatDuration(entry.durationMs)}</span>
|
|
<IconButton variant="ghost" size="sm" onClick={() => dispatch(removeFromQueue(i))} aria-label="Remove from queue">✕</IconButton>
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</ScrollArea>
|
|
</div>
|
|
);
|
|
}
|