87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
|
|
import { useEffect, useId, useRef, useState } from 'react'
|
||
|
|
import { QueueList, type QueueItem } from '../QueueList'
|
||
|
|
import styles from './queue-popover.module.css'
|
||
|
|
|
||
|
|
export type QueuePopoverProps = {
|
||
|
|
queue: QueueItem[]
|
||
|
|
order: number[]
|
||
|
|
playingOrigIdx: number
|
||
|
|
scrollSignal: number
|
||
|
|
onPlay: (origIdx: number) => void
|
||
|
|
onRemove: (origIdx: number) => void
|
||
|
|
onMove: (fromPos: number, toPos: number) => void
|
||
|
|
}
|
||
|
|
|
||
|
|
export function QueuePopover({
|
||
|
|
queue,
|
||
|
|
order,
|
||
|
|
playingOrigIdx,
|
||
|
|
scrollSignal,
|
||
|
|
onPlay,
|
||
|
|
onRemove,
|
||
|
|
onMove,
|
||
|
|
}: QueuePopoverProps) {
|
||
|
|
const [open, setOpen] = useState(false)
|
||
|
|
const rootRef = useRef<HTMLDivElement>(null)
|
||
|
|
const titleId = useId()
|
||
|
|
const panelId = useId()
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!open) return
|
||
|
|
function onDocMouseDown(e: MouseEvent) {
|
||
|
|
const el = rootRef.current
|
||
|
|
if (el && !el.contains(e.target as Node)) setOpen(false)
|
||
|
|
}
|
||
|
|
function onKey(e: KeyboardEvent) {
|
||
|
|
if (e.key === 'Escape') setOpen(false)
|
||
|
|
}
|
||
|
|
document.addEventListener('mousedown', onDocMouseDown)
|
||
|
|
document.addEventListener('keydown', onKey)
|
||
|
|
return () => {
|
||
|
|
document.removeEventListener('mousedown', onDocMouseDown)
|
||
|
|
document.removeEventListener('keydown', onKey)
|
||
|
|
}
|
||
|
|
}, [open])
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={styles.root} ref={rootRef}>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
className={styles.trigger}
|
||
|
|
title="Playback queue"
|
||
|
|
aria-expanded={open}
|
||
|
|
aria-haspopup="dialog"
|
||
|
|
aria-controls={open ? panelId : undefined}
|
||
|
|
onClick={() => setOpen((v) => !v)}
|
||
|
|
>
|
||
|
|
<span className={styles.triggerIcon} aria-hidden>
|
||
|
|
☰
|
||
|
|
</span>
|
||
|
|
</button>
|
||
|
|
{open && (
|
||
|
|
<div
|
||
|
|
id={panelId}
|
||
|
|
className={styles.popover}
|
||
|
|
role="dialog"
|
||
|
|
aria-labelledby={titleId}
|
||
|
|
>
|
||
|
|
<div className={styles.header} id={titleId}>
|
||
|
|
Queue
|
||
|
|
</div>
|
||
|
|
<div className={styles.body}>
|
||
|
|
<QueueList
|
||
|
|
queue={queue}
|
||
|
|
order={order}
|
||
|
|
playingOrigIdx={playingOrigIdx}
|
||
|
|
scrollSignal={scrollSignal}
|
||
|
|
onPlay={onPlay}
|
||
|
|
onRemove={onRemove}
|
||
|
|
onMove={onMove}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|