80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react';
|
|
import { useAudio } from '../../context/AudioContext';
|
|
import { Mic2, AlignLeft } from 'lucide-react';
|
|
|
|
export const LyricsPanel: React.FC = () => {
|
|
const { currentTrack, currentTime, seek, duration } = useAudio();
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
const [autoScroll, setAutoScroll] = useState(true);
|
|
|
|
// Auto-scroll logic
|
|
useEffect(() => {
|
|
if (autoScroll && scrollRef.current && currentTrack?.lyrics) {
|
|
const activeIndex = currentTrack.lyrics.findIndex(
|
|
(line: { time: number; text: string }, i: number) => {
|
|
return (
|
|
currentTime >= line.time &&
|
|
(i === currentTrack.lyrics!.length - 1 ||
|
|
currentTime < currentTrack.lyrics![i + 1].time)
|
|
);
|
|
},
|
|
);
|
|
|
|
if (activeIndex !== -1) {
|
|
const element = scrollRef.current.children[activeIndex] as HTMLElement;
|
|
if (element) {
|
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
}
|
|
}
|
|
}, [currentTime, currentTrack, autoScroll]);
|
|
|
|
if (!currentTrack?.lyrics) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center h-full text-muted-foreground opacity-50">
|
|
<Mic2 className="w-16 h-16 mb-4" />
|
|
<p>No lyrics available</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="h-full flex flex-col relative group"
|
|
onMouseEnter={() => setAutoScroll(false)}
|
|
onMouseLeave={() => setAutoScroll(true)}
|
|
>
|
|
<div className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<button
|
|
onClick={() => setAutoScroll(!autoScroll)}
|
|
className={`p-2 rounded-full backdrop-blur-md ${autoScroll ? 'bg-primary/20 text-primary' : 'bg-black/30 text-muted-foreground'}`}
|
|
title="Auto-scroll"
|
|
>
|
|
<AlignLeft className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
<div
|
|
ref={scrollRef}
|
|
className="flex-1 overflow-y-auto custom-scrollbar px-4 space-y-6 text-center mask-image-linear-to-b py-24"
|
|
>
|
|
{currentTrack.lyrics.map(
|
|
(line: { time: number; text: string }, i: number) => {
|
|
const isActive =
|
|
currentTime >= line.time &&
|
|
(i === currentTrack.lyrics!.length - 1 ||
|
|
currentTime < currentTrack.lyrics![i + 1].time);
|
|
return (
|
|
<p
|
|
key={i}
|
|
className={`text-2xl md:text-3xl font-bold transition-all duration-500 cursor-pointer hover:text-white ${isActive ? 'text-white scale-105 origin-center' : 'text-white/20 blur-[1px]'}`}
|
|
onClick={() => seek((line.time / duration) * 100)}
|
|
>
|
|
{line.text}
|
|
</p>
|
|
);
|
|
},
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|