UNPKG

lit-html

Version:

HTML template literals in JavaScript

101 lines (88 loc) 3.71 kB
/** * @license * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at * http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at * http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ import {directive, Directive, NodePart, reparentNodes} from '../lit-html.js'; interface PartCache { truePart: NodePart; falsePart: NodePart; prevCondition?: boolean; cacheContainer: DocumentFragment; } const partCaches = new WeakMap<NodePart, PartCache>(); /** * Efficiently switches between two templates based on the given condition. The * rendered content is cached, and re-used when switching conditions. Templates * are evaluated lazily, so the passed values must be functions. * * While this directive can render any regular part, it makes the most sense * when used with TemplateResult since most other values are dirty checked * already. * * Example: * * let checked = false; * * html` * when(checked, () => html`Checkmark is checked`, () => html`Checkmark is not * checked`); * ` * * @param condition the condition to test truthiness against * @param trueValue the value to render given a true condition * @param falseValue the value to render given a false condition */ export const when = (condition: any, trueValue: () => any, falseValue: () => any): Directive<NodePart> => directive((parentPart: NodePart) => { let cache = partCaches.get(parentPart); // Create a new cache if this is the first render if (cache === undefined) { // Cache consists of two parts, one for each condition, and a // docment fragment which we cache the nodes of the condition that's // not currently rendered. cache = { truePart: new NodePart(parentPart.templateFactory), falsePart: new NodePart(parentPart.templateFactory), cacheContainer: document.createDocumentFragment(), }; partCaches.set(parentPart, cache); cache.truePart.appendIntoPart(parentPart); cache.falsePart.appendIntoPart(parentPart); } // Based on the condition, select which part to render and which value // to set on that part. const nextPart = condition ? cache.truePart : cache.falsePart; const nextValue = condition ? trueValue() : falseValue(); // If we switched condition, swap nodes to/from the cache. if (!!condition !== cache.prevCondition) { // Get the part which was rendered for the opposite condition. This // should be added to the cache. const prevPart = condition ? cache.falsePart : cache.truePart; // If the next part was rendered, take it from the cache if (nextPart.value) { parentPart.startNode.parentNode!.appendChild( cache.cacheContainer); } // If the prev part was rendered, move it to the cache if (prevPart.value) { reparentNodes( cache.cacheContainer, prevPart.startNode, prevPart.endNode.nextSibling); } } // Set the next part's value nextPart.setValue(nextValue); nextPart.commit(); cache.prevCondition = !!condition; });