UNPKG

lit-element

Version:

A simple base class for creating fast, lightweight web components

197 lines 7.06 kB
/** * @license * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at * http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at * http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ 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. // tslint:disable-next-line:no-any 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); } }; }; /** * Class decorator factory that defines the decorated class as a custom element. * * @param tagName the name of the custom element to define */ export const customElement = (tagName) => (classOrDescriptor) => (typeof classOrDescriptor === 'function') ? legacyCustomElement(tagName, classOrDescriptor) : standardCustomElement(tagName, classOrDescriptor); const standardProperty = (options, element) => { // When decorating an accessor, pass it through and add property metadata. // Note, the `hasOwnProperty` check in `createProperty` ensures we don't // stomp over the user's accessor. if (element.kind === 'method' && element.descriptor && !('value' in element.descriptor)) { return Object.assign({}, element, { finisher(clazz) { clazz.createProperty(element.key, options); } }); } else { // 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); }; /** * A property decorator which creates a LitElement property which reflects a * corresponding attribute value. A `PropertyDeclaration` may optionally be * supplied to configure property features. * * @ExportDecoratedItems */ export function property(options) { // tslint:disable-next-line:no-any decorator return (protoOrDescriptor, name) => (name !== undefined) ? legacyProperty(options, protoOrDescriptor, name) : standardProperty(options, protoOrDescriptor); } /** * A property decorator that converts a class property into a getter that * executes a querySelector on the element's renderRoot. * * @ExportDecoratedItems */ export function query(selector) { return (protoOrDescriptor, // tslint:disable-next-line:no-any decorator name) => { const descriptor = { get() { return this.renderRoot.querySelector(selector); }, enumerable: true, configurable: true, }; return (name !== undefined) ? legacyQuery(descriptor, protoOrDescriptor, name) : standardQuery(descriptor, protoOrDescriptor); }; } /** * A property decorator that converts a class property into a getter * that executes a querySelectorAll on the element's renderRoot. * * @ExportDecoratedItems */ export function queryAll(selector) { return (protoOrDescriptor, // tslint:disable-next-line:no-any decorator name) => { const descriptor = { get() { return this.renderRoot.querySelectorAll(selector); }, enumerable: true, configurable: true, }; return (name !== undefined) ? legacyQuery(descriptor, protoOrDescriptor, name) : standardQuery(descriptor, protoOrDescriptor); }; } const legacyQuery = (descriptor, proto, name) => { Object.defineProperty(proto, name, descriptor); }; const standardQuery = (descriptor, element) => ({ kind: 'method', placement: 'prototype', key: element.key, descriptor, }); const standardEventOptions = (options, element) => { return Object.assign({}, element, { finisher(clazz) { Object.assign(clazz.prototype[element.key], options); } }); }; const legacyEventOptions = // tslint:disable-next-line:no-any legacy decorator (options, proto, name) => { Object.assign(proto[name], options); }; /** * Adds event listener options to a method used as an event listener in a * lit-html template. * * @param options An object that specifis event listener options as accepted by * `EventTarget#addEventListener` and `EventTarget#removeEventListener`. * * Current browsers support the `capture`, `passive`, and `once` options. See: * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters * * @example * * class MyElement { * * clicked = false; * * render() { * return html`<div @click=${this._onClick}`><button></button></div>`; * } * * @eventOptions({capture: true}) * _onClick(e) { * this.clicked = true; * } * } */ 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