import { type LinkProps, Link as WouterLink, useRouter } from "wouter"; import { useEvent } from "../../hooks/use-event"; /* * Transforms `path` into its relative `base` version * If base isn't part of the path provided returns absolute path e.g. `~/app` */ export const relativePath = (base = "", path = "") => !path.toLowerCase().indexOf(base.toLowerCase()) ? path.slice(base.length) || "/" : "~" + path; export const absolutePath = (to, base = "") => (to[0] === "~" ? to.slice(1) : base + to); /* * Removes leading question mark */ export const stripQm = (str) => (str[0] === "?" ? str.slice(1) : str); /* * decodes escape sequences such as %20 */ // biome-ignore lint/suspicious/noShadowRestrictedNames: export const unescape = (str) => { try { return decodeURI(str); } catch (_e) { // fail-safe mode: if string can't be decoded do nothing return str; } }; const useLocationFromRouter = (router) => { const [location, navigate] = router.hook(router); // the function reference should stay the same between re-renders, so that // it can be passed down as an element prop without any performance concerns. // (This is achieved via `useEvent`.) return [ unescape(relativePath(router.base, location)), useEvent((to, navOpts) => navigate(absolutePath(to, router.base), navOpts)), ]; }; export function isLinkActive(href: string, strictness?: number) { const path = window.location.pathname; if (!strictness || strictness === 0) { return path.includes(href); } else if (strictness === 1) { return path === href || path.endsWith(href) || path.includes(href + "/"); } return path === href; } export function Link({ className, native, onClick, ...props }: { className?: string; native?: boolean; transition?: boolean } & LinkProps) { const router = useRouter(); const [path, navigate] = useLocationFromRouter(router); function isActive(absPath: string, href: string) { if (absPath.startsWith(href)) { const l = absPath.replace(href, ""); return l.length === 0 || l[0] === "/"; } return false; } const _href = props.href ?? props.to; const href = router .hrefs( _href[0] === "~" ? _href.slice(1) : router.base + _href, router, // pass router as a second argument for convinience ) .replace("//", "/"); const absPath = absolutePath(path, router.base).replace("//", "/"); const active = href.replace(router.base, "").length <= 1 ? href === absPath : isActive(absPath, href); if (native) { return ; } const wouterOnClick = (e: any) => { onClick?.(e); // prepared for view transition /*if (props.transition !== false) { e.preventDefault(); onClick?.(e); document.startViewTransition(() => { navigate(props.href ?? props.to, props); }); }*/ }; return ( ); }