feat: add settings modal for map display

This commit is contained in:
zv
2026-06-23 20:35:45 +02:00
parent 0a008a4d39
commit c53d109c41
4 changed files with 441 additions and 57 deletions

View File

@@ -5,14 +5,20 @@ import {
Marker,
Popup,
TileLayer,
Tooltip,
useMap,
useMapEvents,
} from 'react-leaflet';
import type { MapTileStyle } from './mapStyles';
import type { BBox, ParsedOsmResult } from '../search/types';
type MapViewProps = {
results: ParsedOsmResult[];
selectedResultId: string | null;
tileStyle: MapTileStyle;
showResultLabels: boolean;
fitMapToResults: boolean;
dimMap: boolean;
onResultSelect: (id: string) => void;
onViewportChange: (bbox: BBox) => void;
};
@@ -23,6 +29,10 @@ const DEFAULT_ZOOM = 13;
export function MapView({
results,
selectedResultId,
tileStyle,
showResultLabels,
fitMapToResults,
dimMap,
onResultSelect,
onViewportChange,
}: MapViewProps) {
@@ -53,14 +63,17 @@ export function MapView({
center={DEFAULT_CENTER}
zoom={DEFAULT_ZOOM}
minZoom={3}
className="map-view"
className={dimMap ? 'map-view map-view-dimmed' : 'map-view'}
zoomControl
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
key={tileStyle.id}
attribution={tileStyle.attribution}
url={tileStyle.url}
maxZoom={tileStyle.maxZoom}
/>
<ViewportReporter onViewportChange={onViewportChange} />
<FitResults results={results} enabled={fitMapToResults} />
<SelectedResultFocus result={results.find((item) => item.id === selectedResultId) ?? null} />
{results.map((result) => (
<Marker
@@ -76,6 +89,11 @@ export function MapView({
<br />
{result.type} {result.osmId}
</Popup>
{showResultLabels ? (
<Tooltip permanent direction="top" offset={[0, -10]} opacity={0.94}>
{result.name ?? `${result.type} ${result.osmId}`}
</Tooltip>
) : null}
</Marker>
))}
</MapContainer>
@@ -99,6 +117,33 @@ function ViewportReporter({
return null;
}
function FitResults({
results,
enabled,
}: {
results: ParsedOsmResult[];
enabled: boolean;
}) {
const map = useMap();
useEffect(() => {
if (!enabled || results.length === 0) {
return;
}
const bounds = L.latLngBounds(results.map((result) => [result.lat, result.lon]));
if (bounds.isValid()) {
map.fitBounds(bounds.pad(0.2), {
animate: true,
maxZoom: 16,
});
}
}, [enabled, map, results]);
return null;
}
function SelectedResultFocus({ result }: { result: ParsedOsmResult | null }) {
const map = useMap();