You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
2.3 KiB
JavaScript

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 <Page params={params} query={queryParams} />
}
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 <a href={'/#' + targetHref}>{children}</a>
}