import { useSignal } from '@preact/signals' import { useEffect, useMemo } from 'preact/hooks' import URLPattern from 'url-pattern' export const Router = ({ pages }) => { const compiledRoutes = useMemo( () => Object.entries(pages).map(([pattern, Page]) => ({ pattern: new URLPattern(pattern), Page, })), [pages] ) const routerUrl = useSignal(location.hash.slice(1)) useEffect(() => { window.addEventListener('hashchange', () => { routerUrl.value = location.hash.slice(1) }) }, []) const route = compiledRoutes.flatMap(({ pattern, Page }) => { const m = pattern.match(routerUrl.value.split('?', 1)[0]) return m ? [{ Page, params: m }] : [] })?.[0] if (!route) { console.log(`Invalid route "${routerUrl.value}", redirecting to homepage`) location.href = '/#/' routerUrl.value = '/' return <>Redirecting... } const { Page, params } = route const queryPart = routerUrl.value.indexOf('?') === -1 ? '' : routerUrl.value.slice(routerUrl.value.indexOf('?') + 1) console.log(queryPart) const queryParams = queryPart?.length > 0 ? Object.fromEntries( queryPart.split('&').map(kvPart => { const eqIndex = kvPart.indexOf('=') return eqIndex === -1 ? [kvPart, true] : [ kvPart.slice(0, eqIndex), decodeURIComponent(kvPart.slice(eqIndex + 1)), ] }) ) : {} return } export const Link = ({ page, params, query, children }) => { for (const [key, value] of Object.entries(params ?? {})) { page = page.replace(':' + key, encodeURIComponent(value)) } let targetHref = page if (query) { targetHref += '?' + Object.entries(query ?? {}) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .join('&') } return {children} }