AK-21/Graphite-Industrial-Intelligence
0
1import { useSyncExternalStore } from "./react-deps.js";2 3// array of callback subscribed to hash updates4const listeners = {5 v: [],6};7 8const onHashChange = () => listeners.v.forEach((cb) => cb());9 10// we subscribe to `hashchange` only once when needed to guarantee that11// all listeners are called synchronously12const subscribeToHashUpdates = (callback) => {13 if (listeners.v.push(callback) === 1)14 addEventListener("hashchange", onHashChange);15 16 return () => {17 listeners.v = listeners.v.filter((i) => i !== callback);18 if (!listeners.v.length) removeEventListener("hashchange", onHashChange);19 };20};21 22// leading '#' is ignored, leading '/' is optional23const currentHashLocation = () => "/" + location.hash.replace(/^#?\/?/, "");24 25export const navigate = (to, { state = null, replace = false } = {}) => {26 const oldURL = location.href;27 28 const [hash, search] = to.replace(/^#?\/?/, "").split("?");29 30 // Works for ALL protocols including data:31 const url = new URL(location.href);32 url.hash = `/${hash}`;33 if (search) url.search = search;34 const newURL = url.href;35 36 if (replace) {37 history.replaceState(state, "", newURL);38 } else {39 history.pushState(state, "", newURL);40 }41 42 const event =43 typeof HashChangeEvent !== "undefined"44 ? new HashChangeEvent("hashchange", { oldURL, newURL })45 : new Event("hashchange", { detail: { oldURL, newURL } });46 47 dispatchEvent(event);48};49 50export const useHashLocation = ({ ssrPath = "/" } = {}) => [51 useSyncExternalStore(52 subscribeToHashUpdates,53 currentHashLocation,54 () => ssrPath55 ),56 navigate,57];58 59useHashLocation.hrefs = (href) => "#" + href;60 