UNPKG

hyperhtml

Version:

A Fast & Light Virtual DOM Alternative

83 lines (74 loc) 3.05 kB
import WeakMap from '@ungap/weakmap'; import tta from '@ungap/template-tag-arguments'; import Wire from 'hyperhtml-wire'; import {Tagger} from '../objects/Updates.js'; // all wires used per each context const wires = new WeakMap; // A wire is a callback used as tag function // to lazily relate a generic object to a template literal. // hyper.wire(user)`<div id=user>${user.name}</div>`; => the div#user // This provides the ability to have a unique DOM structure // related to a unique JS object through a reusable template literal. // A wire can specify a type, as svg or html, and also an id // via html:id or :id convention. Such :id allows same JS objects // to be associated to different DOM structures accordingly with // the used template literal without losing previously rendered parts. const wire = (obj, type) => obj == null ? content(type || 'html') : weakly(obj, type || 'html'); // A wire content is a virtual reference to one or more nodes. // It's represented by either a DOM node, or an Array. // In both cases, the wire content role is to simply update // all nodes through the list of related callbacks. // In few words, a wire content is like an invisible parent node // in charge of updating its content like a bound element would do. const content = type => { let wire, tagger, template; return function () { const args = tta.apply(null, arguments); if (template !== args[0]) { template = args[0]; tagger = new Tagger(type); wire = wireContent(tagger.apply(tagger, args)); } else { tagger.apply(tagger, args); } return wire; }; }; // wires are weakly created through objects. // Each object can have multiple wires associated // and this is thanks to the type + :id feature. const weakly = (obj, type) => { const i = type.indexOf(':'); let wire = wires.get(obj); let id = type; if (-1 < i) { id = type.slice(i + 1); type = type.slice(0, i) || 'html'; } if (!wire) wires.set(obj, wire = {}); return wire[id] || (wire[id] = content(type)); }; // A document fragment loses its nodes // as soon as it is appended into another node. // This has the undesired effect of losing wired content // on a second render call, because (by then) the fragment would be empty: // no longer providing access to those sub-nodes that ultimately need to // stay associated with the original interpolation. // To prevent hyperHTML from forgetting about a fragment's sub-nodes, // fragments are instead returned as an Array of nodes or, if there's only one entry, // as a single referenced node which, unlike fragments, will indeed persist // wire content throughout multiple renderings. // The initial fragment, at this point, would be used as unique reference to this // array of nodes or to this single referenced node. const wireContent = node => { const childNodes = node.childNodes; const {length} = childNodes; return length === 1 ? childNodes[0] : (length ? new Wire(childNodes) : node); }; export { content, weakly }; export default wire;