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
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>
|
|
}
|