UNPKG

@virtualstate/app-history

Version:

Native JavaScript [app-history](https://github.com/WICG/app-history) implementation

169 lines 5.47 kB
import { AppHistoryTransitionCommittedDeferred } from "./app-history-transition.js"; export const AppLocationCheckChange = Symbol.for("@virtualstate/app-history/location/checkChange"); export const AppLocationAwaitFinished = Symbol.for("@virtualstate/app-history/location/awaitFinished"); export const AppLocationTransitionURL = Symbol.for("@virtualstate/app-history/location/transitionURL"); const baseUrl = "https://html.spec.whatwg.org/"; /** * @experimental */ export class AppHistoryLocation { #options; #appHistory; constructor(options) { this.#options = options; this.#appHistory = options.appHistory; const reset = () => { this.#transitioningURL = undefined; this.#initialURL = undefined; }; this.#appHistory.addEventListener("navigate", () => { const transition = this.#appHistory.transition; if (transition && isCommittedAvailable(transition)) { transition[AppHistoryTransitionCommittedDeferred] .promise .then(reset, reset); } function isCommittedAvailable(transition) { return AppHistoryTransitionCommittedDeferred in transition; } }); this.#appHistory.addEventListener("currentchange", reset); } ancestorOrigins; #urls = new WeakMap(); #transitioningURL; #initialURL; get #url() { if (this.#transitioningURL) { return this.#transitioningURL; } const { current } = this.#appHistory; if (!current) { const initialUrl = this.#options.initialUrl ?? "/"; this.#initialURL = typeof initialUrl === "string" ? new URL(initialUrl, baseUrl) : initialUrl; return this.#initialURL; } const existing = this.#urls.get(current); if (existing) return existing; const next = new URL(current.url, baseUrl); this.#urls.set(current, next); return next; } get hash() { return this.#url.hash; } set hash(value) { this.#setUrlValue("hash", value); } get host() { return this.#url.host; } set host(value) { this.#setUrlValue("host", value); } get hostname() { return this.#url.hostname; } set hostname(value) { this.#setUrlValue("hostname", value); } get href() { return this.#url.href; } set href(value) { this.#setUrlValue("href", value); } get origin() { return this.#url.origin; } get pathname() { return this.#url.pathname; } set pathname(value) { this.#setUrlValue("pathname", value); } get port() { return this.#url.port; } set port(value) { this.#setUrlValue("port", value); } get protocol() { return this.#url.protocol; } set protocol(value) { this.#setUrlValue("protocol", value); } get search() { return this.#url.search; } set search(value) { this.#setUrlValue("search", value); } #setUrlValue = (key, value) => { const currentUrlString = this.#url.toString(); const nextUrl = new URL(currentUrlString); nextUrl[key] = value; const nextUrlString = nextUrl.toString(); if (currentUrlString === nextUrlString) { return; } void this.#transitionURL(nextUrl, () => this.#appHistory.navigate(nextUrlString)); }; async replace(url) { return this.#transitionURL(url, (url) => this.#appHistory.navigate(url.toString(), { replace: true })); } async reload() { return this.#awaitFinished(this.#appHistory.reload()); } async assign(url) { await this.#transitionURL(url, (url) => this.#appHistory.navigate(url.toString())); } [AppLocationTransitionURL](url, fn) { return this.#transitionURL(url, fn); } #transitionURL = async (url, fn) => { const instance = this.#transitioningURL = typeof url === "string" ? new URL(url, this.#url.toString()) : url; try { await this.#awaitFinished(fn(instance)); } finally { if (this.#transitioningURL === instance) { this.#transitioningURL = undefined; } } }; [AppLocationAwaitFinished](result) { return this.#awaitFinished(result); } #awaitFinished = async (result) => { this.#initialURL = undefined; if (!result) return; const { committed, finished } = result; await Promise.all([ committed || Promise.resolve(undefined), finished || Promise.resolve(undefined) ]); }; #triggerIfUrlChanged = () => { const current = this.#url; const currentUrl = current.toString(); const expectedUrl = this.#appHistory.current.url; if (currentUrl !== expectedUrl) { return this.#transitionURL(current, () => this.#appHistory.navigate(currentUrl)); } }; /** * This is needed if you have changed searchParams using its mutating methods * * TODO replace get searchParams with an observable change to auto trigger this function */ [AppLocationCheckChange]() { return this.#triggerIfUrlChanged(); } } //# sourceMappingURL=location.js.map