fix(ui): F8-10 owner-feedback — persistent filters, camera, disabled visual, dropdown narrowing (#53)
Polish pass after the first F8-10 walkthrough:
- table-planets: moved the `foreign` chip to the end of the row and
hid the race dropdown until `foreign` is on (it never made sense
to pick a race while the bucket itself was off).
- persistent per-table filter / sort state — extracted to
`table-{planets,ship-groups,fleets}-state.svelte.ts` singletons so
a row click → map → back to the table restores the prior chip /
dropdown / sort state. Held in memory only; an F5 still resets.
- table-ship-groups: the planet and class dropdowns now narrow to
the slice surviving the owner checkboxes, so toggling `foreign`
off removes planets / classes touched only by foreign rows.
- map.svelte: camera (centre + zoom) is captured on every dispose
path into a new `GameStateStore.lastCamera` and consumed on the
next mount, so leaving the map for any other active view and
coming back restores the prior pan / zoom. A pending focus from
the tables still wins for the centre point.
- table-ship-classes: `:disabled` now reads as disabled (muted
colour, no hover ring, not-allowed cursor) — the click was already
a no-op, only the visual was lying.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -30,14 +30,10 @@ Click semantics:
|
||||
} from "$lib/selection.svelte";
|
||||
import ViewState from "$lib/ui/view-state.svelte";
|
||||
import { formatFloat, formatInt } from "$lib/util/number-format";
|
||||
|
||||
type SortColumn =
|
||||
| "name"
|
||||
| "groupCount"
|
||||
| "state"
|
||||
| "location"
|
||||
| "speed";
|
||||
type SortDirection = "asc" | "desc";
|
||||
import {
|
||||
fleetsTableState as persistent,
|
||||
type FleetsSortColumn as SortColumn,
|
||||
} from "./table-fleets-state.svelte";
|
||||
|
||||
const COLUMN_LABELS: Record<SortColumn, TranslationKey> = {
|
||||
name: "game.table.fleets.column.name",
|
||||
@@ -60,9 +56,8 @@ Click semantics:
|
||||
);
|
||||
const selection = getContext<SelectionStore | undefined>(SELECTION_CONTEXT_KEY);
|
||||
|
||||
let sortColumn: SortColumn = $state("name");
|
||||
let sortDirection: SortDirection = $state("asc");
|
||||
let planetFilter: string = $state("");
|
||||
// `persistent` (module-level rune above) drives the dropdown
|
||||
// selection so the user's planet filter survives navigation.
|
||||
|
||||
const reportLoaded = $derived(
|
||||
rendered?.report !== null && rendered?.report !== undefined,
|
||||
@@ -85,7 +80,8 @@ Click semantics:
|
||||
});
|
||||
|
||||
const filtered = $derived.by(() => {
|
||||
const planet = planetFilter === "" ? null : Number(planetFilter);
|
||||
const planet =
|
||||
persistent.planetFilter === "" ? null : Number(persistent.planetFilter);
|
||||
return fleets.filter((f) => {
|
||||
if (planet === null) return true;
|
||||
return f.destination === planet || f.origin === planet;
|
||||
@@ -94,8 +90,8 @@ Click semantics:
|
||||
|
||||
const sorted = $derived.by(() => {
|
||||
const list = [...filtered];
|
||||
const dir = sortDirection === "asc" ? 1 : -1;
|
||||
list.sort((a, b) => compare(a, b, sortColumn) * dir);
|
||||
const dir = persistent.sortDirection === "asc" ? 1 : -1;
|
||||
list.sort((a, b) => compare(a, b, persistent.sortColumn) * dir);
|
||||
return list;
|
||||
});
|
||||
|
||||
@@ -123,17 +119,17 @@ Click semantics:
|
||||
}
|
||||
|
||||
function toggleSort(column: SortColumn): void {
|
||||
if (sortColumn === column) {
|
||||
sortDirection = sortDirection === "asc" ? "desc" : "asc";
|
||||
if (persistent.sortColumn === column) {
|
||||
persistent.sortDirection = persistent.sortDirection === "asc" ? "desc" : "asc";
|
||||
return;
|
||||
}
|
||||
sortColumn = column;
|
||||
sortDirection = "asc";
|
||||
persistent.sortColumn = column;
|
||||
persistent.sortDirection = "asc";
|
||||
}
|
||||
|
||||
function ariaSort(column: SortColumn): "ascending" | "descending" | "none" {
|
||||
if (sortColumn !== column) return "none";
|
||||
return sortDirection === "asc" ? "ascending" : "descending";
|
||||
if (persistent.sortColumn !== column) return "none";
|
||||
return persistent.sortDirection === "asc" ? "ascending" : "descending";
|
||||
}
|
||||
|
||||
function isInSpace(f: ReportLocalFleet): boolean {
|
||||
@@ -189,7 +185,7 @@ Click semantics:
|
||||
<span>{i18n.t("game.table.fleets.filter.planet")}</span>
|
||||
<select
|
||||
data-testid="fleets-filter-planet"
|
||||
bind:value={planetFilter}
|
||||
bind:value={persistent.planetFilter}
|
||||
>
|
||||
<option value=""
|
||||
>{i18n.t("game.table.fleets.filter.planet.all")}</option
|
||||
@@ -233,9 +229,9 @@ Click semantics:
|
||||
onclick={() => toggleSort(column)}
|
||||
>
|
||||
{i18n.t(COLUMN_LABELS[column])}
|
||||
{#if sortColumn === column}
|
||||
{#if persistent.sortColumn === column}
|
||||
<span class="sort-indicator" aria-hidden="true">
|
||||
{sortDirection === "asc" ? "▲" : "▼"}
|
||||
{persistent.sortDirection === "asc" ? "▲" : "▼"}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user