52 lines
1.9 KiB
JavaScript
52 lines
1.9 KiB
JavaScript
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
const SyncedLyricsPlayer = ({ audioUrl, alignedWords, onClose, accentColor }) => {
|
|
const audioRef = useRef(null);
|
|
const [playing, setPlaying] = useState(false);
|
|
const [currentTime, setCurrentTime] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const el = audioRef.current;
|
|
if (!el) return;
|
|
const handler = () => setCurrentTime(el.currentTime);
|
|
el.addEventListener('timeupdate', handler);
|
|
return () => el.removeEventListener('timeupdate', handler);
|
|
}, []);
|
|
|
|
if (!alignedWords || alignedWords.length === 0) return null;
|
|
|
|
return (
|
|
<div className="ms-synced-player" style={{ '--synced-accent': accentColor }}>
|
|
<div className="ms-synced-player__header">
|
|
<h4 className="ms-synced-player__title">Synced Lyrics</h4>
|
|
<button type="button" className="ms-modal__close" onClick={onClose}>✕</button>
|
|
</div>
|
|
<audio
|
|
ref={audioRef}
|
|
src={audioUrl}
|
|
onPlay={() => setPlaying(true)}
|
|
onPause={() => setPlaying(false)}
|
|
onEnded={() => setPlaying(false)}
|
|
controls
|
|
className="ms-synced-player__audio"
|
|
/>
|
|
<div className="ms-synced-player__lyrics">
|
|
{alignedWords.map((word, idx) => {
|
|
const isActive = currentTime >= word.startS && currentTime < word.endS;
|
|
const isPast = currentTime >= word.endS;
|
|
return (
|
|
<span
|
|
key={idx}
|
|
className={`ms-synced-word ${isActive ? 'is-active' : ''} ${isPast ? 'is-past' : ''}`}
|
|
>
|
|
{word.word}{' '}
|
|
</span>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SyncedLyricsPlayer;
|