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 | */
|
14 | const 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 | };
|
23 | const 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 | */
|
53 | export const customElement = (tagName) => (classOrDescriptor) => (typeof classOrDescriptor === 'function')
|
54 | ? legacyCustomElement(tagName, classOrDescriptor)
|
55 | : standardCustomElement(tagName, classOrDescriptor);
|
56 | const 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 | };
|
84 | const 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 | */
|
92 | export 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 | */
|
99 | export 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 | */
|
104 | export const queryAll = _query((target, selector) => target.querySelectorAll(selector));
|
105 | const legacyQuery = (descriptor, proto, name) => { Object.defineProperty(proto, name, descriptor); };
|
106 | const 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 | */
|
118 | function _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 | }
|
130 | const standardEventOptions = (options, element) => {
|
131 | return Object.assign({}, element, { finisher(clazz) {
|
132 | Object.assign(clazz.prototype[element.key], options);
|
133 | } });
|
134 | };
|
135 | const 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 | */
|
162 | export 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 |