UNPKG

@polymer/lit-element

Version:

Polymer based lit-html custom element

171 lines 6.42 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. 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 * * In TypeScript, the `tagName` passed to `customElement` should be a key of the * `HTMLElementTagNameMap` interface. To add your element to the interface, * declare the interface in this module: * * @customElement('my-element') * export class MyElement extends LitElement {} * * declare global { * interface HTMLElementTagNameMap { * 'my-element': MyElement; * } * } * */ 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); }; /** * A property decorator which creates a LitElement property which reflects a * corresponding attribute value. A `PropertyDeclaration` may optionally be * supplied to configure property features. */ export const property = (options) => (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. */ export const query = _query((target, selector) => target.querySelector(selector)); /** * A property decorator that converts a class property into a getter * that executes a querySelectorAll on the element's renderRoot. */ 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, }); /** * Base-implementation of `@query` and `@queryAll` decorators. * * @param queryFn exectute a `selector` (ie, querySelector or querySelectorAll) * against `target`. */ function _query(queryFn) { 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); }; /** * 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