127 lines
4.4 KiB
TypeScript
127 lines
4.4 KiB
TypeScript
import React from 'react';
|
|
import { Play, Pause, SkipBack, SkipForward, Volume2, Upload, Shuffle, Repeat } from 'lucide-react';
|
|
import { formatTime } from '../utils/parsers';
|
|
import { PlaybackMode } from '../types';
|
|
|
|
interface ControlsProps {
|
|
isPlaying: boolean;
|
|
currentTime: number;
|
|
duration: number;
|
|
onPlayPause: () => void;
|
|
onSeek: (time: number) => void;
|
|
onNext: () => void;
|
|
onPrev: () => void;
|
|
title: string;
|
|
artist: string;
|
|
onUploadClick: () => void;
|
|
playbackMode: PlaybackMode;
|
|
onToggleMode: () => void;
|
|
}
|
|
|
|
const Controls: React.FC<ControlsProps> = ({
|
|
isPlaying,
|
|
currentTime,
|
|
duration,
|
|
onPlayPause,
|
|
onSeek,
|
|
onNext,
|
|
onPrev,
|
|
title,
|
|
artist,
|
|
onUploadClick,
|
|
playbackMode,
|
|
onToggleMode
|
|
}) => {
|
|
const progressPercent = duration ? (currentTime / duration) * 100 : 0;
|
|
|
|
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
onSeek(Number(e.target.value));
|
|
};
|
|
|
|
return (
|
|
<div className="w-full glass-panel border-t border-white/10 p-6 flex flex-col gap-4 relative z-40">
|
|
{/* Progress Bar */}
|
|
<div className="flex items-center gap-3 text-xs font-mono text-neutral-400">
|
|
<span className="w-10 text-right">{formatTime(currentTime)}</span>
|
|
<div className="flex-1 relative h-1.5 bg-neutral-800 rounded-full group cursor-pointer">
|
|
<div
|
|
className="absolute top-0 left-0 h-full bg-amber-500 rounded-full"
|
|
style={{ width: `${progressPercent}%` }}
|
|
></div>
|
|
<input
|
|
type="range"
|
|
min={0}
|
|
max={duration || 100}
|
|
value={currentTime}
|
|
onChange={handleSeek}
|
|
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
/>
|
|
</div>
|
|
<span className="w-10">{formatTime(duration)}</span>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
{/* Track Info */}
|
|
<div className="hidden md:flex flex-col w-1/3">
|
|
<h3 className="text-white font-bold truncate">{title}</h3>
|
|
<p className="text-neutral-400 text-sm truncate">{artist}</p>
|
|
</div>
|
|
|
|
{/* Main Controls */}
|
|
<div className="flex items-center justify-center gap-6 w-full md:w-1/3">
|
|
<button
|
|
onClick={onToggleMode}
|
|
className={`transition-colors ${playbackMode === 'SHUFFLE' ? 'text-amber-500' : 'text-neutral-600 hover:text-neutral-400'}`}
|
|
title={playbackMode === 'SHUFFLE' ? "Shuffle On" : "Sequential Play"}
|
|
>
|
|
{playbackMode === 'SHUFFLE' ? <Shuffle size={20} /> : <Repeat size={20} />}
|
|
</button>
|
|
|
|
<button onClick={onPrev} className="text-neutral-400 hover:text-white transition-colors">
|
|
<SkipBack size={24} />
|
|
</button>
|
|
|
|
<button
|
|
onClick={onPlayPause}
|
|
className="w-14 h-14 rounded-full bg-white text-black flex items-center justify-center hover:scale-105 transition-transform shadow-lg shadow-white/10"
|
|
>
|
|
{isPlaying ? (
|
|
<Pause fill="currentColor" size={24} />
|
|
) : (
|
|
<Play fill="currentColor" size={24} className="ml-1" />
|
|
)}
|
|
</button>
|
|
|
|
<button onClick={onNext} className="text-neutral-400 hover:text-white transition-colors">
|
|
<SkipForward size={24} />
|
|
</button>
|
|
|
|
{/* Placeholder for symmetry or maybe Like button later */}
|
|
<div className="w-5"></div>
|
|
</div>
|
|
|
|
{/* Secondary Actions */}
|
|
<div className="flex items-center justify-end gap-4 w-1/3">
|
|
<div className="hidden sm:flex items-center gap-2 text-neutral-400">
|
|
<Volume2 size={20} />
|
|
<div className="w-20 h-1 bg-neutral-700 rounded-full overflow-hidden">
|
|
<div className="w-[70%] h-full bg-neutral-400"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="h-6 w-[1px] bg-neutral-700 hidden sm:block"></div>
|
|
|
|
<button
|
|
onClick={onUploadClick}
|
|
className="flex items-center gap-2 bg-neutral-800 hover:bg-neutral-700 text-white px-4 py-2 rounded-full text-xs font-medium transition-colors border border-neutral-700"
|
|
>
|
|
<Upload size={14} />
|
|
<span className="hidden sm:inline">Load / Settings</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Controls; |