From aa04d7824bf7fcbdd64c1ff21d831c14df18c8d4 Mon Sep 17 00:00:00 2001 From: barrulus Date: Wed, 10 Jun 2026 09:42:34 +0100 Subject: [PATCH] fix(states-editor): state painter pagination, merge sort, visible province demotion Addresses tester feedback on the state merge + demote-to-province workflow: - State painter (manual assignment) only let you select states on the first page: the editor list is paginated at 200 and the pager is hidden in paint mode, so any state beyond page 1 had no clickable row. Render the full set while customization === 2 so every state is selectable. - Merge dialog listed states by state id regardless of the editor's active sort. Apply sortDataByActiveHeader so the list mirrors the column the user sorted by (culture, name, etc.). - "Merge down to provinces" could look like a plain merge: the new province was only drawn if the provinces layer happened to be on. Force the provinces layer on (matching how states force their own layer) so the demoted territory is always visible as a province. - Demoting a state now keeps its government form: a merged Duchy becomes " Duchy" instead of " Province" (falls back to "Province" when the state has no form name). Bump states-editor cache-bust token to 1.122.15. --- .../modules/dynamic/editors/states-editor.js | 37 +++++++++++++++---- public/modules/ui/editors.js | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/public/modules/dynamic/editors/states-editor.js b/public/modules/dynamic/editors/states-editor.js index dbd645c55..e5a8efc9c 100644 --- a/public/modules/dynamic/editors/states-editor.js +++ b/public/modules/dynamic/editors/states-editor.js @@ -192,10 +192,15 @@ function statesEditorAddLines() { totalBurgs += s.burgs; } + // Manual assignment picks the brush state by clicking its row, so every state must have a + // row in the DOM. Pagination would hide states beyond the first page (the pager is hidden in + // this mode too), making them unselectable. Render the full set while painting. + const isManualAssignment = customization === 2; const pageInfo = getEditorPage(allStates, statesPage); + const rows = isManualAssignment ? allStates : pageInfo.items; let lines = ""; - for (const s of pageInfo.items) { + for (const s of rows) { const area = getArea(s.area); const rural = s.rural * populationRate; const urban = s.urban * populationRate * urbanization; @@ -309,10 +314,15 @@ function statesEditorAddLines() { ensureEl("statesFooterPopulation").innerHTML = si(totalPopulation); ensureEl("statesFooterPopulation").dataset.population = totalPopulation; - renderEditorPagination(ensureEl("statesFooter"), pageInfo, page => { - statesPage.page = page; - statesEditorAddLines(); - }); + // No pager while painting: all rows are rendered and the footer is hidden anyway. + if (isManualAssignment) { + ensureEl("statesFooter").querySelector(":scope > .editorPagination")?.remove(); + } else { + renderEditorPagination(ensureEl("statesFooter"), pageInfo, page => { + statesPage.page = page; + statesEditorAddLines(); + }); + } // add listeners $body.querySelectorAll(":scope > div").forEach($line => { @@ -1350,6 +1360,9 @@ function exitAddStateMode() { function openStateMergeDialog() { const emblem = i => /* html */ ``; const validStates = pack.states.filter(s => s.i && !s.removed); + // Mirror the editor's active sort so the merge list reads in the same order the user is + // looking at (e.g. by culture), instead of always by state id. + sortDataByActiveHeader(ensureEl("statesHeader"), validStates, STATES_SORT_ACCESSORS); const statesSelector = validStates .map( @@ -1493,7 +1506,9 @@ function openStateMergeDialog() { const burg = state.capital || 0; const center = burg ? pack.burgs[burg].cell : state.center; const name = state.name; - const formName = "Province"; + // Keep the former state's government form (e.g. a demoted Duchy stays " Duchy"), + // falling back to a generic "Province" if the state had no form name. + const formName = state.formName || "Province"; const fullName = name + " " + formName; const color = state.color; const coa = state.coa; // reuse the former state's emblem @@ -1571,10 +1586,16 @@ function openStateMergeDialog() { debug.selectAll(".highlight").remove(); States.getPoles(); - if (mergeToProvinces) Provinces.getPoles(); layerIsOn("toggleStates") ? drawStates() : toggleStates(); layerIsOn("toggleBorders") ? drawBorders() : toggleBorders(); - layerIsOn("toggleProvinces") && drawProvinces(); + // When demoting to provinces, force the provinces layer on so the newly created province is + // actually visible. Otherwise (layer off) the result looks identical to a plain merge. + if (mergeToProvinces) { + Provinces.getPoles(); + layerIsOn("toggleProvinces") ? drawProvinces() : toggleProvinces(); + } else if (layerIsOn("toggleProvinces")) { + drawProvinces(); + } drawStateLabels([rulingStateId]); refreshStatesEditor(); diff --git a/public/modules/ui/editors.js b/public/modules/ui/editors.js index ba3593cc0..9ec4c937e 100644 --- a/public/modules/ui/editors.js +++ b/public/modules/ui/editors.js @@ -1079,7 +1079,7 @@ function refreshAllEditors() { // dynamically loaded editors async function editStates() { if (customization) return; - const Editor = await import("../dynamic/editors/states-editor.js?v=1.122.14"); + const Editor = await import("../dynamic/editors/states-editor.js?v=1.122.15"); Editor.open(); }