Files
QueueCube/frontend/src/components/SongRow.tsx

104 lines
2.9 KiB
TypeScript
Raw Normal View History

2025-02-15 16:28:47 -08:00
import classNames from 'classnames';
import React, { useState, useRef, useEffect, ReactNode } from 'react';
2025-02-23 18:04:14 -08:00
import { FaPlay, FaVolumeUp, FaVolumeOff } from 'react-icons/fa';
2025-02-15 22:15:59 -08:00
import { getDisplayTitle, PlaylistItem } from '../api/player';
2025-02-15 15:22:07 -08:00
2025-02-15 16:28:47 -08:00
export enum PlayState {
NotPlaying,
Playing,
Paused,
}
export interface SongRowProps {
song: PlaylistItem;
auxControl?: ReactNode;
2025-02-15 16:28:47 -08:00
playState: PlayState;
2025-02-15 15:22:07 -08:00
onDelete: () => void;
onPlay: () => void;
}
const SongRow: React.FC<SongRowProps> = ({ song, auxControl, playState, onDelete, onPlay }) => {
2025-02-15 15:22:07 -08:00
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const buttonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
setShowDeleteConfirm(false);
}
};
if (showDeleteConfirm) {
document.addEventListener('click', handleClickOutside);
}
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [showDeleteConfirm]);
2025-02-15 22:15:59 -08:00
const displayTitle = getDisplayTitle(song);
2025-02-15 15:22:07 -08:00
return (
2025-02-23 17:03:29 -08:00
<div className={classNames(
"flex flex-row w-full h-20 px-2 py-2 items-center border-b gap-2 transition-colors shrink-0", {
"qc-highlighted": (playState === PlayState.Playing || playState === PlayState.Paused),
2025-02-15 16:28:47 -08:00
"bg-black/30": playState === PlayState.NotPlaying,
})}>
2025-02-15 15:22:07 -08:00
<div className="flex flex-row gap-2">
<button
2025-02-15 16:28:47 -08:00
className="text-white/40 hover:text-white transition-colors px-3 py-1 rounded"
2025-02-15 15:22:07 -08:00
onClick={onPlay}
>
2025-02-15 16:28:47 -08:00
{
playState === PlayState.Playing ? <FaVolumeUp size={12} />
: playState === PlayState.Paused ? <FaVolumeOff size={12} />
: <FaPlay size={12} />
}
2025-02-15 15:22:07 -08:00
</button>
</div>
<div className="flex-grow min-w-0">
2025-02-15 16:28:47 -08:00
{
displayTitle ? (
2025-02-15 16:28:47 -08:00
<div>
<div className="text-white text-md truncate text-bold">
{displayTitle}
2025-02-15 16:28:47 -08:00
</div>
<div className="text-white/80 text-xs truncate">
{song.filename}
</div>
</div>
) : (
<div className="text-white text-md truncate text-bold">
{song.filename}
</div>
)
}
2025-02-15 15:22:07 -08:00
</div>
<div className="flex flex-row gap-2">
{auxControl}
2025-02-15 15:22:07 -08:00
<button
ref={buttonRef}
className="text-red-100 px-3 py-1 bg-red-500/40 rounded"
2025-02-15 15:22:07 -08:00
onClick={(e) => {
e.stopPropagation();
if (showDeleteConfirm) {
setShowDeleteConfirm(false);
onDelete();
} else {
setShowDeleteConfirm(true);
}
}}
>
{showDeleteConfirm ? 'Delete' : '×'}
</button>
</div>
</div>
);
};
export default SongRow;