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