easy-render
v0.2.0
Published
Easy-Render is a vDOM renderer designed to be as easy to use as possible.
Downloads
2
Maintainers
Readme
easy-render
Easy-Render is a vDOM renderer designed to be as easy to use as possible.
import { render } from 'easy-render';
const name = 'Easy-Render';
render`
<p>Hello ${name}!</p>
`;
import { render, r } from 'easy-render';
const listElements = (elements = []) => {
render`
<ul>
${elements.map(v => r`
<li>${v}</li>
`)}
</ul>
`;
}
listElements();
let numbers = [];
setInterval(() => {
numbers.push(numbers.length);
listElements(numbers);
}, 1000);
r (render component)
r
should return a brynja builder. This way r
would be implicitly responsible for rendering its own subtree, independant from the entire tree.
type r = (staticSegments: TemplateStringsArray, ...dynamicSegments: DynamicSegments[]) => BrynjaBuilder
This would mean that the following code:
render`
<div>
${[
r`<h1>Hello World</h1>`
]}
</div>
`;
...would result in the following brynja builder:
const _0 = _=>_
.child('h1', _=>_
.text('Hello World')
)
render(_=>_
.child('div', _=>_
.do(_0)
)
);
This whould also mean that easy-render
would support full interop with brynja
:
import { render } from 'easy-render';
import { createComponent } from 'brynja';
const HelloWorld = createComponent(() => _=>_
.child('h1', _=>_
.text('Hello World')
)
);
render`
<div>
${[
HelloWorld()
]}
</div>
`;
...and vice versa... interestingly:
import { r } from 'easy-render';
import { render } from 'brynja';
const HelloWorld = () => r`
<h1>Hello World</h1>
`;
render(_=>_
.child('div', _=>_
.do(HelloWorld())
)
);
Internallay r
would make up the majority of the logic in the render
function (mock implementation):
export function Renderer(config: { rootElement: HTMLElement, rootElementBuilder?: BuilderCB }): IRenderer {
const brynja = BrynjaRenderer({
rootElement: config.rootElement,
});
return {
render: (staticSegments, ...dynamicSegments) => {
const brynjaBuilder = r(staticSegments, ...dynamicSegments);
// Render using brynja
brynja.render(_=>_
.do(config.rootElementBuilder ?? (_=> {
// Echo props from root node if no custom rootElementBuilder was provided
_.while(i => i < config.rootElement.attributes.length, (_, i)=> {
const attribute = config.rootElement.attributes.item(i);
if (attribute === null) { return; }
_.prop(attribute.name, attribute.value);
})
}))
.do(brynjaBuilder)
);
}
}
}
Mock implementation of r
:
const r = (staticSegments: TemplateStringsArray, ...dynamicSegments: DynamicSegments[]): BrynjaBuilder => {
const { xml, dynamics } = processTagFunctionData({
static: staticSegments,
dynamic: dynamicSegments,
});
const DOM = parseXML(xml);
const builder = constructBrynjaBuilder({ DOM, dynamics });
return builder;
}
WIP
import { render } from 'easy-render';
render`
<div class="box">
<button
click=${e => console.log(e)}
style=${{
background: 'orangered',
':hover': {
background: 'skyblue',
}
}}
>Click me</button>
</div>
`;
Data in ${}
may either be a CSS object, an event handler function, a string, a number, or a list of strings or numbers
- Intermediate structure, to preserve clojures for functions, to reduce amount of parcing for style object, and to allow for caching the parsed XML result unless the string structure changes.
<div class="box">
<button
click="placeholder-function-0"
style="placeholder-object-0"
>Click me</button>
</div>
Check if intermediate structure is the same as the cached structure.
If cache miss: I) Pass the intermediate structure into an xml parser. II) Create a Brynja builder for the resulting XML elements.
Call the stored Brynja builder, passing in any placeholder values as arguments.
Same as the following:
import { render } from 'brynja';
const builder = (args) =>
render(_=>_
.child('div', _=>_
.class('box')
.child('button', _=>_
.on('click', args.function[0])
.style(args.object[0])
.text('Click me')
)
)
);
builder({
function: [
e => console.log(e),
],
object: [
{background: 'orangered', ':hover':{background: 'skyblue'}},
],
});
Resources:
- XML parser: https://github.com/TobiasNickel/tXml