veza/apps/web/src/features/player/components/MiniPlayer.tsx

156 lines
4.7 KiB
TypeScript
Raw Normal View History

/**
* Composant MiniPlayer
* Version compacte du player avec position fixe et toggle
*/
import { ChevronUp, X } from 'lucide-react';
import { cn } from '@/lib/utils';
import { usePlayer } from '../hooks/usePlayer';
import { TrackInfo } from './TrackInfo';
import { PlayPauseButton } from './PlayPauseButton';
import { NextPreviousButtons } from './NextPreviousButtons';
import { ProgressBar } from './ProgressBar';
import { VolumeControl } from './VolumeControl';
export interface MiniPlayerProps {
isVisible: boolean;
onToggle: () => void;
onClose?: () => void;
className?: string;
position?: 'bottom' | 'top';
}
export function MiniPlayer({
isVisible,
onToggle,
onClose,
className,
position = 'bottom',
}: MiniPlayerProps) {
const player = usePlayer();
if (!isVisible || !player.currentTrack) {
return null;
}
const handlePlayPause = () => {
if (player.isPlaying) {
player.pause();
} else {
player.resume();
}
};
2025-12-13 02:34:34 +00:00
const canGoNext =
player.queue.length > 0 && player.currentIndex < player.queue.length - 1;
const canGoPrevious = player.queue.length > 0 && player.currentIndex > 0;
return (
<div
className={cn(
'fixed left-0 right-0 z-50 bg-white dark:bg-kodo-ink border-t border-kodo-steel dark:border-kodo-steel shadow-lg',
'transition-transform duration-300 ease-in-out',
position === 'bottom' ? 'bottom-0' : 'top-0',
2025-12-13 02:34:34 +00:00
className,
)}
role="region"
aria-label="Mini lecteur audio"
>
<div className="container mx-auto px-4 py-2">
<div className="flex items-center gap-4">
{/* Track Info - Compact */}
<div className="flex-1 min-w-0">
<TrackInfo
track={player.currentTrack}
showCover={true}
coverSize="sm"
showMetadata={false}
className="p-0"
/>
</div>
{/* Progress Bar - Compact */}
<div className="hidden md:flex flex-1 max-w-xs">
<ProgressBar
currentTime={player.currentTime}
duration={player.duration}
onSeek={player.seek}
className="h-1"
showTooltip={false}
/>
</div>
{/* Controls */}
<div className="flex items-center gap-2">
<NextPreviousButtons
onNext={player.next}
onPrevious={player.previous}
canGoNext={canGoNext}
canGoPrevious={canGoPrevious}
size="sm"
variant="ghost"
disabled={!player.currentTrack}
/>
<PlayPauseButton
isPlaying={player.isPlaying}
onClick={handlePlayPause}
size="sm"
variant="default"
/>
<div className="hidden sm:block">
<VolumeControl
volume={player.volume}
muted={player.muted}
onVolumeChange={player.setVolume}
onMuteToggle={player.toggleMute}
showValue={false}
showSlider={true}
/>
</div>
{/* Toggle Button */}
<button
type="button"
onClick={onToggle}
className={cn(
'p-2 rounded-lg text-kodo-content-dim dark:text-kodo-content-dim',
'hover:bg-kodo-void dark:hover:bg-kodo-graphite',
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',
2025-12-13 02:34:34 +00:00
'transition-colors',
)}
aria-label="Agrandir le lecteur"
title="Agrandir le lecteur"
>
<ChevronUp className="h-5 w-5" aria-hidden="true" />
<span className="sr-only">Agrandir le lecteur</span>
</button>
{/* Close Button (optional) */}
{onClose && (
<button
type="button"
onClick={onClose}
className={cn(
'p-2 rounded-lg text-kodo-content-dim dark:text-kodo-content-dim',
'hover:bg-kodo-void dark:hover:bg-kodo-graphite',
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',
2025-12-13 02:34:34 +00:00
'transition-colors',
)}
aria-label="Fermer le mini lecteur"
title="Fermer le mini lecteur"
>
<X className="h-5 w-5" aria-hidden="true" />
<span className="sr-only">Fermer le mini lecteur</span>
</button>
)}
</div>
</div>
</div>
</div>
);
}
export default MiniPlayer;