Files
WhiteNightsAdminPanel/src/pages/Route/route-preview/index.tsx
2025-10-10 08:40:39 +03:00

191 lines
5.2 KiB
TypeScript

import { useRef, useEffect, useState } from "react";
import { Widgets } from "./Widgets";
import { Application, extend } from "@pixi/react";
import {
Container,
Graphics,
Sprite,
Texture,
TilingSprite,
Text,
} from "pixi.js";
import { Stack } from "@mui/material";
import { MapDataProvider, useMapData } from "./MapDataContext";
import { TransformProvider, useTransform } from "./TransformContext";
import { InfiniteCanvas } from "./InfiniteCanvas";
import { TravelPath } from "./TravelPath";
import { LeftSidebar } from "./LeftSidebar";
import { RightSidebar } from "./RightSidebar";
import { coordinatesToLocal } from "./utils";
import { LanguageSwitcher } from "@widgets";
import { languageStore } from "@shared";
import { observer } from "mobx-react-lite";
import { Sight } from "./Sight";
import { SightData } from "./types";
import { Station } from "./Station";
import { UP_SCALE } from "./Constants";
import CircularProgress from "@mui/material/CircularProgress";
extend({
Container,
Graphics,
Sprite,
Texture,
TilingSprite,
Text,
});
const Loading = () => {
const { isRouteLoading, isStationLoading, isSightLoading } = useMapData();
if (isRouteLoading || isStationLoading || isSightLoading) {
return (
<div className="fixed flex z-1 items-center justify-center h-screen w-screen bg-[#111]">
<CircularProgress />
</div>
);
}
return null;
};
export const RoutePreview = () => {
const { routeData, stationData, sightData } = useMapData();
return (
<MapDataProvider>
<TransformProvider>
<Stack direction="row" height="100vh" width="100vw" overflow="hidden">
{routeData && stationData && sightData ? <LanguageSwitcher /> : null}
<Loading />
<LeftSidebar />
<Stack direction="row" flex={1} position="relative" height="100%">
<RouteMap />
<Widgets />
<RightSidebar />
</Stack>
</Stack>
</TransformProvider>
</MapDataProvider>
);
};
export const RouteMap = observer(() => {
const { language } = languageStore;
const { setPosition, setTransform, screenCenter } = useTransform();
const {
routeData,
stationData,
sightData,
originalRouteData,
originalSightData,
} = useMapData();
const [points, setPoints] = useState<{ x: number; y: number }[]>([]);
const [isSetup, setIsSetup] = useState(false);
const parentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "auto";
};
}, []);
useEffect(() => {
if (originalRouteData) {
const path = originalRouteData?.path;
const points =
path?.map(([x, y]: [number, number]) => ({
x: x * UP_SCALE,
y: y * UP_SCALE,
})) ?? [];
setPoints(points);
}
}, [originalRouteData]);
useEffect(() => {
if (isSetup || !screenCenter) {
return;
}
if (
originalRouteData?.center_latitude ===
originalRouteData?.center_longitude &&
originalRouteData?.center_latitude === 0
) {
if (points.length > 0) {
let boundingBox = {
from: { x: Infinity, y: Infinity },
to: { x: -Infinity, y: -Infinity },
};
for (const point of points) {
boundingBox.from.x = Math.min(boundingBox.from.x, point.x);
boundingBox.from.y = Math.min(boundingBox.from.y, point.y);
boundingBox.to.x = Math.max(boundingBox.to.x, point.x);
boundingBox.to.y = Math.max(boundingBox.to.y, point.y);
}
const newCenter = {
x: -(boundingBox.from.x + boundingBox.to.x) / 2,
y: -(boundingBox.from.y + boundingBox.to.y) / 2,
};
setPosition(newCenter);
setIsSetup(true);
}
} else if (
originalRouteData?.center_latitude &&
originalRouteData?.center_longitude
) {
const coordinates = coordinatesToLocal(
originalRouteData?.center_latitude,
originalRouteData?.center_longitude
);
setTransform(
coordinates.x,
coordinates.y,
originalRouteData?.rotate,
originalRouteData?.scale_min
);
setIsSetup(true);
}
}, [
points,
originalRouteData?.center_latitude,
originalRouteData?.center_longitude,
originalRouteData?.rotate,
isSetup,
screenCenter,
]);
if (!routeData || !stationData || !sightData) {
return null;
}
return (
<div style={{ width: "100%", height: "100%" }} ref={parentRef}>
<LanguageSwitcher />
<Application resizeTo={parentRef} background="#fff" preference="webgl">
<InfiniteCanvas>
<TravelPath points={points} />
{stationData[language].map((obj, index) => (
<Station
station={obj}
key={obj.id}
ruLabel={
language === "ru"
? stationData.ru[index].name
: stationData.ru[index].name
}
/>
))}
{originalSightData?.map((sight: SightData, index: number) => {
return <Sight sight={sight} id={index} key={sight.id} />;
})}
</InfiniteCanvas>
</Application>
</div>
);
});