/** * @license * Copyright (c) 2017 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 */ /** * The main LitElement module, which defines the [[`LitElement`]] base class and * related APIs. * * LitElement components can define a template and a set of observed * properties. Changing an observed property triggers a re-render of the * element. * * Import [[`LitElement`]] and [[`html`]] from this module to create a * component: * * ```js * import {LitElement, html} from 'lit-element'; * * class MyElement extends LitElement { * * // Declare observed properties * static get properties() { * return { * adjective: {} * } * } * * constructor() { * this.adjective = 'awesome'; * } * * // Define the element's template * render() { * return html`

your ${adjective} template here

`; * } * } * * customElements.define('my-element', MyElement); * ``` * * `LitElement` extends [[`UpdatingElement`]] and adds lit-html templating. * The `UpdatingElement` class is provided for users that want to build * their own custom element base classes that don't use lit-html. * * @packageDocumentation */ import { render } from 'lit-html/lib/shady-render.js'; import { UpdatingElement } from './lib/updating-element.js'; export * from './lib/updating-element.js'; export { UpdatingElement as ReactiveElement } from './lib/updating-element.js'; export * from './lib/decorators.js'; export { html, svg, TemplateResult, SVGTemplateResult } from 'lit-html/lit-html.js'; import { supportsAdoptingStyleSheets, unsafeCSS } from './lib/css-tag.js'; export * from './lib/css-tag.js'; // IMPORTANT: do not change the property name or the assignment expression. // This line will be used in regexes to search for LitElement usage. // TODO(justinfagnani): inject version number at build time (window['litElementVersions'] || (window['litElementVersions'] = [])) .push('2.5.1'); /** * Sentinal value used to avoid calling lit-html's render function when * subclasses do not implement `render` */ const renderNotImplemented = {}; /** * Base element class that manages element properties and attributes, and * renders a lit-html template. * * To define a component, subclass `LitElement` and implement a * `render` method to provide the component's template. Define properties * using the [[`properties`]] property or the [[`property`]] decorator. */ export class LitElement extends UpdatingElement { /** * Return the array of styles to apply to the element. * Override this method to integrate into a style management system. * * @nocollapse */ static getStyles() { return this.styles; } /** @nocollapse */ static _getUniqueStyles() { // Only gather styles once per class if (this.hasOwnProperty(JSCompiler_renameProperty('_styles', this))) { return; } // Take care not to call `this.getStyles()` multiple times since this // generates new CSSResults each time. // TODO(sorvell): Since we do not cache CSSResults by input, any // shared styles will generate new stylesheet objects, which is wasteful. // This should be addressed when a browser ships constructable // stylesheets. const userStyles = this.getStyles(); if (Array.isArray(userStyles)) { // De-duplicate styles preserving the _last_ instance in the set. // This is a performance optimization to avoid duplicated styles that can // occur especially when composing via subclassing. // The last item is kept to try to preserve the cascade order with the // assumption that it's most important that last added styles override // previous styles. const addStyles = (styles, set) => styles.reduceRight((set, s) => // Note: On IE set.add() does not return the set Array.isArray(s) ? addStyles(s, set) : (set.add(s), set), set); // Array.from does not work on Set in IE, otherwise return // Array.from(addStyles(userStyles, new Set())).reverse() const set = addStyles(userStyles, new Set()); const styles = []; set.forEach((v) => styles.unshift(v)); this._styles = styles; } else { this._styles = userStyles === undefined ? [] : [userStyles]; } // Ensure that there are no invalid CSSStyleSheet instances here. They are // invalid in two conditions. // (1) the sheet is non-constructible (`sheet` of a HTMLStyleElement), but // this is impossible to check except via .replaceSync or use // (2) the ShadyCSS polyfill is enabled (:. supportsAdoptingStyleSheets is // false) this._styles = this._styles.map((s) => { if (s instanceof CSSStyleSheet && !supportsAdoptingStyleSheets) { // Flatten the cssText from the passed constructible stylesheet (or // undetectable non-constructible stylesheet). The user might have // expected to update their stylesheets over time, but the alternative // is a crash. const cssText = Array.prototype.slice.call(s.cssRules) .reduce((css, rule) => css + rule.cssText, ''); return unsafeCSS(cssText); } return s; }); } /** * Performs element initialization. By default this calls * [[`createRenderRoot`]] to create the element [[`renderRoot`]] node and * captures any pre-set values for registered properties. */ initialize() { super.initialize(); this.constructor._getUniqueStyles(); this.renderRoot = this.createRenderRoot(); // Note, if renderRoot is not a shadowRoot, styles would/could apply to the // element's getRootNode(). While this could be done, we're choosing not to // support this now since it would require different logic around de-duping. if (window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot) { this.adoptStyles(); } } /** * Returns the node into which the element should render and by default * creates and returns an open shadowRoot. Implement to customize where the * element's DOM is rendered. For example, to render into the element's * childNodes, return `this`. * @returns {Element|DocumentFragment} Returns a node into which to render. */ createRenderRoot() { return this.attachShadow(this.constructor.shadowRootOptions); } /** * Applies styling to the element shadowRoot using the [[`styles`]] * property. Styling will apply using `shadowRoot.adoptedStyleSheets` where * available and will fallback otherwise. When Shadow DOM is polyfilled, * ShadyCSS scopes styles and adds them to the document. When Shadow DOM * is available but `adoptedStyleSheets` is not, styles are appended to the * end of the `shadowRoot` to [mimic spec * behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets). */ adoptStyles() { const styles = this.constructor._styles; if (styles.length === 0) { return; } // There are three separate cases here based on Shadow DOM support. // (1) shadowRoot polyfilled: use ShadyCSS // (2) shadowRoot.adoptedStyleSheets available: use it // (3) shadowRoot.adoptedStyleSheets polyfilled: append styles after // rendering if (window.ShadyCSS !== undefined && !window.ShadyCSS.nativeShadow) { window.ShadyCSS.ScopingShim.prepareAdoptedCssText(styles.map((s) => s.cssText), this.localName); } else if (supportsAdoptingStyleSheets) { this.renderRoot.adoptedStyleSheets = styles.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet); } else { // This must be done after rendering so the actual style insertion is done // in `update`. this._needsShimAdoptedStyleSheets = true; } } connectedCallback() { super.connectedCallback(); // Note, first update/render handles styleElement so we only call this if // connected after first update. if (this.hasUpdated && window.ShadyCSS !== undefined) { window.ShadyCSS.styleElement(this); } } /** * Updates the element. This method reflects property values to attributes * and calls `render` to render DOM via lit-html. Setting properties inside * this method will *not* trigger another update. * @param _changedProperties Map of changed properties with old values */ update(changedProperties) { // Setting properties in `render` should not trigger an update. Since // updates are allowed after super.update, it's important to call `render` // before that. const templateResult = this.render(); super.update(changedProperties); // If render is not implemented by the component, don't call lit-html render if (templateResult !== renderNotImplemented) { this.constructor .render(templateResult, this.renderRoot, { scopeName: this.localName, eventContext: this }); } // When native Shadow DOM is used but adoptedStyles are not supported, // insert styling after rendering to ensure adoptedStyles have highest // priority. if (this._needsShimAdoptedStyleSheets) { this._needsShimAdoptedStyleSheets = false; this.constructor._styles.forEach((s) => { const style = document.createElement('style'); style.textContent = s.cssText; this.renderRoot.appendChild(style); }); } } /** * Invoked on each update to perform rendering tasks. This method may return * any value renderable by lit-html's `NodePart` - typically a * `TemplateResult`. Setting properties inside this method will *not* trigger * the element to update. */ render() { return renderNotImplemented; } } /** * Ensure this class is marked as `finalized` as an optimization ensuring * it will not needlessly try to `finalize`. * * Note this property name is a string to prevent breaking Closure JS Compiler * optimizations. See updating-element.ts for more information. */ LitElement['finalized'] = true; /** * Reference to the underlying library method used to render the element's * DOM. By default, points to the `render` method from lit-html's shady-render * module. * * **Most users will never need to touch this property.** * * This property should not be confused with the `render` instance method, * which should be overridden to define a template for the element. * * Advanced users creating a new base class based on LitElement can override * this property to point to a custom render method with a signature that * matches [shady-render's `render` * method](https://lit-html.polymer-project.org/api/modules/shady_render.html#render). * * @nocollapse */ LitElement.render = render; /** @nocollapse */ LitElement.shadowRootOptions = { mode: 'open' }; //# sourceMappingURL=lit-element.js.map