ui/phase-13: planet inspector — read-only
Plumbs the map → inspector pathway: a click on a planet selects it through the new SelectionStore, the sidebar Inspector tab swaps its empty-state copy for a per-kind read-only field set, and a mobile-only bottom-sheet mirrors the same content over the map. Field projection in api/game-state.ts now surfaces every documented planet field.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// Per-game selection state: which on-map object the user is
|
||||
// currently inspecting. Phase 13 only models planet selection, so
|
||||
// the union has a single variant; later phases (Phase 19 ship-group
|
||||
// inspector) will widen it.
|
||||
//
|
||||
// The store is in-memory only: lifetime matches the in-game shell
|
||||
// layout instance, which itself is preserved across active-view
|
||||
// switches inside `/games/:id/*`. Persisting selection across
|
||||
// reloads is intentionally out of scope — the Phase 13 acceptance
|
||||
// criterion calls out "across view switches", and survival across a
|
||||
// reload would be a surprising contrast with the empty-state copy
|
||||
// users see on first load.
|
||||
//
|
||||
// Like `GameStateStore` and `OrderDraftStore`, the store is
|
||||
// instantiated by the layout and shared with descendants through
|
||||
// Svelte context. The map view pushes selection events into it; the
|
||||
// inspector tab and the mobile bottom-sheet read from it.
|
||||
//
|
||||
// The store deliberately carries no Svelte component imports so it
|
||||
// can be tested directly without rendering any UI.
|
||||
|
||||
/**
|
||||
* Selected describes the currently selected map object. Phase 13
|
||||
* ships only the planet variant; later inspector phases extend the
|
||||
* discriminated union (`ship-group`, etc.) without changing the
|
||||
* store's contract.
|
||||
*/
|
||||
export type Selected = { kind: "planet"; id: number };
|
||||
|
||||
/**
|
||||
* SELECTION_CONTEXT_KEY is the Svelte context key the in-game shell
|
||||
* layout uses to expose its `SelectionStore` instance to descendants.
|
||||
* Map view, inspector tab, and the mobile bottom-sheet resolve the
|
||||
* store via `getContext(SELECTION_CONTEXT_KEY)`.
|
||||
*/
|
||||
export const SELECTION_CONTEXT_KEY = Symbol("selection");
|
||||
|
||||
export class SelectionStore {
|
||||
selected: Selected | null = $state(null);
|
||||
|
||||
private destroyed = false;
|
||||
|
||||
/**
|
||||
* selectPlanet sets the active selection to the planet identified
|
||||
* by its engine `number`. A no-op once the store has been disposed.
|
||||
*/
|
||||
selectPlanet(id: number): void {
|
||||
if (this.destroyed) return;
|
||||
this.selected = { kind: "planet", id };
|
||||
}
|
||||
|
||||
/**
|
||||
* clear drops the current selection. The mobile sheet's close
|
||||
* button calls this; otherwise selection persists across active-
|
||||
* view switches.
|
||||
*/
|
||||
clear(): void {
|
||||
if (this.destroyed) return;
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.destroyed = true;
|
||||
this.selected = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user