diff --git a/css/components.css b/css/components.css index ede237e..da7e7ba 100644 --- a/css/components.css +++ b/css/components.css @@ -60,6 +60,11 @@ align-items: center; } +.btn--active { + background: var(--color-primary); + color: #fff; +} + .btn--danger { background: var(--color-danger); color: #fff; diff --git a/css/layout.css b/css/layout.css index 9b65d7d..7c62b29 100644 --- a/css/layout.css +++ b/css/layout.css @@ -51,6 +51,41 @@ background: var(--color-border); } +.map-panel__map--picking { + cursor: crosshair; +} + +.map-picking-hint { + position: absolute; + top: calc(var(--space-sm) + 40px); + left: var(--space-sm); + z-index: 1000; + display: flex; + align-items: center; + gap: var(--space-sm); + background: var(--color-surface); + color: var(--color-primary-dark); + padding: var(--space-sm) var(--space-md); + border-radius: var(--radius-sm); + box-shadow: var(--shadow-card); + font-size: 0.85rem; +} + +.map-picking-hint[hidden] { + display: none; +} + +.map-picking-hint__cancel { + flex-shrink: 0; + border: none; + background: transparent; + color: var(--color-primary-dark); + font-weight: 600; + text-decoration: underline; + cursor: pointer; + padding: 0; +} + .list-panel { padding: var(--space-md); overflow-y: auto; diff --git a/index.html b/index.html index d209887..fbc7502 100644 --- a/index.html +++ b/index.html @@ -48,6 +48,9 @@ + @@ -58,6 +61,13 @@ 📥 KI-Ausflugsziele importieren + +

Karte wird geladen…

diff --git a/js/main.js b/js/main.js index 9a74a2f..2d9da46 100644 --- a/js/main.js +++ b/js/main.js @@ -2,7 +2,14 @@ (function () { const { initStore } = EventMap.store; - const { initMap, centerOnCoords, requestUserLocation } = EventMap.map; + const { + initMap, + centerOnCoords, + requestUserLocation, + startLocationPicking, + stopLocationPicking, + isPickingLocation, + } = EventMap.map; const { initList, initDetail, @@ -76,6 +83,50 @@ }); } + function setupPickLocationButton() { + const button = document.getElementById('pick-location-btn'); + const hint = document.getElementById('map-picking-hint'); + const cancelBtn = document.getElementById('map-picking-cancel-btn'); + if (!button || !hint || !cancelBtn) return; + + const defaultLabel = button.textContent; + + function endPicking() { + button.textContent = defaultLabel; + button.classList.remove('btn--active'); + hint.hidden = true; + } + + function beginPicking() { + button.textContent = 'Klicke auf die Karte …'; + button.classList.add('btn--active'); + hint.hidden = false; + + startLocationPicking({ + onPick: ({ lat, lng }) => { + EventMap.store.setState({ userLocation: { lat, lng } }); + EventMap.store.updateSettings({ userConsentGeo: true }); + endPicking(); + }, + onCancel: endPicking, + }); + } + + button.addEventListener('click', () => { + if (isPickingLocation()) { + stopLocationPicking(); + endPicking(); + } else { + beginPicking(); + } + }); + + cancelBtn.addEventListener('click', () => { + stopLocationPicking(); + endPicking(); + }); + } + function setupAddPlaceButton() { const button = document.getElementById('add-place-btn'); if (!button) return; @@ -115,6 +166,7 @@ initImport('import-modal'); setupGeolocationButton(); setupManualLocationButton(); + setupPickLocationButton(); setupAddPlaceButton(); setupPromptGeneratorButton(); setupImportButton(); diff --git a/js/map/mapController.js b/js/map/mapController.js index 82a82ab..3ab1455 100644 --- a/js/map/mapController.js +++ b/js/map/mapController.js @@ -102,6 +102,57 @@ return map; } + let pickingActive = false; + let pickingClickHandler = null; + let pickingEscHandler = null; + + /** + * Aktiviert den "Standort auf Karte wählen"-Modus: der nächste Klick auf + * die Karte liefert die geklickten Koordinaten an onPick. Escape oder ein + * erneuter Aufruf von stopLocationPicking() bricht ab (onCancel). + * @param {{onPick?: (loc:{lat:number,lng:number}) => void, onCancel?: () => void}} [handlers] + */ + function startLocationPicking({ onPick, onCancel } = {}) { + if (!map || pickingActive) return; + pickingActive = true; + map.getContainer().classList.add('map-panel__map--picking'); + + pickingClickHandler = (event) => { + const { lat, lng } = event.latlng; + stopLocationPicking(); + if (onPick) onPick({ lat, lng }); + }; + map.once('click', pickingClickHandler); + + pickingEscHandler = (event) => { + if (event.key === 'Escape') { + stopLocationPicking(); + if (onCancel) onCancel(); + } + }; + document.addEventListener('keydown', pickingEscHandler); + } + + /** Beendet den Standort-Auswahlmodus, ohne onPick/onCancel selbst aufzurufen. */ + function stopLocationPicking() { + if (!pickingActive || !map) return; + pickingActive = false; + map.getContainer().classList.remove('map-panel__map--picking'); + + if (pickingClickHandler) { + map.off('click', pickingClickHandler); + pickingClickHandler = null; + } + if (pickingEscHandler) { + document.removeEventListener('keydown', pickingEscHandler); + pickingEscHandler = null; + } + } + + function isPickingLocation() { + return pickingActive; + } + /** Initialisiert die Karte im Element mit der übergebenen id. */ function initMap(containerId) { // Leaflet leert den Container beim Initialisieren nicht selbst - den @@ -175,5 +226,8 @@ EventMap.map.centerOnPlace = centerOnPlace; EventMap.map.centerOnCoords = centerOnCoords; EventMap.map.getMapInstance = getMapInstance; + EventMap.map.startLocationPicking = startLocationPicking; + EventMap.map.stopLocationPicking = stopLocationPicking; + EventMap.map.isPickingLocation = isPickingLocation; EventMap.map.initMap = initMap; })();