fix: Karte lazy-loaded, formatKm bei 0, Enter im Dialog

- TrailMap: IntersectionObserver für WebGL-Kontexte (max 8-16 gleichzeitig)
  → Karte erst laden, wenn sichtbar, rootMargin 200px für frühes Laden
- Trail-Liste: formatKm zeigt 0 m als "0,0 km" statt "–"
- Trail-Anlegen: Enter im Namensfeld triggert save(), saving-Guard hinzugefügt

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Michelberger 2026-08-06 18:34:58 +02:00
parent 60d5891bc9
commit 7bc128601a
2 changed files with 67 additions and 37 deletions

View file

@ -62,6 +62,9 @@
let highlightMarker: Marker | null = null
onMount(() => {
let cleanupMap: (() => void) | null = null
function build() {
const instance = new maplibregl.Map({
container,
style: {
@ -92,7 +95,7 @@
if (placing) onplace?.({ lat: e.lngLat.lat, lng: e.lngLat.lng })
})
return () => {
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()
@ -102,6 +105,26 @@
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()
}
},
{ rootMargin: '200px' },
)
observer.observe(container)
return () => {
observer.disconnect()
cleanupMap?.()
}
})

View file

@ -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 @@
<div class="space-y-4">
<div class="space-y-2">
<Label for="trail-name">Name</Label>
<Input id="trail-name" bind:value={form.name} placeholder="z. B. Steinberg-Abfahrt" />
<Input
id="trail-name"
bind:value={form.name}
placeholder="z. B. Steinberg-Abfahrt"
onkeydown={(e) => e.key === 'Enter' && save()}
/>
</div>
<div class="space-y-2">
<Label for="trail-desc">Beschreibung</Label>