Add wrapper method to renderer

This commit is contained in:
Talia 2025-06-19 14:48:13 +02:00
parent d88c9f95a0
commit dde2aa683e
2 changed files with 53 additions and 4 deletions

View file

@ -173,6 +173,39 @@ const input_2 = html.input({ type: "number", value: state })
## Helpers ## Helpers
### Wrapper
Sometimes writing entire sections in a functional style is a bit inconvenient,
and a side-effect based style would be better.
This is possile using the `wrapper` helper method on the DomRenderers
```js
const h = DomHtmlRenderer.wrapper
const content = h(fragmet => {
fragment.h1("Functions have side effects")
fragment.p("There is no special functionality for nested nodes:")
fragment.ul(h(ul => {
ul.li("Foo")
ul.li("Bar")
ul.li("Baz")
}))
fragment.p("However, it is quite easy to add ", html.b("nested elements"))
})
```
Wrappers create a document fragment, call the passed in function with a special
proxy that, itself, wraps the render proxy of the Renderer object, but has its
return values added to the fragment, then finally returns the document fragment.
This means that the methods on the helper take the same arguments as described
above, but append their results to a buffer instead of returning them. This
makes it very easy to comine both rendering styles as needed.
There is currently no export for a default HTML renderer wrapper, but this is
planned for the future once an appropriate name has been found.
### Empty Values ### Empty Values
Nyooom will produce a warning when it encounters `undefined` as an argument to a Nyooom will produce a warning when it encounters `undefined` as an argument to a

View file

@ -96,14 +96,30 @@ export const noPropagate = fn => event => { event.stopPropagation(); return fn(e
/** Main class doing all the rendering */ /** Main class doing all the rendering */
export class Renderer { export class Renderer {
static proxy() { static get proxy() {
return new Proxy(new this(), { return this._proxy ??= new Proxy(new this(), {
/** @param {string} prop */ /** @param {string} prop */
get: (renderer, prop) => /** @param {any[]} args */ (...args) => renderer.node(prop, args), get: (renderer, prop) => /** @param {any[]} args */ (...args) => renderer.node(prop, args),
has: (renderer, prop) => renderer.nodeSupported(prop), has: (renderer, prop) => renderer.nodeSupported(prop),
}) })
} }
static get wrapper() {
return this._wrapper ??= (callback => {
const template = document.createElement("template")
const buffer = template.content
const renderer = new Proxy(new this(), {
get: (renderer, prop) => /** @param {any[]} args */ (...args) => buffer.append(renderer.node(prop, args)),
has: (renderer, prop) => renderer.nodeSupported(prop),
})
callback(renderer)
return buffer
})
}
/** @param {string} name */ /** @param {string} name */
node(name, ...args) { node(name, ...args) {
throw "Attempting to use an abstract Renderer" throw "Attempting to use an abstract Renderer"
@ -420,8 +436,8 @@ export class DomSvgRenderer extends DomRenderer {
} }
} }
export const html = DomHtmlRenderer.proxy() export const html = DomHtmlRenderer.proxy
export const svg = DomSvgRenderer.proxy() export const svg = DomSvgRenderer.proxy
export const fragment = DomRenderer.documentFragment.bind(DomRenderer) export const fragment = DomRenderer.documentFragment.bind(DomRenderer)
export const text = DomRenderer.createTextOrFragment.bind(DomRenderer) export const text = DomRenderer.createTextOrFragment.bind(DomRenderer)