type RouterLike = { push: (to: unknown) => Promise | unknown } let globalRouter: RouterLike | null = null let clickHandler: ((event: MouseEvent) => void) | null = null const isModifiedClick = (event: MouseEvent): boolean => event.metaKey || event.ctrlKey || event.shiftKey || event.altKey const findLinkAnchor = (event: Event): HTMLAnchorElement | null => { const { target } = event if (!(target instanceof Element)) return null const anchor = target.closest('a[href]') return anchor instanceof HTMLAnchorElement ? anchor : null } /** * Returns true when href is a relative URL suitable for in-app navigation * (e.g. `/dashboard`, `page`, `./page`) — not absolute, protocol-relative, or hash-only. */ export const isRelativeHref = (href: string): boolean => { if (!href || href.startsWith('#')) return false if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return false if (href.startsWith('//')) return false return true } /** * Resolves an anchor href to a value suitable for `router.push`. * Returns `null` when the link should use normal browser navigation. */ export const resolveRouterLinkTarget = (anchor: HTMLAnchorElement): string | null => { if (anchor.hasAttribute('disabled') || anchor.getAttribute('aria-disabled') === 'true') { return null } if (anchor.hasAttribute('download')) return null if (anchor.target && anchor.target !== '_self') return null const href = anchor.getAttribute('href') if (!href || !isRelativeHref(href)) return null try { const url = new URL(href, window.location.href) return url.pathname + url.search + url.hash } catch { return href } } const handleClick = (event: MouseEvent) => { if (event.defaultPrevented) return if (event.button !== 0) return if (isModifiedClick(event)) return const anchor = findLinkAnchor(event) if (!anchor || !globalRouter) return const to = resolveRouterLinkTarget(anchor) if (!to) return event.preventDefault() void globalRouter.push(to) } /** * Interceptar klick på relativa `` och navigerar med `router.push` * i stället för full sidladdning. Användbart för HTML/JSON från t.ex. Payload CMS * eller Poeditor där `router-link` inte kan användas direkt. * * Anropa en gång vid app-start: * ```ts * setupRouterLinkInterceptor(router) * ``` * * HTML-exempel: * ```html * Öppna dashboard * ``` */ export const setupRouterLinkInterceptor = (router: RouterLike) => { teardownRouterLinkInterceptor() globalRouter = router clickHandler = handleClick document.addEventListener('click', clickHandler) } /** Tar bort den globala router-link-interceptorn. */ export const teardownRouterLinkInterceptor = () => { if (clickHandler) { document.removeEventListener('click', clickHandler) clickHandler = null } globalRouter = null }