feat: Move route-preview

This commit is contained in:
2025-06-09 09:31:26 +03:00
parent f4544c1888
commit 64c15b2622
17 changed files with 2143 additions and 43 deletions

View File

@@ -0,0 +1,232 @@
import { Button, Stack, TextField, Typography } from "@mui/material";
import { useMapData } from "./MapDataContext";
import { useEffect, useState } from "react";
import { useTransform } from "./TransformContext";
import { coordinatesToLocal, localToCoordinates } from "./utils";
export function RightSidebar() {
const {
routeData,
setScaleRange,
saveChanges,
originalRouteData,
setMapRotation,
setMapCenter,
} = useMapData();
const {
rotation,
position,
screenToLocal,
screenCenter,
rotateToAngle,
setTransform,
} = useTransform();
const [minScale, setMinScale] = useState<number>(1);
const [maxScale, setMaxScale] = useState<number>(10);
const [localCenter, setLocalCenter] = useState<{ x: number; y: number }>({
x: 0,
y: 0,
});
const [rotationDegrees, setRotationDegrees] = useState<number>(0);
useEffect(() => {
if (originalRouteData) {
setMinScale(originalRouteData.scale_min ?? 1);
setMaxScale(originalRouteData.scale_max ?? 10);
setRotationDegrees(originalRouteData.rotate ?? 0);
setLocalCenter({
x: originalRouteData.center_latitude ?? 0,
y: originalRouteData.center_longitude ?? 0,
});
}
}, [originalRouteData]);
useEffect(() => {
if (minScale && maxScale) {
setScaleRange(minScale, maxScale);
}
}, [minScale, maxScale]);
useEffect(() => {
setRotationDegrees(
((Math.round((rotation * 180) / Math.PI) % 360) + 360) % 360
);
}, [rotation]);
useEffect(() => {
setMapRotation(rotationDegrees);
}, [rotationDegrees]);
useEffect(() => {
const center = screenCenter ?? { x: 0, y: 0 };
const localCenter = screenToLocal(center.x, center.y);
const coordinates = localToCoordinates(localCenter.x, localCenter.y);
setLocalCenter({ x: coordinates.latitude, y: coordinates.longitude });
}, [position]);
useEffect(() => {
setMapCenter(localCenter.x, localCenter.y);
}, [localCenter]);
function setRotationFromDegrees(degrees: number) {
rotateToAngle((degrees * Math.PI) / 180);
}
function pan({ x, y }: { x: number; y: number }) {
const coordinates = coordinatesToLocal(x, y);
setTransform(coordinates.x, coordinates.y);
}
if (!routeData) {
console.error("routeData is null");
return null;
}
return (
<Stack
position="absolute"
right={8}
top={8}
bottom={8}
p={2}
gap={1}
minWidth="400px"
bgcolor="primary.main"
border="1px solid #e0e0e0"
borderRadius={2}
>
<Typography variant="h6" sx={{ mb: 2, color: "#fff" }} textAlign="center">
Детали о достопримечательностях
</Typography>
<Stack spacing={2} direction="row" alignItems="center">
<TextField
type="number"
label="Минимальный масштаб"
variant="filled"
value={minScale}
onChange={(e) => setMinScale(Number(e.target.value))}
style={{ backgroundColor: "#222", borderRadius: 4 }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
slotProps={{
input: {
min: 0.1,
},
}}
/>
<TextField
type="number"
label="Максимальный масштаб"
variant="filled"
value={maxScale}
onChange={(e) => setMaxScale(Number(e.target.value))}
style={{ backgroundColor: "#222", borderRadius: 4, color: "#fff" }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
slotProps={{
input: {
min: 0.1,
},
}}
/>
</Stack>
<TextField
type="number"
label="Поворот (в градусах)"
variant="filled"
value={rotationDegrees}
onChange={(e) => {
const value = Number(e.target.value);
if (!isNaN(value)) {
setRotationFromDegrees(value);
}
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.currentTarget.blur();
}
}}
style={{ backgroundColor: "#222", borderRadius: 4 }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
slotProps={{
input: {
min: 0,
max: 360,
},
}}
/>
<Stack direction="row" spacing={2}>
<TextField
type="number"
label="Центр карты, широта"
variant="filled"
value={Math.round(localCenter.x * 100000) / 100000}
onChange={(e) => {
setLocalCenter((prev) => ({ ...prev, x: Number(e.target.value) }));
pan({ x: Number(e.target.value), y: localCenter.y });
}}
style={{ backgroundColor: "#222", borderRadius: 4 }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
/>
<TextField
type="number"
label="Центр карты, высота"
variant="filled"
value={Math.round(localCenter.y * 100000) / 100000}
onChange={(e) => {
setLocalCenter((prev) => ({ ...prev, y: Number(e.target.value) }));
pan({ x: localCenter.x, y: Number(e.target.value) });
}}
style={{ backgroundColor: "#222", borderRadius: 4 }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
/>
</Stack>
<Button
variant="contained"
color="secondary"
sx={{ mt: 2 }}
onClick={() => {
saveChanges();
}}
>
Сохранить изменения
</Button>
</Stack>
);
}