import { Button, Stack, TextField, Typography, Slider } from "@mui/material"; import { useMapData } from "./MapDataContext"; import { useEffect, useState } from "react"; import { useTransform } from "./TransformContext"; import { SCALE_FACTOR } from "./Constants"; import { toast } from "react-toastify"; export function RightSidebar() { const { routeData, setScaleRange, saveChanges, originalRouteData, setMapRotation, setIconSize: updateIconSize, setFontSize: updateFontSize, } = useMapData(); const { rotation, rotateToAngle, scale, setScaleAtCenter } = useTransform(); const [minScale, setMinScale] = useState(1); const [maxScale, setMaxScale] = useState(5); const [localCenter, setLocalCenter] = useState<{ x: number; y: number }>({ x: 0, y: 0, }); const [rotationDegrees, setRotationDegrees] = useState(0); const [isUserEditing, setIsUserEditing] = useState(false); const [iconSize, setIconSize] = useState(100); const [fontSize, setFontSize] = useState(100); useEffect(() => { if (originalRouteData) { const originalMinScale = originalRouteData.scale_min ?? 1; const resetMinScale = originalMinScale < 1 ? 1 : originalMinScale; const originalMaxScale = originalRouteData.scale_max ?? 5; const resetMaxScale = originalMaxScale < 3 ? 3 : originalMaxScale; setMinScale(resetMinScale); setMaxScale(resetMaxScale); setRotationDegrees(originalRouteData.rotate ?? 0); setLocalCenter({ x: originalRouteData.center_latitude ?? 0, y: originalRouteData.center_longitude ?? 0, }); setIconSize(originalRouteData.icon_size ?? 100); setFontSize(originalRouteData.font_size ?? 100); } }, [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(() => { if (isUserEditing) { return; } const latitude = routeData?.center_latitude ?? 0; const longitude = routeData?.center_longitude ?? 0; setLocalCenter((prev) => { if ( Math.abs(prev.x - latitude) < 1e-6 && Math.abs(prev.y - longitude) < 1e-6 ) { return prev; } return { x: latitude, y: longitude }; }); }, [isUserEditing, routeData?.center_latitude, routeData?.center_longitude]); function setRotationFromDegrees(degrees: number) { rotateToAngle((degrees * Math.PI) / 180); } const handleIconSizeChange = (value: number) => { if (!Number.isFinite(value)) { return; } const clamped = Math.max(50, Math.min(300, Math.round(value))); setIconSize(clamped); updateIconSize(clamped); }; const handleFontSizeChange = (value: number) => { if (!Number.isFinite(value)) { return; } const clamped = Math.max(50, Math.min(300, Math.round(value))); setFontSize(clamped); updateFontSize(clamped); }; useEffect(() => { const next = routeData?.icon_size ?? originalRouteData?.icon_size ?? 100; setIconSize((prev) => (Math.abs(prev - next) > 0.5 ? next : prev)); }, [routeData?.icon_size, originalRouteData?.icon_size]); useEffect(() => { const next = routeData?.font_size ?? originalRouteData?.font_size ?? 100; setFontSize((prev) => (Math.abs(prev - next) > 0.5 ? next : prev)); }, [routeData?.font_size, originalRouteData?.font_size]); if (!routeData) { return null; } return ( Настройка маршрута { let newMinScale = Number(e.target.value); if (newMinScale < 10) { newMinScale = 10; } setMinScale(newMinScale); if (maxScale - newMinScale < 2) { let newMaxScale = newMinScale + 2; if (newMaxScale < 3) { newMaxScale = 3; setMinScale(1); } setMaxScale(newMaxScale); } if (newMinScale > scale * SCALE_FACTOR) { setScaleAtCenter(newMinScale / SCALE_FACTOR); } }} style={{ backgroundColor: "#222", borderRadius: 4 }} sx={{ "& .MuiInputLabel-root": { color: "#fff", }, "& .MuiInputBase-input": { color: "#fff", }, }} slotProps={{ input: { min: 1, max: 10, }, }} /> { let newMaxScale = Number(e.target.value); if (newMaxScale < 13) { newMaxScale = 13; } if (newMaxScale > 300) { newMaxScale = 300; } setMaxScale(newMaxScale); if (newMaxScale - minScale < 2) { let newMinScale = newMaxScale - 2; if (newMinScale < 1) { newMinScale = 1; setMaxScale(3); } setMinScale(newMinScale); } if (newMaxScale < scale * SCALE_FACTOR) { setScaleAtCenter(newMaxScale / SCALE_FACTOR); } }} style={{ backgroundColor: "#222", borderRadius: 4, color: "#fff" }} sx={{ "& .MuiInputLabel-root": { color: "#fff", }, "& .MuiInputBase-input": { color: "#fff", }, }} slotProps={{ input: { min: 3, max: 300, }, }} /> Текущий масштаб: {Math.round(scale * SCALE_FACTOR * 100) / 100} { if (typeof newValue === "number") { setScaleAtCenter(newValue / SCALE_FACTOR); } }} min={minScale} max={maxScale} step={0.1} sx={{ color: "#fff", "& .MuiSlider-thumb": { backgroundColor: "#fff", }, "& .MuiSlider-track": { backgroundColor: "#fff", }, "& .MuiSlider-rail": { backgroundColor: "#666", }, }} /> { const newScale = Number(e.target.value); if ( !isNaN(newScale) && newScale >= minScale && newScale <= maxScale ) { setScaleAtCenter(newScale / SCALE_FACTOR); } }} style={{ backgroundColor: "#222", borderRadius: 4 }} sx={{ "& .MuiInputLabel-root": { color: "#fff", }, "& .MuiInputBase-input": { color: "#fff", }, }} inputProps={{ min: minScale, max: maxScale, }} /> Размер иконок: {iconSize}% { if (typeof value === "number") { handleIconSizeChange(value); } }} min={50} max={300} step={1} sx={{ color: "#fff", "& .MuiSlider-thumb": { backgroundColor: "#fff", }, "& .MuiSlider-track": { backgroundColor: "#fff", }, "& .MuiSlider-rail": { backgroundColor: "#666", }, }} /> Размер шрифта: {fontSize}% { if (typeof value === "number") { handleFontSizeChange(value); } }} min={50} max={300} step={1} sx={{ color: "#fff", "& .MuiSlider-thumb": { backgroundColor: "#fff", }, "& .MuiSlider-track": { backgroundColor: "#fff", }, "& .MuiSlider-rail": { backgroundColor: "#666", }, }} /> { 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, }, }} /> { setIsUserEditing(true); setLocalCenter((prev) => ({ ...prev, x: Number(e.target.value) })); }} onBlur={() => { setIsUserEditing(false); }} style={{ backgroundColor: "#222", borderRadius: 4 }} sx={{ "& .MuiInputLabel-root": { color: "#fff", }, "& .MuiInputBase-input": { color: "#fff", }, }} inputProps={{ step: 0.001, }} /> { setIsUserEditing(true); setLocalCenter((prev) => ({ ...prev, y: Number(e.target.value) })); }} onBlur={() => { setIsUserEditing(false); }} style={{ backgroundColor: "#222", borderRadius: 4 }} sx={{ "& .MuiInputLabel-root": { color: "#fff", }, "& .MuiInputBase-input": { color: "#fff", }, }} inputProps={{ step: 0.001, }} /> ); }