map3
This commit is contained in:
parent
fb16891de3
commit
4b20c94b70
@ -3,7 +3,7 @@ export const PATH_WIDTH = 15;
|
||||
export const STATION_RADIUS = 20;
|
||||
export const STATION_OUTLINE_WIDTH = 10;
|
||||
export const SIGHT_SIZE = 60;
|
||||
export const SCALE_FACTOR = 40;
|
||||
export const SCALE_FACTOR = 50;
|
||||
|
||||
export const BACKGROUND_COLOR = 0x111111;
|
||||
export const PATH_COLOR = 0xff4d4d;
|
@ -116,6 +116,11 @@ export function InfiniteCanvas({children} : Readonly<{children?: ReactNode}>) {
|
||||
setScale(newScale);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
applicationRef?.current?.getApplication()?.render();
|
||||
console.log(position, scale, rotation);
|
||||
}, [position, scale, rotation]);
|
||||
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { useCustom, useShow, useApiUrl } from "@refinedev/core";
|
||||
import { useCustom, useApiUrl } from "@refinedev/core";
|
||||
import { useParams } from "react-router";
|
||||
import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { RouteData, SightData, StationData, StationPatchData } from "./types";
|
||||
import { axiosInstance } from "../../providers/data";
|
||||
|
||||
|
||||
|
||||
const MapDataContext = createContext<{
|
||||
originalRouteData?: RouteData,
|
||||
originalStationData?: StationData[],
|
||||
@ -63,7 +61,7 @@ export function MapDataProvider({ children }: Readonly<{ children: ReactNode }>)
|
||||
});
|
||||
const { data: stationQuery, isLoading: isStationLoading } = useCustom({
|
||||
url: `${apiUrl}/route/${routeId}/station`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
});
|
||||
const { data: sightQuery, isLoading: isSightLoading } = useCustom({
|
||||
url: `${apiUrl}/route/${routeId}/sight`,
|
||||
@ -110,8 +108,7 @@ export function MapDataProvider({ children }: Readonly<{ children: ReactNode }>)
|
||||
}
|
||||
|
||||
async function saveChanges() {
|
||||
console.log("saveChanges", routeData);
|
||||
const response = await axiosInstance.patch(`/route/${routeId}`, routeData);
|
||||
await axiosInstance.patch(`/route/${routeId}`, routeData);
|
||||
saveStationChanges();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { Button, Stack, TextField, Typography } from "@mui/material";
|
||||
import { useMapData } from "./MapDataContext";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTransform } from "./TransformContext";
|
||||
import { localToCoordinates } from "./utils";
|
||||
import { coordinatesToLocal, localToCoordinates } from "./utils";
|
||||
|
||||
export function RightSidebar() {
|
||||
const { routeData, setScaleRange, saveChanges, originalRouteData, setMapRotation, setMapCenter } = useMapData();
|
||||
@ -52,7 +52,8 @@ export function RightSidebar() {
|
||||
}
|
||||
|
||||
function pan({x, y}: {x: number, y: number}) {
|
||||
setTransform(x, y);
|
||||
const coordinates = coordinatesToLocal(y,x);
|
||||
setTransform(coordinates.x, coordinates.y);
|
||||
}
|
||||
|
||||
if(!routeData) {
|
||||
|
@ -40,10 +40,6 @@ export function StationLabel({
|
||||
const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
|
||||
const [startMousePosition, setStartMousePosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
console.log(position);
|
||||
}, [position]);
|
||||
|
||||
if(!station) {
|
||||
console.error("station is null");
|
||||
return null;
|
||||
@ -64,8 +60,8 @@ export function StationLabel({
|
||||
};
|
||||
const handlePointerMove = (e: FederatedMouseEvent) => {
|
||||
if (!isDragging) return;
|
||||
const dx = (e.globalX - startMousePosition.x) / scale;
|
||||
const dy = (e.globalY - startMousePosition.y) / scale;
|
||||
const dx = (e.globalX - startMousePosition.x);
|
||||
const dy = (e.globalY - startMousePosition.y);
|
||||
const cos = Math.cos(rotation);
|
||||
const sin = Math.sin(rotation);
|
||||
const newPosition = {
|
||||
@ -93,13 +89,17 @@ export function StationLabel({
|
||||
onPointerUpOutside={handlePointerUp}
|
||||
width={48}
|
||||
height={48}
|
||||
x={coordinates.x * UP_SCALE + position.x*scale}
|
||||
y={coordinates.y * UP_SCALE + position.y*scale}
|
||||
x={coordinates.x * UP_SCALE}
|
||||
y={coordinates.y * UP_SCALE}
|
||||
rotation={-rotation}
|
||||
>
|
||||
<pixiText
|
||||
anchor={{x: 0.5, y: 0.5}}
|
||||
text={station.name}
|
||||
position={{
|
||||
x: position.x/scale,
|
||||
y: position.y/scale
|
||||
}}
|
||||
style={{
|
||||
fontSize: 48,
|
||||
fontWeight: 'bold',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContext, ReactNode, RefObject, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createContext, ReactNode, RefObject, useContext, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
ApplicationRef
|
||||
} from '@pixi/react';
|
||||
|
@ -72,7 +72,7 @@ export function RouteMap() {
|
||||
}, [originalRouteData]);
|
||||
|
||||
useEffect(() => {
|
||||
if(!applicationRef?.current || isSetup) {
|
||||
if(!applicationRef?.current?.getCanvas() || isSetup) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ export function RouteMap() {
|
||||
);
|
||||
setIsSetup(true);
|
||||
}
|
||||
}, [points, originalRouteData?.center_latitude, originalRouteData?.center_longitude, originalRouteData?.rotate, applicationRef?.current, isSetup]);
|
||||
}, [points, originalRouteData?.center_latitude, originalRouteData?.center_longitude, originalRouteData?.rotate, applicationRef?.current?.getCanvas(), isSetup]);
|
||||
|
||||
if (!routeData || !stationData || !sightData) {
|
||||
console.error("routeData, stationData or sightData is null");
|
||||
|
@ -1,10 +1,9 @@
|
||||
// approximation
|
||||
export function coordinatesToLocal(longitude: number, latitude: number) {
|
||||
|
||||
return {
|
||||
x: latitude,
|
||||
y: -longitude*2
|
||||
}
|
||||
//return {x: longitude, y: latitude}
|
||||
}
|
||||
|
||||
export function localToCoordinates(x: number, y: number) {
|
||||
@ -12,5 +11,4 @@ export function localToCoordinates(x: number, y: number) {
|
||||
latitude: x,
|
||||
longitude: -y/2
|
||||
}
|
||||
// return {latitude: x, longitude: y}
|
||||
}
|
@ -444,8 +444,8 @@ export const SightEdit = observer(() => {
|
||||
onChange={(_, newValue) => setTabValue(newValue)}
|
||||
aria-label="basic tabs example"
|
||||
>
|
||||
<Tab label="Левый виджет" {...a11yProps(1)} />
|
||||
<Tab label="Правый виджет" {...a11yProps(2)} />
|
||||
<Tab label="Левая статья" {...a11yProps(1)} />
|
||||
<Tab label="Правая статья" {...a11yProps(2)} />
|
||||
<Tab label="Основная информация" {...a11yProps(3)} />
|
||||
</Tabs>
|
||||
</Box>
|
||||
@ -594,20 +594,6 @@ export const SightEdit = observer(() => {
|
||||
name="coordinates"
|
||||
/> */}
|
||||
|
||||
<TextField
|
||||
{...register("address", {
|
||||
required: "Это поле является обязательным",
|
||||
})}
|
||||
error={!!(errors as any)?.address}
|
||||
helperText={(errors as any)?.address?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
InputLabelProps={{ shrink: true }}
|
||||
type="text"
|
||||
label={"Адрес *"}
|
||||
name="address"
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="city_id"
|
||||
@ -1153,7 +1139,7 @@ export const SightEdit = observer(() => {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: "fixed",
|
||||
p: 2,
|
||||
p: 0,
|
||||
height: "max-content",
|
||||
width: "30%",
|
||||
|
||||
@ -1172,7 +1158,6 @@ export const SightEdit = observer(() => {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
{!previewSelected && (
|
||||
@ -1192,7 +1177,6 @@ export const SightEdit = observer(() => {
|
||||
alt={mediaFile.filename}
|
||||
style={{
|
||||
maxWidth: "100%",
|
||||
height: "300px",
|
||||
objectFit: "contain",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
@ -1255,14 +1239,14 @@ export const SightEdit = observer(() => {
|
||||
{
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
mb: 2,
|
||||
mt: 0,
|
||||
mb: 0,
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
height: "250px",
|
||||
overflowY: "scroll",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{previewSelected &&
|
||||
@ -1356,7 +1340,16 @@ export const SightEdit = observer(() => {
|
||||
<Typography
|
||||
variant="h4"
|
||||
gutterBottom
|
||||
sx={{ color: "text.primary" }}
|
||||
px={2}
|
||||
py={.5}
|
||||
|
||||
sx={{
|
||||
color: "text.primary",
|
||||
background:
|
||||
"linear-gradient(180deg, hsla(0,0%,100%,.2), hsla(0,0%,100%,0)), hsla(29,15%,65%,.4)",
|
||||
boxShadow: "inset 4px 4px 12px hsla(0,0%,100%,.12)",
|
||||
|
||||
}}
|
||||
>
|
||||
{selectedArticle.heading}
|
||||
</Typography>
|
||||
@ -1366,6 +1359,7 @@ export const SightEdit = observer(() => {
|
||||
<Typography
|
||||
variant="body1"
|
||||
gutterBottom
|
||||
px={2}
|
||||
sx={{ color: "text.primary" }}
|
||||
>
|
||||
{selectedArticle.body}
|
||||
@ -1374,47 +1368,50 @@ export const SightEdit = observer(() => {
|
||||
</Box>
|
||||
)}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Привязанные статьи
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
borderRadius: 2,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
margin: "0 auto",
|
||||
background:
|
||||
"linear-gradient(180deg, hsla(0,0%,100%,.2), hsla(0,0%,100%,0)), hsla(29,15%,65%,.4)",
|
||||
boxShadow: "inset 4px 4px 12px hsla(0,0%,100%,.12)",
|
||||
gap: 1,
|
||||
mt: 2,
|
||||
|
||||
}}
|
||||
>
|
||||
{linkedArticles.map((article, index) => (
|
||||
<Box
|
||||
key={article.id}
|
||||
onClick={() => {
|
||||
setSelectedArticleIndex(index);
|
||||
setPreviewSelected(false);
|
||||
}}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
bgcolor:
|
||||
selectedArticleIndex === index
|
||||
? "primary.main"
|
||||
: "transparent",
|
||||
color:
|
||||
selectedArticleIndex === index
|
||||
? "white"
|
||||
: "inherit",
|
||||
p: 1,
|
||||
borderRadius: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{article.heading}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
{linkedArticles.map((article, index) => (
|
||||
<Box
|
||||
key={article.id}
|
||||
onClick={() => {
|
||||
setSelectedArticleIndex(index);
|
||||
setPreviewSelected(false);
|
||||
}}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
bgcolor: "transparent",
|
||||
color: "inherit",
|
||||
textDecoration:
|
||||
selectedArticleIndex === index ?
|
||||
"underline" : "none",
|
||||
p: 1,
|
||||
borderRadius: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">
|
||||
{article.heading}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -1446,6 +1443,21 @@ export const SightEdit = observer(() => {
|
||||
sx={{ flex: 1, display: "flex", flexDirection: "column" }}
|
||||
autoComplete="off"
|
||||
>
|
||||
|
||||
<TextField
|
||||
{...register("address", {
|
||||
required: "Это поле является обязательным",
|
||||
})}
|
||||
error={!!(errors as any)?.address}
|
||||
helperText={(errors as any)?.address?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
InputLabelProps={{ shrink: true }}
|
||||
type="text"
|
||||
label={"Адрес *"}
|
||||
name="address"
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="thumbnail"
|
||||
@ -1618,6 +1630,23 @@ export const SightEdit = observer(() => {
|
||||
|
||||
{thumbnailPreview && (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ display: "flex", flexDirection: "column", mb: 2 }}
|
||||
>
|
||||
<Box component="span" sx={{ color: "text.secondary" }}>
|
||||
Адрес:{" "}
|
||||
</Box>
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
color: (theme) =>
|
||||
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
|
||||
}}
|
||||
>
|
||||
{`${addressContent}`}
|
||||
</Box>
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
|
Loading…
Reference in New Issue
Block a user