diff --git a/frontend/src/lib/components/TrailMap.svelte b/frontend/src/lib/components/TrailMap.svelte index dad7304..fb4a382 100644 --- a/frontend/src/lib/components/TrailMap.svelte +++ b/frontend/src/lib/components/TrailMap.svelte @@ -62,46 +62,69 @@ let highlightMarker: Marker | null = null onMount(() => { - const instance = new maplibregl.Map({ - container, - style: { - version: 8, - sources: { - osm: { - type: 'raster', - tiles: [TILES], - tileSize: 256, - attribution: ATTRIBUTION, + let cleanupMap: (() => void) | null = null + + function build() { + const instance = new maplibregl.Map({ + container, + style: { + version: 8, + sources: { + osm: { + type: 'raster', + tiles: [TILES], + tileSize: 256, + attribution: ATTRIBUTION, + }, }, + layers: [{ id: 'osm', type: 'raster', source: 'osm' }], }, - layers: [{ id: 'osm', type: 'raster', source: 'osm' }], + center: FALLBACK_CENTER, + zoom: 11, + }) + map = instance + + instance.addControl(new maplibregl.NavigationControl(), 'top-right') + instance.addControl(new maplibregl.ScaleControl(), 'bottom-left') + + instance.on('load', () => { + ready = true + }) + + instance.on('click', (e) => { + if (placing) onplace?.({ lat: e.lngLat.lat, lng: e.lngLat.lng }) + }) + + cleanupMap = () => { + // Map.remove() räumt Controls auf, aber keine Marker — die tragen + // eigene move-Listener auf der Karte und werden hier zuerst entfernt. + for (const m of markerObjects) m.remove() + markerObjects = [] + highlightMarker?.remove() + highlightMarker = null + + instance.remove() + map = null + ready = false + } + } + + // Browser begrenzen WebGL-Kontexte auf rund 8 bis 16. In einer Liste + // mit vielen Trails wären das zu viele — deshalb entsteht die Karte + // erst, wenn ihre Kachel in den Sichtbereich scrollt. + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting && !map) build() + } }, - center: FALLBACK_CENTER, - zoom: 11, - }) - map = instance - - instance.addControl(new maplibregl.NavigationControl(), 'top-right') - instance.addControl(new maplibregl.ScaleControl(), 'bottom-left') - - instance.on('load', () => { - ready = true - }) - - instance.on('click', (e) => { - if (placing) onplace?.({ lat: e.lngLat.lat, lng: e.lngLat.lng }) - }) + { rootMargin: '200px' }, + ) + observer.observe(container) return () => { - // Map.remove() räumt Controls auf, aber keine Marker — die tragen - // eigene move-Listener auf der Karte und werden hier zuerst entfernt. - for (const m of markerObjects) m.remove() - markerObjects = [] - highlightMarker?.remove() - highlightMarker = null - - instance.remove() - map = null + observer.disconnect() + cleanupMap?.() } }) diff --git a/frontend/src/routes/dashboard/trails/+page.svelte b/frontend/src/routes/dashboard/trails/+page.svelte index 9773ddf..6cdbf2d 100644 --- a/frontend/src/routes/dashboard/trails/+page.svelte +++ b/frontend/src/routes/dashboard/trails/+page.svelte @@ -52,11 +52,13 @@ } function formatKm(m: number | undefined) { - if (!m) return '–' + // Nur fehlende Werte zeigen "–"; 0 ist eine gültige Länge. + if (m == null) return '–' return `${(m / 1000).toFixed(1)} km` } async function save() { + if (saving) return if (!form.name.trim()) { error = 'Bitte einen Namen angeben.' return @@ -169,7 +171,12 @@
- + e.key === 'Enter' && save()} + />