From 3e339b78d46923b3b5571839a00ebc4bb9c721f7 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Fri, 9 Jan 2026 22:05:27 +0100 Subject: [PATCH] feat: add search functionality to overview components --- index.html | 32 +++++++++++++++++++++++++++----- modules/ui/burgs-overview.js | 14 ++++++++++++++ modules/ui/markers-overview.js | 21 ++++++++++++++++++--- modules/ui/rivers-overview.js | 24 +++++++++++++++++++----- modules/ui/routes-overview.js | 18 +++++++++++++++--- 5 files changed, 93 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index 686c9994..ecd5f32f 100644 --- a/index.html +++ b/index.html @@ -5369,12 +5369,22 @@
-
- - +
+ - - + + +
@@ -5456,6 +5466,10 @@
+
+ +
+
@@ -5506,6 +5520,10 @@
+
+ +
+
@@ -5705,6 +5723,10 @@ >
+
+ +
+
diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js index ac18ab56..54abae98 100644 --- a/modules/ui/burgs-overview.js +++ b/modules/ui/burgs-overview.js @@ -28,6 +28,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) { byId("burgsChart").addEventListener("click", showBurgsChart); byId("burgsFilterState").addEventListener("change", burgsOverviewAddLines); byId("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines); + byId("burgsSearch").addEventListener("input", burgsOverviewAddLines); byId("regenerateBurgNames").addEventListener("click", regenerateNames); byId("addNewBurg").addEventListener("click", enterAddBurgMode); byId("burgsExport").addEventListener("click", downloadBurgsData); @@ -65,9 +66,22 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) { function burgsOverviewAddLines() { const selectedStateId = +byId("burgsFilterState").value; const selectedCultureId = +byId("burgsFilterCulture").value; + const searchText = (byId("burgsSearch").value || "").toLowerCase().trim(); let filtered = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs if (selectedStateId !== -1) filtered = filtered.filter(b => b.state === selectedStateId); // filtered by state if (selectedCultureId !== -1) filtered = filtered.filter(b => b.culture === selectedCultureId); // filtered by culture + + // filter by search text + if (searchText) { + filtered = filtered.filter(b => { + const name = (b.name || "").toLowerCase(); + const state = (pack.states[b.state]?.name || "").toLowerCase(); + const prov = pack.cells.province[b.cell]; + const province = prov ? (pack.provinces[prov]?.name || "").toLowerCase() : ""; + const culture = (pack.cultures[b.culture]?.name || "").toLowerCase(); + return name.includes(searchText) || state.includes(searchText) || province.includes(searchText) || culture.includes(searchText); + }); + } body.innerHTML = ""; let lines = ""; diff --git a/modules/ui/markers-overview.js b/modules/ui/markers-overview.js index 02999eb0..0155bf11 100644 --- a/modules/ui/markers-overview.js +++ b/modules/ui/markers-overview.js @@ -27,6 +27,8 @@ function overviewMarkers() { position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} }); + const markersSearch = document.getElementById("markersSearch"); + const listeners = [ listen(body, "click", handleLineClick), listen(markersInverPin, "click", invertPin), @@ -36,7 +38,8 @@ function overviewMarkers() { listen(markersGenerationConfig, "click", configMarkersGeneration), listen(markersRemoveAll, "click", triggerRemoveAll), listen(markersExport, "click", exportMarkers), - listen(markerTypeSelector, "click", toggleMarkerTypeMenu) + listen(markerTypeSelector, "click", toggleMarkerTypeMenu), + listen(markersSearch, "input", addLines) ]; const types = [{type: "empty", icon: "❓"}, ...Markers.getConfig()]; @@ -67,7 +70,19 @@ function overviewMarkers() { } function addLines() { - const lines = pack.markers + const searchInput = document.getElementById("markersSearch"); + const searchText = (searchInput?.value || "").toLowerCase().trim(); + let markers = pack.markers; + + // filter by search text + if (searchText) { + markers = markers.filter(marker => { + const type = (marker.type || "").toLowerCase(); + return type.includes(searchText); + }); + } + + const lines = markers .map(({i, type, icon, pinned, lock}) => { return /* html */ `
@@ -91,7 +106,7 @@ function overviewMarkers() { .join(""); body.innerHTML = lines; - markersFooterNumber.innerText = pack.markers.length; + markersFooterNumber.innerText = markers.length; applySorting(markersHeader); } diff --git a/modules/ui/rivers-overview.js b/modules/ui/rivers-overview.js index 7fc32b45..d5cb9b36 100644 --- a/modules/ui/rivers-overview.js +++ b/modules/ui/rivers-overview.js @@ -26,14 +26,28 @@ function overviewRivers() { document.getElementById("riversBasinHighlight").addEventListener("click", toggleBasinsHightlight); document.getElementById("riversExport").addEventListener("click", downloadRiversData); document.getElementById("riversRemoveAll").addEventListener("click", triggerAllRiversRemove); + document.getElementById("riversSearch").addEventListener("input", riversOverviewAddLines); // add line for each river function riversOverviewAddLines() { body.innerHTML = ""; let lines = ""; const unit = distanceUnitInput.value; + const searchText = (document.getElementById("riversSearch").value || "").toLowerCase().trim(); + let filteredRivers = pack.rivers; - for (const r of pack.rivers) { + // filter by search text + if (searchText) { + filteredRivers = filteredRivers.filter(r => { + const name = (r.name || "").toLowerCase(); + const type = (r.type || "").toLowerCase(); + const basin = pack.rivers.find(river => river.i === r.basin); + const basinName = basin ? (basin.name || "").toLowerCase() : ""; + return name.includes(searchText) || type.includes(searchText) || basinName.includes(searchText); + }); + } + + for (const r of filteredRivers) { const discharge = r.discharge + " m³/s"; const length = rn(r.length * distanceScale) + " " + unit; const width = rn(r.width * distanceScale, 3) + " " + unit; @@ -63,12 +77,12 @@ function overviewRivers() { body.insertAdjacentHTML("beforeend", lines); // update footer - riversFooterNumber.innerHTML = pack.rivers.length; - const averageDischarge = rn(d3.mean(pack.rivers.map(r => r.discharge))); + riversFooterNumber.innerHTML = filteredRivers.length; + const averageDischarge = rn(d3.mean(filteredRivers.map(r => r.discharge))); riversFooterDischarge.innerHTML = averageDischarge + " m³/s"; - const averageLength = rn(d3.mean(pack.rivers.map(r => r.length))); + const averageLength = rn(d3.mean(filteredRivers.map(r => r.length))); riversFooterLength.innerHTML = averageLength * distanceScale + " " + unit; - const averageWidth = rn(d3.mean(pack.rivers.map(r => r.width)), 3); + const averageWidth = rn(d3.mean(filteredRivers.map(r => r.width)), 3); riversFooterWidth.innerHTML = rn(averageWidth * distanceScale, 3) + " " + unit; // add listeners diff --git a/modules/ui/routes-overview.js b/modules/ui/routes-overview.js index cf731068..fcb99077 100644 --- a/modules/ui/routes-overview.js +++ b/modules/ui/routes-overview.js @@ -25,13 +25,25 @@ function overviewRoutes() { byId("routesExport").on("click", downloadRoutesData); byId("routesLockAll").on("click", toggleLockAll); byId("routesRemoveAll").on("click", triggerAllRoutesRemove); + byId("routesSearch").addEventListener("input", routesOverviewAddLines); // add line for each route function routesOverviewAddLines() { body.innerHTML = ""; let lines = ""; + const searchText = (byId("routesSearch").value || "").toLowerCase().trim(); + let filteredRoutes = pack.routes; - for (const route of pack.routes) { + // filter by search text + if (searchText) { + filteredRoutes = filteredRoutes.filter(route => { + const name = (route.name || "").toLowerCase(); + const group = (route.group || "").toLowerCase(); + return name.includes(searchText) || group.includes(searchText); + }); + } + + for (const route of filteredRoutes) { if (!route.points || route.points.length < 2) continue; route.name = route.name || Routes.generateName(route); route.length = route.length || Routes.getLength(route.i); @@ -58,8 +70,8 @@ function overviewRoutes() { body.insertAdjacentHTML("beforeend", lines); // update footer - routesFooterNumber.innerHTML = pack.routes.length; - const averageLength = rn(d3.mean(pack.routes.map(r => r.length)) || 0); + routesFooterNumber.innerHTML = filteredRoutes.length; + const averageLength = rn(d3.mean(filteredRoutes.map(r => r.length)) || 0); routesFooterLength.innerHTML = averageLength * distanceScale + " " + distanceUnitInput.value; // add listeners