mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-05-31 08:36:48 +02:00
Moved the scale unit control to its own component file for easier maintenance. Enhanced the behavior of the coordinate display on the map to not display when over the on screen controls, and the navigation bar. Added a toggle to turn off the coordinate display if the user doesn't wish to see it. Intentionally left the coordinate display when over a map marker so that the coordinates of the map marker can be estimated. In the future I intend to add the coordinates of a map marker when the map marker is clicked so that behavior may change in the future.
This commit is contained in:
parent
732296d179
commit
1d7c40c37b
25
admin/inertia/components/maps/CoordinateOverlay.tsx
Normal file
25
admin/inertia/components/maps/CoordinateOverlay.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
type CoordinateOverlayProps = {
|
||||
latitude: number
|
||||
longitude: number
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export default function CoordinateOverlay({
|
||||
latitude,
|
||||
longitude,
|
||||
x,
|
||||
y,
|
||||
}: CoordinateOverlayProps) {
|
||||
return (
|
||||
<div
|
||||
className="pointer-events-none absolute z-[9999] -translate-x-1/2 whitespace-nowrap rounded bg-black/75 px-2 py-1 font-mono text-[11px] text-white"
|
||||
style={{
|
||||
left: x,
|
||||
top: y - 36,
|
||||
}}
|
||||
>
|
||||
{latitude.toFixed(6)}, {longitude.toFixed(6)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,20 +9,35 @@ import Map, {
|
|||
import type { MapRef, MapLayerMouseEvent } from 'react-map-gl/maplibre'
|
||||
import maplibregl from 'maplibre-gl'
|
||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||
|
||||
import { Protocol } from 'pmtiles'
|
||||
import { useEffect, useRef, useState, useCallback } from 'react'
|
||||
|
||||
type ScaleUnit = 'imperial' | 'metric'
|
||||
|
||||
import { useMapMarkers, PIN_COLORS } from '~/hooks/useMapMarkers'
|
||||
import type { PinColorId } from '~/hooks/useMapMarkers'
|
||||
|
||||
import MarkerPin from './MarkerPin'
|
||||
import MarkerPanel from './MarkerPanel'
|
||||
import CoordinateOverlay from './CoordinateOverlay'
|
||||
import ScaleUnitToggle from './ScaleUnitToggle'
|
||||
|
||||
export default function MapComponent({ isHoveringUI, showCoordinatesEnabled, }: {isHoveringUI: boolean, showCoordinatesEnabled: boolean}) {
|
||||
type ScaleUnit = 'imperial' | 'metric'
|
||||
|
||||
type MapComponentProps = {
|
||||
isHoveringUI: boolean
|
||||
showCoordinatesEnabled: boolean
|
||||
}
|
||||
|
||||
export default function MapComponent({
|
||||
isHoveringUI,
|
||||
showCoordinatesEnabled,
|
||||
}: MapComponentProps) {
|
||||
const mapRef = useRef<MapRef>(null)
|
||||
const animationFrameRef = useRef<number | null>(null)
|
||||
|
||||
const { markers, addMarker, deleteMarker } = useMapMarkers()
|
||||
|
||||
const [isDraggingMap, setIsDraggingMap] = useState(false)
|
||||
const [placingMarker, setPlacingMarker] = useState<{ lng: number; lat: number } | null>(null)
|
||||
const [markerName, setMarkerName] = useState('')
|
||||
const [markerColor, setMarkerColor] = useState<PinColorId>('orange')
|
||||
|
|
@ -38,16 +53,8 @@ export default function MapComponent({ isHoveringUI, showCoordinatesEnabled, }:
|
|||
x: number
|
||||
y: number
|
||||
} | null>(null)
|
||||
|
||||
const [showCoordinates, setShowCoordinates] = useState(false)
|
||||
|
||||
const toggleScaleUnit = useCallback(() => {
|
||||
setScaleUnit((prev) => {
|
||||
const next = prev === 'metric' ? 'imperial' : 'metric'
|
||||
localStorage.setItem('nomad:map-scale-unit', next)
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
const [showCoordinates, setShowCoordinates] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const protocol = new Protocol()
|
||||
|
|
@ -58,35 +65,54 @@ export default function MapComponent({ isHoveringUI, showCoordinatesEnabled, }:
|
|||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const hideCoordinates = useCallback(() => {
|
||||
setShowCoordinates(false)
|
||||
setCursorLngLat(null)
|
||||
}, [])
|
||||
|
||||
const handleMouseMove = useCallback((e: MapLayerMouseEvent) => {
|
||||
const target = e.originalEvent.target as HTMLElement | null
|
||||
const handleScaleUnitChange = useCallback((unit: ScaleUnit) => {
|
||||
setScaleUnit(unit)
|
||||
localStorage.setItem('nomad:map-scale-unit', unit)
|
||||
}, [])
|
||||
|
||||
if (target?.closest('.maplibregl-control-container, .maplibregl-ctrl')) {
|
||||
hideCoordinates()
|
||||
return
|
||||
}
|
||||
const handleMouseMove = useCallback(
|
||||
(e: MapLayerMouseEvent) => {
|
||||
const target = e.originalEvent.target as HTMLElement | null
|
||||
|
||||
if (!showCoordinatesEnabled ||
|
||||
isHoveringUI ||
|
||||
target?.closest('.maplibregl-control-container, .maplibregl-ctrl')
|
||||
) {
|
||||
hideCoordinates()
|
||||
return
|
||||
}
|
||||
if (
|
||||
!showCoordinatesEnabled ||
|
||||
isHoveringUI ||
|
||||
isDraggingMap ||
|
||||
target?.closest('.maplibregl-control-container, .maplibregl-ctrl')
|
||||
) {
|
||||
hideCoordinates()
|
||||
return
|
||||
}
|
||||
|
||||
setShowCoordinates(true)
|
||||
setCursorLngLat({
|
||||
lng: e.lngLat.lng,
|
||||
lat: e.lngLat.lat,
|
||||
x: e.point.x,
|
||||
y: e.point.y,
|
||||
})
|
||||
}, [hideCoordinates, isHoveringUI])
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current)
|
||||
}
|
||||
|
||||
animationFrameRef.current = requestAnimationFrame(() => {
|
||||
setShowCoordinates(true)
|
||||
setCursorLngLat({
|
||||
lng: e.lngLat.lng,
|
||||
lat: e.lngLat.lat,
|
||||
x: e.point.x,
|
||||
y: e.point.y,
|
||||
})
|
||||
})
|
||||
},
|
||||
[hideCoordinates, isHoveringUI, isDraggingMap, showCoordinatesEnabled]
|
||||
)
|
||||
|
||||
const handleMapClick = useCallback((e: MapLayerMouseEvent) => {
|
||||
setPlacingMarker({ lng: e.lngLat.lng, lat: e.lngLat.lat })
|
||||
|
|
@ -120,203 +146,169 @@ export default function MapComponent({ isHoveringUI, showCoordinatesEnabled, }:
|
|||
|
||||
return (
|
||||
<MapProvider>
|
||||
<div
|
||||
style={{ position: 'relative', width: '100%', height: '100vh' }}
|
||||
onMouseLeave={hideCoordinates}
|
||||
onMouseMoveCapture={(e) => {
|
||||
const target = e.target as HTMLElement | null
|
||||
|
||||
if (
|
||||
target?.closest(
|
||||
'.maplibregl-control-container, .maplibregl-ctrl, .maplibregl-ctrl-group, .maplibregl-ctrl-scale'
|
||||
)
|
||||
) {
|
||||
<div
|
||||
style={{ position: 'relative', width: '100%', height: '100vh' }}
|
||||
onMouseLeave={() => {
|
||||
setIsDraggingMap(false)
|
||||
hideCoordinates()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Map
|
||||
ref={mapRef}
|
||||
reuseMaps
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100vh',
|
||||
}}
|
||||
cursor="crosshair"
|
||||
mapStyle={`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/maps/styles`}
|
||||
mapLib={maplibregl}
|
||||
initialViewState={{
|
||||
longitude: -101,
|
||||
latitude: 40,
|
||||
zoom: 3.5,
|
||||
onMouseMoveCapture={(e) => {
|
||||
const target = e.target as HTMLElement | null
|
||||
|
||||
if (
|
||||
target?.closest(
|
||||
'.maplibregl-control-container, .maplibregl-ctrl, .maplibregl-ctrl-group, .maplibregl-ctrl-scale'
|
||||
)
|
||||
) {
|
||||
hideCoordinates()
|
||||
}
|
||||
}}
|
||||
onClick={handleMapClick}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={hideCoordinates}
|
||||
>
|
||||
<NavigationControl style={{ marginTop: '110px', marginRight: '36px' }} />
|
||||
<FullscreenControl style={{ marginTop: '30px', marginRight: '36px' }} />
|
||||
<ScaleControl position="bottom-left" maxWidth={150} unit={scaleUnit} />
|
||||
<Map
|
||||
ref={mapRef}
|
||||
reuseMaps
|
||||
style={{ width: '100%', height: '100vh' }}
|
||||
cursor={isDraggingMap ? 'grabbing' : 'crosshair'}
|
||||
mapStyle={`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/maps/styles`}
|
||||
mapLib={maplibregl}
|
||||
initialViewState={{
|
||||
longitude: -101,
|
||||
latitude: 40,
|
||||
zoom: 3.5,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
setIsDraggingMap(true)
|
||||
hideCoordinates()
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
setIsDraggingMap(false)
|
||||
}}
|
||||
onDragStart={() => {
|
||||
setIsDraggingMap(true)
|
||||
hideCoordinates()
|
||||
}}
|
||||
onDragEnd={() => {
|
||||
setIsDraggingMap(false)
|
||||
hideCoordinates()
|
||||
}}
|
||||
onClick={handleMapClick}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={hideCoordinates}
|
||||
>
|
||||
<NavigationControl style={{ marginTop: '110px', marginRight: '36px' }} />
|
||||
<FullscreenControl style={{ marginTop: '30px', marginRight: '36px' }} />
|
||||
<ScaleControl position="bottom-left" maxWidth={150} unit={scaleUnit} />
|
||||
|
||||
{showCoordinates && showCoordinates && cursorLngLat && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: cursorLngLat.x,
|
||||
top: cursorLngLat.y - 36,
|
||||
transform: 'translateX(-50%)',
|
||||
zIndex: 9999,
|
||||
pointerEvents: 'none',
|
||||
background: 'rgba(0, 0, 0, 0.75)',
|
||||
color: 'white',
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '11px',
|
||||
fontFamily: 'monospace',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{cursorLngLat.lat.toFixed(6)}, {cursorLngLat.lng.toFixed(6)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ position: 'absolute', bottom: '30px', left: '10px', zIndex: 2 }}>
|
||||
<div
|
||||
onMouseEnter={hideCoordinates}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0 0 0 2px rgba(0,0,0,0.1)',
|
||||
overflow: 'hidden',
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'metric') toggleScaleUnit()
|
||||
}}
|
||||
style={{
|
||||
background: scaleUnit === 'metric' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'metric' ? 'white' : '#666',
|
||||
border: 'none',
|
||||
padding: '4px 8px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Metric
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'imperial') toggleScaleUnit()
|
||||
}}
|
||||
style={{
|
||||
background: scaleUnit === 'imperial' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'imperial' ? 'white' : '#666',
|
||||
border: 'none',
|
||||
padding: '4px 8px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Imperial
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{markers.map((marker) => (
|
||||
<Marker
|
||||
key={marker.id}
|
||||
longitude={marker.longitude}
|
||||
latitude={marker.latitude}
|
||||
anchor="bottom"
|
||||
onClick={(e) => {
|
||||
e.originalEvent.stopPropagation()
|
||||
setSelectedMarkerId(marker.id === selectedMarkerId ? null : marker.id)
|
||||
setPlacingMarker(null)
|
||||
}}
|
||||
>
|
||||
<MarkerPin
|
||||
color={PIN_COLORS.find((c) => c.id === marker.color)?.hex}
|
||||
active={marker.id === selectedMarkerId}
|
||||
{showCoordinates && cursorLngLat && (
|
||||
<CoordinateOverlay
|
||||
latitude={cursorLngLat.lat}
|
||||
longitude={cursorLngLat.lng}
|
||||
x={cursorLngLat.x}
|
||||
y={cursorLngLat.y}
|
||||
/>
|
||||
</Marker>
|
||||
))}
|
||||
)}
|
||||
|
||||
{selectedMarker && (
|
||||
<Popup
|
||||
longitude={selectedMarker.longitude}
|
||||
latitude={selectedMarker.latitude}
|
||||
anchor="bottom"
|
||||
offset={[0, -36] as [number, number]}
|
||||
onClose={() => setSelectedMarkerId(null)}
|
||||
closeOnClick={false}
|
||||
>
|
||||
<div className="text-sm font-medium">{selectedMarker.name}</div>
|
||||
</Popup>
|
||||
)}
|
||||
<ScaleUnitToggle
|
||||
scaleUnit={scaleUnit}
|
||||
onChange={handleScaleUnitChange}
|
||||
onMouseEnter={hideCoordinates}
|
||||
/>
|
||||
|
||||
{placingMarker && (
|
||||
<Popup
|
||||
longitude={placingMarker.lng}
|
||||
latitude={placingMarker.lat}
|
||||
anchor="bottom"
|
||||
onClose={() => setPlacingMarker(null)}
|
||||
closeOnClick={false}
|
||||
>
|
||||
<div onMouseEnter={hideCoordinates} className="p-1">
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder="Name this location"
|
||||
value={markerName}
|
||||
onChange={(e) => setMarkerName(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleSaveMarker()
|
||||
if (e.key === 'Escape') setPlacingMarker(null)
|
||||
}}
|
||||
className="block w-full rounded border border-gray-300 px-2 py-1 text-sm placeholder:text-gray-400 focus:outline-none focus:border-gray-500"
|
||||
{markers.map((marker) => (
|
||||
<Marker
|
||||
key={marker.id}
|
||||
longitude={marker.longitude}
|
||||
latitude={marker.latitude}
|
||||
anchor="bottom"
|
||||
onClick={(e) => {
|
||||
e.originalEvent.stopPropagation()
|
||||
setSelectedMarkerId(marker.id === selectedMarkerId ? null : marker.id)
|
||||
setPlacingMarker(null)
|
||||
}}
|
||||
>
|
||||
<MarkerPin
|
||||
color={PIN_COLORS.find((c) => c.id === marker.color)?.hex}
|
||||
active={marker.id === selectedMarkerId}
|
||||
/>
|
||||
</Marker>
|
||||
))}
|
||||
|
||||
<div className="mt-1.5 flex gap-1 items-center">
|
||||
{PIN_COLORS.map((c) => (
|
||||
{selectedMarker && (
|
||||
<Popup
|
||||
longitude={selectedMarker.longitude}
|
||||
latitude={selectedMarker.latitude}
|
||||
anchor="bottom"
|
||||
offset={[0, -36]}
|
||||
onClose={() => setSelectedMarkerId(null)}
|
||||
closeOnClick={false}
|
||||
>
|
||||
<div className="text-sm font-medium">{selectedMarker.name}</div>
|
||||
</Popup>
|
||||
)}
|
||||
|
||||
{placingMarker && (
|
||||
<Popup
|
||||
longitude={placingMarker.lng}
|
||||
latitude={placingMarker.lat}
|
||||
anchor="bottom"
|
||||
onClose={() => setPlacingMarker(null)}
|
||||
closeOnClick={false}
|
||||
>
|
||||
<div onMouseEnter={hideCoordinates} className="p-1">
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder="Name this location"
|
||||
value={markerName}
|
||||
onChange={(e) => setMarkerName(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleSaveMarker()
|
||||
if (e.key === 'Escape') setPlacingMarker(null)
|
||||
}}
|
||||
className="block w-full rounded border border-gray-300 px-2 py-1 text-sm placeholder:text-gray-400 focus:outline-none focus:border-gray-500"
|
||||
/>
|
||||
|
||||
<div className="mt-1.5 flex gap-1 items-center">
|
||||
{PIN_COLORS.map((c) => (
|
||||
<button
|
||||
key={c.id}
|
||||
type="button"
|
||||
onClick={() => setMarkerColor(c.id)}
|
||||
title={c.label}
|
||||
className="rounded-full p-0.5 transition-transform"
|
||||
style={{
|
||||
outline:
|
||||
markerColor === c.id ? `2px solid ${c.hex}` : '2px solid transparent',
|
||||
outlineOffset: '1px',
|
||||
}}
|
||||
>
|
||||
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: c.hex }} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-1.5 flex gap-1.5 justify-end">
|
||||
<button
|
||||
key={c.id}
|
||||
onClick={() => setMarkerColor(c.id)}
|
||||
title={c.label}
|
||||
className="rounded-full p-0.5 transition-transform"
|
||||
style={{
|
||||
outline:
|
||||
markerColor === c.id ? `2px solid ${c.hex}` : '2px solid transparent',
|
||||
outlineOffset: '1px',
|
||||
}}
|
||||
type="button"
|
||||
onClick={() => setPlacingMarker(null)}
|
||||
className="text-xs text-gray-500 hover:text-gray-700 px-2 py-1 rounded transition-colors"
|
||||
>
|
||||
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: c.hex }} />
|
||||
Cancel
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-1.5 flex gap-1.5 justify-end">
|
||||
<button
|
||||
onClick={() => setPlacingMarker(null)}
|
||||
className="text-xs text-gray-500 hover:text-gray-700 px-2 py-1 rounded transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleSaveMarker}
|
||||
disabled={!markerName.trim()}
|
||||
className="text-xs bg-[#424420] text-white rounded px-2.5 py-1 hover:bg-[#525530] disabled:opacity-40 transition-colors"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSaveMarker}
|
||||
disabled={!markerName.trim()}
|
||||
className="text-xs bg-[#424420] text-white rounded px-2.5 py-1 hover:bg-[#525530] disabled:opacity-40 transition-colors"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
)}
|
||||
</Map>
|
||||
</Popup>
|
||||
)}
|
||||
</Map>
|
||||
</div>
|
||||
|
||||
<div onMouseEnter={hideCoordinates}>
|
||||
|
|
|
|||
46
admin/inertia/components/maps/ScaleUnitToggle.tsx
Normal file
46
admin/inertia/components/maps/ScaleUnitToggle.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
type ScaleUnit = 'imperial' | 'metric'
|
||||
|
||||
type ScaleUnitToggleProps = {
|
||||
scaleUnit: ScaleUnit
|
||||
onChange: (unit: ScaleUnit) => void
|
||||
onMouseEnter?: () => void
|
||||
}
|
||||
|
||||
export default function ScaleUnitToggle({
|
||||
scaleUnit,
|
||||
onChange,
|
||||
onMouseEnter,
|
||||
}: ScaleUnitToggleProps) {
|
||||
return (
|
||||
<div
|
||||
className="absolute bottom-[30px] left-[10px] z-[2]"
|
||||
onMouseEnter={onMouseEnter}
|
||||
>
|
||||
<div className="inline-flex overflow-hidden rounded text-[11px] font-semibold leading-none shadow-[0_0_0_2px_rgba(0,0,0,0.1)]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange('metric')}
|
||||
className="border-0 px-2 py-1"
|
||||
style={{
|
||||
background: scaleUnit === 'metric' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'metric' ? 'white' : '#666',
|
||||
}}
|
||||
>
|
||||
Metric
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange('imperial')}
|
||||
className="border-0 px-2 py-1"
|
||||
style={{
|
||||
background: scaleUnit === 'imperial' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'imperial' ? 'white' : '#666',
|
||||
}}
|
||||
>
|
||||
Imperial
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,29 +1,34 @@
|
|||
import MapsLayout from '~/layouts/MapsLayout'
|
||||
import { useState } from 'react'
|
||||
import { Head, Link, router } from '@inertiajs/react'
|
||||
import { IconArrowLeft } from '@tabler/icons-react'
|
||||
|
||||
import MapsLayout from '~/layouts/MapsLayout'
|
||||
import MapComponent from '~/components/maps/MapComponent'
|
||||
import StyledButton from '~/components/StyledButton'
|
||||
import { IconArrowLeft } from '@tabler/icons-react'
|
||||
import { FileEntry } from '../../types/files'
|
||||
import Alert from '~/components/Alert'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { FileEntry } from '../../types/files'
|
||||
|
||||
export default function Maps(props: {
|
||||
maps: { baseAssetsExist: boolean; regionFiles: FileEntry[] }
|
||||
}) {
|
||||
const [isHoveringUI, setIsHoveringUI] = useState(false)
|
||||
const [showMapCoordinates, setShowMapCoordinates] = useState(true)
|
||||
|
||||
const alertMessage = !props.maps.baseAssetsExist
|
||||
? 'The base map assets have not been installed. Please download them first to enable map functionality.'
|
||||
: props.maps.regionFiles.length === 0
|
||||
? 'No map regions have been downloaded yet. Please download some regions to enable map functionality.'
|
||||
: null
|
||||
? 'No map regions have been downloaded yet. Please download some regions to enable map functionality.'
|
||||
: null
|
||||
|
||||
return (
|
||||
<MapsLayout>
|
||||
<Head title="Maps" />
|
||||
|
||||
<div className="relative w-full h-screen overflow-hidden">
|
||||
{/* Nav and alerts are overlayed */}
|
||||
<div className="absolute top-0 left-0 right-0 z-50 flex justify-between p-4 bg-surface-secondary backdrop-blur-sm shadow-sm"
|
||||
{/* Navbar */}
|
||||
<div
|
||||
className="absolute top-0 left-0 right-0 z-50 flex justify-between p-4 bg-surface-secondary backdrop-blur-sm shadow-sm"
|
||||
onMouseEnter={() => setIsHoveringUI(true)}
|
||||
onMouseLeave={() => setIsHoveringUI(false)}
|
||||
>
|
||||
|
|
@ -31,24 +36,28 @@ export default function Maps(props: {
|
|||
<IconArrowLeft className="mr-2" size={24} />
|
||||
<p className="text-lg text-text-secondary">Back to Home</p>
|
||||
</Link>
|
||||
<div className="flex items-center gap-3 mr-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowMapCoordinates((prev) => !prev)}
|
||||
className="rounded px-3 py-2 text-sm bg-surface-primary text-text-secondary hover:opacity-80 transition"
|
||||
>
|
||||
{showMapCoordinates ? 'Hide Coordinates' : 'Show Coordinates'}
|
||||
</button>
|
||||
|
||||
<Link href="/settings/maps">
|
||||
<StyledButton variant="primary" icon="IconSettings">
|
||||
Manage Map Regions
|
||||
</StyledButton>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 mr-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowMapCoordinates((prev) => !prev)}
|
||||
className="rounded px-3 py-2 text-sm bg-surface-primary text-text-secondary hover:opacity-80 transition"
|
||||
>
|
||||
{showMapCoordinates ? 'Hide Coordinates' : 'Show Coordinates'}
|
||||
</button>
|
||||
|
||||
<Link href="/settings/maps">
|
||||
<StyledButton variant="primary" icon="IconSettings">
|
||||
Manage Map Regions
|
||||
</StyledButton>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Alert */}
|
||||
{alertMessage && (
|
||||
<div className="absolute top-20 left-4 right-4 z-50"
|
||||
<div
|
||||
className="absolute top-20 left-4 right-4 z-50"
|
||||
onMouseEnter={() => setIsHoveringUI(true)}
|
||||
onMouseLeave={() => setIsHoveringUI(false)}
|
||||
>
|
||||
|
|
@ -66,8 +75,13 @@ export default function Maps(props: {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Map */}
|
||||
<div className="absolute inset-0">
|
||||
<MapComponent isHoveringUI={isHoveringUI} showCoordinatesEnabled={showMapCoordinates} />
|
||||
<MapComponent
|
||||
isHoveringUI={isHoveringUI}
|
||||
showCoordinatesEnabled={showMapCoordinates}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</MapsLayout>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user