wouter-preact
Version:
Minimalist-friendly ~1.5KB router for Preact
61 lines (47 loc) • 1.75 kB
JavaScript
import { a as useSyncExternalStore } from './preact-deps-dec5c677.js';
import 'preact/hooks';
import 'preact';
// array of callback subscribed to hash updates
const listeners = {
v: [],
};
const onHashChange = () => listeners.v.forEach((cb) => cb());
// we subscribe to `hashchange` only once when needed to guarantee that
// all listeners are called synchronously
const subscribeToHashUpdates = (callback) => {
if (listeners.v.push(callback) === 1)
addEventListener("hashchange", onHashChange);
return () => {
listeners.v = listeners.v.filter((i) => i !== callback);
if (!listeners.v.length) removeEventListener("hashchange", onHashChange);
};
};
// leading '#' is ignored, leading '/' is optional
const currentHashLocation = () => "/" + location.hash.replace(/^#?\/?/, "");
const navigate = (to, { state = null, replace = false } = {}) => {
const [hash, search] = to.replace(/^#?\/?/, "").split("?");
const newRelativePath =
location.pathname + (search ? `?${search}` : location.search) + `#/${hash}`;
const oldURL = location.href;
const newURL = new URL(newRelativePath, location.origin).href;
if (replace) {
history.replaceState(state, "", newRelativePath);
} else {
history.pushState(state, "", newRelativePath);
}
const event =
typeof HashChangeEvent !== "undefined"
? new HashChangeEvent("hashchange", { oldURL, newURL })
: new Event("hashchange", { detail: { oldURL, newURL } });
dispatchEvent(event);
};
const useHashLocation = ({ ssrPath = "/" } = {}) => [
useSyncExternalStore(
subscribeToHashUpdates,
currentHashLocation,
() => ssrPath
),
navigate,
];
useHashLocation.hrefs = (href) => "#" + href;
export { navigate, useHashLocation };