import { useEffect, useRef, useState, useCallback } from 'react'; const DEFAULT_CENTER = { lat: 33.5731, lng: -7.5898 }; const LEAFLET_CSS = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'; const LEAFLET_JS = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; function loadLeaflet() { if (window.L) return Promise.resolve(window.L); return new Promise((resolve, reject) => { if (!document.querySelector(`link[href="${LEAFLET_CSS}"]`)) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = LEAFLET_CSS; document.head.appendChild(link); } const existing = document.querySelector(`script[src="${LEAFLET_JS}"]`); if (existing) { existing.addEventListener('load', () => resolve(window.L)); existing.addEventListener('error', reject); return; } const script = document.createElement('script'); script.src = LEAFLET_JS; script.async = true; script.onload = () => resolve(window.L); script.onerror = reject; document.body.appendChild(script); }); } async function geocodeQuery(query) { const q = query.trim(); if (!q) return null; const url = `https://nominatim.openstreetmap.org/search?format=json&limit=1&q=${encodeURIComponent(q)}`; const res = await fetch(url, { headers: { 'Accept-Language': 'fr' } }); if (!res.ok) return null; const data = await res.json(); if (!data?.[0]) return null; return { lat: parseFloat(data[0].lat), lng: parseFloat(data[0].lon), displayName: data[0].display_name, }; } export default function LocationMapPicker({ latitude, longitude, searchQuery, onLocationChange, className = '', }) { const containerRef = useRef(null); const mapRef = useRef(null); const markerRef = useRef(null); const [searching, setSearching] = useState(false); const [hint, setHint] = useState('Cliquez sur la carte pour placer votre commerce.'); const lat = parseFloat(latitude); const lng = parseFloat(longitude); const hasCoords = Number.isFinite(lat) && Number.isFinite(lng); const setMarker = useCallback( (L, map, latLng, fly = true) => { if (markerRef.current) { markerRef.current.setLatLng(latLng); } else { markerRef.current = L.marker(latLng, { draggable: true }).addTo(map); markerRef.current.on('dragend', () => { const pos = markerRef.current.getLatLng(); onLocationChange(pos.lat, pos.lng); setHint(`Position : ${pos.lat.toFixed(5)}, ${pos.lng.toFixed(5)}`); }); } if (fly) map.flyTo(latLng, Math.max(map.getZoom(), 15), { duration: 0.6 }); onLocationChange(latLng[0], latLng[1]); setHint(`Position : ${latLng[0].toFixed(5)}, ${latLng[1].toFixed(5)}`); }, [onLocationChange], ); useEffect(() => { let cancelled = false; loadLeaflet() .then((L) => { if (cancelled || !containerRef.current || mapRef.current) return; const start = hasCoords ? [lat, lng] : [DEFAULT_CENTER.lat, DEFAULT_CENTER.lng]; const map = L.map(containerRef.current, { scrollWheelZoom: true }).setView(start, hasCoords ? 16 : 12); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19, }).addTo(map); map.on('click', (e) => { setMarker(L, map, [e.latlng.lat, e.latlng.lng], false); }); if (hasCoords) { setMarker(L, map, [lat, lng], false); } mapRef.current = map; }) .catch(() => { setHint('Carte indisponible — utilisez la recherche ci-dessous.'); }); return () => { cancelled = true; if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; markerRef.current = null; } }; // eslint-disable-next-line react-hooks/exhaustive-deps -- init once }, []); useEffect(() => { if (!mapRef.current || !window.L || !hasCoords) return; const L = window.L; setMarker(L, mapRef.current, [lat, lng], true); }, [lat, lng, hasCoords, setMarker]); const runSearch = async (query) => { const text = (query ?? searchQuery ?? '').trim(); if (!text || !mapRef.current || !window.L) return; setSearching(true); try { const result = await geocodeQuery(text); if (result) { setMarker(window.L, mapRef.current, [result.lat, result.lng]); onLocationChange(result.lat, result.lng, result.displayName); } else { setHint('Adresse introuvable. Essayez une recherche plus précise.'); } } finally { setSearching(false); } }; return (
{ if (e.key === 'Enter') { e.preventDefault(); runSearch(e.target.value); } }} id="map-search-input" />

{hint}

); }