import { useEffect, useRef, useState } from 'react' import MyLocationOutlinedIcon from '@mui/icons-material/MyLocationOutlined' import Box from '@mui/material/Box' import CircularProgress from '@mui/material/CircularProgress' import IconButton from '@mui/material/IconButton' import Tooltip from '@mui/material/Tooltip' import Map, { Marker, type MapMouseEvent, type MapRef } from 'react-map-gl/maplibre' import { reverseGeocode } from '../api/map-geocoding' import type { LatLng } from '../model/types' import type * as maplibregl from 'maplibre-gl' let maplibreglPromise: Promise | null = null function loadMaplibre() { if (!maplibreglPromise) { maplibreglPromise = Promise.all([import('maplibre-gl'), import('maplibre-gl/dist/maplibre-gl.css')]).then( ([mod]) => mod, ) } return maplibreglPromise } type MapPickerMapProps = { value: { lat: number; lng: number } | null onChange: (v: { lat: number; lng: number; addressLine?: string | null }) => void center: { lat: number; lng: number } } export function MapPickerMap({ value, onChange, center }: MapPickerMapProps) { const mapRef = useRef(null) const [maplibre, setMaplibre] = useState(null) const [loading, setLoading] = useState(true) const [locating, setLocating] = useState(false) useEffect(() => { let cancelled = false loadMaplibre().then((mod) => { if (!cancelled) { setMaplibre(mod) setLoading(false) } }) return () => { cancelled = true } }, []) const pick = async (pos: LatLng) => { onChange({ lat: pos.lat, lng: pos.lng }) try { const addr = await reverseGeocode(pos) if (addr) { onChange({ lat: pos.lat, lng: pos.lng, addressLine: addr }) } } catch { // ignore } } if (loading || !maplibre) { return ( ) } return ( { const { lng, lat } = e.lngLat void pick({ lat, lng }) }} > {value && ( { const { lng, lat } = e.lngLat void pick({ lat, lng }) }} anchor="bottom" > )} { if (!('geolocation' in navigator)) return setLocating(true) navigator.geolocation.getCurrentPosition( (pos) => { const lat = pos.coords.latitude const lng = pos.coords.longitude mapRef.current?.flyTo({ center: [lng, lat], zoom: 15, duration: 800 }) void pick({ lat, lng }) setLocating(false) }, () => { setLocating(false) }, { enableHighAccuracy: true, timeout: 8000, maximumAge: 60_000 }, ) }} sx={{ position: 'absolute', top: 10, right: 10, bgcolor: 'background.paper', border: 1, borderColor: 'divider', boxShadow: 2, '&:hover': { bgcolor: 'background.paper' }, }} aria-label="Моё местоположение" > {locating ? : } ) }