Kacemath's picture
Simple deployment: Grid Search Pathfinding with frontend and backend
e067c2d
raw
history blame
4.31 kB
import React, { useEffect } from 'react';
import { useGridStore } from '../../store/gridStore';
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
import { Button } from '../ui/button';
import { Slider } from '../ui/slider';
import {
Play,
Pause,
SkipBack,
SkipForward,
ChevronsLeft,
ChevronsRight,
Film,
} from 'lucide-react';
export const PlaybackControls: React.FC = () => {
const {
steps,
currentStep,
isPlaying,
playbackSpeed,
play,
pause,
reset,
nextStep,
prevStep,
setCurrentStep,
setSpeed,
} = useGridStore();
useEffect(() => {
if (!isPlaying) return;
const interval = setInterval(() => {
nextStep();
}, playbackSpeed);
return () => clearInterval(interval);
}, [isPlaying, playbackSpeed, nextStep]);
if (steps.length === 0) {
return null;
}
const progress = ((currentStep + 1) / steps.length) * 100;
return (
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2 text-zinc-400 font-medium">
<Film className="w-4 h-4" />
Playback
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* Progress bar */}
<div>
<div className="h-1 bg-zinc-800 rounded-full overflow-hidden">
<div
className="h-full bg-zinc-400 transition-all duration-100"
style={{ width: `${progress}%` }}
/>
</div>
<div className="flex justify-between text-xs text-zinc-600 mt-1 font-mono">
<span>{currentStep + 1}</span>
<span>{steps.length}</span>
</div>
</div>
{/* Control buttons */}
<div className="flex items-center justify-center gap-1">
<Button onClick={reset} variant="ghost" size="icon" className="h-8 w-8">
<ChevronsLeft className="w-4 h-4" />
</Button>
<Button onClick={prevStep} variant="ghost" size="icon" className="h-8 w-8">
<SkipBack className="w-4 h-4" />
</Button>
<Button
onClick={isPlaying ? pause : play}
variant={isPlaying ? 'secondary' : 'primary'}
size="icon"
className="h-9 w-9"
>
{isPlaying ? <Pause className="w-4 h-4" /> : <Play className="w-4 h-4" />}
</Button>
<Button onClick={nextStep} variant="ghost" size="icon" className="h-8 w-8">
<SkipForward className="w-4 h-4" />
</Button>
<Button onClick={() => setCurrentStep(steps.length - 1)} variant="ghost" size="icon" className="h-8 w-8">
<ChevronsRight className="w-4 h-4" />
</Button>
</div>
{/* Speed control */}
<Slider
label="Speed"
showValue
min={50}
max={1000}
step={50}
value={playbackSpeed}
onChange={(e) => setSpeed(parseInt(e.target.value))}
/>
{/* Current step info */}
{steps[currentStep] && (
<div className="grid grid-cols-2 gap-2 text-xs">
<div className="bg-zinc-800 rounded p-2">
<span className="text-zinc-600">Position</span>
<p className="text-zinc-300 font-mono">
({steps[currentStep].currentNode.x}, {steps[currentStep].currentNode.y})
</p>
</div>
<div className="bg-zinc-800 rounded p-2">
<span className="text-zinc-600">Cost</span>
<p className="text-zinc-300 font-mono">
{steps[currentStep].pathCost}
</p>
</div>
<div className="bg-zinc-800 rounded p-2">
<span className="text-zinc-600">Frontier</span>
<p className="text-zinc-300 font-mono">
{steps[currentStep].frontier.length}
</p>
</div>
<div className="bg-zinc-800 rounded p-2">
<span className="text-zinc-600">Explored</span>
<p className="text-zinc-300 font-mono">
{steps[currentStep].explored.length}
</p>
</div>
</div>
)}
</CardContent>
</Card>
);
};
export default PlaybackControls;