forked from VinokurovVE/tests
Drop @mui, addded ems api
This commit is contained in:
@ -9,7 +9,7 @@ import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
||||
import { Type } from 'ol/geom/Geometry'
|
||||
import { click, never, noModifierKeys, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
|
||||
import Feature from 'ol/Feature'
|
||||
import { SatelliteMapsProvider } from '../../interfaces/map'
|
||||
import { IGeometryType, SatelliteMapsProvider } from '../../interfaces/map'
|
||||
import { containsExtent, Extent } from 'ol/extent'
|
||||
import { drawingLayerStyle, regionsLayerStyle, selectStyle } from './MapStyles'
|
||||
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
|
||||
@ -24,36 +24,28 @@ import { Stroke, Fill, Circle as CircleStyle, Style, Text } from 'ol/style'
|
||||
import { calculateCenter, calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
|
||||
import MapBrowserEvent from 'ol/MapBrowserEvent'
|
||||
import { get, transform } from 'ol/proj'
|
||||
import { useCities } from '../../hooks/swrHooks'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Accordion, ActionIcon, Autocomplete, Box, Button, CloseButton, Flex, Grid, Select as MantineSelect, Text as MantineText, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Divider, Portal } from '@mantine/core'
|
||||
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||
import { Accordion, ActionIcon, Autocomplete, Box, CloseButton, Flex, Select as MantineSelect, Text as MantineText, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Divider, Portal, Tree, Group, TreeNodeData } from '@mantine/core'
|
||||
import { IconApi, IconArrowBackUp, IconArrowsMove, IconChevronDown, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||
import { getGridCellPosition } from './mapUtils'
|
||||
import { IFigure, ILine } from '../../interfaces/gis'
|
||||
import axios from 'axios'
|
||||
import ObjectParameter from './ObjectParameter'
|
||||
import { IObjectData, IObjectParam } from '../../interfaces/objects'
|
||||
import { IObjectData, IObjectList, IObjectParam } from '../../interfaces/objects'
|
||||
import ObjectData from './ObjectData'
|
||||
import { uploadCoordinates } from '../../actions/map'
|
||||
import MapToolbar from './MapToolbar/MapToolbar'
|
||||
import MapStatusbar from './MapStatusbar/MapStatusbar'
|
||||
|
||||
const MapComponent = () => {
|
||||
//const { cities } = useCities(100, 1)
|
||||
|
||||
// useEffect(() => {
|
||||
// if (cities) {
|
||||
// cities.map((city: any) => {
|
||||
// citiesLayer.current?.getSource()?.addFeature(new Feature(new Point(fromLonLat([city.longitude, city.width]))))
|
||||
// })
|
||||
// }
|
||||
// }, [cities])
|
||||
|
||||
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
||||
const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
|
||||
const [currentX, setCurrentX] = useState<number | undefined>(undefined)
|
||||
const [currentY, setCurrentY] = useState<number | undefined>(undefined)
|
||||
|
||||
const [file, setFile] = useState(null)
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const [polygonExtent, setPolygonExtent] = useState<Extent | undefined>(undefined)
|
||||
const [bottomLeft, setBottomLeft] = useState<Coordinate | undefined>(undefined)
|
||||
const [topLeft, setTopLeft] = useState<Coordinate | undefined>(undefined)
|
||||
@ -70,7 +62,7 @@ const MapComponent = () => {
|
||||
const gMapsSatSource = useRef<XYZ>(googleMapsSatelliteSource)
|
||||
|
||||
const customMapSource = useRef<XYZ>(new XYZ({
|
||||
url: `${import.meta.env.VITE_API_EMS_URL}/tile/custom/{z}/{x}/{y}`,
|
||||
url: `${import.meta.env.VITE_API_EMS_URL}/tiles/tile/custom/{z}/{x}/{y}`,
|
||||
attributions: 'Custom map data'
|
||||
}))
|
||||
|
||||
@ -133,7 +125,7 @@ const MapComponent = () => {
|
||||
|
||||
draw.current.on('drawend', function (s) {
|
||||
console.log(s.feature.getGeometry()?.getType())
|
||||
let type = 'POLYGON'
|
||||
let type: IGeometryType = 'POLYGON'
|
||||
|
||||
switch (s.feature.getGeometry()?.getType()) {
|
||||
case 'LineString':
|
||||
@ -146,7 +138,7 @@ const MapComponent = () => {
|
||||
type = 'POLYGON'
|
||||
break
|
||||
}
|
||||
const coordinates = (s.feature.getGeometry() as SimpleGeometry).getCoordinates()
|
||||
const coordinates = (s.feature.getGeometry() as SimpleGeometry).getCoordinates() as Coordinate[]
|
||||
uploadCoordinates(coordinates, type)
|
||||
})
|
||||
|
||||
@ -222,10 +214,12 @@ const MapComponent = () => {
|
||||
});
|
||||
|
||||
// tile processing
|
||||
const handleImageDrop = useCallback((event: any) => {
|
||||
const handleImageDrop = useCallback((event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (!event.dataTransfer?.files) return
|
||||
|
||||
const files = event.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
const file = files[0];
|
||||
@ -657,27 +651,6 @@ const MapComponent = () => {
|
||||
}
|
||||
}, [currentTool])
|
||||
|
||||
const uploadCoordinates = async (coordinates: any, type: any) => {
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_API_EMS_URL}/nodes`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ coordinates, object_id: 1, type: type }) // Replace with actual object_id
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('Node created:', data);
|
||||
} else {
|
||||
console.error('Failed to upload coordinates');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(1)
|
||||
|
||||
const [statusText, setStatusText] = useState('')
|
||||
@ -721,7 +694,7 @@ const MapComponent = () => {
|
||||
formData.append('brX', bottomRight[0].toString())
|
||||
formData.append('brY', bottomRight[1].toString())
|
||||
|
||||
await fetch(`${import.meta.env.VITE_API_EMS_URL}/upload`, { method: 'POST', body: formData })
|
||||
await fetch(`${import.meta.env.VITE_API_EMS_URL}/tiles/upload`, { method: 'POST', body: formData })
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,6 +837,122 @@ const MapComponent = () => {
|
||||
|
||||
const [searchCity, setSearchCity] = useState<string | undefined>("")
|
||||
|
||||
const { data: existingObjectsList } = useSWR(
|
||||
selectedYear && selectedCity ? `/general/objects/list?year=${selectedYear}&city_id=${selectedCity}&planning=0` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const { data: planningObjectsList } = useSWR(
|
||||
selectedYear && selectedCity ? `/general/objects/list?year=${selectedYear}&city_id=${selectedCity}&planning=1` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const [objectsList, setObjectsList] = useState<TreeNodeData[] | null>(null)
|
||||
const [selectedObjectList, setSelectedObjectList] = useState<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedObjectList || !map.current) return;
|
||||
|
||||
// Define the highlight style
|
||||
const highlightStyle = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'yellow',
|
||||
width: 3,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 0, 0.3)',
|
||||
}),
|
||||
});
|
||||
|
||||
if (figuresLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
figuresLayer.current.getSource().getFeatures().forEach((feature) => {
|
||||
if (selectedObjectList == feature.get('type')) {
|
||||
feature.setStyle(highlightStyle);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (linesLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
linesLayer.current.getSource().getFeatures().forEach((feature) => {
|
||||
if (selectedObjectList == feature.get('type')) {
|
||||
feature.setStyle(highlightStyle);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [selectedObjectList])
|
||||
|
||||
useEffect(() => {
|
||||
if (existingObjectsList && planningObjectsList) {
|
||||
setObjectsList([
|
||||
{
|
||||
label: 'Существующие',
|
||||
value: 'existing',
|
||||
children: existingObjectsList.map((list: IObjectList) => ({
|
||||
label: `${list.name} (${list.count})`,
|
||||
value: list.id,
|
||||
})),
|
||||
},
|
||||
{
|
||||
label: 'Планируемые',
|
||||
value: 'planning',
|
||||
children: planningObjectsList.map((list: IObjectList) => ({
|
||||
label: `${list.name} (${list.count})`,
|
||||
value: list.id
|
||||
}))
|
||||
}
|
||||
])
|
||||
}
|
||||
}, [existingObjectsList, planningObjectsList])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentObjectId) {
|
||||
// Define the highlight style
|
||||
const highlightStyle = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'red',
|
||||
width: 3,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 0, 0.3)',
|
||||
}),
|
||||
});
|
||||
|
||||
if (figuresLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
figuresLayer.current.getSource().getFeatures().forEach((feature) => {
|
||||
if (currentObjectId == feature.get('object_id')) {
|
||||
feature.setStyle(highlightStyle);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (linesLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
linesLayer.current.getSource().getFeatures().forEach((feature) => {
|
||||
if (currentObjectId == feature.get('object_id')) {
|
||||
feature.setStyle(highlightStyle);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [currentObjectId])
|
||||
|
||||
const { data: currentObjectData } = useSWR(
|
||||
currentObjectId ? `/general/objects/${currentObjectId}` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
@ -939,7 +1028,9 @@ const MapComponent = () => {
|
||||
const feature = new Feature(ellipseGeom)
|
||||
|
||||
feature.setStyle(firstStyleFunction(feature))
|
||||
feature.set('type', figure.type)
|
||||
feature.set('object_id', figure.object_id)
|
||||
feature.set('planning', figure.planning)
|
||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||
}
|
||||
|
||||
@ -965,6 +1056,8 @@ const MapComponent = () => {
|
||||
})
|
||||
|
||||
feature.set('object_id', figure.object_id)
|
||||
feature.set('planning', figure.planning)
|
||||
feature.set('type', figure.type)
|
||||
feature.setStyle(thirdStyleFunction(feature))
|
||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||
}
|
||||
@ -994,6 +1087,8 @@ const MapComponent = () => {
|
||||
geometry1.rotate(-figure.angle * Math.PI / 180, anchor1)
|
||||
const feature1 = new Feature(geometry1)
|
||||
feature1.set('object_id', figure.object_id)
|
||||
feature1.set('planning', figure.planning)
|
||||
feature1.set('type', figure.type)
|
||||
feature1.set('angle', figure.angle)
|
||||
feature1.setStyle(fourthStyleFunction(feature1))
|
||||
figuresLayer.current?.getSource()?.addFeature(feature1)
|
||||
@ -1025,6 +1120,8 @@ const MapComponent = () => {
|
||||
|
||||
const feature = new Feature(new LineString(testCoords))
|
||||
feature.setStyle(styleFunction(feature))
|
||||
feature.set('type', line.type)
|
||||
feature.set('planning', line.planning)
|
||||
feature.set('object_id', line.object_id)
|
||||
|
||||
linesLayer.current?.getSource()?.addFeature(feature)
|
||||
@ -1037,6 +1134,12 @@ const MapComponent = () => {
|
||||
<Box w={'100%'} h='100%' pos={'relative'}>
|
||||
<Portal target='#header-portal'>
|
||||
<Flex gap={'sm'} direction={'row'}>
|
||||
<Flex align='center' direction='row' gap='sm'>
|
||||
<Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
|
||||
|
||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||
</Flex>
|
||||
|
||||
<form>
|
||||
<Autocomplete
|
||||
placeholder="Район"
|
||||
@ -1091,76 +1194,14 @@ const MapComponent = () => {
|
||||
</Portal>
|
||||
|
||||
<Flex w={'100%'} h={'100%'} pos={'absolute'}>
|
||||
<ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
|
||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||
fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
|
||||
}}>
|
||||
<IconApi />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||
saveFeatures()
|
||||
}}>
|
||||
<IconExclamationCircle />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||
draw.current?.removeLastPoint()
|
||||
}}>
|
||||
<IconArrowBackUp />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Point' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Point')
|
||||
}}>
|
||||
<IconPoint />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'LineString' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('LineString')
|
||||
}}>
|
||||
<IconLine />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Polygon' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Polygon')
|
||||
}}>
|
||||
<IconPolygon />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Circle' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Circle')
|
||||
}}>
|
||||
<IconCircle />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant='transparent'
|
||||
onClick={() => map?.current?.addInteraction(new Translate())}
|
||||
>
|
||||
<IconArrowsMove />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant='transparent'
|
||||
>
|
||||
<IconRuler />
|
||||
</ActionIcon>
|
||||
</ActionIcon.Group>
|
||||
<MapToolbar
|
||||
currentTool={currentTool}
|
||||
onSave={() => saveFeatures()}
|
||||
onRemove={() => draw.current?.removeLastPoint()}
|
||||
handleToolSelect={handleToolSelect}
|
||||
onMover={() => map?.current?.addInteraction(new Translate())}
|
||||
colorScheme={colorScheme}
|
||||
/>
|
||||
|
||||
<Flex direction='column' mah={'86%'} pl='sm' style={{
|
||||
...mapControlsStyle,
|
||||
@ -1188,73 +1229,76 @@ const MapComponent = () => {
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
|
||||
<Flex align='center' direction='row' p='sm' gap='sm'>
|
||||
<Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
|
||||
|
||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||
</Flex>
|
||||
|
||||
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
|
||||
<Accordion.Item key={'objects'} value={'Объекты'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
|
||||
{objectsList &&
|
||||
<Tree
|
||||
data={objectsList}
|
||||
selectOnClick
|
||||
levelOffset={23}
|
||||
renderNode={({ node, expanded, hasChildren, elementProps }) => (
|
||||
<Group gap={6} {...elementProps} onClick={(e) => {
|
||||
elementProps.onClick(e)
|
||||
if (node.value !== 'existing' && node.value !== 'planning') {
|
||||
setSelectedObjectList(Number(node.value))
|
||||
}
|
||||
}}>
|
||||
{hasChildren && (
|
||||
<IconChevronDown
|
||||
size={18}
|
||||
style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<MantineText size='sm'>{node.label}</MantineText>
|
||||
</Group>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item key={'current_object'} value='current_object'>
|
||||
<Accordion.Control icon={<IconTable />}>
|
||||
{'Текущий объект'}
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<ObjectData {...currentObjectData as IObjectData} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
{currentObjectId &&
|
||||
<Accordion.Item key={'current_object'} value={currentObjectId}>
|
||||
<Accordion.Control icon={<IconTable />}>
|
||||
{'Текущий объект'}
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<ObjectData {...currentObjectData as IObjectData} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
}
|
||||
|
||||
{valuesData && <Accordion.Item key={'parameters'} value={'Параметры объекта'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Flex gap={'sm'} direction={'column'}>
|
||||
{Array.isArray(valuesData) && valuesData.map((param: IObjectParam) => {
|
||||
return (
|
||||
<ObjectParameter key={param.id_param} value={param.value} id_param={param.id_param} />
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>}
|
||||
{valuesData &&
|
||||
<Accordion.Item key={'parameters'} value={'Параметры объекта'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Flex gap={'sm'} direction={'column'}>
|
||||
{Array.isArray(valuesData) && valuesData.map((param: IObjectParam) => {
|
||||
return (
|
||||
<ObjectParameter key={param.id_param} value={param.value} id_param={param.id_param} />
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
}
|
||||
</Accordion>
|
||||
</ScrollAreaAutosize>
|
||||
|
||||
</Flex>
|
||||
|
||||
<Flex gap='sm' p={'4px'} miw={'100%'} fz={'xs'} pos='absolute' bottom='0px' left='0px' style={{ ...mapControlsStyle, borderRadius: 0 }}>
|
||||
<MantineText fz='xs' w={rem(130)}>
|
||||
x: {currentCoordinate?.[0]}
|
||||
</MantineText>
|
||||
|
||||
<MantineText fz='xs' w={rem(130)}>
|
||||
y: {currentCoordinate?.[1]}
|
||||
</MantineText>
|
||||
|
||||
<Divider orientation='vertical' />
|
||||
|
||||
<MantineText fz='xs'>
|
||||
Z={currentZ}
|
||||
</MantineText>
|
||||
|
||||
<MantineText fz='xs'>
|
||||
X={currentX}
|
||||
</MantineText>
|
||||
|
||||
<MantineText fz='xs'>
|
||||
Y={currentY}
|
||||
</MantineText>
|
||||
|
||||
<MantineText fz='xs' ml='auto'>
|
||||
{statusText}
|
||||
</MantineText>
|
||||
</Flex>
|
||||
<MapStatusbar
|
||||
mapControlsStyle={mapControlsStyle}
|
||||
currentCoordinate={currentCoordinate}
|
||||
currentX={currentX}
|
||||
currentY={currentY}
|
||||
currentZ={currentZ}
|
||||
statusText={statusText}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user