@dossierhq/leaflet
Version:
A library for rendering maps in Dossier with Leaflet.
117 lines • 5.27 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { control } from 'leaflet';
import 'leaflet.locatecontrol';
import { Suspense, useEffect, useRef, } from 'react';
import { MapContainer as LeafletMapContainer, Marker, TileLayer, Tooltip, useMap, useMapEvents, } from 'react-leaflet';
import { toLatLngLiteral } from './CoreTypes.js';
import { getMarkerIcon } from './MarkerUtils.js';
const defaultZoom = 13;
export const MapContainer = ({ className, style, center, zoom, minZoom, resetSignal, maxBoundingBox, onBoundingBoxChanged, onZoomMetricsChanged, children, }) => {
const mapRef = useRef(null);
useEffect(() => {
if (resetSignal && mapRef.current) {
mapRef.current.setView(center, zoom ?? defaultZoom, { animate: true });
}
//TODO fix dependencies
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [resetSignal]);
useEffect(() => {
// Fix issue where tiles are sometimes not loaded on initial display
// https://github.com/PaulLeCam/react-leaflet/issues/340
setTimeout(() => mapRef.current?.invalidateSize(), 0);
}, []);
return (_jsx(LeafletMapContainer, { ref: mapRef, className: className, style: style, center: center, zoom: zoom ?? defaultZoom, minZoom: minZoom, maxBounds: maxBoundingBox ? toLatLngLiteral(maxBoundingBox) : undefined, scrollWheelZoom: true, children: _jsxs(Suspense, { children: [!!onBoundingBoxChanged || !!onZoomMetricsChanged ? (_jsx(MapEventListener, { onBoundingBoxChanged: onBoundingBoxChanged, onZoomMetricsChanged: onZoomMetricsChanged })) : null, _jsx(TileLayer, { attribution: '\u00A9 <a href="http://osm.org/copyright" target="_blank" rel="noopener">OpenStreetMap</a> contributors', url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" }), children] }) }));
};
MapContainer.displayName = 'MapContainer';
function MapEventListener({ onBoundingBoxChanged, onZoomMetricsChanged, }) {
const map = useMapEvents({
...(onBoundingBoxChanged
? {
move: (_event) => {
onBoundingBoxChanged(getBoundingBox(map));
},
}
: {}),
...(onZoomMetricsChanged
? {
zoom: (_event) => {
onZoomMetricsChanged(getZoomMetrics(map));
},
}
: {}),
});
useEffect(() => {
if (onBoundingBoxChanged)
onBoundingBoxChanged(getBoundingBox(map));
if (onZoomMetricsChanged)
onZoomMetricsChanged(getZoomMetrics(map));
}, [map, onBoundingBoxChanged, onZoomMetricsChanged]);
return null;
}
function getBoundingBox(map) {
const bounds = map.getBounds();
const boundingBox = {
minLat: bounds.getSouth(),
maxLat: bounds.getNorth(),
minLng: bounds.getWest(),
maxLng: bounds.getEast(),
};
return boundingBox;
}
function getZoomMetrics(map) {
const location0 = map.containerPointToLatLng([0, 0]);
const location1 = map.containerPointToLatLng([1, 1]);
const metrics = {
pixelToLat: location0.lat - location1.lat,
pixelToLng: location1.lng - location0.lng,
};
return metrics;
}
function LocateControl({ outsideMapBoundsMsg, showPopup, autoStart, title }) {
const map = useMap();
useEffect(() => {
const strings = {};
if (outsideMapBoundsMsg)
strings.outsideMapBoundsMsg = outsideMapBoundsMsg;
if (title)
strings.title = title;
const locateControl = control.locate({
icon: 'icon-map-location leaflet-icon',
iconLoading: 'icon-map-location leaflet-icon is-pulsing',
showPopup,
strings,
});
map.addControl(locateControl);
if (autoStart) {
void navigator.permissions.query({ name: 'geolocation' }).then((result) => {
if (result.state === 'granted')
locateControl.start();
});
}
return () => {
map.removeControl(locateControl);
};
}, [map, outsideMapBoundsMsg, showPopup, autoStart, title]);
return null;
}
MapContainer.LocateControl = LocateControl;
MapContainer.LocateControl.displayName = 'MapContainer.LocateControl';
function MapContainerMarker({ color, location, tooltip, onClick }) {
return (_jsx(Marker, { position: [location.lat, location.lng], icon: getMarkerIcon(color), eventHandlers: onClick ? { click: onClick } : undefined, children: tooltip ? _jsx(Tooltip, { children: tooltip }) : null }));
}
MapContainer.Marker = MapContainerMarker;
MapContainer.Marker.displayName = 'MapContainer.Marker';
function EditLocationMarker({ value, onChange }) {
useMapEvents({
click: (event) => {
onChange({
lat: Math.round(event.latlng.lat * 1e6) / 1e6,
lng: Math.round(event.latlng.lng * 1e6) / 1e6,
});
},
});
return value ? (_jsx(Marker, { position: [value.lat, value.lng], icon: getMarkerIcon('current') })) : null;
}
MapContainer.EditLocationMarker = EditLocationMarker;
MapContainer.EditLocationMarker.displayName = 'MapContainer.EditLocationMarker';
//# sourceMappingURL=MapContainer.js.map