import Map, { FullscreenControl, NavigationControl, ScaleControl, Marker, Popup, MapProvider, } from 'react-map-gl/maplibre' 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' 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 toggleScaleUnit = useCallback(() => { setScaleUnit((prev) => { const next = prev === 'metric' ? 'imperial' : 'metric' localStorage.setItem('nomad:map-scale-unit', next) return next }) }, []) // Add the PMTiles protocol to maplibre-gl useEffect(() => { let protocol = new Protocol() maplibregl.addProtocol('pmtiles', protocol.tile) return () => { maplibregl.removeProtocol('pmtiles') } }, []) const handleMapClick = useCallback((e: MapLayerMouseEvent) => { setPlacingMarker({ lng: e.lngLat.lng, lat: e.lngLat.lat }) setMarkerName('') setMarkerColor('orange') setSelectedMarkerId(null) }, []) const handleSaveMarker = useCallback(() => { if (placingMarker && markerName.trim()) { addMarker(markerName.trim(), placingMarker.lng, placingMarker.lat, markerColor) setPlacingMarker(null) setMarkerName('') setMarkerColor('orange') } }, [placingMarker, markerName, markerColor, addMarker]) const handleFlyTo = useCallback((longitude: number, latitude: number) => { mapRef.current?.flyTo({ center: [longitude, latitude], zoom: 12, duration: 1500 }) }, []) const handleDeleteMarker = useCallback( (id: number) => { if (selectedMarkerId === id) setSelectedMarkerId(null) deleteMarker(id) }, [selectedMarkerId, deleteMarker] ) const selectedMarker = selectedMarkerId ? markers.find((m) => m.id === selectedMarkerId) : null return (
{/* Existing markers */} {markers.map((marker) => ( { e.originalEvent.stopPropagation() setSelectedMarkerId(marker.id === selectedMarkerId ? null : marker.id) setPlacingMarker(null) }} > c.id === marker.color)?.hex} active={marker.id === selectedMarkerId} /> ))} {/* Popup for selected marker */} {selectedMarker && ( setSelectedMarkerId(null)} closeOnClick={false} >
{selectedMarker.name}
)} {/* Popup for placing a new marker */} {placingMarker && ( setPlacingMarker(null)} closeOnClick={false} >
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" />
{PIN_COLORS.map((c) => ( ))}
)}
{/* Marker panel overlay */}
) }