import L, { LatLngTuple } from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { useEffect, useRef } from 'react'; import { MapContainer, Marker, TileLayer, useMap } from 'react-leaflet'; // Fix for default marker icon issue in Leaflet with webpack/vite delete (L.Icon.Default.prototype as unknown as { _getIconUrl?: unknown })._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png', iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png', }); interface MapPickerProps { value?: { lat: number; lng: number }; onChange?: (value: { lat: number; lng: number }) => void; location?: { lat: number; lng: number }; onLocationChange?: (value: { lat: number; lng: number }) => void; } /** * Component to sync map center with value prop and zoom in when location changes */ const MapCenterSync = ({ value }: { value: { lat: number; lng: number } }) => { const map = useMap(); const lastValueRef = useRef<{ lat: number; lng: number } | null>(null); useEffect(() => { // Check if this is a new location (not just initial render) const isNewLocation = !lastValueRef.current || Math.abs(lastValueRef.current.lat - value.lat) > 0.0001 || Math.abs(lastValueRef.current.lng - value.lng) > 0.0001; if (isNewLocation) { // Zoom in to a closer level when a place is selected const targetZoom = 16; // Closer zoom level for selected location map.setView([value.lat, value.lng], targetZoom, { animate: true }); lastValueRef.current = { lat: value.lat, lng: value.lng }; } }, [map, value.lat, value.lng]); return null; }; const MapPicker = ({ value, onChange, location, onLocationChange }: MapPickerProps) => { // Support both prop naming conventions for backward compatibility const actualValue = value || location || { lat: 54.5384152, lng: 52.7955953 }; const actualOnChange = onChange || onLocationChange || (() => {}); const position: LatLngTuple = [actualValue.lat, actualValue.lng]; return (
{/* Optional: Add a tile layer for reference (can be removed if you want blank map) */} { const marker = e.target; const newPosition = marker.getLatLng(); actualOnChange({ lat: newPosition.lat, lng: newPosition.lng }); }, }} /> {/* Click handler for map */}
); }; /** * Component to handle map clicks */ const MapClickHandler = ({ onChange, }: { onChange: (value: { lat: number; lng: number }) => void; }) => { const map = useMap(); useEffect(() => { const handleClick = (e: L.LeafletMouseEvent) => { onChange({ lat: e.latlng.lat, lng: e.latlng.lng }); }; map.on('click', handleClick); return () => { map.off('click', handleClick); }; }, [map, onChange]); return null; }; export default MapPicker;