diff --git a/admin/inertia/components/maps/MapComponent.tsx b/admin/inertia/components/maps/MapComponent.tsx index d9e6c15..59bcb7b 100644 --- a/admin/inertia/components/maps/MapComponent.tsx +++ b/admin/inertia/components/maps/MapComponent.tsx @@ -12,23 +12,82 @@ 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' +type ScaleUnit = 'imperial' | 'metric' + +type MapLocationParams = { + lat: number + lng: number + zoom: number +} + +const getMapLocationParams = (): MapLocationParams | null => { + const params = new URLSearchParams(window.location.search) + + const lat = Number(params.get('lat')) + const lngParam = params.get('lng') + const longParam = params.get('long') + const lng = Number(lngParam ?? longParam) + const zoom = Number(params.get('zoom') ?? 12) + + if ( + !Number.isFinite(lat) || + !Number.isFinite(lng) || + lat < -90 || + lat > 90 || + lng < -180 || + lng > 180 + ) { + return null + } + + if (!lngParam && longParam) { + params.set('lng', longParam) + params.delete('long') + + const query = params.toString() + window.history.replaceState( + null, + '', + `${window.location.pathname}${query ? `?${query}` : ''}${window.location.hash}` + ) + } + + return { + lat, + lng, + zoom: Number.isFinite(zoom) ? zoom : 12, + } +} + export default function MapComponent() { const mapRef = useRef(null) const { markers, addMarker, deleteMarker } = useMapMarkers() + const [placingMarker, setPlacingMarker] = useState<{ lng: number; lat: number } | null>(null) const [markerName, setMarkerName] = useState('') const [markerColor, setMarkerColor] = useState('orange') const [selectedMarkerId, setSelectedMarkerId] = useState(null) + const [scaleUnit, setScaleUnit] = useState( () => (localStorage.getItem('nomad:map-scale-unit') as ScaleUnit) || 'metric' ) + const flyToLocationParams = useCallback(() => { + const location = getMapLocationParams() + if (!location) return + + mapRef.current?.flyTo({ + center: [location.lng, location.lat], + zoom: location.zoom, + duration: 1500, + }) + }, []) + const toggleScaleUnit = useCallback(() => { setScaleUnit((prev) => { const next = prev === 'metric' ? 'imperial' : 'metric' @@ -37,37 +96,18 @@ export default function MapComponent() { }) }, []) - // Add the PMTiles protocol to maplibre-gl useEffect(() => { - let protocol = new Protocol() + const protocol = new Protocol() maplibregl.addProtocol('pmtiles', protocol.tile) + return () => { maplibregl.removeProtocol('pmtiles') } }, []) - useEffect(() => { - const params = new URLSearchParams(window.location.search) - - const lat = Number(params.get('lat')) - const lng = Number(params.get('lng')) - const zoom = Number(params.get('zoom') ?? 12) - - if ( - Number.isFinite(lat) && - Number.isFinite(lng) && - lat >= -90 && - lat <= 90 && - lng >= -180 && - lng <= 180 - ) { - mapRef.current?.flyTo({ - center: [lng, lat], - zoom: Number.isFinite(zoom) ? zoom : 12, - duration: 1500, - }) - } - }, []) + const handleMapLoad = useCallback(() => { + flyToLocationParams() + }, [flyToLocationParams]) const handleMapClick = useCallback((e: MapLayerMouseEvent) => { setPlacingMarker({ lng: e.lngLat.lng, lat: e.lngLat.lat }) @@ -97,7 +137,7 @@ export default function MapComponent() { [selectedMarkerId, deleteMarker] ) - const selectedMarker = selectedMarkerId ? markers.find((m) => m.id === selectedMarkerId) : null + const selectedMarker = selectedMarkerId ? markers.find((marker) => marker.id === selectedMarkerId) : null return ( @@ -115,11 +155,13 @@ export default function MapComponent() { latitude: 40, zoom: 3.5, }} + onLoad={handleMapLoad} onClick={handleMapClick} > +
+
- {/* Existing markers */} {markers.map((marker) => ( c.id === marker.color)?.hex} + color={PIN_COLORS.find((color) => color.id === marker.color)?.hex} active={marker.id === selectedMarkerId} /> ))} - {/* Popup for selected marker */} {selectedMarker && ( )} - {/* Popup for placing a new marker */} {placingMarker && ( +
- {PIN_COLORS.map((c) => ( + {PIN_COLORS.map((color) => ( ))}
+
+