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:
parent
1489e1dc90
commit
cc9283a753
@ -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), {
|
||||
|
Loading…
Reference in New Issue
Block a user