easy-render
TypeScript icon, indicating that this package has built-in type declarations

0.2.0 • Public • Published

easy-render

NPM Version Bundle Size Available Types License CircleCI Test Coverage

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

  1. 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>
  1. Check if intermediate structure is the same as the cached structure.

  2. If cache miss: I) Pass the intermediate structure into an xml parser. II) Create a Brynja builder for the resulting XML elements.

  3. 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:

Package Sidebar

Install

npm i easy-render

Weekly Downloads

1

Version

0.2.0

License

MIT

Unpacked Size

35.8 kB

Total Files

38

Last publish

Collaborators

  • olian04