UNPKG

@lit/reactive-element

Version:

A simple low level base class for creating fast, lightweight web components

122 lines (119 loc) 4.47 kB
import { desc } from './base.js'; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ let issueWarning; { // Ensure warnings are issued only 1x, even if multiple versions of Lit // are loaded. globalThis.litIssuedWarnings ??= new Set(); /** * Issue a warning if we haven't already, based either on `code` or `warning`. * Warnings are disabled automatically only by `warning`; disabling via `code` * can be done by users. */ issueWarning = (code, warning) => { warning += code ? ` See https://lit.dev/msg/${code} for more information.` : ''; if (!globalThis.litIssuedWarnings.has(warning) && !globalThis.litIssuedWarnings.has(code)) { console.warn(warning); globalThis.litIssuedWarnings.add(warning); } }; } /** * A property decorator that converts a class property into a getter that * executes a querySelector on the element's renderRoot. * * @param selector A DOMString containing one or more selectors to match. * @param cache An optional boolean which when true performs the DOM query only * once and caches the result. * * See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector * * ```ts * class MyElement { * @query('#first') * first: HTMLDivElement; * * render() { * return html` * <div id="first"></div> * <div id="second"></div> * `; * } * } * ``` * @category Decorator */ function query(selector, cache) { return ((protoOrTarget, nameOrContext, descriptor) => { const doQuery = (el) => { const result = (el.renderRoot?.querySelector(selector) ?? null); if (result === null && cache && !el.hasUpdated) { const name = typeof nameOrContext === 'object' ? nameOrContext.name : nameOrContext; issueWarning('', `@query'd field ${JSON.stringify(String(name))} with the 'cache' ` + `flag set for selector '${selector}' has been accessed before ` + `the first update and returned null. This is expected if the ` + `renderRoot tree has not been provided beforehand (e.g. via ` + `Declarative Shadow DOM). Therefore the value hasn't been cached.`); } // TODO: if we want to allow users to assert that the query will never // return null, we need a new option and to throw here if the result // is null. return result; }; if (cache) { // Accessors to wrap from either: // 1. The decorator target, in the case of standard decorators // 2. The property descriptor, in the case of experimental decorators // on auto-accessors. // 3. Functions that access our own cache-key property on the instance, // in the case of experimental decorators on fields. const { get, set } = typeof nameOrContext === 'object' ? protoOrTarget : (descriptor ?? (() => { const key = Symbol(`${String(nameOrContext)} (@query() cache)`) ; return { get() { return this[key]; }, set(v) { this[key] = v; }, }; })()); return desc(protoOrTarget, nameOrContext, { get() { let result = get.call(this); if (result === undefined) { result = doQuery(this); if (result !== null || this.hasUpdated) { set.call(this, result); } } return result; }, }); } else { // This object works as the return type for both standard and // experimental decorators. return desc(protoOrTarget, nameOrContext, { get() { return doQuery(this); }, }); } }); } export { query }; //# sourceMappingURL=query.js.map