UNPKG

8.93 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 * This code may only be used under the BSD style license found at
5 * http://polymer.github.io/LICENSE.txt
6 * The complete set of authors may be found at
7 * http://polymer.github.io/AUTHORS.txt
8 * The complete set of contributors may be found at
9 * http://polymer.github.io/CONTRIBUTORS.txt
10 * Code distributed by Google as part of the polymer project is also
11 * subject to an additional IP rights grant found at
12 * http://polymer.github.io/PATENTS.txt
13 */
14import { TemplateResult } from 'lit-html';
15import { render } from 'lit-html/lib/shady-render.js';
16import { UpdatingElement } from './lib/updating-element.js';
17export * from './lib/updating-element.js';
18export * from './lib/decorators.js';
19export { html, svg, TemplateResult, SVGTemplateResult } from 'lit-html/lit-html.js';
20import { supportsAdoptingStyleSheets } from './lib/css-tag.js';
21export * from './lib/css-tag.js';
22// IMPORTANT: do not change the property name or the assignment expression.
23// This line will be used in regexes to search for LitElement usage.
24// TODO(justinfagnani): inject version number at build time
25(window['litElementVersions'] || (window['litElementVersions'] = []))
26 .push('2.2.1');
27/**
28 * Minimal implementation of Array.prototype.flat
29 * @param arr the array to flatten
30 * @param result the accumlated result
31 */
32function arrayFlat(styles, result = []) {
33 for (let i = 0, length = styles.length; i < length; i++) {
34 const value = styles[i];
35 if (Array.isArray(value)) {
36 arrayFlat(value, result);
37 }
38 else {
39 result.push(value);
40 }
41 }
42 return result;
43}
44/** Deeply flattens styles array. Uses native flat if available. */
45const flattenStyles = (styles) => styles.flat ? styles.flat(Infinity) : arrayFlat(styles);
46export class LitElement extends UpdatingElement {
47 /** @nocollapse */
48 static finalize() {
49 // The Closure JS Compiler does not always preserve the correct "this"
50 // when calling static super methods (b/137460243), so explicitly bind.
51 super.finalize.call(this);
52 // Prepare styling that is stamped at first render time. Styling
53 // is built from user provided `styles` or is inherited from the superclass.
54 this._styles =
55 this.hasOwnProperty(JSCompiler_renameProperty('styles', this)) ?
56 this._getUniqueStyles() :
57 this._styles || [];
58 }
59 /** @nocollapse */
60 static _getUniqueStyles() {
61 // Take care not to call `this.styles` multiple times since this generates
62 // new CSSResults each time.
63 // TODO(sorvell): Since we do not cache CSSResults by input, any
64 // shared styles will generate new stylesheet objects, which is wasteful.
65 // This should be addressed when a browser ships constructable
66 // stylesheets.
67 const userStyles = this.styles;
68 const styles = [];
69 if (Array.isArray(userStyles)) {
70 const flatStyles = flattenStyles(userStyles);
71 // As a performance optimization to avoid duplicated styling that can
72 // occur especially when composing via subclassing, de-duplicate styles
73 // preserving the last item in the list. The last item is kept to
74 // try to preserve cascade order with the assumption that it's most
75 // important that last added styles override previous styles.
76 const styleSet = flatStyles.reduceRight((set, s) => {
77 set.add(s);
78 // on IE set.add does not return the set.
79 return set;
80 }, new Set());
81 // Array.from does not work on Set in IE
82 styleSet.forEach((v) => styles.unshift(v));
83 }
84 else if (userStyles) {
85 styles.push(userStyles);
86 }
87 return styles;
88 }
89 /**
90 * Performs element initialization. By default this calls `createRenderRoot`
91 * to create the element `renderRoot` node and captures any pre-set values for
92 * registered properties.
93 */
94 initialize() {
95 super.initialize();
96 this.renderRoot =
97 this.createRenderRoot();
98 // Note, if renderRoot is not a shadowRoot, styles would/could apply to the
99 // element's getRootNode(). While this could be done, we're choosing not to
100 // support this now since it would require different logic around de-duping.
101 if (window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot) {
102 this.adoptStyles();
103 }
104 }
105 /**
106 * Returns the node into which the element should render and by default
107 * creates and returns an open shadowRoot. Implement to customize where the
108 * element's DOM is rendered. For example, to render into the element's
109 * childNodes, return `this`.
110 * @returns {Element|DocumentFragment} Returns a node into which to render.
111 */
112 createRenderRoot() {
113 return this.attachShadow({ mode: 'open' });
114 }
115 /**
116 * Applies styling to the element shadowRoot using the `static get styles`
117 * property. Styling will apply using `shadowRoot.adoptedStyleSheets` where
118 * available and will fallback otherwise. When Shadow DOM is polyfilled,
119 * ShadyCSS scopes styles and adds them to the document. When Shadow DOM
120 * is available but `adoptedStyleSheets` is not, styles are appended to the
121 * end of the `shadowRoot` to [mimic spec
122 * behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets).
123 */
124 adoptStyles() {
125 const styles = this.constructor._styles;
126 if (styles.length === 0) {
127 return;
128 }
129 // There are three separate cases here based on Shadow DOM support.
130 // (1) shadowRoot polyfilled: use ShadyCSS
131 // (2) shadowRoot.adoptedStyleSheets available: use it.
132 // (3) shadowRoot.adoptedStyleSheets polyfilled: append styles after
133 // rendering
134 if (window.ShadyCSS !== undefined && !window.ShadyCSS.nativeShadow) {
135 window.ShadyCSS.ScopingShim.prepareAdoptedCssText(styles.map((s) => s.cssText), this.localName);
136 }
137 else if (supportsAdoptingStyleSheets) {
138 this.renderRoot.adoptedStyleSheets =
139 styles.map((s) => s.styleSheet);
140 }
141 else {
142 // This must be done after rendering so the actual style insertion is done
143 // in `update`.
144 this._needsShimAdoptedStyleSheets = true;
145 }
146 }
147 connectedCallback() {
148 super.connectedCallback();
149 // Note, first update/render handles styleElement so we only call this if
150 // connected after first update.
151 if (this.hasUpdated && window.ShadyCSS !== undefined) {
152 window.ShadyCSS.styleElement(this);
153 }
154 }
155 /**
156 * Updates the element. This method reflects property values to attributes
157 * and calls `render` to render DOM via lit-html. Setting properties inside
158 * this method will *not* trigger another update.
159 * * @param _changedProperties Map of changed properties with old values
160 */
161 update(changedProperties) {
162 super.update(changedProperties);
163 const templateResult = this.render();
164 if (templateResult instanceof TemplateResult) {
165 this.constructor
166 .render(templateResult, this.renderRoot, { scopeName: this.localName, eventContext: this });
167 }
168 // When native Shadow DOM is used but adoptedStyles are not supported,
169 // insert styling after rendering to ensure adoptedStyles have highest
170 // priority.
171 if (this._needsShimAdoptedStyleSheets) {
172 this._needsShimAdoptedStyleSheets = false;
173 this.constructor._styles.forEach((s) => {
174 const style = document.createElement('style');
175 style.textContent = s.cssText;
176 this.renderRoot.appendChild(style);
177 });
178 }
179 }
180 /**
181 * Invoked on each update to perform rendering tasks. This method must return
182 * a lit-html TemplateResult. Setting properties inside this method will *not*
183 * trigger the element to update.
184 */
185 render() {
186 }
187}
188/**
189 * Ensure this class is marked as `finalized` as an optimization ensuring
190 * it will not needlessly try to `finalize`.
191 *
192 * Note this property name is a string to prevent breaking Closure JS Compiler
193 * optimizations. See updating-element.ts for more information.
194 */
195LitElement['finalized'] = true;
196/**
197 * Render method used to render the lit-html TemplateResult to the element's
198 * DOM.
199 * @param {TemplateResult} Template to render.
200 * @param {Element|DocumentFragment} Node into which to render.
201 * @param {String} Element name.
202 * @nocollapse
203 */
204LitElement.render = render;
205//# sourceMappingURL=lit-element.js.map
\No newline at end of file