UNPKG

@logo-elements/polymer-legacy-adapter

Version:

A set of backwards compatibility adapters for Polymer Classic -specific legacy APIs for Logo Elements

136 lines (108 loc) 4.17 kB
import { PolymerElement } from '@polymer/polymer'; import { templatize } from '@polymer/polymer/lib/utils/templatize.js'; export class Templatizer extends PolymerElement { static create(component, template) { const templatizer = new this(); templatizer.__template = template; templatizer.__component = component; return templatizer; } static get is() { return 'logo-template-renderer-templatizer'; } constructor() { super(); this.__template = null; this.__component = null; this.__TemplateClass = null; this.__templateInstances = new Set(); } /** * If the template instance was created by this templatizer's instance and is still attached to DOM, * it only re-renders the instance with the new properties. * Otherwise, it disposes of the old template instance (if it exists), * creates a new template instance with the given properties and renders the instance's root to the element. */ render(element, properties = {}) { let instance = element.__templateInstance; if (this.__hasTemplateInstance(instance) && this.__isTemplateInstanceAttachedToDOM(instance)) { this.__updateProperties(instance, properties); return; } if (this.__hasTemplateInstance(instance)) { this.__disposeOfTemplateInstance(instance); } instance = this.__createTemplateInstance(properties); element.__templateInstance = instance; element.innerHTML = ''; element.appendChild(instance.root); } /** @private */ __updateProperties(instance, properties) { // The Polymer uses `===` to check whether a property is changed and should be re-rendered. // This means, object properties won't be re-rendered when mutated inside. // This workaround forces the `item` property to re-render even // the new item is stricly equal to the old item. if (instance.item === properties.item) { instance._setPendingProperty('item'); } instance.__properties = properties; instance.setProperties(properties); } /** @private */ __createTemplateInstance(properties) { this.__createTemplateClass(properties); const instance = new this.__TemplateClass(properties); instance.__properties = properties; this.__templateInstances.add(instance); return instance; } /** @private */ __disposeOfTemplateInstance(instance) { this.__templateInstances.delete(instance); } /** @private */ __hasTemplateInstance(instance) { return this.__templateInstances.has(instance); } /** @private */ __isTemplateInstanceAttachedToDOM(instance) { // The edge-case case when the template is empty if (instance.children.length === 0) { return false; } return !!instance.children[0].parentElement; } /** @private */ __createTemplateClass(properties) { if (this.__TemplateClass) return; const instanceProps = Object.keys(properties).reduce((accum, key) => { return { ...accum, [key]: true }; }, {}); this.__TemplateClass = templatize(this.__template, this, { // This property prevents the template instance properties // from passing into the `forwardHostProp` callback instanceProps, // When changing a property of the data host component, this callback forwards // the changed property to the template instances so that cause their re-rendering. forwardHostProp(prop, value) { this.__templateInstances.forEach((instance) => { instance.forwardHostProp(prop, value); }); }, notifyInstanceProp(instance, path, value) { let rootProperty; // Extracts the root property name from the path rootProperty = path.split('.')[0]; // Capitalizes the property name rootProperty = rootProperty[0].toUpperCase() + rootProperty.slice(1); const callback = `_on${rootProperty}PropertyChanged`; if (this[callback]) { this[callback](instance, path, value); } } }); } } if (!customElements.get(Templatizer.is)) { customElements.define(Templatizer.is, Templatizer); } // customElements.define(Templatizer.is, Templatizer);