UNPKG

@microsoft/mgt

Version:
135 lines 5.36 kB
/** * ------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. * See License in the project root for license information. * ------------------------------------------------------------------------------------------- */ import { html } from 'lit-element'; import { equals } from '../utils/Utils'; import { MgtBaseComponent } from './baseComponent'; import { TemplateHelper } from './templateHelper'; /** * An abstract class that defines a templatable web component * * @export * @abstract * @class MgtTemplatedComponent * @extends {MgtBaseComponent} */ export class MgtTemplatedComponent extends MgtBaseComponent { constructor() { super(); /** * Collection of functions to be used in template binding * * @type {*} * @memberof MgtTemplatedComponent */ this.templateConverters = {}; /** * Holds all templates defined by developer * * @protected * @memberof MgtTemplatedComponent */ this.templates = {}; this._renderedSlots = false; this._renderedTemplates = {}; this._slotNamesAddedDuringRender = []; this.templateConverters.lower = (str) => str.toLowerCase(); this.templateConverters.upper = (str) => str.toUpperCase(); } /** * Updates the element. This method reflects property values to attributes. * It can be overridden to render and keep updated element DOM. * Setting properties inside this method will *not* trigger * another update. * * * @param _changedProperties Map of changed properties with old values */ update(changedProperties) { this.templates = this.getTemplates(); this._slotNamesAddedDuringRender = []; super.update(changedProperties); } /** * Invoked whenever the element is updated. Implement to perform * post-updating tasks via DOM APIs, for example, focusing an element. * * Setting properties inside this method will trigger the element to update * again after this update cycle completes. * * * @param changedProperties Map of changed properties with old values */ updated(changedProperties) { super.updated(changedProperties); this.removeUnusedSlottedElements(); } /** * Render a <template> by type and return content to render * * @param templateType type of template (indicated by the data-type attribute) * @param context the data context that should be expanded in template * @param slotName the slot name that will be used to host the new rendered template. set to a unique value if multiple templates of this type will be rendered. default is templateType */ renderTemplate(templateType, context, slotName) { if (!this.templates[templateType]) { return null; } slotName = slotName || templateType; this._slotNamesAddedDuringRender.push(slotName); this._renderedSlots = true; const template = html ` <slot name=${slotName}></slot> `; if (this._renderedTemplates.hasOwnProperty(slotName)) { const { context: existingContext, slot } = this._renderedTemplates[slotName]; if (equals(existingContext, context)) { return template; } this.removeChild(slot); } const templateContent = TemplateHelper.renderTemplate(this.templates[templateType], context, this.templateConverters); const div = document.createElement('div'); div.slot = slotName; div.dataset.generated = 'template'; if (templateContent) { div.appendChild(templateContent); } this.appendChild(div); this._renderedTemplates[slotName] = { context, slot: div }; this.fireCustomEvent('templateRendered', { templateType, context, element: div }); return template; } getTemplates() { const templates = {}; // tslint:disable-next-line: prefer-for-of for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; if (child.nodeName === 'TEMPLATE') { const template = child; if (template.dataset.type) { templates[template.dataset.type] = template; } else { templates.default = template; } } } return templates; } removeUnusedSlottedElements() { if (this._renderedSlots) { for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; if (child.dataset && child.dataset.generated && !this._slotNamesAddedDuringRender.includes(child.slot)) { this.removeChild(child); delete this._renderedTemplates[child.slot]; i--; } } this._renderedSlots = false; } } } //# sourceMappingURL=templatedComponent.js.map