Files
racket-wiki/static/js/wiki/cmap/cmap-navigation-controller.js
T
2026-09-03 17:24:56 +02:00

71 lines
2.4 KiB
JavaScript

"use strict";
/** Coordinate CMap routes, parent contexts and browser-history state. */
export class CmapNavigationController {
constructor({ navigateToHash, cmapRoute, getEditor, getCurrentMap,
getParentMap, storage, historyObject = window.history, locationObject = window.location }) {
this.navigateToHash = navigateToHash;
this.cmapRoute = cmapRoute;
this.getEditor = getEditor;
this.getCurrentMap = getCurrentMap;
this.getParentMap = getParentMap;
this.storage = storage;
this.history = historyObject;
this.location = locationObject;
}
/** Open a stored CMap through the central hash-navigation contract. */
open(slug) {
return this.navigateToHash(this.cmapRoute(slug));
}
/** Remember the active separate-submap root before following a linked CMap. */
openLinked(record) {
if (!record?.cmapSlug) return false;
this.rememberContext();
this.open(record.cmapSlug).catch((error) => console.error(error));
return true;
}
/** Return to a source CMap when present, otherwise leave one inline submap level. */
openParent() {
const source = this.getParentMap();
if (source?.slug) {
this.open(source.slug).catch((error) => console.error(error));
return true;
}
const editor = this.getEditor();
return Boolean(editor?.openParentMap());
}
requestTransition(action) {
return this.storage.requestTransition(action);
}
/** Store the active separate-submap context in the current browser history entry. */
rememberContext() {
const editor = this.getEditor();
const current = this.getCurrentMap();
const root = editor?.activeMapRoot;
if (!current?.slug || !root) return;
this.history.replaceState({ ...this.history.state, cmapContext: {
mapSlug: current.slug,
rootItemId: Number(root.id)
} }, "", this.location.href);
}
/**
* Restore a remembered separate-submap root after browser navigation.
* post : A consumed context is removed from history state to prevent repeated restoration.
*/
async restoreContext(slug) {
const context = this.history.state?.cmapContext;
if (!context || context.mapSlug !== slug) return false;
const editor = this.getEditor();
const root = editor?.itemRecord(context.rootItemId);
if (root?.kind === "submap" && root.separateMap) editor.openSubmapMap(root);
this.history.replaceState({ ...this.history.state, cmapContext: null }, "", this.location.href);
return true;
}
}