mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-04 17:41:23 +01:00
Overview dialogs search (#1260)
* feat: add search functionality to overview components * feat: enhance search functionality * chore: correct typo in pull request template * chore: update version to 1.110.0 and add peer dependencies in package-lock.json; enhance versioning.js with new features * Fix null safety and performance in overview dialogs search (#1272) * Initial plan * fix: add optional chaining and optimize performance in overview dialogs Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com>
This commit is contained in:
parent
9e0eb03618
commit
f30ffd812e
9 changed files with 149 additions and 63 deletions
|
|
@ -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);
|
||||
|
|
@ -63,9 +64,30 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
|
||||
// add line for each burg
|
||||
function burgsOverviewAddLines() {
|
||||
const searchText = byId("burgsSearch").value.toLowerCase().trim();
|
||||
const selectedStateId = +byId("burgsFilterState").value;
|
||||
const selectedCultureId = +byId("burgsFilterCulture").value;
|
||||
let filtered = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||
|
||||
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||
let filtered = validBurgs;
|
||||
|
||||
if (searchText) {
|
||||
// filter by search text
|
||||
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) ||
|
||||
b.group.toLowerCase().includes(searchText)
|
||||
);
|
||||
});
|
||||
}
|
||||
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
|
||||
|
||||
|
|
@ -119,7 +141,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
body.insertAdjacentHTML("beforeend", lines);
|
||||
|
||||
// update footer
|
||||
burgsFooterBurgs.innerHTML = filtered.length;
|
||||
burgsFooterBurgs.innerHTML = `${filtered.length} of ${validBurgs.length}`;
|
||||
burgsFooterPopulation.innerHTML = filtered.length ? si(totalPopulation / filtered.length) : 0;
|
||||
|
||||
// add listeners
|
||||
|
|
|
|||
|
|
@ -4,18 +4,19 @@ function overviewMarkers() {
|
|||
closeDialogs("#markersOverview, .stable");
|
||||
if (!layerIsOn("toggleMarkers")) toggleMarkers();
|
||||
|
||||
const markerGroup = document.getElementById("markers");
|
||||
const body = document.getElementById("markersBody");
|
||||
const markersInverPin = document.getElementById("markersInverPin");
|
||||
const markersInverLock = document.getElementById("markersInverLock");
|
||||
const markersFooterNumber = document.getElementById("markersFooterNumber");
|
||||
const markersOverviewRefresh = document.getElementById("markersOverviewRefresh");
|
||||
const markersAddFromOverview = document.getElementById("markersAddFromOverview");
|
||||
const markersGenerationConfig = document.getElementById("markersGenerationConfig");
|
||||
const markersRemoveAll = document.getElementById("markersRemoveAll");
|
||||
const markersExport = document.getElementById("markersExport");
|
||||
const markerTypeInput = document.getElementById("addedMarkerType");
|
||||
const markerTypeSelector = document.getElementById("markerTypeSelector");
|
||||
const markerGroup = byId("markers");
|
||||
const body = byId("markersBody");
|
||||
const markersInverPin = byId("markersInverPin");
|
||||
const markersInverLock = byId("markersInverLock");
|
||||
const markersFooterNumber = byId("markersFooterNumber");
|
||||
const markersOverviewRefresh = byId("markersOverviewRefresh");
|
||||
const markersAddFromOverview = byId("markersAddFromOverview");
|
||||
const markersGenerationConfig = byId("markersGenerationConfig");
|
||||
const markersRemoveAll = byId("markersRemoveAll");
|
||||
const markersExport = byId("markersExport");
|
||||
const markerTypeInput = byId("addedMarkerType");
|
||||
const markerTypeSelector = byId("markerTypeSelector");
|
||||
const markersSearch = byId("markersSearch");
|
||||
|
||||
addLines();
|
||||
|
||||
|
|
@ -36,7 +37,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 +69,17 @@ function overviewMarkers() {
|
|||
}
|
||||
|
||||
function addLines() {
|
||||
const lines = pack.markers
|
||||
let markers = pack.markers;
|
||||
|
||||
const searchText = byId("markersSearch").value.toLowerCase().trim();
|
||||
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 */ `
|
||||
<div class="states" data-i=${i} data-type="${type}">
|
||||
|
|
@ -91,7 +103,8 @@ function overviewMarkers() {
|
|||
.join("");
|
||||
|
||||
body.innerHTML = lines;
|
||||
markersFooterNumber.innerText = pack.markers.length;
|
||||
markersFooterNumber.innerText = markers.length;
|
||||
markersFooterTotal.innerText = pack.markers.length;
|
||||
|
||||
applySorting(markersHeader);
|
||||
}
|
||||
|
|
@ -127,7 +140,7 @@ function overviewMarkers() {
|
|||
}
|
||||
|
||||
function focusOnMarker(i) {
|
||||
highlightElement(document.getElementById(`marker${i}`), 2);
|
||||
highlightElement(byId(`marker${i}`), 2);
|
||||
}
|
||||
|
||||
function pinMarker(el, i) {
|
||||
|
|
@ -165,7 +178,7 @@ function overviewMarkers() {
|
|||
}
|
||||
|
||||
function toggleMarkerTypeMenu() {
|
||||
document.getElementById("markerTypeSelectMenu").classList.toggle("visible");
|
||||
byId("markerTypeSelectMenu").classList.toggle("visible");
|
||||
}
|
||||
|
||||
function toggleAddMarker() {
|
||||
|
|
@ -182,7 +195,7 @@ function overviewMarkers() {
|
|||
function removeMarker(i) {
|
||||
notes = notes.filter(note => note.id !== `marker${i}`);
|
||||
pack.markers = pack.markers.filter(marker => marker.i !== i);
|
||||
document.getElementById(`marker${i}`)?.remove();
|
||||
byId(`marker${i}`)?.remove();
|
||||
addLines();
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +213,7 @@ function overviewMarkers() {
|
|||
if (lock) return true;
|
||||
|
||||
const id = `marker${i}`;
|
||||
document.getElementById(id)?.remove();
|
||||
byId(id)?.remove();
|
||||
notes = notes.filter(note => note.id !== id);
|
||||
return false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ function overviewRivers() {
|
|||
closeDialogs("#riversOverview, .stable");
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
|
||||
const body = document.getElementById("riversBody");
|
||||
const body = byId("riversBody");
|
||||
riversOverviewAddLines();
|
||||
$("#riversOverview").dialog();
|
||||
|
||||
|
|
@ -20,12 +20,13 @@ function overviewRivers() {
|
|||
});
|
||||
|
||||
// add listeners
|
||||
document.getElementById("riversOverviewRefresh").addEventListener("click", riversOverviewAddLines);
|
||||
document.getElementById("addNewRiver").addEventListener("click", toggleAddRiver);
|
||||
document.getElementById("riverCreateNew").addEventListener("click", createRiver);
|
||||
document.getElementById("riversBasinHighlight").addEventListener("click", toggleBasinsHightlight);
|
||||
document.getElementById("riversExport").addEventListener("click", downloadRiversData);
|
||||
document.getElementById("riversRemoveAll").addEventListener("click", triggerAllRiversRemove);
|
||||
byId("riversOverviewRefresh").on("click", riversOverviewAddLines);
|
||||
byId("addNewRiver").on("click", toggleAddRiver);
|
||||
byId("riverCreateNew").on("click", createRiver);
|
||||
byId("riversBasinHighlight").on("click", toggleBasinsHightlight);
|
||||
byId("riversExport").on("click", downloadRiversData);
|
||||
byId("riversRemoveAll").on("click", triggerAllRiversRemove);
|
||||
byId("riversSearch").on("input", riversOverviewAddLines);
|
||||
|
||||
// add line for each river
|
||||
function riversOverviewAddLines() {
|
||||
|
|
@ -33,11 +34,26 @@ function overviewRivers() {
|
|||
let lines = "";
|
||||
const unit = distanceUnitInput.value;
|
||||
|
||||
for (const r of pack.rivers) {
|
||||
// Precompute a lookup map from river id to river for efficient basin lookup
|
||||
const riversById = new Map(pack.rivers.map(river => [river.i, river]));
|
||||
|
||||
let filteredRivers = pack.rivers;
|
||||
const searchText = byId("riversSearch").value.toLowerCase().trim();
|
||||
if (searchText) {
|
||||
filteredRivers = filteredRivers.filter(r => {
|
||||
const name = (r.name || "").toLowerCase();
|
||||
const type = (r.type || "").toLowerCase();
|
||||
const basin = riversById.get(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;
|
||||
const basin = pack.rivers.find(river => river.i === r.basin)?.name;
|
||||
const basin = riversById.get(r.basin)?.name;
|
||||
|
||||
lines += /* html */ `<div
|
||||
class="states"
|
||||
|
|
@ -63,22 +79,20 @@ 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} of ${pack.rivers.length}`;
|
||||
const averageDischarge = rn(d3.mean(filteredRivers.map(r => r.discharge))) || 0;
|
||||
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))) || 0;
|
||||
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) || 0;
|
||||
riversFooterWidth.innerHTML = rn(averageWidth * distanceScale, 3) + " " + unit;
|
||||
|
||||
// add listeners
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => riverHighlightOff(ev)));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomToRiver));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openRiverEditor));
|
||||
body
|
||||
.querySelectorAll("div > span.icon-trash-empty")
|
||||
.forEach(el => el.addEventListener("click", triggerRiverRemove));
|
||||
body.querySelectorAll("div.states").forEach(el => el.on("mouseenter", ev => riverHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.on("mouseleave", ev => riverHighlightOff(ev)));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.on("click", zoomToRiver));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.on("click", openRiverEditor));
|
||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", triggerRiverRemove));
|
||||
|
||||
applySorting(riversHeader);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,25 @@ function overviewRoutes() {
|
|||
byId("routesExport").on("click", downloadRoutesData);
|
||||
byId("routesLockAll").on("click", toggleLockAll);
|
||||
byId("routesRemoveAll").on("click", triggerAllRoutesRemove);
|
||||
byId("routesSearch").on("input", routesOverviewAddLines);
|
||||
|
||||
// add line for each route
|
||||
function routesOverviewAddLines() {
|
||||
body.innerHTML = "";
|
||||
let lines = "";
|
||||
|
||||
for (const route of pack.routes) {
|
||||
let filteredRoutes = pack.routes;
|
||||
|
||||
const searchText = byId("routesSearch").value.toLowerCase().trim();
|
||||
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} of ${pack.routes.length}`;
|
||||
const averageLength = rn(d3.mean(filteredRoutes.map(r => r.length)) || 0) || 0;
|
||||
routesFooterLength.innerHTML = averageLength * distanceScale + " " + distanceUnitInput.value;
|
||||
|
||||
// add listeners
|
||||
|
|
@ -67,7 +79,7 @@ function overviewRoutes() {
|
|||
body.querySelectorAll("div.states").forEach(el => el.on("mouseleave", routeHighlightOff));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.on("click", zoomToRoute));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.on("click", openRouteEditor));
|
||||
body.querySelectorAll("div > span.locks").forEach(el => el.addEventListener("click", toggleLockStatus));
|
||||
body.querySelectorAll("div > span.locks").forEach(el => el.on("click", toggleLockStatus));
|
||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", triggerRouteRemove));
|
||||
|
||||
applySorting(routesHeader);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue