import { useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { baseURL } from "../api/axiosInstance"; import { usePlayer } from "../context/playerContext"; import { toEmbedUrl } from "../utils/utils"; import PlaylistDropdown from "./playlist/PlaylistDropdown"; export interface SongItemData { id: number; title: string; cover?: string | null; genre?: string; link?: string | null; releasedBy?: string; isLikedByCurrentUser?: boolean; } interface SongItemProps { song: SongItemData; /** Optional label shown before the artist, e.g. "Song" for search results */ label?: string; /** Optional role badge for artist contributions, e.g. "PERFORMER" */ role?: string; /** Optional index number for playlist/collection views */ index?: number; /** Callback when the like button is clicked */ onLikeToggle?: (songId: number) => void; /** Controlled: whether the playlist dropdown is open */ isDropdownOpen?: boolean; /** Controlled: callback when dropdown should open/close */ onDropdownToggle?: (songId: number | null) => void; } const ROLE_COLORS: { [key: string]: string } = { COMPOSER: "bg-purple-500/20 text-purple-300", PERFORMER: "bg-blue-500/20 text-blue-300", PRODUCER: "bg-green-500/20 text-green-300", MAIN_VOCAL: "bg-pink-500/20 text-pink-300", }; const SongItem = ({ song, label, role, index, onLikeToggle, isDropdownOpen, onDropdownToggle, }: SongItemProps) => { const navigate = useNavigate(); const { play, currentSong } = usePlayer(); const [internalOpen, setInternalOpen] = useState(false); const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 }); const [dropdownDirection, setDropdownDirection] = useState<"above" | "below">( "below", ); const buttonRef = useRef(null); const playlistOpen = isDropdownOpen ?? internalOpen; const setPlaylistOpen = (open: boolean) => { if (onDropdownToggle) { onDropdownToggle(open ? song.id : null); } else { setInternalOpen(open); } }; const isPlaying = currentSong?.id === song.id; const subtitleParts: string[] = []; if (label) subtitleParts.push(label); if (song.releasedBy) subtitleParts.push(song.releasedBy); const subtitle = subtitleParts.join(" • "); return (
navigate(`/songs/${song.id}`)} onMouseEnter={() => { if (onDropdownToggle && !playlistOpen) { onDropdownToggle(null); } }} className="flex items-center gap-4 p-3 rounded-lg hover:bg-white/5 cursor-pointer transition-colors group" > {/* Optional index */} {index != null && ( {index} )} {/* Cover */} {song.title} { (e.target as HTMLImageElement).src = "/favicon.png"; }} /> {/* Title & subtitle */}

{song.title}

{subtitle && (

{subtitle}

)}
{/* Role badge (artist contributions) */} {role && ( )} {/* Genre */} {song.genre && ( {song.genre} )} {/* Play button */} {song.link && ( )} {/* Like button */} {onLikeToggle && ( )} {/* Three-dot menu */}
setPlaylistOpen(false)} position={dropdownPosition} usePortal={true} direction={dropdownDirection} />
); }; export default SongItem;