Neurotic Programmer Masquerade

    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:

    Install

    npm i easy-render

    DownloadsWeekly Downloads

    1

    Version

    0.2.0

    License

    MIT

    Unpacked Size

    35.8 kB

    Total Files

    38

    Last publish

    Collaborators

    • olian04