/**
* @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