diff --git a/src/AnimationControl.tsx b/src/AnimationControl.tsx
index 14c4d97..3695f18 100644
--- a/src/AnimationControl.tsx
+++ b/src/AnimationControl.tsx
@@ -1,30 +1,13 @@
-import SkipPreviousIcon from '@mui/icons-material/SkipPrevious';
-import PlayArrowIcon from '@mui/icons-material/PlayArrow';
-import PauseTwoToneIcon from '@mui/icons-material/PauseTwoTone';
-import SkipNextIcon from '@mui/icons-material/SkipNext';
-import Button from '@mui/material/Button';
-import ButtonGroup from '@mui/material/ButtonGroup';
import Box from '@mui/material/Box';
-import RestartAltIcon from '@mui/icons-material/RestartAlt';
-import Slider from '@mui/material/Slider';
-import RepeatIcon from '@mui/icons-material/Repeat';
-import RepeatOnIcon from '@mui/icons-material/RepeatOn';
import Stack from '@mui/material/Stack';
+import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
-import LooksOneIcon from '@mui/icons-material/LooksOne';
-import LooksOneOutlinedIcon from '@mui/icons-material/LooksOneOutlined';
+import PlayArrowIcon from '@mui/icons-material/PlayArrow';
+import PauseTwoToneIcon from '@mui/icons-material/PauseTwoTone';
+import SkipPreviousIcon from '@mui/icons-material/SkipPrevious';
+import SkipNextIcon from '@mui/icons-material/SkipNext';
+import MenuIcon from '@mui/icons-material/Menu';
import { useEffect } from 'react';
-import DirectionsIcon from '@mui/icons-material/Directions';
-import DirectionsOutlinedIcon from '@mui/icons-material/DirectionsOutlined';
-import FilterCenterFocusOutlinedIcon from '@mui/icons-material/FilterCenterFocusOutlined';
-import ScreenshotMonitorOutlinedIcon from '@mui/icons-material/ScreenshotMonitorOutlined';
-import StartIcon from '@mui/icons-material/Start';
-import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
-import SmartToyIcon from '@mui/icons-material/SmartToy';
-import FlagIcon from '@mui/icons-material/Flag';
-import OutlinedFlagIcon from '@mui/icons-material/OutlinedFlag';
-import PolylineIcon from '@mui/icons-material/Polyline';
-import PolylineOutlinedIcon from '@mui/icons-material/PolylineOutlined';
const STEP_SIZE_INCREMENT = 0.2;
const STEP_SIZE_MAX = 10;
@@ -47,13 +30,14 @@ const SHOW_GOAL_VECTORS_KEY = 'v';
interface AnimationControlProps {
playAnimation: boolean;
- onPlayAnimationChange: (playAnimation: boolean) => void;
+ onPlayChange: (play: boolean) => void;
onSkipBackward: () => void;
onSkipForward: () => void;
+ onOpenDrawer: () => void;
onRestart: () => void;
stepSize: number;
onStepSizeChange: (speed: number) => void;
- loopAnimation: boolean,
+ loopAnimation: boolean;
onLoopAnimationChange: (loopAnimation: boolean) => void;
onFitView: () => void;
showAgentId: boolean;
@@ -72,9 +56,10 @@ interface AnimationControlProps {
function AnimationControl({
playAnimation,
- onPlayAnimationChange,
+ onPlayChange,
onSkipBackward,
onSkipForward,
+ onOpenDrawer,
onRestart,
stepSize,
onStepSizeChange,
@@ -85,7 +70,6 @@ function AnimationControl({
onShowAgentIdChange,
tracePaths,
onTracePathsChange,
- canScreenshot,
takeScreenshot,
showCellId,
setShowCellId,
@@ -94,178 +78,170 @@ function AnimationControl({
showGoalVectors,
setShowGoalVectors,
}: AnimationControlProps) {
- const roundAndSetStepSize = (value: number) => {
- onStepSizeChange(Number(value.toFixed(1)));
- }
- const handleSliderChange = (event: Event, value: number | number[]) => {
- event.preventDefault();
- if (typeof value === 'number') roundAndSetStepSize(value);
- };
+ useEffect(() => {
+ const roundAndSetStepSize = (value: number) => {
+ onStepSizeChange(Number(value.toFixed(1)));
+ };
- useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (!event.ctrlKey && !event.altKey && !event.metaKey) {
event.preventDefault();
}
- if (event.key === STEP_BACKWARD_KEY) {
- onSkipBackward();
- } else if (event.key === PLAY_PAUSE_KEY) {
- onPlayAnimationChange(!playAnimation);
- } else if (event.key === STEP_FORWARD_KEY) {
- onSkipForward();
- } else if (event.key === RESTART_KEY) {
- onRestart();
- } else if (event.key === LOOP_KEY) {
- onLoopAnimationChange(!loopAnimation);
- } else if (event.key === FIT_VIEW_KEY) {
- onFitView();
- } else if (event.key === SHOW_AGENT_ID_KEY) {
- onShowAgentIdChange(!showAgentId);
- } else if (event.key === STEP_SIZE_UP_KEY && stepSize + STEP_SIZE_INCREMENT <= STEP_SIZE_MAX) {
- roundAndSetStepSize(stepSize + STEP_SIZE_INCREMENT);
- } else if (event.key === STEP_SIZE_DOWN_KEY && stepSize - STEP_SIZE_INCREMENT >= STEP_SIZE_MIN) {
- roundAndSetStepSize(stepSize - STEP_SIZE_INCREMENT);
- } else if (event.key === TRACE_PATHS_KEY) {
- onTracePathsChange(!tracePaths);
- } else if (event.key === SCREENSHOT_KEY) {
- takeScreenshot();
- } else if (event.key === SHOW_CELL_ID_KEY) {
- setShowCellId(!showCellId);
- } else if (event.key === SHOW_GOALS_KEY) {
- setShowGoals(!showGoals);
- } else if (event.key === SHOW_GOAL_VECTORS_KEY) {
- setShowGoalVectors(!showGoalVectors);
+ switch (event.key) {
+ case STEP_BACKWARD_KEY:
+ onSkipBackward();
+ break;
+ case PLAY_PAUSE_KEY:
+ onPlayChange(!playAnimation);
+ break;
+ case STEP_FORWARD_KEY:
+ onSkipForward();
+ break;
+ case RESTART_KEY:
+ onRestart();
+ break;
+ case LOOP_KEY:
+ onLoopAnimationChange(!loopAnimation);
+ break;
+ case FIT_VIEW_KEY:
+ onFitView();
+ break;
+ case SHOW_AGENT_ID_KEY:
+ onShowAgentIdChange(!showAgentId);
+ break;
+ case STEP_SIZE_UP_KEY:
+ if (stepSize + STEP_SIZE_INCREMENT <= STEP_SIZE_MAX) {
+ roundAndSetStepSize(stepSize + STEP_SIZE_INCREMENT);
+ }
+ break;
+ case STEP_SIZE_DOWN_KEY:
+ if (stepSize - STEP_SIZE_INCREMENT >= STEP_SIZE_MIN) {
+ roundAndSetStepSize(stepSize - STEP_SIZE_INCREMENT);
+ }
+ break;
+ case TRACE_PATHS_KEY:
+ onTracePathsChange(!tracePaths);
+ break;
+ case SCREENSHOT_KEY:
+ takeScreenshot();
+ break;
+ case SHOW_CELL_ID_KEY:
+ setShowCellId(!showCellId);
+ break;
+ case SHOW_GOALS_KEY:
+ setShowGoals(!showGoals);
+ break;
+ case SHOW_GOAL_VECTORS_KEY:
+ setShowGoalVectors(!showGoalVectors);
+ break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
- }, [playAnimation, onPlayAnimationChange, loopAnimation, onFitView,
- onLoopAnimationChange, onRestart, onShowAgentIdChange, onSkipBackward,
- onSkipForward, onStepSizeChange, showAgentId, stepSize, onTracePathsChange, tracePaths,
- takeScreenshot, showCellId, setShowCellId, showGoals, setShowGoals, showGoalVectors,
- setShowGoalVectors]);
+ }, [
+ playAnimation,
+ onPlayChange,
+ onSkipBackward,
+ onSkipForward,
+ onRestart,
+ stepSize,
+ onStepSizeChange,
+ loopAnimation,
+ onLoopAnimationChange,
+ onFitView,
+ showAgentId,
+ onShowAgentIdChange,
+ tracePaths,
+ onTracePathsChange,
+ takeScreenshot,
+ showCellId,
+ setShowCellId,
+ showGoals,
+ setShowGoals,
+ showGoalVectors,
+ setShowGoalVectors,
+ ]);
return (
-
-
-
- Adjust animation step size
- ({STEP_SIZE_UP_KEY}/{STEP_SIZE_DOWN_KEY})
-
- }
- >
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+ onPlayChange(!playAnimation)}
+ sx={{
+ width: 48,
+ height: 48,
+ backgroundColor: 'primary.main',
+ '&:hover': {
+ backgroundColor: 'primary.dark',
+ }
+ }}
+ >
+ {playAnimation ? : }
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/App.tsx b/src/App.tsx
index 1b43c99..42ccca8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,7 @@
import Box from '@mui/material/Box';
-import Stack from '@mui/material/Stack';
import Drawer from '@mui/material/Drawer';
-import IconButton from '@mui/material/IconButton';
-import MenuIcon from '@mui/icons-material/Menu';
import ConfigBar from './ConfigBar';
+import AnimationControl from './AnimationControl';
import Visualizer from './Visualizer';
import { Graph } from './Graph';
import { Solution } from './Solution';
@@ -32,31 +30,31 @@ function App() {
const [showGoalVectors, setShowGoalVectors] = React.useState(false);
const [drawerOpen, setDrawerOpen] = React.useState(true);
- const handleSkipBackward = () => {
+ const onSkipBackward = () => {
if (pixiAppRef.current?.skipBackward) {
pixiAppRef.current.skipBackward();
}
}
- const handleSkipForward = () => {
+ const onSkipForward = () => {
if (pixiAppRef.current?.skipForward) {
pixiAppRef.current.skipForward();
}
}
- const handleRestart = () => {
+ const onRestart = () => {
if (pixiAppRef.current?.restart) {
pixiAppRef.current.restart();
}
}
- const handleFitView = () => {
+ const onFitView = () => {
if (pixiAppRef.current?.fit) {
pixiAppRef.current.fit();
}
}
- const handleTakeScreenshot = () => {
+ const onTakeScreenshot = () => {
if (pixiAppRef.current?.takeScreenshot) {
pixiAppRef.current.takeScreenshot();
}
@@ -64,23 +62,8 @@ function App() {
return (
-
- setDrawerOpen(true)}
- sx={{
- position: 'absolute',
- top: 16,
- right: 16,
- zIndex: 1000,
- backgroundColor: 'background.paper',
- '&:hover': {
- backgroundColor: 'action.hover',
- },
- }}
- >
-
-
-
+
+
-
+
+
+ setDrawerOpen(true)}
+ onRestart={onRestart}
+ stepSize={stepSize}
+ onStepSizeChange={setStepSize}
+ loopAnimation={loopAnimation}
+ onLoopAnimationChange={setLoopAnimation}
+ onFitView={onFitView}
+ showAgentId={showAgentId}
+ onShowAgentIdChange={setShowAgentId}
+ tracePaths={tracePaths}
+ onTracePathsChange={setTracePaths}
+ canScreenshot={canScreenshot}
+ takeScreenshot={onTakeScreenshot}
+ showCellId={showCellId}
+ setShowCellId={setShowCellId}
+ showGoals={showGoals}
+ setShowGoals={setShowGoals}
+ showGoalVectors={showGoalVectors}
+ setShowGoalVectors={setShowGoalVectors}
+ />
+
@@ -111,28 +122,25 @@ function App() {
graph={graph}
onGraphChange={useCallback((graph: Graph | null) => setGraph(graph), [])}
onSolutionChange={useCallback((solution: Solution | null) => setSolution(solution), [])}
- playAnimation={playAnimation}
- onPlayAnimationChange={setPlayAnimation}
- onSkipBackward={handleSkipBackward}
- onSkipForward={handleSkipForward}
- onRestart={handleRestart}
+ onRestart={onRestart}
stepSize={stepSize}
onStepSizeChange={setStepSize}
loopAnimation={loopAnimation}
onLoopAnimationChange={setLoopAnimation}
- onFitView={handleFitView}
+ onFitView={onFitView}
showAgentId={showAgentId}
onShowAgentIdChange={setShowAgentId}
tracePaths={tracePaths}
onTracePathsChange={setTracePaths}
canScreenshot={canScreenshot}
- takeScreenshot={handleTakeScreenshot}
+ takeScreenshot={onTakeScreenshot}
showCellId={showCellId}
setShowCellId={setShowCellId}
showGoals={showGoals}
setShowGoals={setShowGoals}
showGoalVectors={showGoalVectors}
setShowGoalVectors={setShowGoalVectors}
+ onCloseDrawer={() => setDrawerOpen(false)}
/>
diff --git a/src/ConfigBar.tsx b/src/ConfigBar.tsx
index e32e9e2..ca3d6e6 100644
--- a/src/ConfigBar.tsx
+++ b/src/ConfigBar.tsx
@@ -1,21 +1,18 @@
-import AnimationControl from './AnimationControl';
import { Graph } from './Graph';
import { parseSolution, Solution } from './Solution';
-import { Divider, Stack, Button } from '@mui/material';
-import { MuiFileInput } from "mui-file-input";
+import { Divider, Stack, Box, IconButton } from '@mui/material';
import React, { useEffect } from 'react';
-import ClearIcon from '@mui/icons-material/Clear';
-import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
-import Tooltip from '@mui/material/Tooltip';
+import CloseIcon from '@mui/icons-material/Close';
+import QuickStartSection from './QuickStartSection';
+import FilesSection from './FilesSection';
+import SpeedControlSection from './SpeedControlSection';
+import ControlsSection from './ControlsSection';
+import DisplaySection from './DisplaySection';
interface ConfigBarProps {
graph: Graph | null;
onGraphChange: (graph: Graph | null) => void;
onSolutionChange: (solution: Solution | null) => void;
- playAnimation: boolean;
- onPlayAnimationChange: (playAnimation: boolean) => void;
- onSkipBackward: () => void;
- onSkipForward: () => void;
onRestart: () => void;
stepSize: number;
onStepSizeChange: (speed: number) => void;
@@ -34,16 +31,13 @@ interface ConfigBarProps {
setShowGoals: (showGoals: boolean) => void;
showGoalVectors: boolean;
setShowGoalVectors: (showGoalVectors: boolean) => void;
+ onCloseDrawer: () => void;
}
function ConfigBar({
graph,
onGraphChange,
onSolutionChange,
- playAnimation,
- onPlayAnimationChange,
- onSkipBackward,
- onSkipForward,
onRestart,
stepSize,
onStepSizeChange,
@@ -62,6 +56,7 @@ function ConfigBar({
setShowGoals,
showGoalVectors,
setShowGoalVectors,
+ onCloseDrawer,
}: ConfigBarProps) {
const repoName = "JustinShetty/mapf-visualizer";
const [mapFile, setMapFile] = React.useState(null);
@@ -142,100 +137,79 @@ function ConfigBar({
blurActiveElement();
};
- const downloadFile = (file: File) => {
- const url = URL.createObjectURL(file);
- const a = document.createElement('a');
- a.href = url;
- a.download = file.name;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- }
-
return (
-
-
-
-
-
-
-
- Map
-
-
- }}
- />
- {mapFile &&
-
-
-
- }
-
- {mapError && {mapError}
}
-
-
-
- Solution
-
-
- }}
- />
- {solutionFile &&
-
-
-
- }
-
- {solutionError && {solutionError}
}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- {repoName}
-
+
+
+
+
+
+ {repoName}
+
+
);
}
diff --git a/src/ControlsSection.tsx b/src/ControlsSection.tsx
new file mode 100644
index 0000000..c9d9c9f
--- /dev/null
+++ b/src/ControlsSection.tsx
@@ -0,0 +1,71 @@
+import { Stack, Button, Box } from '@mui/material';
+import StartIcon from '@mui/icons-material/Start';
+import RepeatIcon from '@mui/icons-material/Repeat';
+import RepeatOnIcon from '@mui/icons-material/RepeatOn';
+import FilterCenterFocusOutlinedIcon from '@mui/icons-material/FilterCenterFocusOutlined';
+import ScreenshotMonitorOutlinedIcon from '@mui/icons-material/ScreenshotMonitorOutlined';
+
+interface ControlsSectionProps {
+ onRestart: () => void;
+ loopAnimation: boolean;
+ onLoopAnimationChange: (loopAnimation: boolean) => void;
+ onFitView: () => void;
+ canScreenshot: boolean;
+ takeScreenshot: () => void;
+}
+
+function ControlsSection({
+ onRestart,
+ loopAnimation,
+ onLoopAnimationChange,
+ onFitView,
+ canScreenshot,
+ takeScreenshot,
+}: ControlsSectionProps) {
+ return (
+
+ Controls
+
+ }
+ sx={{ justifyContent: 'flex-start', textTransform: 'none' }}
+ >
+ Restart
+
+
+ }
+ sx={{ justifyContent: 'flex-start', textTransform: 'none' }}
+ >
+ Fit View
+
+ }
+ sx={{ justifyContent: 'flex-start', textTransform: 'none' }}
+ >
+ Screenshot
+
+
+
+ );
+}
+
+export default ControlsSection;
diff --git a/src/DisplaySection.tsx b/src/DisplaySection.tsx
new file mode 100644
index 0000000..74bdc40
--- /dev/null
+++ b/src/DisplaySection.tsx
@@ -0,0 +1,81 @@
+import { Stack, Box, Checkbox } from '@mui/material';
+import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
+import LooksOneOutlinedIcon from '@mui/icons-material/LooksOneOutlined';
+import DirectionsOutlinedIcon from '@mui/icons-material/DirectionsOutlined';
+import OutlinedFlagIcon from '@mui/icons-material/OutlinedFlag';
+import PolylineOutlinedIcon from '@mui/icons-material/PolylineOutlined';
+
+interface DisplaySectionProps {
+ showAgentId: boolean;
+ onShowAgentIdChange: (showAgentId: boolean) => void;
+ showCellId: boolean;
+ setShowCellId: (showCellId: boolean) => void;
+ tracePaths: boolean;
+ onTracePathsChange: (tracePaths: boolean) => void;
+ showGoals: boolean;
+ setShowGoals: (showGoals: boolean) => void;
+ showGoalVectors: boolean;
+ setShowGoalVectors: (showGoalVectors: boolean) => void;
+}
+
+function DisplaySection({
+ showAgentId,
+ onShowAgentIdChange,
+ showCellId,
+ setShowCellId,
+ tracePaths,
+ onTracePathsChange,
+ showGoals,
+ setShowGoals,
+ showGoalVectors,
+ setShowGoalVectors,
+}: DisplaySectionProps) {
+ return (
+
+ Display
+
+ onShowAgentIdChange(!showAgentId)}>
+ onShowAgentIdChange(e.target.checked)} sx={{ py: 0.5 }} />
+
+
+ Agent IDs
+
+
+
+ setShowCellId(!showCellId)}>
+ setShowCellId(e.target.checked)} sx={{ py: 0.5 }} />
+
+
+ Cell IDs
+
+
+
+ onTracePathsChange(!tracePaths)}>
+ onTracePathsChange(e.target.checked)} sx={{ py: 0.5 }} />
+
+
+ Paths
+
+
+
+ setShowGoals(!showGoals)}>
+ setShowGoals(e.target.checked)} sx={{ py: 0.5 }} />
+
+
+ Goals
+
+
+
+ setShowGoalVectors(!showGoalVectors)}>
+ setShowGoalVectors(e.target.checked)} sx={{ py: 0.5 }} />
+
+
+ Vectors
+
+
+
+
+ );
+}
+
+export default DisplaySection;
diff --git a/src/FilesSection.tsx b/src/FilesSection.tsx
new file mode 100644
index 0000000..136de6c
--- /dev/null
+++ b/src/FilesSection.tsx
@@ -0,0 +1,99 @@
+import { Stack, Box, Button, Tooltip } from '@mui/material';
+import { MuiFileInput } from "mui-file-input";
+import ClearIcon from '@mui/icons-material/Clear';
+import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
+
+interface FilesSectionProps {
+ mapFile: File | null;
+ onMapChange: (file: File | null) => void;
+ mapError: string | null;
+ solutionFile: File | null;
+ onSolutionChange: (file: File | null) => void;
+ solutionError: string | null;
+}
+
+function FilesSection({
+ mapFile,
+ onMapChange,
+ mapError,
+ solutionFile,
+ onSolutionChange,
+ solutionError,
+}: FilesSectionProps) {
+ const downloadFile = (file: File) => {
+ const url = URL.createObjectURL(file);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = file.name;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ };
+
+ return (
+
+
+ Map
+
+
+ }}
+ />
+ {mapFile && (
+
+
+
+ )}
+
+ {mapError && {mapError}}
+
+
+
+ Solution
+
+
+ }}
+ />
+ {solutionFile && (
+
+
+
+ )}
+
+ {solutionError && {solutionError}}
+
+
+ );
+}
+
+export default FilesSection;
diff --git a/src/PixiApp.tsx b/src/PixiApp.tsx
index 078ee9d..6606e4c 100644
--- a/src/PixiApp.tsx
+++ b/src/PixiApp.tsx
@@ -385,15 +385,16 @@ const PixiApp = forwardRef(({
new PIXI.Text({
x: width / 100,
y: height / 100,
+ anchor: new PIXI.Point(0, 0),
style: textStyle,
})
);
hudRef.current.addChild(
new PIXI.Text({
- x: width / 100,
- y: height - height / 100,
- anchor: new PIXI.Point(0, 1),
+ x: width - width / 100,
+ y: height / 100,
+ anchor: new PIXI.Point(1, 0),
text: "Click and drag to pan. Scroll to zoom.",
style: textStyle,
})
@@ -449,8 +450,8 @@ const PixiApp = forwardRef(({
if (hudRef.current) {
hudRef.current.children[0].x = width / 100;
hudRef.current.children[0].y = height / 100;
- hudRef.current.children[1].x = width / 100;
- hudRef.current.children[1].y = height - height / 100;
+ hudRef.current.children[1].x = width - width / 100;
+ hudRef.current.children[1].y = height / 100;
}
fit();
}
diff --git a/src/QuickStartSection.tsx b/src/QuickStartSection.tsx
new file mode 100644
index 0000000..8313572
--- /dev/null
+++ b/src/QuickStartSection.tsx
@@ -0,0 +1,33 @@
+import { Stack, Button, Box } from '@mui/material';
+
+interface QuickStartSectionProps {
+ onLoadDemo: (mapName: string) => void;
+}
+
+function QuickStartSection({ onLoadDemo }: QuickStartSectionProps) {
+ return (
+
+ Quick Start
+
+
+
+
+
+ );
+}
+
+export default QuickStartSection;
diff --git a/src/SpeedControlSection.tsx b/src/SpeedControlSection.tsx
new file mode 100644
index 0000000..818622d
--- /dev/null
+++ b/src/SpeedControlSection.tsx
@@ -0,0 +1,39 @@
+import { Stack, Box, Slider } from '@mui/material';
+
+interface SpeedControlSectionProps {
+ stepSize: number;
+ onStepSizeChange: (stepSize: number) => void;
+}
+
+function SpeedControlSection({ stepSize, onStepSizeChange }: SpeedControlSectionProps) {
+ const roundAndSetStepSize = (value: number) => {
+ onStepSizeChange(Number(value.toFixed(1)));
+ };
+
+ const handleSliderChange = (_event: Event, value: number | number[]) => {
+ if (typeof value === 'number') roundAndSetStepSize(value);
+ };
+
+ return (
+
+ Speed
+
+
+
+ {stepSize.toFixed(1)}x
+
+
+
+ );
+}
+
+export default SpeedControlSection;