UNPKG

6.42 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 * This code may only be used under the BSD style license found at
5 * http://polymer.github.io/LICENSE.txt
6 * The complete set of authors may be found at
7 * http://polymer.github.io/AUTHORS.txt
8 * The complete set of contributors may be found at
9 * http://polymer.github.io/CONTRIBUTORS.txt
10 * Code distributed by Google as part of the polymer project is also
11 * subject to an additional IP rights grant found at
12 * http://polymer.github.io/PATENTS.txt
13 */
14const legacyCustomElement = (tagName, clazz) => {
15 window.customElements.define(tagName, clazz);
16 // Cast as any because TS doesn't recognize the return type as being a
17 // subtype of the decorated class when clazz is typed as
18 // `Constructor<HTMLElement>` for some reason.
19 // `Constructor<HTMLElement>` is helpful to make sure the decorator is
20 // applied to elements however.
21 return clazz;
22};
23const standardCustomElement = (tagName, descriptor) => {
24 const { kind, elements } = descriptor;
25 return {
26 kind,
27 elements,
28 // This callback is called once the class is otherwise fully defined
29 finisher(clazz) {
30 window.customElements.define(tagName, clazz);
31 }
32 };
33};
34/**
35 * Class decorator factory that defines the decorated class as a custom element.
36 *
37 * @param tagName the name of the custom element to define
38 *
39 * In TypeScript, the `tagName` passed to `customElement` should be a key of the
40 * `HTMLElementTagNameMap` interface. To add your element to the interface,
41 * declare the interface in this module:
42 *
43 * @customElement('my-element')
44 * export class MyElement extends LitElement {}
45 *
46 * declare global {
47 * interface HTMLElementTagNameMap {
48 * 'my-element': MyElement;
49 * }
50 * }
51 *
52 */
53export const customElement = (tagName) => (classOrDescriptor) => (typeof classOrDescriptor === 'function')
54 ? legacyCustomElement(tagName, classOrDescriptor)
55 : standardCustomElement(tagName, classOrDescriptor);
56const standardProperty = (options, element) => {
57 // createProperty() takes care of defining the property, but we still must
58 // return some kind of descriptor, so return a descriptor for an unused
59 // prototype field. The finisher calls createProperty().
60 return {
61 kind: 'field',
62 key: Symbol(),
63 placement: 'own',
64 descriptor: {},
65 // When @babel/plugin-proposal-decorators implements initializers,
66 // do this instead of the initializer below. See:
67 // https://github.com/babel/babel/issues/9260 extras: [
68 // {
69 // kind: 'initializer',
70 // placement: 'own',
71 // initializer: descriptor.initializer,
72 // }
73 // ],
74 initializer() {
75 if (typeof element.initializer === 'function') {
76 this[element.key] = element.initializer.call(this);
77 }
78 },
79 finisher(clazz) {
80 clazz.createProperty(element.key, options);
81 }
82 };
83};
84const legacyProperty = (options, proto, name) => {
85 proto.constructor.createProperty(name, options);
86};
87/**
88 * A property decorator which creates a LitElement property which reflects a
89 * corresponding attribute value. A `PropertyDeclaration` may optionally be
90 * supplied to configure property features.
91 */
92export const property = (options) => (protoOrDescriptor, name) => (name !== undefined)
93 ? legacyProperty(options, protoOrDescriptor, name)
94 : standardProperty(options, protoOrDescriptor);
95/**
96 * A property decorator that converts a class property into a getter that
97 * executes a querySelector on the element's renderRoot.
98 */
99export const query = _query((target, selector) => target.querySelector(selector));
100/**
101 * A property decorator that converts a class property into a getter
102 * that executes a querySelectorAll on the element's renderRoot.
103 */
104export const queryAll = _query((target, selector) => target.querySelectorAll(selector));
105const legacyQuery = (descriptor, proto, name) => { Object.defineProperty(proto, name, descriptor); };
106const standardQuery = (descriptor, element) => ({
107 kind: 'method',
108 placement: 'prototype',
109 key: element.key,
110 descriptor,
111});
112/**
113 * Base-implementation of `@query` and `@queryAll` decorators.
114 *
115 * @param queryFn exectute a `selector` (ie, querySelector or querySelectorAll)
116 * against `target`.
117 */
118function _query(queryFn) {
119 return (selector) => (protoOrDescriptor, name) => {
120 const descriptor = {
121 get() { return queryFn(this.renderRoot, selector); },
122 enumerable: true,
123 configurable: true,
124 };
125 return (name !== undefined)
126 ? legacyQuery(descriptor, protoOrDescriptor, name)
127 : standardQuery(descriptor, protoOrDescriptor);
128 };
129}
130const standardEventOptions = (options, element) => {
131 return Object.assign({}, element, { finisher(clazz) {
132 Object.assign(clazz.prototype[element.key], options);
133 } });
134};
135const legacyEventOptions = (options, proto, name) => { Object.assign(proto[name], options); };
136/**
137 * Adds event listener options to a method used as an event listener in a
138 * lit-html template.
139 *
140 * @param options An object that specifis event listener options as accepted by
141 * `EventTarget#addEventListener` and `EventTarget#removeEventListener`.
142 *
143 * Current browsers support the `capture`, `passive`, and `once` options. See:
144 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters
145 *
146 * @example
147 *
148 * class MyElement {
149 *
150 * clicked = false;
151 *
152 * render() {
153 * return html`<div @click=${this._onClick}`><button></button></div>`;
154 * }
155 *
156 * @eventOptions({capture: true})
157 * _onClick(e) {
158 * this.clicked = true;
159 * }
160 * }
161 */
162export const eventOptions = (options) =>
163// Return value typed as any to prevent TypeScript from complaining that
164// standard decorator function signature does not match TypeScript decorator
165// signature
166// TODO(kschaaf): unclear why it was only failing on this decorator and not
167// the others
168((protoOrDescriptor, name) => (name !== undefined)
169 ? legacyEventOptions(options, protoOrDescriptor, name)
170 : standardEventOptions(options, protoOrDescriptor));
171//# sourceMappingURL=decorators.js.map
\No newline at end of file