feat: add selectable location search results

This commit is contained in:
Oliver 2026-02-15 14:16:54 +01:00
parent 15bbb677d8
commit 628d73afd6
No known key found for this signature in database
3 changed files with 76 additions and 2 deletions

View file

@ -15,6 +15,7 @@ const lonEl = document.getElementById("lon");
const searchQueryEl = document.getElementById("search-query"); const searchQueryEl = document.getElementById("search-query");
const searchLocationEl = document.getElementById("search-location"); const searchLocationEl = document.getElementById("search-location");
const searchStatusEl = document.getElementById("search-status"); const searchStatusEl = document.getElementById("search-status");
const searchResultsEl = document.getElementById("search-results");
const mapEl = document.getElementById("map"); const mapEl = document.getElementById("map");
const loadScoreEl = document.getElementById("load-score"); const loadScoreEl = document.getElementById("load-score");
const useLocationEl = document.getElementById("use-location"); const useLocationEl = document.getElementById("use-location");
@ -40,6 +41,8 @@ let lastHealthCheckAt = null;
let lastHealthLatencyMs = null; let lastHealthLatencyMs = null;
let map = null; let map = null;
let mapMarker = null; let mapMarker = null;
let searchResults = [];
let selectedSearchIndex = -1;
const deviceToken = ensureDeviceToken(); const deviceToken = ensureDeviceToken();
initialize(); initialize();
@ -166,6 +169,8 @@ async function fillLocationFromDevice() {
useLocationEl.disabled = true; useLocationEl.disabled = true;
navigator.geolocation.getCurrentPosition( navigator.geolocation.getCurrentPosition(
(position) => { (position) => {
selectedSearchIndex = -1;
renderSearchResults();
setCoordinates(position.coords.latitude, position.coords.longitude, { zoom: 16 }); setCoordinates(position.coords.latitude, position.coords.longitude, { zoom: 16 });
searchStatusEl.textContent = "Aktueller Standort übernommen."; searchStatusEl.textContent = "Aktueller Standort übernommen.";
useLocationEl.disabled = false; useLocationEl.disabled = false;
@ -191,6 +196,8 @@ function initializeMap() {
}).addTo(map); }).addTo(map);
map.on("click", (event) => { map.on("click", (event) => {
selectedSearchIndex = -1;
renderSearchResults();
setCoordinates(event.latlng.lat, event.latlng.lng, { fromMap: true }); setCoordinates(event.latlng.lat, event.latlng.lng, { fromMap: true });
searchStatusEl.textContent = "Position aus Karte übernommen."; searchStatusEl.textContent = "Position aus Karte übernommen.";
}); });
@ -252,20 +259,51 @@ async function searchLocation() {
const payload = await response.json(); const payload = await response.json();
if (!payload.results || !payload.results.length) { if (!payload.results || !payload.results.length) {
searchResults = [];
selectedSearchIndex = -1;
renderSearchResults();
searchStatusEl.textContent = "Keine Treffer gefunden."; searchStatusEl.textContent = "Keine Treffer gefunden.";
return; return;
} }
const best = payload.results[0]; searchResults = payload.results;
selectedSearchIndex = 0;
renderSearchResults();
const best = searchResults[0];
setCoordinates(best.lat, best.lon, { zoom: 16 }); setCoordinates(best.lat, best.lon, { zoom: 16 });
searchStatusEl.textContent = `Treffer: ${best.display_name}`; searchStatusEl.textContent = `Treffer ausgewählt: ${best.display_name}`;
} catch { } catch {
searchResults = [];
selectedSearchIndex = -1;
renderSearchResults();
searchStatusEl.textContent = "Suche fehlgeschlagen. Bitte später erneut versuchen."; searchStatusEl.textContent = "Suche fehlgeschlagen. Bitte später erneut versuchen.";
} finally { } finally {
searchLocationEl.disabled = false; searchLocationEl.disabled = false;
} }
} }
function renderSearchResults() {
searchResultsEl.innerHTML = "";
searchResults.forEach((result, index) => {
const li = document.createElement("li");
const button = document.createElement("button");
button.type = "button";
button.className = "search-result-btn";
if (index === selectedSearchIndex) {
button.classList.add("active");
}
button.textContent = result.display_name;
button.addEventListener("click", () => {
selectedSearchIndex = index;
setCoordinates(result.lat, result.lon, { zoom: 16 });
searchStatusEl.textContent = `Treffer ausgewählt: ${result.display_name}`;
renderSearchResults();
});
li.appendChild(button);
searchResultsEl.appendChild(li);
});
}
function cacheKey(lat, lon) { function cacheKey(lat, lon) {
return `${Number(lat).toFixed(4)}:${Number(lon).toFixed(4)}`; return `${Number(lat).toFixed(4)}:${Number(lon).toFixed(4)}`;
} }

View file

@ -40,6 +40,7 @@
</div> </div>
</label> </label>
<small id="search-status">Suche über OpenStreetMap Nominatim (DE).</small> <small id="search-status">Suche über OpenStreetMap Nominatim (DE).</small>
<ul id="search-results" class="search-results" aria-live="polite"></ul>
<div id="map" class="map"></div> <div id="map" class="map"></div>
<div class="button-row"> <div class="button-row">

View file

@ -141,6 +141,41 @@ input {
color: var(--muted); color: var(--muted);
} }
.search-results {
list-style: none;
margin: 8px 0 0;
padding: 0;
border: 1px solid var(--line);
border-radius: 10px;
max-height: 190px;
overflow: auto;
background: #fff;
}
.search-results:empty {
display: none;
}
.search-result-btn {
width: 100%;
text-align: left;
border: 0;
border-bottom: 1px solid var(--line);
background: #fff;
color: var(--ink);
padding: 10px;
cursor: pointer;
}
.search-result-btn:last-child {
border-bottom: 0;
}
.search-result-btn:hover,
.search-result-btn.active {
background: #eef6fb;
}
.map { .map {
margin-top: 8px; margin-top: 8px;
width: 100%; width: 100%;