Skip to content
/ ulit Public

minimalist tagged template literal library, currently 3kB gzipped dependency free runtime.

License

Notifications You must be signed in to change notification settings

andyrj/ulit

Repository files navigation

ulit

npm version Codacy Badge Codacy Badge Build Status

Tagged Template Literal html template library. Inspired by lit-html/hyperHTML.

Why another tagged template literal library?

"I cannot understand what I cannot build." - Feynman

Started this from a desire to see how hard it would be to improve upon the implementations of lit-html and hyperHTML, focusing on feature density and utilizing the platform, but abandoning support for legacy browsers to use the latest features without transpiling to es5 (instead targeting Chrome, Firefox, Safari, Edge latest versions).

What was the result?

Rough parity with lit-html + lit-extended on features and general api setup.

Pros:

  • Transparent svg support - (no need for special svg tagged template function)
  • SSR support via serializable part paths. This uses followPath(Array<Number|String>), which handles most of the setup work, and it can be pre-rendered down to static html that can be delivered to the client and hydrated, bypassing the more expensive process required if a serialized template is not present in the dom.
  • By using "{{}}" for attributes and <!--{{}}--> for part placeholders, the initialization can be simplified and require less regex/string manipulation.

Cons:

  • No plan to support partial parts. i.e.
    // DO NOT DO THIS
    bar => html`<div style="{foo: ${bar}}">boom</div>`
    // DO NOT DO THIS
    fail => html`<div id=prefix-${fail}-suffix></div>`
    you should instead always write your templates to replace the whole property/attribute as a single variable i.e.
    // DO THIS INSTEAD
    good => html`<div id=${good}></div>`
  • Style tags within html tagged template literals are not supported initially, will need to add a style tagged template literal helper for this purpose, can be made outside of core.
// basic idea if someone feels like implementing something
html`${style`...`}<div></div>`; // template out -> <style>...</style><div></div> 

How can I use it?

If your build system/browser supports es2015 modules you can install and use it as normal.

Install

npm install --save ulit

API Dump

// repeat is used for rendering keyed lists of dom nodes
// until allows you to conditionally load a template that is replaced upon promise completion (code-splitting, fetch, etc...)
import { directive, html, render, repeat, until } from "ulit";

const target = document.getElementById("app");

// "components" are just template functions
const hello = subject => html`<h1>hello ${subject}</h1>`;
render(hello("world"), target);
target.innerHTML === "<h1>hello world</h1>"; // true

// calling render multiple times on the same container will update the current template in place if possible or replace it.
render(hello("internet"), target);
target.innerHTML === "<h1>hello internet</h1>"; // true

// Build your own directive to extend ulit...
// the example below passthroughDirective is a dummy directive example that is equivalent to just passing the value to the part
// in the template expressions.
const passthroughDirective = value => directive(part => {
  part.update(value);
});

render(html`<h1>${passthroughDirective("pass through example...")}</h1>`, target);
target.innerHTML === "<h1>pass through example...</h1>"; // true

// Example Part API brain dump
const partApiDirective = directive(part => {
  // update part with a new PartValue
  part.update("test");

  // parts have a disposer with a dispose event so that you can clean up anything your directives create on dispose...
  // parts are disposed when templates replace one another and have differing static parts, or when a part changes from a directive to another valid PartValue
  const handler = handlerPart => {
    // normally clean up whatever LUT/cache you have seems like Map<part, ...> is pretty useful inside directives
  };
  part.disposer.addDisposer(handler);
  part.disposer.removeDisposer(handler);

  // parts and templates share a common key of "target" which is a DomTarget that is used to store
  // the edges of the part/template
  part.target.first(); // Node that begins this part
  part.target.last(); // Node that ends this part...
  part.path; // readonly Array<string | number>, the path from containing templates root to this part
  part.target.remove(); // moves the part out of the dom and into a document fragment.
});

// Arrays/iterables are valid PartValue and render templates, this uses repeat() internally
const nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
render(nums.map(num => {
  hello(num);
}), target);
target.innerHTML === "<h1>hello 0</h1><h1>hello 1</h1>..."; //true
render(hello(nums), target);
target.innerHTML === "<h1>hello 012345678910</h1>"; // true NOTE: each number would be it's own textNode in this case...

// Promises are valid PartValues, by default they will leave the previous part value in place initially, and update to what is resolved...
render(hello(new Promise(resolve => {
  const doWork = setTimeout(resolve("async!"), 1000);
})), target);
// initially
target.innerHTML === "<h1>hello 012345678910</h1>"; // true
setTimeout(() => target.innerHTML === "<h1>hello async!</h1>", 1001); // true

// until gives better support by allowing you to specify a default template while the promise resolves instead of a comment node
render(
  hello(until(new Promise(resolve => {
    const doWork = setTimeout(resolve("async!"), 1000);
  },
  "loading..."
  ))), target);
target.innerHTML === "<h1>hello loading...</h1>"; //true
setTimeout(() => target.innerHTML === "<h1>hello async!</h1>", 1001); // true

// events
const eventTemplate = html`<button onclick=${e => console.log(e)}>click me</button>`;
render(eventTemplate, target);
target.innerHTML === "<button>click me</button>"; // true

// nested templates
const nested = hello(hello("nested"));
render(nested, target);
target.innerHTML === "<h1>hello <h1>hello nested</h1></h1>"; // true

License

ulit is MIT licensed. See LICENSE.

About

minimalist tagged template literal library, currently 3kB gzipped dependency free runtime.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages