|
| 1 | + |
| 2 | +/** |
| 3 | + * @license |
| 4 | + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. |
| 5 | + * This code may only be used under the BSD style license found at |
| 6 | + * http://polymer.github.io/LICENSE.txt |
| 7 | + * The complete set of authors may be found at |
| 8 | + * http://polymer.github.io/AUTHORS.txt |
| 9 | + * The complete set of contributors may be found at |
| 10 | + * http://polymer.github.io/CONTRIBUTORS.txt |
| 11 | + * Code distributed by Google as part of the polymer project is also |
| 12 | + * subject to an additional IP rights grant found at |
| 13 | + * http://polymer.github.io/PATENTS.txt |
| 14 | + */ |
| 15 | + |
| 16 | +import {LitElement} from '../lit-element.js'; |
| 17 | + |
| 18 | +import {PropertyDeclaration, UpdatingElement} from './updating-element.js'; |
| 19 | + |
| 20 | +export type Constructor<T> = { |
| 21 | + new (...args: unknown[]): T |
| 22 | +}; |
| 23 | + |
| 24 | +/** |
| 25 | + * Class decorator factory that defines the decorated class as a custom element. |
| 26 | + * |
| 27 | + * @param tagName the name of the custom element to define |
| 28 | + * |
| 29 | + * In TypeScript, the `tagName` passed to `customElement` must be a key of the |
| 30 | + * `HTMLElementTagNameMap` interface. To add your element to the interface, |
| 31 | + * declare the interface in this module: |
| 32 | + * |
| 33 | + * @customElement('my-element') |
| 34 | + * export class MyElement extends LitElement {} |
| 35 | + * |
| 36 | + * declare global { |
| 37 | + * interface HTMLElementTagNameMap { |
| 38 | + * 'my-element': MyElement; |
| 39 | + * } |
| 40 | + * } |
| 41 | + * |
| 42 | + */ |
| 43 | +export const customElement = (tagName: keyof HTMLElementTagNameMap) => |
| 44 | + (clazz: Constructor<HTMLElement>) => { |
| 45 | + window.customElements.define(tagName, clazz); |
| 46 | + // Cast as any because TS doesn't recognize the return type as being a |
| 47 | + // subtype of the decorated class when clazz is typed as |
| 48 | + // `Constructor<HTMLElement>` for some reason. `Constructor<HTMLElement>` |
| 49 | + // is helpful to make sure the decorator is applied to elements however. |
| 50 | + return clazz as any; |
| 51 | + }; |
| 52 | + |
| 53 | +/** |
| 54 | + * A property decorator which creates a LitElement property which reflects a |
| 55 | + * corresponding attribute value. A `PropertyDeclaration` may optionally be |
| 56 | + * supplied to configure property features. |
| 57 | + */ |
| 58 | +export const property = (options?: PropertyDeclaration) => (proto: Object, |
| 59 | + name: string) => { |
| 60 | + (proto.constructor as typeof UpdatingElement).createProperty(name, options); |
| 61 | +}; |
| 62 | + |
| 63 | +/** |
| 64 | + * A property decorator that converts a class property into a getter that |
| 65 | + * executes a querySelector on the element's renderRoot. |
| 66 | + */ |
| 67 | +export const query = _query((target: NodeSelector, selector: string) => |
| 68 | + target.querySelector(selector)); |
| 69 | + |
| 70 | +/** |
| 71 | + * A property decorator that converts a class property into a getter |
| 72 | + * that executes a querySelectorAll on the element's renderRoot. |
| 73 | + */ |
| 74 | +export const queryAll = _query((target: NodeSelector, selector: string) => |
| 75 | + target.querySelectorAll(selector)); |
| 76 | + |
| 77 | +/** |
| 78 | + * Base-implementation of `@query` and `@queryAll` decorators. |
| 79 | + * |
| 80 | + * @param queryFn exectute a `selector` (ie, querySelector or querySelectorAll) |
| 81 | + * against `target`. |
| 82 | + */ |
| 83 | +function _query<T>(queryFn: (target: NodeSelector, selector: string) => T) { |
| 84 | + return (selector: string) => (proto: any, propName: string) => { |
| 85 | + Object.defineProperty(proto, propName, { |
| 86 | + get(this: LitElement) { return queryFn(this.renderRoot!, selector); }, |
| 87 | + enumerable : true, |
| 88 | + configurable : true, |
| 89 | + }); |
| 90 | + }; |
| 91 | +} |
0 commit comments