0
0
mirror of https://github.com/honojs/hono.git synced 2024-11-24 02:07:30 +01:00

[PoC]: SPA option for JSX Renderer

This commit is contained in:
Yusuke Wada 2024-01-12 00:30:18 +09:00
parent 1489e1dc90
commit cc9283a753

View File

@ -1,11 +1,39 @@
import type { Context, Renderer } from '../../context'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { html, raw } from '../../helper/html'
import { jsx, createContext, useContext } from '../../jsx'
import type { FC, JSXNode } from '../../jsx'
import { renderToReadableStream } from '../../jsx/streaming'
import type { Env, Input, MiddlewareHandler } from '../../types'
const SPA_CONTENT_REQUEST_QUERY = '__spa_content'
const SPA_ROOT_ID = '__root'
const SPA_CLIENT_SCRIPT = `async function mountContent(pathname) {
const res = await fetch(pathname + '?${SPA_CONTENT_REQUEST_QUERY}')
const content = await res.text()
const root = document.querySelector('#${SPA_ROOT_ID}')
root.innerHTML = content
}
window.addEventListener(
'click',
(e) => {
if ((e.target).tagName !== 'A') {
return
}
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
return
}
const href = (e.target).getAttribute('href')
if (!href.startsWith('/')) {
return
}
e.preventDefault()
window.history.pushState(null, null, href)
mountContent(href)
},
true
)
`
export const RequestContext = createContext<Context | null>(null)
type PropsForRenderer = [...Required<Parameters<Renderer>>] extends [unknown, infer Props]
@ -15,11 +43,19 @@ type PropsForRenderer = [...Required<Parameters<Renderer>>] extends [unknown, in
type RendererOptions = {
docType?: boolean | string
stream?: boolean | Record<string, string>
spa?: boolean
}
const createRenderer =
(c: Context, component?: FC<PropsForRenderer>, options?: RendererOptions) =>
(children: JSXNode, props: PropsForRenderer) => {
if (options?.spa) {
if (c.req.query(SPA_CONTENT_REQUEST_QUERY) !== undefined) {
return c.html(children)
}
children = jsx('hono-spa', { id: SPA_ROOT_ID }, children)
}
const docType =
typeof options?.docType === 'string'
? options.docType
@ -31,7 +67,7 @@ const createRenderer =
RequestContext.Provider,
{ value: c },
(component ? component({ children, ...(props || {}) }) : children) as any
)}`
)}${options?.spa ? raw(`<script>${SPA_CLIENT_SCRIPT}</script>`) : ''}`
if (options?.stream) {
return c.body(renderToReadableStream(body), {