From 1b26cfda7abbb2f2b1e89fdc2e3eea6b24859182 Mon Sep 17 00:00:00 2001 From: Hermann Date: Fri, 24 Jul 2026 13:01:35 +0200 Subject: [PATCH] Feature: Hover-Vorschau auf Kartenmarkern Marker zeigen jetzt beim Mouseover zusaetzlich zum bestehenden Klick-Popup eine kompakte Vorschau (Leaflet-Tooltip) mit Name, Kategorie, Kurzbeschreibung sowie Kurzfakten (Altersempfehlung, Kostenart, Bewertung, falls vorhanden) - ohne dass geklickt werden muss. Inhalt wird wie ueberall sonst als DOM-Node ueber el()/textContent erzeugt, kein innerHTML. --- css/components.css | 42 ++++++++++++++++++++++++++++++++++++++ js/map/markers.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/css/components.css b/css/components.css index da7e7ba..9f66759 100644 --- a/css/components.css +++ b/css/components.css @@ -220,3 +220,45 @@ font-size: 0.85rem; color: var(--color-text-muted); } + +/* Hover-Vorschau (Leaflet-Tooltip) auf Kartenmarkern */ + +.leaflet-tooltip.eventmap-tooltip-wrapper { + background: var(--color-surface); + color: var(--color-text); + border: none; + border-radius: var(--radius-sm); + box-shadow: var(--shadow-card); + padding: var(--space-sm) var(--space-md); +} + +.leaflet-tooltip.eventmap-tooltip-wrapper::before { + border-top-color: var(--color-surface); +} + +.eventmap-tooltip { + display: flex; + flex-direction: column; + gap: 4px; + max-width: 220px; +} + +.eventmap-tooltip__title { + font-size: 0.9rem; +} + +.eventmap-tooltip__category { + font-size: 0.78rem; + font-weight: 600; +} + +.eventmap-tooltip__desc { + font-size: 0.82rem; + color: var(--color-text-muted); +} + +.eventmap-tooltip__facts { + font-size: 0.78rem; + color: var(--color-text-muted); + font-weight: 600; +} diff --git a/js/map/markers.js b/js/map/markers.js index 1ebf2d5..8aa05c6 100644 --- a/js/map/markers.js +++ b/js/map/markers.js @@ -41,6 +41,46 @@ ]); } + const COST_LABELS = { free: 'Kostenlos', paid: 'Kostenpflichtig', mixed: 'Teils kostenpflichtig', unknown: null }; + + function formatTooltipAge(ageRecommendation) { + const { min, max } = ageRecommendation || {}; + if (min == null && max == null) return null; + if (min != null && max != null) return `${min}–${max} Jahre`; + if (min != null) return `Ab ${min} Jahren`; + return `Bis ${max} Jahre`; + } + + /** Kompakte Hover-Vorschau (Leaflet-Tooltip), zusätzlich zum Klick-Popup. */ + function buildTooltipContent(place) { + const category = getPlaceCategory(place); + + const facts = []; + const age = formatTooltipAge(place.ageRecommendation); + if (age) facts.push(age); + const costLabel = COST_LABELS[(place.cost || {}).type]; + if (costLabel) facts.push(costLabel); + if (typeof place.rating === 'number') facts.push(`★ ${place.rating.toFixed(1)}`); + + const children = [ + el('strong', { className: 'eventmap-tooltip__title', text: place.name }), + el('div', { + className: 'eventmap-tooltip__category', + style: { color: category.color }, + text: `${category.icon} ${category.label}`, + }), + ]; + + if (place.shortDescription) { + children.push(el('p', { className: 'eventmap-tooltip__desc', text: place.shortDescription })); + } + if (facts.length > 0) { + children.push(el('p', { className: 'eventmap-tooltip__facts', text: facts.join(' · ') })); + } + + return el('div', { className: 'eventmap-tooltip' }, children); + } + /** * Erzeugt einen Leaflet-Marker für ein Place-Objekt. * @param {object} place @@ -51,6 +91,17 @@ const marker = L.marker([lat, lng], { icon: getIconForCategory(place.category) }); marker.bindPopup(buildPopupContent(place)); + // Hover-Vorschau zusätzlich zum Klick-Popup: sticky folgt dem Mauszeiger, + // damit die Vorschau nicht die Position wechselt, während man über den + // Marker fährt. Schließt sich automatisch bei mouseout bzw. wenn der + // Klick-Popup geöffnet wird. + marker.bindTooltip(buildTooltipContent(place), { + direction: 'top', + offset: [0, -18], + opacity: 0.97, + sticky: false, + className: 'eventmap-tooltip-wrapper', + }); marker.on('click', () => { if (onSelect) { onSelect(place.id);