feat: add selectable location search results
This commit is contained in:
parent
15bbb677d8
commit
628d73afd6
3 changed files with 76 additions and 2 deletions
42
src/app.js
42
src/app.js
|
|
@ -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)}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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%;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue