- src/lib/regions.ts (new): cached, turf-free same-origin fetches of /region-keys.json (feature id -> key) and /regions.geo.json (id -> name) so WorldMap and NationPanel share one fetch each. - WorldMap.tsx: stamps each feature's properties.regionKey (from the manifest, by its stable id) and resolves owner / flash / hover / click from that key via the shared mapFeatureOwner; the per-region flash lights the exact sub-region whose owner changed; click carries only the sub-region name. - NationPanel.tsx: territory chips show the region's human name (falling back to the raw key) with the key in the title / data-attr. - App.tsx: the selected-region dossier label drops the now-unused parent code.
47 lines
1.9 KiB
TypeScript
47 lines
1.9 KiB
TypeScript
// Client-side region-key data access. Fetches the two static JSON files served
|
|
// from public/ and builds the lookups the region-level UI needs:
|
|
// - idToKey(): feature id -> region key (to stamp each map feature + resolve owner)
|
|
// - keyToName(): region key -> human name (to show region names in the dossier)
|
|
// Each is fetched once and cached (Promise singleton) so WorldMap and NationPanel
|
|
// share a single fetch. Same-origin relative URLs (proxy/Traefik safe) — never a
|
|
// hardcoded origin.
|
|
type GeoLike = {
|
|
features?: Array<{ id?: string | number; properties?: { name?: string } }>;
|
|
};
|
|
|
|
let idKeyPromise: Promise<Record<string, string>> | null = null;
|
|
let keyNamePromise: Promise<Record<string, string>> | null = null;
|
|
|
|
async function fetchJson<T>(url: string): Promise<T> {
|
|
const r = await fetch(url);
|
|
if (!r.ok) throw new Error(`${url} → HTTP ${r.status}`);
|
|
return (await r.json()) as T;
|
|
}
|
|
|
|
/** feature id (from regions.geo.json) -> region key (from region-keys.json). */
|
|
export function idToKey(): Promise<Record<string, string>> {
|
|
idKeyPromise ??= fetchJson<Record<string, string>>('/region-keys.json');
|
|
return idKeyPromise;
|
|
}
|
|
|
|
/**
|
|
* region key -> human region name. Built by joining region-keys.json (id -> key)
|
|
* with regions.geo.json (id -> properties.name). A key with no known name is
|
|
* simply absent (callers fall back to the raw key).
|
|
*/
|
|
export function keyToName(): Promise<Record<string, string>> {
|
|
keyNamePromise ??= Promise.all([idToKey(), fetchJson<GeoLike>('/regions.geo.json')]).then(
|
|
([keys, geo]) => {
|
|
const out: Record<string, string> = {};
|
|
for (const f of geo.features ?? []) {
|
|
const id = f.id == null ? '' : String(f.id);
|
|
const key = keys[id];
|
|
const name = f.properties?.name;
|
|
if (key && typeof name === 'string') out[key] = name;
|
|
}
|
|
return out;
|
|
},
|
|
);
|
|
return keyNamePromise;
|
|
}
|