Gridlock / app /frontend /src /components /LocationPicker.tsx
parvmittal07's picture
Improved Map UI and minor fixes
2c64d54
Raw
History Blame Contribute Delete
1.87 kB
import { useEffect } from "react";
import { MapContainer, TileLayer, Marker, useMap, useMapEvents } from "react-leaflet";
import L from "leaflet";
// Leaflet's default marker icons break under bundlers; build a crisp SVG pin.
const pinIcon = L.divIcon({
className: "",
html: `<div style="transform:translate(-50%,-100%)">
<svg width="26" height="34" viewBox="0 0 26 34" xmlns="http://www.w3.org/2000/svg">
<path d="M13 0C5.8 0 0 5.8 0 13c0 9.2 13 21 13 21s13-11.8 13-21C26 5.8 20.2 0 13 0z" fill="#439878"/>
<circle cx="13" cy="13" r="5" fill="#0f1318"/>
</svg></div>`,
iconSize: [26, 34],
});
const CARTO_DARK =
"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png";
function ClickCapture({ onPick }: { onPick: (lat: number, lng: number) => void }) {
useMapEvents({
click(e) {
onPick(
Number(e.latlng.lat.toFixed(5)),
Number(e.latlng.lng.toFixed(5))
);
},
});
return null;
}
function Recenter({ lat, lng }: { lat: number; lng: number }) {
const map = useMap();
useEffect(() => {
map.setView([lat, lng], map.getZoom(), { animate: true });
}, [lat, lng, map]);
return null;
}
export function LocationPicker({
lat,
lng,
onPick,
}: {
lat: number;
lng: number;
onPick: (lat: number, lng: number) => void;
}) {
return (
<div className="h-56 overflow-hidden rounded-lg border border-border">
<MapContainer
center={[lat, lng]}
zoom={12}
scrollWheelZoom
style={{ height: "100%", width: "100%" }}
attributionControl={false}
>
<TileLayer
url={CARTO_DARK}
subdomains="abcd"
maxZoom={19}
/>
<Marker position={[lat, lng]} icon={pinIcon} />
<ClickCapture onPick={onPick} />
<Recenter lat={lat} lng={lng} />
</MapContainer>
</div>
);
}