Files
g1flux/src/components/AnimationPlayer.tsx
T
syoul 16cebb6ec9
ci/woodpecker/push/woodpecker Pipeline was successful
feat: adaptation mobile — drawer bottom + layout responsive
Sur smartphone (< 640px) : panneau stats masqué par défaut, accessible
via un bottom drawer animé (bouton ☰). PeriodSelector passe en flex-wrap
avec padding tactile 44px. AnimationPlayer s'adapte à la largeur écran.
Badge ville focus affiché directement sur la carte en mode mobile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 11:07:48 +01:00

110 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { TimeFrame } from '../hooks/useAnimation';
interface AnimationPlayerProps {
frames: TimeFrame[];
currentIndex: number;
playing: boolean;
speed: 1 | 2 | 4;
onSeek: (i: number) => void;
onPlay: () => void;
onPause: () => void;
onSpeedChange: (s: 1 | 2 | 4) => void;
onClose: () => void;
}
export function AnimationPlayer({
frames,
currentIndex,
playing,
speed,
onSeek,
onPlay,
onPause,
onSpeedChange,
onClose,
}: AnimationPlayerProps) {
const frame = frames[currentIndex];
return (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 z-[1001] w-[min(640px,calc(100vw-1rem))]">
<div className="bg-[#0a0b0f]/90 backdrop-blur-sm border border-[#2e2f3a] rounded-2xl px-4 py-3 flex flex-col gap-2.5 shadow-xl">
{/* Frame label + position */}
<div className="flex items-center justify-between">
<span className="text-[#d4a843] text-sm font-medium">
{frame?.label ?? '—'}
</span>
<span className="text-[#4b5563] text-xs tabular-nums">
{currentIndex + 1} / {frames.length}
</span>
</div>
{/* Slider */}
<input
type="range"
min={0}
max={frames.length - 1}
value={currentIndex}
onChange={(e) => onSeek(Number(e.target.value))}
className="w-full h-1 accent-[#d4a843] cursor-pointer"
/>
{/* Controls row */}
<div className="flex flex-wrap items-center justify-between gap-y-2">
{/* Playback buttons */}
<div className="flex items-center gap-1">
<button
onClick={() => onSeek(Math.max(0, currentIndex - 1))}
className="px-2.5 py-1.5 text-[#6b7280] hover:text-white transition-colors text-sm"
title="Frame précédente"
>
</button>
<button
onClick={playing ? onPause : onPlay}
className="px-4 py-1.5 bg-[#d4a843] text-[#0a0b0f] rounded-lg font-bold text-sm hover:bg-[#e8c060] transition-colors min-w-[52px] text-center shadow-[0_0_10px_rgba(212,168,67,0.3)]"
>
{playing ? '⏸' : '▶'}
</button>
<button
onClick={() => onSeek(Math.min(frames.length - 1, currentIndex + 1))}
className="px-2.5 py-1.5 text-[#6b7280] hover:text-white transition-colors text-sm"
title="Frame suivante"
>
</button>
</div>
{/* Speed selector */}
<div className="flex items-center gap-1">
<span className="text-[#4b5563] text-xs mr-1">Vitesse</span>
{([1, 2, 4] as const).map((s) => (
<button
key={s}
onClick={() => onSpeedChange(s)}
className={`px-2 py-1 rounded text-xs font-medium transition-colors ${
speed === s
? 'bg-[#d4a843] text-[#0a0b0f]'
: 'text-[#6b7280] hover:text-[#d4a843] hover:bg-[#1a1b23]'
}`}
>
×{s}
</button>
))}
</div>
{/* Close */}
<button
onClick={onClose}
className="text-[#4b5563] hover:text-white transition-colors px-2 py-1 text-sm ml-2"
title="Quitter l'animation"
>
</button>
</div>
</div>
</div>
);
}