Socket
Socket
Sign inDemoInstall

@polymer/lit-element

Package Overview
Dependencies
Maintainers
12
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@polymer/lit-element - npm Package Compare versions

Comparing version 0.6.5 to 0.7.0

lib/css-tag.d.ts

28

CHANGELOG.md

@@ -12,8 +12,27 @@ # Change Log

-->
<!-- ### Added -->
<!-- ### Changed -->
<!-- ### Added -->
<!-- ### Removed -->
<!-- ### Fixed -->
## Unreleased
## [0.7.0] - 2019-01-10
### Added
* Updated decorator implementations to support TC39 decorator API proposal (supported by Babel 7.1+) in addition to the legacy decorator API (supported by older Babel and TypeScript) ([#156](https://github.com/Polymer/lit-element/issues/156)).
* Added `static get styles()` to allow defining element styling separate from `render` method.
This takes advantage of [`adoptedStyleSheets`](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets) when possible ([#391](https://github.com/Polymer/lit-element/issues/391)).
* Added the `performUpdate` method to allow control of update timing ([#290](https://github.com/Polymer/lit-element/issues/290)).
* Updates deferred until first connection ([#258](https://github.com/Polymer/lit-element/issues/258)).
* Export `TemplateResult` and `SVGTemplateResult` ([#415](https://github.com/Polymer/lit-element/pull/415)).
### Changed
* [Breaking] The `createRenderRoot` method has moved from `UpdatingElement` to `LitElement`. Therefore, `UpdatingElement` no longer creates a `shadowRoot` by default ([#391](https://github.com/Polymer/lit-element/issues/391)).
* [Breaking] Changes property options to add `converter`. This option works the same as the previous `type` option except that the `converter` methods now also get `type` as the second argument. This effectively changes `type` to be a hint for the `converter`. A default `converter` is used if none is provided and it now supports `Boolean`, `String`, `Number`, `Object`, and `Array` ([#264](https://github.com/Polymer/lit-element/issues/264)).
* [Breaking] Numbers and strings now become null if their reflected attribute is removed (https://github.com/Polymer/lit-element/issues/264)).
* [Breaking] Previously, when an attribute changed as a result of a reflecting property changing, the property was prevented from mutating again as can happen when a custom
`converter` is used. Now, the oppose is also true. When a property changes as a result of an attribute changing, the attribute is prevented from mutating again (https://github.com/Polymer/lit-element/issues/264))
### Fixed
* [Breaking] User defined accessors are now wrapped to enable better composition ([#286](https://github.com/Polymer/lit-element/issues/286))
* Type for `eventOptions` decorator now properly includes `passive` and `once` options ([#325](https://github.com/Polymer/lit-element/issues/325))
## [0.6.5] - 2018-12-13

@@ -81,6 +100,1 @@ ### Changed:

(https://github.com/Polymer/lit-element/pull/173).
<!-- ### Changed -->
<!-- ### Removed -->
<!-- ### Fixed -->

@@ -37,3 +37,3 @@ /**

*/
export declare const customElement: (tagName: string) => (clazz: Constructor<HTMLElement>) => any;
export declare const customElement: (tagName: string) => (classOrDescriptor: Constructor<HTMLElement> | ClassDescriptor) => any;
/**

@@ -44,3 +44,3 @@ * A property decorator which creates a LitElement property which reflects a

*/
export declare const property: (options?: PropertyDeclaration<any> | undefined) => (proto: Object, name: string | number | symbol) => void;
export declare const property: (options?: PropertyDeclaration<any, any> | undefined) => (protoOrDescriptor: Object | ClassElement, name?: string | number | symbol | undefined) => any;
/**

@@ -50,3 +50,3 @@ * A property decorator that converts a class property into a getter that

*/
export declare const query: (selector: string) => (proto: any, propName: string) => void;
export declare const query: (selector: string) => (protoOrDescriptor: Object | ClassElement, name?: string | number | symbol | undefined) => any;
/**

@@ -56,3 +56,3 @@ * A property decorator that converts a class property into a getter

*/
export declare const queryAll: (selector: string) => (proto: any, propName: string) => void;
export declare const queryAll: (selector: string) => (protoOrDescriptor: Object | ClassElement, name?: string | number | symbol | undefined) => any;
/**

@@ -84,2 +84,3 @@ * Adds event listener options to a method used as an event listener in a

*/
export declare const eventOptions: (options: EventListenerOptions) => (proto: any, name: string) => void;
export declare const eventOptions: (options: AddEventListenerOptions) => any;
//# sourceMappingURL=decorators.d.ts.map

@@ -14,2 +14,22 @@ /**

*/
const legacyCustomElement = (tagName, clazz) => {
window.customElements.define(tagName, clazz);
// Cast as any because TS doesn't recognize the return type as being a
// subtype of the decorated class when clazz is typed as
// `Constructor<HTMLElement>` for some reason.
// `Constructor<HTMLElement>` is helpful to make sure the decorator is
// applied to elements however.
return clazz;
};
const standardCustomElement = (tagName, descriptor) => {
const { kind, elements } = descriptor;
return {
kind,
elements,
// This callback is called once the class is otherwise fully defined
finisher(clazz) {
window.customElements.define(tagName, clazz);
}
};
};
/**

@@ -34,10 +54,36 @@ * Class decorator factory that defines the decorated class as a custom element.

*/
export const customElement = (tagName) => (clazz) => {
window.customElements.define(tagName, clazz);
// Cast as any because TS doesn't recognize the return type as being a
// subtype of the decorated class when clazz is typed as
// `Constructor<HTMLElement>` for some reason. `Constructor<HTMLElement>`
// is helpful to make sure the decorator is applied to elements however.
return clazz;
export const customElement = (tagName) => (classOrDescriptor) => (typeof classOrDescriptor === 'function')
? legacyCustomElement(tagName, classOrDescriptor)
: standardCustomElement(tagName, classOrDescriptor);
const standardProperty = (options, element) => {
// createProperty() takes care of defining the property, but we still must
// return some kind of descriptor, so return a descriptor for an unused
// prototype field. The finisher calls createProperty().
return {
kind: 'field',
key: Symbol(),
placement: 'own',
descriptor: {},
// When @babel/plugin-proposal-decorators implements initializers,
// do this instead of the initializer below. See:
// https://github.com/babel/babel/issues/9260 extras: [
// {
// kind: 'initializer',
// placement: 'own',
// initializer: descriptor.initializer,
// }
// ],
initializer() {
if (typeof element.initializer === 'function') {
this[element.key] = element.initializer.call(this);
}
},
finisher(clazz) {
clazz.createProperty(element.key, options);
}
};
};
const legacyProperty = (options, proto, name) => {
proto.constructor.createProperty(name, options);
};
/**

@@ -48,5 +94,5 @@ * A property decorator which creates a LitElement property which reflects a

*/
export const property = (options) => (proto, name) => {
proto.constructor.createProperty(name, options);
};
export const property = (options) => (protoOrDescriptor, name) => (name !== undefined)
? legacyProperty(options, protoOrDescriptor, name)
: standardProperty(options, protoOrDescriptor);
/**

@@ -62,2 +108,9 @@ * A property decorator that converts a class property into a getter that

export const queryAll = _query((target, selector) => target.querySelectorAll(selector));
const legacyQuery = (descriptor, proto, name) => { Object.defineProperty(proto, name, descriptor); };
const standardQuery = (descriptor, element) => ({
kind: 'method',
placement: 'prototype',
key: element.key,
descriptor,
});
/**

@@ -70,10 +123,19 @@ * Base-implementation of `@query` and `@queryAll` decorators.

function _query(queryFn) {
return (selector) => (proto, propName) => {
Object.defineProperty(proto, propName, {
return (selector) => (protoOrDescriptor, name) => {
const descriptor = {
get() { return queryFn(this.renderRoot, selector); },
enumerable: true,
configurable: true,
});
};
return (name !== undefined)
? legacyQuery(descriptor, protoOrDescriptor, name)
: standardQuery(descriptor, protoOrDescriptor);
};
}
const standardEventOptions = (options, element) => {
return Object.assign({}, element, { finisher(clazz) {
Object.assign(clazz.prototype[element.key], options);
} });
};
const legacyEventOptions = (options, proto, name) => { Object.assign(proto[name], options); };
/**

@@ -105,6 +167,11 @@ * Adds event listener options to a method used as an event listener in a

*/
export const eventOptions = (options) => (proto, name) => {
// This comment is here to fix a disagreement between formatter and linter
Object.assign(proto[name], options);
};
export const eventOptions = (options) =>
// Return value typed as any to prevent TypeScript from complaining that
// standard decorator function signature does not match TypeScript decorator
// signature
// TODO(kschaaf): unclear why it was only failing on this decorator and not
// the others
((protoOrDescriptor, name) => (name !== undefined)
? legacyEventOptions(options, protoOrDescriptor, name)
: standardEventOptions(options, protoOrDescriptor));
//# sourceMappingURL=decorators.js.map

@@ -17,19 +17,19 @@ /**

*/
export interface AttributeSerializer<T = any> {
export interface ComplexAttributeConverter<Type = any, TypeHint = any> {
/**
* Deserializing function called to convert an attribute value to a property
* Function called to convert an attribute value to a property
* value.
*/
fromAttribute?(value: string): T;
fromAttribute?(value: string, type?: TypeHint): Type;
/**
* Serializing function called to convert a property value to an attribute
* Function called to convert a property value to an attribute
* value.
*/
toAttribute?(value: T): string | null;
toAttribute?(value: Type, type?: TypeHint): string | null;
}
declare type AttributeType<T = any> = AttributeSerializer<T> | ((value: string) => T);
declare type AttributeConverter<Type = any, TypeHint = any> = ComplexAttributeConverter<Type> | ((value: string, type?: TypeHint) => Type);
/**
* Defines options for a property accessor.
*/
export interface PropertyDeclaration<T = any> {
export interface PropertyDeclaration<Type = any, TypeHint = any> {
/**

@@ -44,12 +44,20 @@ * Indicates how and whether the property becomes an observed attribute.

/**
* Indicates how to serialize and deserialize the attribute to/from a
* property. If this value is a function, it is used to deserialize the
* attribute value a the property value. If it's an object, it can have keys
* for `fromAttribute` and `toAttribute` where `fromAttribute` is the
* deserialize function and `toAttribute` is a serialize function used to set
* the property to an attribute. If no `toAttribute` function is provided and
* Indicates the type of the property. This is used only as a hint for the
* `converter` to determine how to convert the attribute
* to/from a property.
*/
type?: TypeHint;
/**
* Indicates how to convert the attribute to/from a property. If this value
* is a function, it is used to convert the attribute value a the property
* value. If it's an object, it can have keys for `fromAttribute` and
* `toAttribute`. If no `toAttribute` function is provided and
* `reflect` is set to `true`, the property value is set directly to the
* attribute.
* attribute. A default `converter` is used if none is provided; it supports
* `Boolean`, `String`, `Number`, `Object`, and `Array`. Note,
* when a property changes and the converter is used to update the attribute,
* the property is never updated again as a result of the attribute changing,
* and vice versa.
*/
type?: AttributeType<T>;
converter?: AttributeConverter<Type, TypeHint>;
/**

@@ -59,4 +67,4 @@ * Indicates if the property should reflect to an attribute.

* attribute name determined according to the rules for the `attribute`
* property option and the value of the property serialized using the rules
* from the `type` property option.
* property option and the value of the property converted using the rules
* from the `converter` property option.
*/

@@ -69,3 +77,12 @@ reflect?: boolean;

*/
hasChanged?(value: T, oldValue: T): boolean;
hasChanged?(value: Type, oldValue: Type): boolean;
/**
* Indicates whether an accessor will be created for this property. By
* default, an accessor will be generated for this property that requests an
* update when set. If this flag is `true`, no accessor will be created, and
* it will be the user's responsibility to call
* `this.requestUpdate(propertyName, oldValue)` to request an update when
* the property changes.
*/
noAccessor?: boolean;
}

@@ -81,2 +98,3 @@ /**

export declare type PropertyValues = Map<PropertyKey, unknown>;
export declare const defaultConverter: ComplexAttributeConverter;
export interface HasChanged {

@@ -97,4 +115,5 @@ (value: unknown, old: unknown): boolean;

/**
* Maps attribute names to properties; for example `foobar` attribute
* to `fooBar` property.
* Maps attribute names to properties; for example `foobar` attribute to
* `fooBar` property. Created lazily on user subclasses when finalizing the
* class.
*/

@@ -105,13 +124,26 @@ private static _attributeToPropertyMap;

*/
private static _finalized;
protected static finalized: boolean;
/**
* Memoized list of all class properties, including any superclass properties.
* Created lazily on user subclasses when finalizing the class.
*/
private static _classProperties;
private static _classProperties?;
/**
* User-supplied object that maps property names to `PropertyDeclaration`
* objects containing options for configuring the property.
*/
static properties: PropertyDeclarations;
/**
* Returns a list of attributes corresponding to the registered properties.
* @nocollapse
*/
static readonly observedAttributes: string[];
/**
* Ensures the private `_classProperties` property metadata is created.
* In addition to `_finalize` this is also called in `createProperty` to
* ensure the `@property` decorator can add property metadata.
*/
/** @nocollapse */
private static _ensureClassProperties;
/**
* Creates a property accessor on the element prototype if one does not exist.

@@ -121,2 +153,3 @@ * The property setter calls the property's `hasChanged` property option

* an update.
* @nocollapse
*/

@@ -127,2 +160,3 @@ static createProperty(name: PropertyKey, options?: PropertyDeclaration): void;

* any superclasses are also finalized.
* @nocollapse
*/

@@ -132,2 +166,3 @@ private static _finalize;

* Returns the property name for the given attribute `name`.
* @nocollapse
*/

@@ -139,2 +174,3 @@ private static _attributeNameForProperty;

* option for the property if present or a strict identity check.
* @nocollapse
*/

@@ -144,4 +180,5 @@ private static _valueHasChanged;

* Returns the property value for the given attribute value.
* Called via the `attributeChangedCallback` and uses the property's `type`
* or `type.fromAttribute` property option.
* Called via the `attributeChangedCallback` and uses the property's
* `converter` or `converter.fromAttribute` property option.
* @nocollapse
*/

@@ -155,2 +192,3 @@ private static _propertyValueFromAttribute;

* This uses the property's `reflect` and `type.toAttribute` property options.
* @nocollapse
*/

@@ -161,2 +199,3 @@ private static _propertyValueToAttribute;

private _updatePromise;
private _hasConnectedResolver;
/**

@@ -171,11 +210,5 @@ * Map with keys for any properties that have changed since the last

private _reflectingProperties;
/**
* Node or ShadowRoot into which element DOM should be rendered. Defaults
* to an open shadowRoot.
*/
protected renderRoot?: Element | DocumentFragment;
constructor();
/**
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default captures any pre-set values for
* registered properties.

@@ -201,13 +234,2 @@ */

private _applyInstanceProperties;
/**
* 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.
*/
protected createRenderRoot(): Element | ShadowRoot;
/**
* Uses ShadyCSS to keep element DOM updated.
*/
connectedCallback(): void;

@@ -241,19 +263,22 @@ /**

/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
* Sets up the element to asynchronously update.
*/
private _requestPropertyUpdate;
/**
* Invalidates the element causing it to asynchronously update regardless
* of whether or not any property changes are pending. This method is
* automatically called when any registered property changes.
*/
private _invalidate;
private _enqueueUpdate;
private readonly _hasConnected;
private readonly _hasRequestedUpdate;
protected readonly hasUpdated: number;
/**
* Validates the element by updating it.
* Performs an element update.
*
* You can override this method to change the timing of updates. For instance,
* to schedule updates to occur just before the next frame:
*
* ```
* protected async performUpdate(): Promise<unknown> {
* await new Promise((resolve) => requestAnimationFrame(() => resolve()));
* super.performUpdate();
* }
* ```
*/
private _validate;
protected performUpdate(): void | Promise<unknown>;
private _markUpdated;

@@ -283,4 +308,4 @@ /**

* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's
* `renderRoot`. Setting properties inside this method will *not* trigger
* It can be overridden to render and keep updated element DOM.
* Setting properties inside this method will *not* trigger
* another update.

@@ -313,1 +338,2 @@ *

export {};
//# sourceMappingURL=updating-element.d.ts.map

@@ -14,6 +14,53 @@ /**

*/
// serializer/deserializers for boolean attribute
const fromBooleanAttribute = (value) => value !== null;
const toBooleanAttribute = (value) => value ? '' : null;
/**
* When using Closure Compiler, JSCompiler_renameProperty(property, object) is
* replaced at compile time by the munged name for object[property]. We cannot
* alias this function, so we have to use a small shim that has the same
* behavior when not compiling.
*/
const JSCompiler_renameProperty = (prop, _obj) => prop;
/**
* Returns the property descriptor for a property on this prototype by walking
* up the prototype chain. Note that we stop just before Object.prototype, which
* also avoids issues with Symbol polyfills (core-js, get-own-property-symbols),
* which create accessors for the symbols on Object.prototype.
*/
const descriptorFromPrototype = (name, proto) => {
if (name in proto) {
while (proto !== Object.prototype) {
if (proto.hasOwnProperty(name)) {
return Object.getOwnPropertyDescriptor(proto, name);
}
proto = Object.getPrototypeOf(proto);
}
}
return undefined;
};
export const defaultConverter = {
toAttribute(value, type) {
switch (type) {
case Boolean:
return value ? '' : null;
case Object:
case Array:
// if the value is `null` or `undefined` pass this through
// to allow removing/no change behavior.
return value == null ? value : JSON.stringify(value);
}
return value;
},
fromAttribute(value, type) {
switch (type) {
case Boolean:
return value !== null;
case Number:
return value === null ? null : Number(value);
case Object:
case Array:
return JSON.parse(value);
}
return value;
}
};
/**
* Change function that returns true if `value` is different from `oldValue`.

@@ -29,9 +76,12 @@ * This method is used as the default for a property's `hasChanged` function.

type: String,
converter: defaultConverter,
reflect: false,
hasChanged: notEqual
};
const microtaskPromise = new Promise((resolve) => resolve(true));
const microtaskPromise = Promise.resolve(true);
const STATE_HAS_UPDATED = 1;
const STATE_UPDATE_REQUESTED = 1 << 2;
const STATE_IS_REFLECTING = 1 << 3;
const STATE_IS_REFLECTING_TO_ATTRIBUTE = 1 << 3;
const STATE_IS_REFLECTING_TO_PROPERTY = 1 << 4;
const STATE_HAS_CONNECTED = 1 << 5;
/**

@@ -48,2 +98,3 @@ * Base element class which manages element properties and attributes. When

this._updatePromise = microtaskPromise;
this._hasConnectedResolver = undefined;
/**

@@ -62,2 +113,3 @@ * Map with keys for any properties that have changed since the last

* Returns a list of attributes corresponding to the registered properties.
* @nocollapse
*/

@@ -78,10 +130,10 @@ static get observedAttributes() {

/**
* Creates a property accessor on the element prototype if one does not exist.
* The property setter calls the property's `hasChanged` property option
* or uses a strict identity check to determine whether or not to request
* an update.
* Ensures the private `_classProperties` property metadata is created.
* In addition to `_finalize` this is also called in `createProperty` to
* ensure the `@property` decorator can add property metadata.
*/
static createProperty(name, options = defaultPropertyDeclaration) {
/** @nocollapse */
static _ensureClassProperties() {
// ensure private storage for property declarations.
if (!this.hasOwnProperty('_classProperties')) {
if (!this.hasOwnProperty(JSCompiler_renameProperty('_classProperties', this))) {
this._classProperties = new Map();

@@ -94,19 +146,48 @@ // NOTE: Workaround IE11 not supporting Map constructor argument.

}
}
/**
* Creates a property accessor on the element prototype if one does not exist.
* The property setter calls the property's `hasChanged` property option
* or uses a strict identity check to determine whether or not to request
* an update.
* @nocollapse
*/
static createProperty(name, options = defaultPropertyDeclaration) {
// Note, since this can be called by the `@property` decorator which
// is called before `_finalize`, we ensure storage exists for property
// metadata.
this._ensureClassProperties();
this._classProperties.set(name, options);
// Allow user defined accessors by not replacing an existing own-property
// accessor.
if (this.prototype.hasOwnProperty(name)) {
return;
if (!options.noAccessor) {
const superDesc = descriptorFromPrototype(name, this.prototype);
let desc;
// If there is a super accessor, capture it and "super" to it
if (superDesc !== undefined && (superDesc.set && superDesc.get)) {
const { set, get } = superDesc;
desc = {
get() { return get.call(this); },
set(value) {
const oldValue = this[name];
set.call(this, value);
this.requestUpdate(name, oldValue);
},
configurable: true,
enumerable: true
};
}
else {
const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
desc = {
get() { return this[key]; },
set(value) {
const oldValue = this[name];
this[key] = value;
this.requestUpdate(name, oldValue);
},
configurable: true,
enumerable: true
};
}
Object.defineProperty(this.prototype, name, desc);
}
const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
Object.defineProperty(this.prototype, name, {
get() { return this[key]; },
set(value) {
const oldValue = this[name];
this[key] = value;
this._requestPropertyUpdate(name, oldValue, options);
},
configurable: true,
enumerable: true
});
}

@@ -116,5 +197,7 @@ /**

* any superclasses are also finalized.
* @nocollapse
*/
static _finalize() {
if (this.hasOwnProperty('_finalized') && this._finalized) {
if (this.hasOwnProperty(JSCompiler_renameProperty('finalized', this)) &&
this.finalized) {
return;

@@ -127,18 +210,24 @@ }

}
this._finalized = true;
this.finalized = true;
this._ensureClassProperties();
// initialize Map populated in observedAttributes
this._attributeToPropertyMap = new Map();
// make any properties
const props = this.properties;
// support symbols in properties (IE11 does not support this)
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, props[p]);
// Note, only process "own" properties since this element will inherit
// any properties defined on the superClass, and finalization ensures
// the entire prototype chain is finalized.
if (this.hasOwnProperty(JSCompiler_renameProperty('properties', this))) {
const props = this.properties;
// support symbols in properties (IE11 does not support this)
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, props[p]);
}
}

@@ -148,5 +237,6 @@ }

* Returns the property name for the given attribute `name`.
* @nocollapse
*/
static _attributeNameForProperty(name, options) {
const attribute = options !== undefined && options.attribute;
const attribute = options.attribute;
return attribute === false

@@ -163,2 +253,3 @@ ? undefined

* option for the property if present or a strict identity check.
* @nocollapse
*/

@@ -170,15 +261,11 @@ static _valueHasChanged(value, old, hasChanged = notEqual) {

* Returns the property value for the given attribute value.
* Called via the `attributeChangedCallback` and uses the property's `type`
* or `type.fromAttribute` property option.
* Called via the `attributeChangedCallback` and uses the property's
* `converter` or `converter.fromAttribute` property option.
* @nocollapse
*/
static _propertyValueFromAttribute(value, options) {
const type = options && options.type;
if (type === undefined) {
return value;
}
// Note: special case `Boolean` so users can use it as a `type`.
const fromAttribute = type === Boolean
? fromBooleanAttribute
: (typeof type === 'function' ? type : type.fromAttribute);
return fromAttribute ? fromAttribute(value) : value;
const type = options.type;
const converter = options.converter || defaultConverter;
const fromAttribute = (typeof converter === 'function' ? converter : converter.fromAttribute);
return fromAttribute ? fromAttribute(value, type) : value;
}

@@ -191,24 +278,19 @@ /**

* This uses the property's `reflect` and `type.toAttribute` property options.
* @nocollapse
*/
static _propertyValueToAttribute(value, options) {
if (options === undefined || options.reflect === undefined) {
if (options.reflect === undefined) {
return;
}
// Note: special case `Boolean` so users can use it as a `type`.
const toAttribute = options.type === Boolean
? toBooleanAttribute
: (options.type &&
options.type.toAttribute ||
String);
return toAttribute(value);
const type = options.type;
const converter = options.converter;
const toAttribute = converter && converter.toAttribute ||
defaultConverter.toAttribute;
return toAttribute(value, type);
}
/**
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default captures any pre-set values for
* registered properties.
*/
initialize() {
this.renderRoot = this.createRenderRoot();
this._saveInstanceProperties();
}
initialize() { this._saveInstanceProperties(); }
/**

@@ -248,20 +330,11 @@ * Fixes any properties set on the instance before upgrade time.

}
/**
* 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({ mode: 'open' });
}
/**
* Uses ShadyCSS to keep element DOM updated.
*/
connectedCallback() {
if ((this._updateState & STATE_HAS_UPDATED)) {
if (window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
}
this._updateState = this._updateState | STATE_HAS_CONNECTED;
// Ensure connection triggers an update. Updates cannot complete before
// connection and if one is pending connection the `_hasConnectionResolver`
// will exist. If so, resolve it to complete the update, otherwise
// requestUpdate.
if (this._hasConnectedResolver) {
this._hasConnectedResolver();
this._hasConnectedResolver = undefined;
}

@@ -288,24 +361,26 @@ else {

const ctor = this.constructor;
const attrValue = ctor._propertyValueToAttribute(value, options);
if (attrValue !== undefined) {
const attr = ctor._attributeNameForProperty(name, options);
if (attr !== undefined) {
// Track if the property is being reflected to avoid
// setting the property again via `attributeChangedCallback`. Note:
// 1. this takes advantage of the fact that the callback is synchronous.
// 2. will behave incorrectly if multiple attributes are in the reaction
// stack at time of calling. However, since we process attributes
// in `update` this should not be possible (or an extreme corner case
// that we'd like to discover).
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING;
if (attrValue === null) {
this.removeAttribute(attr);
}
else {
this.setAttribute(attr, attrValue);
}
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING;
const attr = ctor._attributeNameForProperty(name, options);
if (attr !== undefined) {
const attrValue = ctor._propertyValueToAttribute(value, options);
// an undefined value does not change the attribute.
if (attrValue === undefined) {
return;
}
// Track if the property is being reflected to avoid
// setting the property again via `attributeChangedCallback`. Note:
// 1. this takes advantage of the fact that the callback is synchronous.
// 2. will behave incorrectly if multiple attributes are in the reaction
// stack at time of calling. However, since we process attributes
// in `update` this should not be possible (or an extreme corner case
// that we'd like to discover).
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING_TO_ATTRIBUTE;
if (attrValue == null) {
this.removeAttribute(attr);
}
else {
this.setAttribute(attr, attrValue);
}
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE;
}

@@ -316,11 +391,16 @@ }

// just set from a property setter.
if (!(this._updateState & STATE_IS_REFLECTING)) {
const ctor = this.constructor;
const propName = ctor._attributeToPropertyMap.get(name);
if (propName !== undefined) {
const options = ctor._classProperties.get(propName);
this[propName] =
ctor._propertyValueFromAttribute(value, options);
}
if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) {
return;
}
const ctor = this.constructor;
const propName = ctor._attributeToPropertyMap.get(name);
if (propName !== undefined) {
const options = ctor._classProperties.get(propName) || defaultPropertyDeclaration;
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY;
this[propName] =
ctor._propertyValueFromAttribute(value, options);
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
}
}

@@ -341,51 +421,57 @@ /**

requestUpdate(name, oldValue) {
if (name !== undefined) {
const options = this.constructor
._classProperties.get(name) ||
defaultPropertyDeclaration;
return this._requestPropertyUpdate(name, oldValue, options);
let shouldRequestUpdate = true;
// if we have a property key, perform property update steps.
if (name !== undefined && !this._changedProperties.has(name)) {
const ctor = this.constructor;
const options = ctor._classProperties.get(name) || defaultPropertyDeclaration;
if (ctor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
// track old value when changing.
this._changedProperties.set(name, oldValue);
// add to reflecting properties set
if (options.reflect === true &&
!(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
// abort the request if the property should not be considered changed.
}
else {
shouldRequestUpdate = false;
}
}
return this._invalidate();
if (!this._hasRequestedUpdate && shouldRequestUpdate) {
this._enqueueUpdate();
}
return this.updateComplete;
}
/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
* Sets up the element to asynchronously update.
*/
_requestPropertyUpdate(name, oldValue, options) {
if (!this.constructor
._valueHasChanged(this[name], oldValue, options.hasChanged)) {
return this.updateComplete;
async _enqueueUpdate() {
// Mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolve;
const previousUpdatePromise = this._updatePromise;
this._updatePromise = new Promise((res) => resolve = res);
// Ensure any previous update has resolved before updating.
// This `await` also ensures that property changes are batched.
await previousUpdatePromise;
// Make sure the element has connected before updating.
if (!this._hasConnected) {
await new Promise((res) => this._hasConnectedResolver = res);
}
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
// Allow `performUpdate` to be asynchronous to enable scheduling of updates.
const result = this.performUpdate();
// Note, this is to avoid delaying an additional microtask unless we need
// to.
if (result != null &&
typeof result.then === 'function') {
await result;
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
return this._invalidate();
resolve(!this._hasRequestedUpdate);
}
/**
* Invalidates the element causing it to asynchronously update regardless
* of whether or not any property changes are pending. This method is
* automatically called when any registered property changes.
*/
async _invalidate() {
if (!this._hasRequestedUpdate) {
// mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolver;
const previousValidatePromise = this._updatePromise;
this._updatePromise = new Promise((r) => resolver = r);
await previousValidatePromise;
this._validate();
resolver(!this._hasRequestedUpdate);
}
return this.updateComplete;
get _hasConnected() {
return (this._updateState & STATE_HAS_CONNECTED);
}

@@ -395,6 +481,17 @@ get _hasRequestedUpdate() {

}
get hasUpdated() { return (this._updateState & STATE_HAS_UPDATED); }
/**
* Validates the element by updating it.
* Performs an element update.
*
* You can override this method to change the timing of updates. For instance,
* to schedule updates to occur just before the next frame:
*
* ```
* protected async performUpdate(): Promise<unknown> {
* await new Promise((resolve) => requestAnimationFrame(() => resolve()));
* super.performUpdate();
* }
* ```
*/
_validate() {
performUpdate() {
// Mixin instance properties once, if they exist.

@@ -447,4 +544,4 @@ if (this._instanceProperties) {

* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's
* `renderRoot`. Setting properties inside this method will *not* trigger
* It can be overridden to render and keep updated element DOM.
* Setting properties inside this method will *not* trigger
* another update.

@@ -485,15 +582,5 @@ *

/**
* Maps attribute names to properties; for example `foobar` attribute
* to `fooBar` property.
*/
UpdatingElement._attributeToPropertyMap = new Map();
/**
* Marks class as having finished creating properties.
*/
UpdatingElement._finalized = true;
/**
* Memoized list of all class properties, including any superclass properties.
*/
UpdatingElement._classProperties = new Map();
UpdatingElement.properties = {};
UpdatingElement.finalized = true;
//# sourceMappingURL=updating-element.js.map

@@ -18,5 +18,12 @@ /**

export * from './lib/decorators.js';
export { html, svg } from 'lit-html/lit-html';
export { html, svg, TemplateResult, SVGTemplateResult } from 'lit-html/lit-html';
import { CSSResult } from './lib/css-tag.js';
export * from './lib/css-tag.js';
export declare class LitElement extends UpdatingElement {
/**
* Ensure this class is marked as `finalized` as an optimization ensuring
* it will not needlessly try to `finalize`.
*/
protected static finalized: boolean;
/**
* Render method used to render the lit-html TemplateResult to the element's

@@ -27,5 +34,44 @@ * DOM.

* @param {String} Element name.
* @nocollapse
*/
static render: (result: TemplateResult, container: Element | DocumentFragment, options: import("lit-html/lib/shady-render").ShadyRenderOptions) => void;
/**
* Array of styles to apply to the element. The styles should be defined
* using the `css` tag function.
*/
static readonly styles: CSSResult[];
private static _styles;
private static readonly _uniqueStyles;
private _needsShimAdoptedStyleSheets?;
/**
* Node or ShadowRoot into which element DOM should be rendered. Defaults
* to an open shadowRoot.
*/
protected renderRoot?: Element | DocumentFragment;
/**
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* registered properties.
*/
protected initialize(): void;
/**
* 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.
*/
protected createRenderRoot(): Element | ShadowRoot;
/**
* Applies styling to the element shadowRoot using the `static get 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).
*/
protected adoptStyles(): void;
connectedCallback(): void;
/**
* Updates the element. This method reflects property values to attributes

@@ -44,1 +90,2 @@ * and calls `render` to render DOM via lit-html. Setting properties inside

}
//# sourceMappingURL=lit-element.d.ts.map

@@ -19,5 +19,96 @@ /**

export * from './lib/decorators.js';
export { html, svg } from 'lit-html/lit-html';
export { html, svg, TemplateResult, SVGTemplateResult } from 'lit-html/lit-html';
import { supportsAdoptingStyleSheets } from './lib/css-tag.js';
export * from './lib/css-tag.js';
export class LitElement extends UpdatingElement {
/**
* Array of styles to apply to the element. The styles should be defined
* using the `css` tag function.
*/
static get styles() { return []; }
static get _uniqueStyles() {
if (this._styles === undefined) {
const styles = this.styles;
// As a performance optimization to avoid duplicated styling that can
// occur especially when composing via subclassing, de-duplicate styles
// preserving the last item in the list. The last item is kept to
// try to preserve cascade order with the assumption that it's most
// important that last added styles override previous styles.
const styleSet = styles.reduceRight((set, s) => {
set.add(s);
// on IE set.add does not return the set.
return set;
}, new Set());
// Array.form does not work on Set in IE
this._styles = [];
styleSet.forEach((v) => this._styles.unshift(v));
}
return this._styles;
}
/**
* 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.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({ mode: 'open' });
}
/**
* Applies styling to the element shadowRoot using the `static get 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._uniqueStyles;
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.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

@@ -35,2 +126,13 @@ * and calls `render` to render DOM via lit-html. Setting properties inside

}
// 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._uniqueStyles.forEach((s) => {
const style = document.createElement('style');
style.textContent = s.cssText;
this.renderRoot.appendChild(style);
});
}
}

@@ -45,2 +147,7 @@ /**

/**
* Ensure this class is marked as `finalized` as an optimization ensuring
* it will not needlessly try to `finalize`.
*/
LitElement.finalized = true;
/**
* Render method used to render the lit-html TemplateResult to the element's

@@ -51,4 +158,5 @@ * DOM.

* @param {String} Element name.
* @nocollapse
*/
LitElement.render = render;
//# sourceMappingURL=lit-element.js.map
{
"name": "@polymer/lit-element",
"version": "0.6.5",
"version": "0.7.0",
"description": "Polymer based lit-html custom element",

@@ -12,6 +12,17 @@ "license": "BSD-3-Clause",

},
"files": [
"/lib/",
"/src/",
"!/src/demo/",
"!/src/test/",
"/lit-element.d.ts",
"/lit-element.d.ts.map",
"/lit-element.js",
"/lit-element.js.map"
],
"scripts": {
"build": "tsc",
"gen-docs": "typedoc --readme none --tsconfig tsconfig_apidoc.json --mode modules --theme minimal --excludeNotExported --excludePrivate --ignoreCompilerErrors --exclude '{**/*test*,**/node_modules/**,**/test/**}' --out ./docs/api src/**/*.ts",
"test": "npm run build && wct",
"build:babel-test": "babel src/test/lib/decorators_test.ts --out-file test/lib/decorators-babel_test.js",
"gen-docs": "typedoc --readme docs/_api/api-readme.md --tsconfig tsconfig_apidoc.json --mode modules --theme docs/_api/theme --excludeNotExported --excludePrivate --ignoreCompilerErrors --exclude '{**/*test*,**/node_modules/**,**/test/**}' --out ./docs/api src/**/*.ts",
"test": "npm run build && npm run build:babel-test && wct",
"checksize": "rollup -c ; rm lit-element.bundled.js",

@@ -25,6 +36,10 @@ "format": "find src test | grep '\\.js$\\|\\.ts$' | xargs clang-format --style=file -i",

"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-proposal-decorators": "^7.2.3",
"@babel/plugin-transform-typescript": "^7.2.0",
"@types/chai": "^4.0.1",
"@types/mocha": "^5.2.4",
"@webcomponents/shadycss": "^1.5.2",
"@webcomponents/webcomponentsjs": "^2.1.3",
"@webcomponents/shadycss": "^1.8.0",
"@webcomponents/webcomponentsjs": "^2.2.3",
"chai": "^4.0.2",

@@ -36,12 +51,12 @@ "mocha": "^5.0.5",

"rollup-plugin-terser": "^1.0.1",
"tslint": "^5.7.0",
"tslint": "^5.12.0",
"typedoc": "^0.8.0",
"typescript": "^3.0.3",
"typescript": "^3.2.2",
"uglify-es": "^3.3.9",
"wct-mocha": "^1.0.0",
"web-component-tester": "^6.9.0"
"web-component-tester": "^6.9.2"
},
"typings": "lit-element.d.ts",
"dependencies": {
"lit-html": "^1.0.0-rc.1"
"lit-html": "^1.0.0-rc.2"
},

@@ -48,0 +63,0 @@ "publishConfig": {

@@ -1,2 +0,2 @@

> ## 🛠 Status: In Development
> ## 🛠 Status: Moved to `lit-element`
> LitElement is currently in development. It's on the fast track to a 1.0 release, so we encourage you to use it and give us your feedback, but there are things that haven't been finalized yet and you can expect some changes.

@@ -38,15 +38,18 @@

If a string, the string value is observed (e.g `attribute: 'foo-bar'`).
* `type`: Indicates how to serialize and deserialize the attribute to/from a property.
* `converter`: Indicates how to convert the attribute to/from a property.
The value can be a function used for both serialization and deserialization, or it can
be an object with individual functions via the optional keys, `fromAttribute` and `toAttribute`.
`type` defaults to the `String` constructor, and so does the `toAttribute` and `fromAttribute`
keys.
A default `converter` is used if none is provided; it supports
`Boolean`, `String`, `Number`, `Object`, and `Array`.
* `type`: Indicates the type of the property. This is used only as a hint for the
`converter` to determine how to convert the attribute
to/from a property. Note, when a property changes and the converter is used
to update the attribute, the property is never updated again as a result of
the attribute changing, and vice versa.
* `reflect`: Indicates whether the property should reflect to its associated
attribute (as determined by the attribute option).
If `true`, when the property is set, the attribute which name is determined
according to the rules for the `attribute` property option, will be set to the
value of the property serialized using the rules from the `type` property option.
Note, `type: Boolean` has special handling by default which means that truthy
values result in the presence of the attribute, whereas falsy values result
in the absence of the attribute.
attribute (as determined by the attribute option). If `true`, when the
property is set, the attribute which name is determined according to the
rules for the `attribute` property option will be set to the value of the
property converted using the rules from the `type` and `converter`
property options.
* `hasChanged`: A function that indicates whether a property should be considered

@@ -166,2 +169,6 @@ changed when it is set and thus result in an update. The function should take the

* `performUpdate()` (protected): Implement to control the timing of an update, for example
to integrate with a scheduler. If a Promise is returned from `performUpdate` it will be
awaited before finishing the update.
* `update(changedProperties)` (protected): This method calls `render()` and then uses `lit-html`

@@ -208,2 +215,3 @@ in order to render the template DOM. It also updates any reflected attributes based on

of the event loop, before the next paint).
* `performUpdate()`: Performs the update, calling the rest of the update API.
* `shouldUpdate(changedProperties)`: The update proceeds if this returns `true`, which

@@ -210,0 +218,0 @@ it does by default.

interface ShadyCSS {
styleElement(host: Element, overrideProps?: {[key: string]: string}): void;
getComputedStyleValue(element: Element, property: string): string;
ScopingShim: {prepareAdoptedCssText(cssText: string[], name: string): void;};
nativeShadow: boolean;
}

@@ -13,2 +15,33 @@

ShadyDOM?: ShadyDOM;
ShadowRoot: typeof ShadowRoot;
}
// Augment existing types with styling API
interface ShadowRoot {
adoptedStyleSheets: CSSStyleSheet[];
}
declare var ShadowRoot: {prototype: ShadowRoot; new () : ShadowRoot;}
interface CSSStyleSheet {
replaceSync(cssText: string): void;
replace(cssText: string): Promise<unknown>;
}
// From the TC39 Decorators proposal
interface ClassDescriptor {
kind: 'class';
elements: ClassElement[];
finisher?: (clazz: Constructor<T>) => undefined | Constructor<T>;
}
// From the TC39 Decorators proposal
interface ClassElement {
kind: 'field'|'method';
key: PropertyKey;
placement: 'static'|'prototype'|'own';
initializer?: Function;
extras?;
finisher?;
descriptor?: PropertyDescriptor;
}

@@ -24,2 +24,26 @@

const legacyCustomElement =
(tagName: string, clazz: Constructor<HTMLElement>) => {
window.customElements.define(tagName, clazz);
// Cast as any because TS doesn't recognize the return type as being a
// subtype of the decorated class when clazz is typed as
// `Constructor<HTMLElement>` for some reason.
// `Constructor<HTMLElement>` is helpful to make sure the decorator is
// applied to elements however.
return clazz as any;
};
const standardCustomElement =
(tagName: string, descriptor: ClassDescriptor) => {
const {kind, elements} = descriptor;
return {
kind,
elements,
// This callback is called once the class is otherwise fully defined
finisher(clazz: Constructor<HTMLElement>) {
window.customElements.define(tagName, clazz);
}
};
};
/**

@@ -44,12 +68,44 @@ * Class decorator factory that defines the decorated class as a custom element.

*/
export const customElement = (tagName: string) =>
(clazz: Constructor<HTMLElement>) => {
window.customElements.define(tagName, clazz);
// Cast as any because TS doesn't recognize the return type as being a
// subtype of the decorated class when clazz is typed as
// `Constructor<HTMLElement>` for some reason. `Constructor<HTMLElement>`
// is helpful to make sure the decorator is applied to elements however.
return clazz as any;
export const customElement = (tagName: string) => (
classOrDescriptor: Constructor<HTMLElement>|ClassDescriptor) =>
(typeof classOrDescriptor === 'function')
? legacyCustomElement(tagName,
classOrDescriptor as Constructor<HTMLElement>)
: standardCustomElement(tagName, classOrDescriptor as ClassDescriptor);
const standardProperty =
(options: PropertyDeclaration, element: ClassElement) => {
// createProperty() takes care of defining the property, but we still must
// return some kind of descriptor, so return a descriptor for an unused
// prototype field. The finisher calls createProperty().
return {
kind : 'field',
key : Symbol(),
placement : 'own',
descriptor : {},
// When @babel/plugin-proposal-decorators implements initializers,
// do this instead of the initializer below. See:
// https://github.com/babel/babel/issues/9260 extras: [
// {
// kind: 'initializer',
// placement: 'own',
// initializer: descriptor.initializer,
// }
// ],
initializer(this: any) {
if (typeof element.initializer === 'function') {
this[element.key] = element.initializer!.call(this);
}
},
finisher(clazz: typeof UpdatingElement) {
clazz.createProperty(element.key, options);
}
};
};
const legacyProperty = (options: PropertyDeclaration, proto: Object,
name: PropertyKey) => {
(proto.constructor as typeof UpdatingElement).createProperty(name!, options);
};
/**

@@ -60,6 +116,7 @@ * A property decorator which creates a LitElement property which reflects a

*/
export const property = (options?: PropertyDeclaration) => (proto: Object,
name: PropertyKey) => {
(proto.constructor as typeof UpdatingElement).createProperty(name, options);
};
export const property = (options?: PropertyDeclaration) =>
(protoOrDescriptor: Object|ClassElement, name?: PropertyKey): any =>
(name !== undefined)
? legacyProperty(options!, protoOrDescriptor as Object, name)
: standardProperty(options!, protoOrDescriptor as ClassElement);

@@ -80,2 +137,14 @@ /**

const legacyQuery =
(descriptor: PropertyDescriptor, proto: Object,
name: PropertyKey) => { Object.defineProperty(proto, name, descriptor); };
const standardQuery = (descriptor: PropertyDescriptor, element: ClassElement) =>
({
kind : 'method',
placement : 'prototype',
key : element.key,
descriptor,
});
/**

@@ -88,11 +157,30 @@ * Base-implementation of `@query` and `@queryAll` decorators.

function _query<T>(queryFn: (target: NodeSelector, selector: string) => T) {
return (selector: string) => (proto: any, propName: string) => {
Object.defineProperty(proto, propName, {
return (selector: string) => (protoOrDescriptor: Object|ClassElement,
name?: PropertyKey): any => {
const descriptor = {
get(this: LitElement) { return queryFn(this.renderRoot!, selector); },
enumerable : true,
configurable : true,
});
};
return (name !== undefined)
? legacyQuery(descriptor, protoOrDescriptor as Object, name)
: standardQuery(descriptor, protoOrDescriptor as ClassElement);
};
}
const standardEventOptions =
(options: AddEventListenerOptions, element: ClassElement) => {
return {
...element,
finisher(clazz: typeof UpdatingElement) {
Object.assign(clazz.prototype[element.key as keyof UpdatingElement],
options);
}
};
};
const legacyEventOptions =
(options: AddEventListenerOptions, proto: any,
name: PropertyKey) => { Object.assign(proto[name], options); };
/**

@@ -124,6 +212,12 @@ * Adds event listener options to a method used as an event listener in a

*/
export const eventOptions = (options: EventListenerOptions) =>
(proto: any, name: string) => {
// This comment is here to fix a disagreement between formatter and linter
Object.assign(proto[name], options);
};
export const eventOptions = (options: AddEventListenerOptions) =>
// Return value typed as any to prevent TypeScript from complaining that
// standard decorator function signature does not match TypeScript decorator
// signature
// TODO(kschaaf): unclear why it was only failing on this decorator and not
// the others
((protoOrDescriptor: Object|ClassElement, name?: string) =>
(name !== undefined)
? legacyEventOptions(options, protoOrDescriptor as Object, name)
: standardEventOptions(options,
protoOrDescriptor as ClassElement)) as any;

@@ -16,20 +16,47 @@ /**

/**
* When using Closure Compiler, JSCompiler_renameProperty(property, object) is
* replaced at compile time by the munged name for object[property]. We cannot
* alias this function, so we have to use a small shim that has the same
* behavior when not compiling.
*/
const JSCompiler_renameProperty = (prop: PropertyKey, _obj: any) => prop;
/**
* Returns the property descriptor for a property on this prototype by walking
* up the prototype chain. Note that we stop just before Object.prototype, which
* also avoids issues with Symbol polyfills (core-js, get-own-property-symbols),
* which create accessors for the symbols on Object.prototype.
*/
const descriptorFromPrototype = (name: PropertyKey, proto: UpdatingElement) => {
if (name in proto) {
while (proto !== Object.prototype) {
if (proto.hasOwnProperty(name)) {
return Object.getOwnPropertyDescriptor(proto, name);
}
proto = Object.getPrototypeOf(proto);
}
}
return undefined;
};
/**
* Converts property values to and from attribute values.
*/
export interface AttributeSerializer<T = any> {
export interface ComplexAttributeConverter<Type = any, TypeHint = any> {
/**
* Deserializing function called to convert an attribute value to a property
* Function called to convert an attribute value to a property
* value.
*/
fromAttribute?(value: string): T;
fromAttribute?(value: string, type?: TypeHint): Type;
/**
* Serializing function called to convert a property value to an attribute
* Function called to convert a property value to an attribute
* value.
*/
toAttribute?(value: T): string|null;
toAttribute?(value: Type, type?: TypeHint): string|null;
}
type AttributeType<T = any> = AttributeSerializer<T>|((value: string) => T);
type AttributeConverter<Type = any, TypeHint = any> =
ComplexAttributeConverter<Type>|((value: string, type?: TypeHint) => Type);

@@ -39,3 +66,3 @@ /**

*/
export interface PropertyDeclaration<T = any> {
export interface PropertyDeclaration<Type = any, TypeHint = any> {

@@ -52,12 +79,21 @@ /**

/**
* Indicates how to serialize and deserialize the attribute to/from a
* property. If this value is a function, it is used to deserialize the
* attribute value a the property value. If it's an object, it can have keys
* for `fromAttribute` and `toAttribute` where `fromAttribute` is the
* deserialize function and `toAttribute` is a serialize function used to set
* the property to an attribute. If no `toAttribute` function is provided and
* Indicates the type of the property. This is used only as a hint for the
* `converter` to determine how to convert the attribute
* to/from a property.
*/
type?: TypeHint;
/**
* Indicates how to convert the attribute to/from a property. If this value
* is a function, it is used to convert the attribute value a the property
* value. If it's an object, it can have keys for `fromAttribute` and
* `toAttribute`. If no `toAttribute` function is provided and
* `reflect` is set to `true`, the property value is set directly to the
* attribute.
* attribute. A default `converter` is used if none is provided; it supports
* `Boolean`, `String`, `Number`, `Object`, and `Array`. Note,
* when a property changes and the converter is used to update the attribute,
* the property is never updated again as a result of the attribute changing,
* and vice versa.
*/
type?: AttributeType<T>;
converter?: AttributeConverter<Type, TypeHint>;

@@ -68,4 +104,4 @@ /**

* attribute name determined according to the rules for the `attribute`
* property option and the value of the property serialized using the rules
* from the `type` property option.
* property option and the value of the property converted using the rules
* from the `converter` property option.
*/

@@ -79,3 +115,13 @@ reflect?: boolean;

*/
hasChanged?(value: T, oldValue: T): boolean;
hasChanged?(value: Type, oldValue: Type): boolean;
/**
* Indicates whether an accessor will be created for this property. By
* default, an accessor will be generated for this property that requests an
* update when set. If this flag is `true`, no accessor will be created, and
* it will be the user's responsibility to call
* `this.requestUpdate(propertyName, oldValue)` to request an update when
* the property changes.
*/
noAccessor?: boolean;
}

@@ -98,6 +144,32 @@

// serializer/deserializers for boolean attribute
const fromBooleanAttribute = (value: string) => value !== null;
const toBooleanAttribute = (value: string) => value ? '' : null;
export const defaultConverter: ComplexAttributeConverter = {
toAttribute(value: any, type?: any) {
switch (type) {
case Boolean:
return value ? '' : null;
case Object:
case Array:
// if the value is `null` or `undefined` pass this through
// to allow removing/no change behavior.
return value == null ? value : JSON.stringify(value);
}
return value;
},
fromAttribute(value: any, type?: any) {
switch (type) {
case Boolean:
return value !== null;
case Number:
return value === null ? null : Number(value);
case Object:
case Array:
return JSON.parse(value);
}
return value;
}
};
export interface HasChanged {

@@ -119,2 +191,3 @@ (value: unknown, old: unknown): boolean;

type : String,
converter : defaultConverter,
reflect : false,

@@ -124,9 +197,12 @@ hasChanged : notEqual

const microtaskPromise = new Promise((resolve) => resolve(true));
const microtaskPromise = Promise.resolve(true);
const STATE_HAS_UPDATED = 1;
const STATE_UPDATE_REQUESTED = 1 << 2;
const STATE_IS_REFLECTING = 1 << 3;
const STATE_IS_REFLECTING_TO_ATTRIBUTE = 1 << 3;
const STATE_IS_REFLECTING_TO_PROPERTY = 1 << 4;
const STATE_HAS_CONNECTED = 1 << 5;
type UpdateState = typeof STATE_HAS_UPDATED|typeof STATE_UPDATE_REQUESTED|
typeof STATE_IS_REFLECTING;
typeof STATE_IS_REFLECTING_TO_ATTRIBUTE|
typeof STATE_IS_REFLECTING_TO_PROPERTY|typeof STATE_HAS_CONNECTED;

@@ -140,7 +216,14 @@ /**

/*
* Due to closure compiler ES6 compilation bugs, @nocollapse is required on
* all static methods and properties with initializers. Reference:
* - https://github.com/google/closure-compiler/issues/1776
*/
/**
* Maps attribute names to properties; for example `foobar` attribute
* to `fooBar` property.
* Maps attribute names to properties; for example `foobar` attribute to
* `fooBar` property. Created lazily on user subclasses when finalizing the
* class.
*/
private static _attributeToPropertyMap: AttributeMap = new Map();
private static _attributeToPropertyMap: AttributeMap;

@@ -150,13 +233,19 @@ /**

*/
private static _finalized = true;
protected static finalized = true;
/**
* Memoized list of all class properties, including any superclass properties.
* Created lazily on user subclasses when finalizing the class.
*/
private static _classProperties: PropertyDeclarationMap = new Map();
private static _classProperties?: PropertyDeclarationMap;
static properties: PropertyDeclarations = {};
/**
* User-supplied object that maps property names to `PropertyDeclaration`
* objects containing options for configuring the property.
*/
static properties: PropertyDeclarations;
/**
* Returns a list of attributes corresponding to the registered properties.
* @nocollapse
*/

@@ -167,3 +256,3 @@ static get observedAttributes() {

const attributes = [];
for (const [p, v] of this._classProperties) {
for (const [p, v] of this._classProperties!) {
const attr = this._attributeNameForProperty(p, v);

@@ -179,2 +268,22 @@ if (attr !== undefined) {

/**
* Ensures the private `_classProperties` property metadata is created.
* In addition to `_finalize` this is also called in `createProperty` to
* ensure the `@property` decorator can add property metadata.
*/
/** @nocollapse */
private static _ensureClassProperties() {
// ensure private storage for property declarations.
if (!this.hasOwnProperty(
JSCompiler_renameProperty('_classProperties', this))) {
this._classProperties = new Map();
// NOTE: Workaround IE11 not supporting Map constructor argument.
const superProperties = Object.getPrototypeOf(this)._classProperties;
if (superProperties !== undefined) {
superProperties.forEach((v: any, k: PropertyKey) =>
this._classProperties!.set(k, v));
}
}
}
/**
* Creates a property accessor on the element prototype if one does not exist.

@@ -184,2 +293,3 @@ * The property setter calls the property's `hasChanged` property option

* an update.
* @nocollapse
*/

@@ -189,29 +299,38 @@ static createProperty(name: PropertyKey,

PropertyDeclaration = defaultPropertyDeclaration) {
// ensure private storage for property declarations.
if (!this.hasOwnProperty('_classProperties')) {
this._classProperties = new Map();
// NOTE: Workaround IE11 not supporting Map constructor argument.
const superProperties = Object.getPrototypeOf(this)._classProperties;
if (superProperties !== undefined) {
superProperties.forEach((v: any, k: PropertyKey) =>
this._classProperties.set(k, v));
// Note, since this can be called by the `@property` decorator which
// is called before `_finalize`, we ensure storage exists for property
// metadata.
this._ensureClassProperties();
this._classProperties!.set(name, options);
if (!options.noAccessor) {
const superDesc = descriptorFromPrototype(name, this.prototype);
let desc;
// If there is a super accessor, capture it and "super" to it
if (superDesc !== undefined && (superDesc.set && superDesc.get)) {
const {set, get} = superDesc;
desc = {
get() { return get.call(this); },
set(value: any) {
const oldValue = this[name];
set.call(this, value);
this.requestUpdate(name, oldValue);
},
configurable : true,
enumerable : true
};
} else {
const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
desc = {
get() { return this[key]; },
set(value: any) {
const oldValue = this[name];
this[key] = value;
this.requestUpdate(name, oldValue);
},
configurable : true,
enumerable : true
};
}
Object.defineProperty(this.prototype, name, desc);
}
this._classProperties.set(name, options);
// Allow user defined accessors by not replacing an existing own-property
// accessor.
if (this.prototype.hasOwnProperty(name)) {
return;
}
const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
Object.defineProperty(this.prototype, name, {
get() { return this[key]; },
set(value) {
const oldValue = this[name];
this[key] = value;
this._requestPropertyUpdate(name, oldValue, options);
},
configurable : true,
enumerable : true
});
}

@@ -222,5 +341,7 @@

* any superclasses are also finalized.
* @nocollapse
*/
private static _finalize() {
if (this.hasOwnProperty('_finalized') && this._finalized) {
if (this.hasOwnProperty(JSCompiler_renameProperty('finalized', this)) &&
this.finalized) {
return;

@@ -233,18 +354,24 @@ }

}
this._finalized = true;
this.finalized = true;
this._ensureClassProperties();
// initialize Map populated in observedAttributes
this._attributeToPropertyMap = new Map();
// make any properties
const props = this.properties;
// support symbols in properties (IE11 does not support this)
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, (props as any)[p]);
// Note, only process "own" properties since this element will inherit
// any properties defined on the superClass, and finalization ensures
// the entire prototype chain is finalized.
if (this.hasOwnProperty(JSCompiler_renameProperty('properties', this))) {
const props = this.properties;
// support symbols in properties (IE11 does not support this)
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, (props as any)[p]);
}
}

@@ -255,6 +382,7 @@ }

* Returns the property name for the given attribute `name`.
* @nocollapse
*/
private static _attributeNameForProperty(name: PropertyKey,
options?: PropertyDeclaration) {
const attribute = options !== undefined && options.attribute;
options: PropertyDeclaration) {
const attribute = options.attribute;
return attribute === false

@@ -272,2 +400,3 @@ ? undefined

* option for the property if present or a strict identity check.
* @nocollapse
*/

@@ -281,17 +410,13 @@ private static _valueHasChanged(value: unknown, old: unknown,

* Returns the property value for the given attribute value.
* Called via the `attributeChangedCallback` and uses the property's `type`
* or `type.fromAttribute` property option.
* Called via the `attributeChangedCallback` and uses the property's
* `converter` or `converter.fromAttribute` property option.
* @nocollapse
*/
private static _propertyValueFromAttribute(value: string,
options?: PropertyDeclaration) {
const type = options && options.type;
if (type === undefined) {
return value;
}
// Note: special case `Boolean` so users can use it as a `type`.
options: PropertyDeclaration) {
const type = options.type;
const converter = options.converter || defaultConverter;
const fromAttribute =
type === Boolean
? fromBooleanAttribute
: (typeof type === 'function' ? type : type.fromAttribute);
return fromAttribute ? fromAttribute(value) : value;
(typeof converter === 'function' ? converter : converter.fromAttribute);
return fromAttribute ? fromAttribute(value, type) : value;
}

@@ -305,16 +430,15 @@

* This uses the property's `reflect` and `type.toAttribute` property options.
* @nocollapse
*/
private static _propertyValueToAttribute(value: unknown,
options?: PropertyDeclaration) {
if (options === undefined || options.reflect === undefined) {
options: PropertyDeclaration) {
if (options.reflect === undefined) {
return;
}
// Note: special case `Boolean` so users can use it as a `type`.
const type = options.type;
const converter = options.converter;
const toAttribute =
options.type === Boolean
? toBooleanAttribute
: (options.type &&
(options.type as AttributeSerializer).toAttribute ||
String);
return toAttribute(value);
converter && (converter as ComplexAttributeConverter).toAttribute ||
defaultConverter.toAttribute;
return toAttribute!(value, type);
}

@@ -325,2 +449,3 @@

private _updatePromise: Promise<unknown> = microtaskPromise;
private _hasConnectedResolver: (() => void)|undefined = undefined;

@@ -339,8 +464,2 @@ /**

/**
* Node or ShadowRoot into which element DOM should be rendered. Defaults
* to an open shadowRoot.
*/
protected renderRoot?: Element|DocumentFragment;
constructor() {

@@ -352,10 +471,6 @@ super();

/**
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default captures any pre-set values for
* registered properties.
*/
protected initialize() {
this.renderRoot = this.createRenderRoot();
this._saveInstanceProperties();
}
protected initialize() { this._saveInstanceProperties(); }

@@ -376,3 +491,3 @@ /**

for (const [p] of (this.constructor as typeof UpdatingElement)
._classProperties) {
._classProperties!) {
if (this.hasOwnProperty(p)) {

@@ -399,21 +514,11 @@ const value = this[p as keyof this];

/**
* 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.
*/
protected createRenderRoot(): Element|ShadowRoot {
return this.attachShadow({mode : 'open'});
}
/**
* Uses ShadyCSS to keep element DOM updated.
*/
connectedCallback() {
if ((this._updateState & STATE_HAS_UPDATED)) {
if (window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
}
this._updateState = this._updateState | STATE_HAS_CONNECTED;
// Ensure connection triggers an update. Updates cannot complete before
// connection and if one is pending connection the `_hasConnectionResolver`
// will exist. If so, resolve it to complete the update, otherwise
// requestUpdate.
if (this._hasConnectedResolver) {
this._hasConnectedResolver();
this._hasConnectedResolver = undefined;
} else {

@@ -444,23 +549,25 @@ this.requestUpdate();

const ctor = (this.constructor as typeof UpdatingElement);
const attrValue = ctor._propertyValueToAttribute(value, options);
if (attrValue !== undefined) {
const attr = ctor._attributeNameForProperty(name, options);
if (attr !== undefined) {
// Track if the property is being reflected to avoid
// setting the property again via `attributeChangedCallback`. Note:
// 1. this takes advantage of the fact that the callback is synchronous.
// 2. will behave incorrectly if multiple attributes are in the reaction
// stack at time of calling. However, since we process attributes
// in `update` this should not be possible (or an extreme corner case
// that we'd like to discover).
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING;
if (attrValue === null) {
this.removeAttribute(attr);
} else {
this.setAttribute(attr, attrValue);
}
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING;
const attr = ctor._attributeNameForProperty(name, options);
if (attr !== undefined) {
const attrValue = ctor._propertyValueToAttribute(value, options);
// an undefined value does not change the attribute.
if (attrValue === undefined) {
return;
}
// Track if the property is being reflected to avoid
// setting the property again via `attributeChangedCallback`. Note:
// 1. this takes advantage of the fact that the callback is synchronous.
// 2. will behave incorrectly if multiple attributes are in the reaction
// stack at time of calling. However, since we process attributes
// in `update` this should not be possible (or an extreme corner case
// that we'd like to discover).
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING_TO_ATTRIBUTE;
if (attrValue == null) {
this.removeAttribute(attr);
} else {
this.setAttribute(attr, attrValue);
}
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE;
}

@@ -472,11 +579,17 @@ }

// just set from a property setter.
if (!(this._updateState & STATE_IS_REFLECTING)) {
const ctor = (this.constructor as typeof UpdatingElement);
const propName = ctor._attributeToPropertyMap.get(name);
if (propName !== undefined) {
const options = ctor._classProperties.get(propName);
this[propName as keyof this] =
ctor._propertyValueFromAttribute(value, options);
}
if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) {
return;
}
const ctor = (this.constructor as typeof UpdatingElement);
const propName = ctor._attributeToPropertyMap.get(name);
if (propName !== undefined) {
const options =
ctor._classProperties!.get(propName) || defaultPropertyDeclaration;
// mark state reflecting
this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY;
this[propName as keyof this] =
ctor._propertyValueFromAttribute(value, options);
// mark state not reflecting
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
}
}

@@ -498,55 +611,60 @@

requestUpdate(name?: PropertyKey, oldValue?: any) {
if (name !== undefined) {
const options = (this.constructor as typeof UpdatingElement)
._classProperties.get(name) ||
defaultPropertyDeclaration;
return this._requestPropertyUpdate(name, oldValue, options);
let shouldRequestUpdate = true;
// if we have a property key, perform property update steps.
if (name !== undefined && !this._changedProperties.has(name)) {
const ctor = this.constructor as typeof UpdatingElement;
const options =
ctor._classProperties!.get(name) || defaultPropertyDeclaration;
if (ctor._valueHasChanged(this[name as keyof this], oldValue,
options.hasChanged)) {
// track old value when changing.
this._changedProperties.set(name, oldValue);
// add to reflecting properties set
if (options.reflect === true &&
!(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
// abort the request if the property should not be considered changed.
} else {
shouldRequestUpdate = false;
}
}
return this._invalidate();
if (!this._hasRequestedUpdate && shouldRequestUpdate) {
this._enqueueUpdate();
}
return this.updateComplete;
}
/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
* Sets up the element to asynchronously update.
*/
private _requestPropertyUpdate(name: PropertyKey, oldValue: any,
options: PropertyDeclaration) {
if (!(this.constructor as typeof UpdatingElement)
._valueHasChanged(this[name as keyof this], oldValue,
options.hasChanged)) {
return this.updateComplete;
private async _enqueueUpdate() {
// Mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolve: (r: boolean) => void;
const previousUpdatePromise = this._updatePromise;
this._updatePromise = new Promise((res) => resolve = res);
// Ensure any previous update has resolved before updating.
// This `await` also ensures that property changes are batched.
await previousUpdatePromise;
// Make sure the element has connected before updating.
if (!this._hasConnected) {
await new Promise((res) => this._hasConnectedResolver = res);
}
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
// Allow `performUpdate` to be asynchronous to enable scheduling of updates.
const result = this.performUpdate();
// Note, this is to avoid delaying an additional microtask unless we need
// to.
if (result != null &&
typeof (result as PromiseLike<unknown>).then === 'function') {
await result;
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
return this._invalidate();
resolve!(!this._hasRequestedUpdate);
}
/**
* Invalidates the element causing it to asynchronously update regardless
* of whether or not any property changes are pending. This method is
* automatically called when any registered property changes.
*/
private async _invalidate() {
if (!this._hasRequestedUpdate) {
// mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolver: any;
const previousValidatePromise = this._updatePromise;
this._updatePromise = new Promise((r) => resolver = r);
await previousValidatePromise;
this._validate();
resolver!(!this._hasRequestedUpdate);
}
return this.updateComplete;
private get _hasConnected() {
return (this._updateState & STATE_HAS_CONNECTED);
}

@@ -558,6 +676,18 @@

protected get hasUpdated() { return (this._updateState & STATE_HAS_UPDATED); }
/**
* Validates the element by updating it.
* Performs an element update.
*
* You can override this method to change the timing of updates. For instance,
* to schedule updates to occur just before the next frame:
*
* ```
* protected async performUpdate(): Promise<unknown> {
* await new Promise((resolve) => requestAnimationFrame(() => resolve()));
* super.performUpdate();
* }
* ```
*/
private _validate() {
protected performUpdate(): void|Promise<unknown> {
// Mixin instance properties once, if they exist.

@@ -580,2 +710,3 @@ if (this._instanceProperties) {

}
private _markUpdated() {

@@ -613,4 +744,4 @@ this._changedProperties = new Map();

* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's
* `renderRoot`. Setting properties inside this method will *not* trigger
* It can be overridden to render and keep updated element DOM.
* Setting properties inside this method will *not* trigger
* another update.

@@ -617,0 +748,0 @@ *

@@ -21,3 +21,5 @@ /**

export * from './lib/decorators.js';
export {html, svg} from 'lit-html/lit-html';
export {html, svg, TemplateResult, SVGTemplateResult} from 'lit-html/lit-html';
import {supportsAdoptingStyleSheets, CSSResult} from './lib/css-tag.js';
export * from './lib/css-tag.js';

@@ -27,2 +29,8 @@ export class LitElement extends UpdatingElement {

/**
* Ensure this class is marked as `finalized` as an optimization ensuring
* it will not needlessly try to `finalize`.
*/
protected static finalized = true;
/**
* Render method used to render the lit-html TemplateResult to the element's

@@ -33,2 +41,3 @@ * DOM.

* @param {String} Element name.
* @nocollapse
*/

@@ -38,2 +47,106 @@ static render = render;

/**
* Array of styles to apply to the element. The styles should be defined
* using the `css` tag function.
*/
static get styles(): CSSResult[] { return []; }
private static _styles: CSSResult[]|undefined;
private static get _uniqueStyles(): CSSResult[] {
if (this._styles === undefined) {
const styles = this.styles;
// As a performance optimization to avoid duplicated styling that can
// occur especially when composing via subclassing, de-duplicate styles
// preserving the last item in the list. The last item is kept to
// try to preserve cascade order with the assumption that it's most
// important that last added styles override previous styles.
const styleSet = styles.reduceRight((set, s) => {
set.add(s);
// on IE set.add does not return the set.
return set;
}, new Set());
// Array.form does not work on Set in IE
this._styles = [];
styleSet.forEach((v) => this._styles!.unshift(v));
}
return this._styles;
}
private _needsShimAdoptedStyleSheets?: boolean;
/**
* Node or ShadowRoot into which element DOM should be rendered. Defaults
* to an open shadowRoot.
*/
protected renderRoot?: Element|DocumentFragment;
/**
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* registered properties.
*/
protected initialize() {
super.initialize();
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.
*/
protected createRenderRoot(): Element|ShadowRoot {
return this.attachShadow({mode : 'open'});
}
/**
* Applies styling to the element shadowRoot using the `static get 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).
*/
protected adoptStyles() {
const styles = (this.constructor as typeof LitElement)._uniqueStyles;
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 as ShadowRoot).adoptedStyleSheets =
styles.map((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

@@ -52,2 +165,13 @@ * and calls `render` to render DOM via lit-html. Setting properties inside

}
// 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 as typeof LitElement)._uniqueStyles.forEach((s) => {
const style = document.createElement('style');
style.textContent = s.cssText;
this.renderRoot!.appendChild(style);
});
}
}

@@ -54,0 +178,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc