UNPKG

svelte5-router

Version:

A declarative Svelte routing library with SSR support

93 lines (92 loc) 3.49 kB
/** * Adapted from https://github.com/reach/router/blob/b60e6dd781d5d3a4bdaaf4de665649c0f6a7e78d/src/lib/history.js * https://github.com/reach/router/blob/master/LICENSE */ import { canUseDOM } from "./utils"; const getLocation = (source) => { return { ...source.location, state: source.history.state, key: (source.history.state && source.history.state.key) || "initial", }; }; const createHistory = (source) => { const listeners = []; let location = getLocation(source); return { get location() { return location; }, listen(listener) { listeners.push(listener); const popstateListener = () => { location = getLocation(source); listener({ location, action: "POP" }); }; source.addEventListener("popstate", popstateListener); return () => { source.removeEventListener("popstate", popstateListener); const index = listeners.indexOf(listener); listeners.splice(index, 1); }; }, navigate(to, options) { let { state = {}, replace = false, preserveScroll = false, blurActiveElement = false, } = options ?? {}; state = { ...state, key: Date.now() + "" }; // try...catch iOS Safari limits to 100 pushState calls try { if (replace) source.history.replaceState(state, "", to); else source.history.pushState(state, "", to); } catch (e) { source.location[replace ? "replace" : "assign"](to); } location = getLocation(source); listeners.forEach((listener) => listener({ location, action: "PUSH", preserveScroll })); if (blurActiveElement && document.activeElement instanceof HTMLElement) document.activeElement.blur(); }, }; }; // Stores history entries in memory for testing or other platforms like Native const createMemorySource = (initialPathname = "/") => { let index = 0; const stack = [{ pathname: initialPathname, search: "" }]; const states = []; return { get location() { return stack[index]; }, addEventListener() { }, removeEventListener() { }, history: { get entries() { return stack; }, get index() { return index; }, get state() { return states[index]; }, pushState(state, _, uri) { const [pathname, search = ""] = uri.split("?"); index++; stack.push({ pathname, search }); states.push(state); }, replaceState(state, _, uri) { const [pathname, search = ""] = uri.split("?"); stack[index] = { pathname, search }; states[index] = state; }, }, }; }; // Global history uses window.history as the source if available, // otherwise a memory history const globalHistory = createHistory(canUseDOM() ? window : createMemorySource()); const { navigate, listen } = globalHistory; export { globalHistory, navigate, listen, createHistory, createMemorySource };