runtime/coordinators/route.coordinator.ts
runtime/coordinators/route.coordinator.ts is a file in Bane's Lab Site. 74 lines of code and 13 definitions.
import { ROUTE_CHANGED, ROUTE_REQUESTED } from "#core/ids/route.ids";
import { applyHead, headOf, holdsHead } from "#presentation/renderers/page.renderer";
import { emitEvent, subscribeEvent } from "#core/buses/base.bus";
import { holdScroll, isReload } from "#core/observers/route.observer";
import { loadPage, loadedPage } from "#domain/registries/page.registry";
import { nodeFromPath, pageFromPath, pagePath } from "#core/assets/link.assets";
import { readScroll, writeScroll } from "#core/stores/route.store";
import type { Disposer } from "#types/base.types";
import { HOME_PAGE } from "#core/ids/page.ids";
import { renderPage } from "#presentation/renderers/page.shell.renderer";
let latest = 0;
const idle: Disposer = () => {};
const render = async function render(main: HTMLElement, page: string, rewriteHead = true): Promise<void> {
latest += 1;
const ticket = latest;
await loadPage(page);
if (ticket !== latest) {
return;
}
main.replaceChildren(...renderPage(page));
if (rewriteHead && nodeFromPath(window.location.pathname) === null) {
applyHead(document, headOf(page, loadedPage(page), window.location.pathname));
}
main.scrollTo(0, 0);
emitEvent({ name: ROUTE_CHANGED, page });
};
const restoreOnReload = function restoreOnReload(main: HTMLElement): Disposer {
const top = isReload() ? readScroll(window.location.pathname) : null;
if (top === null) {
return idle;
}
let release = idle;
const unsubscribe = subscribeEvent(ROUTE_CHANGED, () => {
unsubscribe();
requestAnimationFrame(() => {
release = holdScroll(main, top);
});
});
return () => {
unsubscribe();
release();
};
};
const navigate = function navigate(main: HTMLElement, page: string, path: string): void {
if (window.location.pathname + window.location.search + window.location.hash !== path) {
window.history.pushState({ page }, "", path);
}
void render(main, page);
};
export const startRouting = function startRouting(main: HTMLElement): Disposer {
const onPopState = function onPopState(): void {
void render(main, pageFromPath(window.location.pathname, HOME_PAGE));
};
const onPageHide = function onPageHide(): void {
writeScroll(window.location.pathname, main.scrollTop);
};
window.addEventListener("popstate", onPopState);
window.addEventListener("pagehide", onPageHide);
const unsubscribe = subscribeEvent(ROUTE_REQUESTED, (event) => {
navigate(main, event.page, event.path ?? pagePath(event.page));
});
const restore = restoreOnReload(main);
void render(
main,
pageFromPath(window.location.pathname, HOME_PAGE),
!holdsHead(document, window.location.pathname),
);
return () => {
window.removeEventListener("popstate", onPopState);
window.removeEventListener("pagehide", onPageHide);
unsubscribe();
restore();
};
};