123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020 |
- /**
- * @license
- * Copyright 2017 Google LLC
- * SPDX-License-Identifier: BSD-3-Clause
- */
- var _a, _b, _c;
- var _d;
- /**
- * Use this module if you want to create your own base class extending
- * {@link ReactiveElement}.
- * @packageDocumentation
- */
- import { getCompatibleStyle, adoptStyles, } from './css-tag.js';
- export * from './css-tag.js';
- const DEV_MODE = true;
- let requestUpdateThenable;
- let issueWarning;
- const trustedTypes = window
- .trustedTypes;
- // Temporary workaround for https://crbug.com/993268
- // Currently, any attribute starting with "on" is considered to be a
- // TrustedScript source. Such boolean attributes must be set to the equivalent
- // trusted emptyScript value.
- const emptyStringForBooleanAttribute = trustedTypes
- ? trustedTypes.emptyScript
- : '';
- const polyfillSupport = DEV_MODE
- ? window.reactiveElementPolyfillSupportDevMode
- : window.reactiveElementPolyfillSupport;
- if (DEV_MODE) {
- // Ensure warnings are issued only 1x, even if multiple versions of Lit
- // are loaded.
- const issuedWarnings = ((_a = globalThis.litIssuedWarnings) !== null && _a !== void 0 ? _a : (globalThis.litIssuedWarnings = new Set()));
- // Issue a warning, if we haven't already.
- issueWarning = (code, warning) => {
- warning += ` See https://lit.dev/msg/${code} for more information.`;
- if (!issuedWarnings.has(warning)) {
- console.warn(warning);
- issuedWarnings.add(warning);
- }
- };
- issueWarning('dev-mode', `Lit is in dev mode. Not recommended for production!`);
- // Issue polyfill support warning.
- if (((_b = window.ShadyDOM) === null || _b === void 0 ? void 0 : _b.inUse) && polyfillSupport === undefined) {
- issueWarning('polyfill-support-missing', `Shadow DOM is being polyfilled via \`ShadyDOM\` but ` +
- `the \`polyfill-support\` module has not been loaded.`);
- }
- requestUpdateThenable = (name) => ({
- then: (onfulfilled, _onrejected) => {
- issueWarning('request-update-promise', `The \`requestUpdate\` method should no longer return a Promise but ` +
- `does so on \`${name}\`. Use \`updateComplete\` instead.`);
- if (onfulfilled !== undefined) {
- onfulfilled(false);
- }
- },
- });
- }
- /**
- * Useful for visualizing and logging insights into what the Lit template system is doing.
- *
- * Compiled out of prod mode builds.
- */
- const debugLogEvent = DEV_MODE
- ? (event) => {
- const shouldEmit = window
- .emitLitDebugLogEvents;
- if (shouldEmit) {
- window.dispatchEvent(new CustomEvent('lit-debug', {
- detail: event,
- }));
- }
- }
- : undefined;
- /*
- * When using Closure Compiler, JSCompiler_renameProperty(property, object) is
- * replaced at compile time by the munged name for object[property]. We cannot
- * alias this function, so we have to use a small shim that has the same
- * behavior when not compiling.
- */
- /*@__INLINE__*/
- const JSCompiler_renameProperty = (prop, _obj) => prop;
- export const defaultConverter = {
- toAttribute(value, type) {
- switch (type) {
- case Boolean:
- value = value ? emptyStringForBooleanAttribute : null;
- break;
- case Object:
- case Array:
- // if the value is `null` or `undefined` pass this through
- // to allow removing/no change behavior.
- value = value == null ? value : JSON.stringify(value);
- break;
- }
- return value;
- },
- fromAttribute(value, type) {
- let fromValue = value;
- switch (type) {
- case Boolean:
- fromValue = value !== null;
- break;
- case Number:
- fromValue = value === null ? null : Number(value);
- break;
- case Object:
- case Array:
- // Do *not* generate exception when invalid JSON is set as elements
- // don't normally complain on being mis-configured.
- // TODO(sorvell): Do generate exception in *dev mode*.
- try {
- // Assert to adhere to Bazel's "must type assert JSON parse" rule.
- fromValue = JSON.parse(value);
- }
- catch (e) {
- fromValue = null;
- }
- break;
- }
- return fromValue;
- },
- };
- /**
- * Change function that returns true if `value` is different from `oldValue`.
- * This method is used as the default for a property's `hasChanged` function.
- */
- export const notEqual = (value, old) => {
- // This ensures (old==NaN, value==NaN) always returns false
- return old !== value && (old === old || value === value);
- };
- const defaultPropertyDeclaration = {
- attribute: true,
- type: String,
- converter: defaultConverter,
- reflect: false,
- hasChanged: notEqual,
- };
- /**
- * The Closure JS Compiler doesn't currently have good support for static
- * property semantics where "this" is dynamic (e.g.
- * https://github.com/google/closure-compiler/issues/3177 and others) so we use
- * this hack to bypass any rewriting by the compiler.
- */
- const finalized = 'finalized';
- /**
- * Base element class which manages element properties and attributes. When
- * properties change, the `update` method is asynchronously called. This method
- * should be supplied by subclassers to render updates as desired.
- * @noInheritDoc
- */
- export class ReactiveElement extends HTMLElement {
- constructor() {
- super();
- this.__instanceProperties = new Map();
- /**
- * True if there is a pending update as a result of calling `requestUpdate()`.
- * Should only be read.
- * @category updates
- */
- this.isUpdatePending = false;
- /**
- * Is set to `true` after the first update. The element code cannot assume
- * that `renderRoot` exists before the element `hasUpdated`.
- * @category updates
- */
- this.hasUpdated = false;
- /**
- * Name of currently reflecting property
- */
- this.__reflectingProperty = null;
- this._initialize();
- }
- /**
- * Adds an initializer function to the class that is called during instance
- * construction.
- *
- * This is useful for code that runs against a `ReactiveElement`
- * subclass, such as a decorator, that needs to do work for each
- * instance, such as setting up a `ReactiveController`.
- *
- * ```ts
- * const myDecorator = (target: typeof ReactiveElement, key: string) => {
- * target.addInitializer((instance: ReactiveElement) => {
- * // This is run during construction of the element
- * new MyController(instance);
- * });
- * }
- * ```
- *
- * Decorating a field will then cause each instance to run an initializer
- * that adds a controller:
- *
- * ```ts
- * class MyElement extends LitElement {
- * @myDecorator foo;
- * }
- * ```
- *
- * Initializers are stored per-constructor. Adding an initializer to a
- * subclass does not add it to a superclass. Since initializers are run in
- * constructors, initializers will run in order of the class hierarchy,
- * starting with superclasses and progressing to the instance's class.
- *
- * @nocollapse
- */
- static addInitializer(initializer) {
- var _a;
- (_a = this._initializers) !== null && _a !== void 0 ? _a : (this._initializers = []);
- this._initializers.push(initializer);
- }
- /**
- * Returns a list of attributes corresponding to the registered properties.
- * @nocollapse
- * @category attributes
- */
- static get observedAttributes() {
- // note: piggy backing on this to ensure we're finalized.
- this.finalize();
- const attributes = [];
- // Use forEach so this works even if for/of loops are compiled to for loops
- // expecting arrays
- this.elementProperties.forEach((v, p) => {
- const attr = this.__attributeNameForProperty(p, v);
- if (attr !== undefined) {
- this.__attributeToPropertyMap.set(attr, p);
- attributes.push(attr);
- }
- });
- return attributes;
- }
- /**
- * Creates a property accessor on the element prototype if one does not exist
- * and stores a {@linkcode PropertyDeclaration} for the property with the
- * given options. The property setter calls the property's `hasChanged`
- * property option or uses a strict identity check to determine whether or not
- * to request an update.
- *
- * This method may be overridden to customize properties; however,
- * when doing so, it's important to call `super.createProperty` to ensure
- * the property is setup correctly. This method calls
- * `getPropertyDescriptor` internally to get a descriptor to install.
- * To customize what properties do when they are get or set, override
- * `getPropertyDescriptor`. To customize the options for a property,
- * implement `createProperty` like this:
- *
- * ```ts
- * static createProperty(name, options) {
- * options = Object.assign(options, {myOption: true});
- * super.createProperty(name, options);
- * }
- * ```
- *
- * @nocollapse
- * @category properties
- */
- static createProperty(name, options = defaultPropertyDeclaration) {
- var _a;
- // if this is a state property, force the attribute to false.
- if (options.state) {
- // Cast as any since this is readonly.
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- options.attribute = false;
- }
- // Note, since this can be called by the `@property` decorator which
- // is called before `finalize`, we ensure finalization has been kicked off.
- this.finalize();
- this.elementProperties.set(name, options);
- // Do not generate an accessor if the prototype already has one, since
- // it would be lost otherwise and that would never be the user's intention;
- // Instead, we expect users to call `requestUpdate` themselves from
- // user-defined accessors. Note that if the super has an accessor we will
- // still overwrite it
- if (!options.noAccessor && !this.prototype.hasOwnProperty(name)) {
- const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
- const descriptor = this.getPropertyDescriptor(name, key, options);
- if (descriptor !== undefined) {
- Object.defineProperty(this.prototype, name, descriptor);
- if (DEV_MODE) {
- // If this class doesn't have its own set, create one and initialize
- // with the values in the set from the nearest ancestor class, if any.
- if (!this.hasOwnProperty('__reactivePropertyKeys')) {
- this.__reactivePropertyKeys = new Set((_a = this.__reactivePropertyKeys) !== null && _a !== void 0 ? _a : []);
- }
- this.__reactivePropertyKeys.add(name);
- }
- }
- }
- }
- /**
- * Returns a property descriptor to be defined on the given named property.
- * If no descriptor is returned, the property will not become an accessor.
- * For example,
- *
- * ```ts
- * class MyElement extends LitElement {
- * static getPropertyDescriptor(name, key, options) {
- * const defaultDescriptor =
- * super.getPropertyDescriptor(name, key, options);
- * const setter = defaultDescriptor.set;
- * return {
- * get: defaultDescriptor.get,
- * set(value) {
- * setter.call(this, value);
- * // custom action.
- * },
- * configurable: true,
- * enumerable: true
- * }
- * }
- * }
- * ```
- *
- * @nocollapse
- * @category properties
- */
- static getPropertyDescriptor(name, key, options) {
- return {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- get() {
- return this[key];
- },
- set(value) {
- const oldValue = this[name];
- this[key] = value;
- this.requestUpdate(name, oldValue, options);
- },
- configurable: true,
- enumerable: true,
- };
- }
- /**
- * Returns the property options associated with the given property.
- * These options are defined with a `PropertyDeclaration` via the `properties`
- * object or the `@property` decorator and are registered in
- * `createProperty(...)`.
- *
- * Note, this method should be considered "final" and not overridden. To
- * customize the options for a given property, override
- * {@linkcode createProperty}.
- *
- * @nocollapse
- * @final
- * @category properties
- */
- static getPropertyOptions(name) {
- return this.elementProperties.get(name) || defaultPropertyDeclaration;
- }
- /**
- * Creates property accessors for registered properties, sets up element
- * styling, and ensures any superclasses are also finalized. Returns true if
- * the element was finalized.
- * @nocollapse
- */
- static finalize() {
- if (this.hasOwnProperty(finalized)) {
- return false;
- }
- this[finalized] = true;
- // finalize any superclasses
- const superCtor = Object.getPrototypeOf(this);
- superCtor.finalize();
- this.elementProperties = new Map(superCtor.elementProperties);
- // initialize Map populated in observedAttributes
- this.__attributeToPropertyMap = new Map();
- // make any properties
- // Note, only process "own" properties since this element will inherit
- // any properties defined on the superClass, and finalization ensures
- // the entire prototype chain is finalized.
- if (this.hasOwnProperty(JSCompiler_renameProperty('properties', this))) {
- const props = this.properties;
- // support symbols in properties (IE11 does not support this)
- const propKeys = [
- ...Object.getOwnPropertyNames(props),
- ...Object.getOwnPropertySymbols(props),
- ];
- // This for/of is ok because propKeys is an array
- for (const p of propKeys) {
- // note, use of `any` is due to TypeScript lack of support for symbol in
- // index types
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- this.createProperty(p, props[p]);
- }
- }
- this.elementStyles = this.finalizeStyles(this.styles);
- // DEV mode warnings
- if (DEV_MODE) {
- const warnRemovedOrRenamed = (name, renamed = false) => {
- if (this.prototype.hasOwnProperty(name)) {
- issueWarning(renamed ? 'renamed-api' : 'removed-api', `\`${name}\` is implemented on class ${this.name}. It ` +
- `has been ${renamed ? 'renamed' : 'removed'} ` +
- `in this version of LitElement.`);
- }
- };
- warnRemovedOrRenamed('initialize');
- warnRemovedOrRenamed('requestUpdateInternal');
- warnRemovedOrRenamed('_getUpdateComplete', true);
- }
- return true;
- }
- /**
- * Takes the styles the user supplied via the `static styles` property and
- * returns the array of styles to apply to the element.
- * Override this method to integrate into a style management system.
- *
- * Styles are deduplicated preserving the _last_ instance in the list. This
- * is a performance optimization to avoid duplicated styles that can occur
- * especially when composing via subclassing. The last item is kept to try
- * to preserve the cascade order with the assumption that it's most important
- * that last added styles override previous styles.
- *
- * @nocollapse
- * @category styles
- */
- static finalizeStyles(styles) {
- const elementStyles = [];
- if (Array.isArray(styles)) {
- // Dedupe the flattened array in reverse order to preserve the last items.
- // Casting to Array<unknown> works around TS error that
- // appears to come from trying to flatten a type CSSResultArray.
- const set = new Set(styles.flat(Infinity).reverse());
- // Then preserve original order by adding the set items in reverse order.
- for (const s of set) {
- elementStyles.unshift(getCompatibleStyle(s));
- }
- }
- else if (styles !== undefined) {
- elementStyles.push(getCompatibleStyle(styles));
- }
- return elementStyles;
- }
- /**
- * Returns the property name for the given attribute `name`.
- * @nocollapse
- */
- static __attributeNameForProperty(name, options) {
- const attribute = options.attribute;
- return attribute === false
- ? undefined
- : typeof attribute === 'string'
- ? attribute
- : typeof name === 'string'
- ? name.toLowerCase()
- : undefined;
- }
- /**
- * Internal only override point for customizing work done when elements
- * are constructed.
- *
- * @internal
- */
- _initialize() {
- var _a;
- this.__updatePromise = new Promise((res) => (this.enableUpdating = res));
- this._$changedProperties = new Map();
- this.__saveInstanceProperties();
- // ensures first update will be caught by an early access of
- // `updateComplete`
- this.requestUpdate();
- (_a = this.constructor._initializers) === null || _a === void 0 ? void 0 : _a.forEach((i) => i(this));
- }
- /**
- * Registers a `ReactiveController` to participate in the element's reactive
- * update cycle. The element automatically calls into any registered
- * controllers during its lifecycle callbacks.
- *
- * If the element is connected when `addController()` is called, the
- * controller's `hostConnected()` callback will be immediately called.
- * @category controllers
- */
- addController(controller) {
- var _a, _b;
- ((_a = this.__controllers) !== null && _a !== void 0 ? _a : (this.__controllers = [])).push(controller);
- // If a controller is added after the element has been connected,
- // call hostConnected. Note, re-using existence of `renderRoot` here
- // (which is set in connectedCallback) to avoid the need to track a
- // first connected state.
- if (this.renderRoot !== undefined && this.isConnected) {
- (_b = controller.hostConnected) === null || _b === void 0 ? void 0 : _b.call(controller);
- }
- }
- /**
- * Removes a `ReactiveController` from the element.
- * @category controllers
- */
- removeController(controller) {
- var _a;
- // Note, if the indexOf is -1, the >>> will flip the sign which makes the
- // splice do nothing.
- (_a = this.__controllers) === null || _a === void 0 ? void 0 : _a.splice(this.__controllers.indexOf(controller) >>> 0, 1);
- }
- /**
- * Fixes any properties set on the instance before upgrade time.
- * Otherwise these would shadow the accessor and break these properties.
- * The properties are stored in a Map which is played back after the
- * constructor runs. Note, on very old versions of Safari (<=9) or Chrome
- * (<=41), properties created for native platform properties like (`id` or
- * `name`) may not have default values set in the element constructor. On
- * these browsers native properties appear on instances and therefore their
- * default value will overwrite any element default (e.g. if the element sets
- * this.id = 'id' in the constructor, the 'id' will become '' since this is
- * the native platform default).
- */
- __saveInstanceProperties() {
- // Use forEach so this works even if for/of loops are compiled to for loops
- // expecting arrays
- this.constructor.elementProperties.forEach((_v, p) => {
- if (this.hasOwnProperty(p)) {
- this.__instanceProperties.set(p, this[p]);
- delete this[p];
- }
- });
- }
- /**
- * Returns the node into which the element should render and by default
- * creates and returns an open shadowRoot. Implement to customize where the
- * element's DOM is rendered. For example, to render into the element's
- * childNodes, return `this`.
- *
- * @return Returns a node into which to render.
- * @category rendering
- */
- createRenderRoot() {
- var _a;
- const renderRoot = (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this.attachShadow(this.constructor.shadowRootOptions);
- adoptStyles(renderRoot, this.constructor.elementStyles);
- return renderRoot;
- }
- /**
- * On first connection, creates the element's renderRoot, sets up
- * element styling, and enables updating.
- * @category lifecycle
- */
- connectedCallback() {
- var _a;
- // create renderRoot before first update.
- if (this.renderRoot === undefined) {
- this.renderRoot = this.createRenderRoot();
- }
- this.enableUpdating(true);
- (_a = this.__controllers) === null || _a === void 0 ? void 0 : _a.forEach((c) => { var _a; return (_a = c.hostConnected) === null || _a === void 0 ? void 0 : _a.call(c); });
- }
- /**
- * Note, this method should be considered final and not overridden. It is
- * overridden on the element instance with a function that triggers the first
- * update.
- * @category updates
- */
- enableUpdating(_requestedUpdate) { }
- /**
- * Allows for `super.disconnectedCallback()` in extensions while
- * reserving the possibility of making non-breaking feature additions
- * when disconnecting at some point in the future.
- * @category lifecycle
- */
- disconnectedCallback() {
- var _a;
- (_a = this.__controllers) === null || _a === void 0 ? void 0 : _a.forEach((c) => { var _a; return (_a = c.hostDisconnected) === null || _a === void 0 ? void 0 : _a.call(c); });
- }
- /**
- * Synchronizes property values when attributes change.
- *
- * Specifically, when an attribute is set, the corresponding property is set.
- * You should rarely need to implement this callback. If this method is
- * overridden, `super.attributeChangedCallback(name, _old, value)` must be
- * called.
- *
- * See [using the lifecycle callbacks](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks)
- * on MDN for more information about the `attributeChangedCallback`.
- * @category attributes
- */
- attributeChangedCallback(name, _old, value) {
- this._$attributeToProperty(name, value);
- }
- __propertyToAttribute(name, value, options = defaultPropertyDeclaration) {
- var _a, _b;
- const attr = this.constructor.__attributeNameForProperty(name, options);
- if (attr !== undefined && options.reflect === true) {
- const toAttribute = (_b = (_a = options.converter) === null || _a === void 0 ? void 0 : _a.toAttribute) !== null && _b !== void 0 ? _b : defaultConverter.toAttribute;
- const attrValue = toAttribute(value, options.type);
- if (DEV_MODE &&
- this.constructor.enabledWarnings.indexOf('migration') >= 0 &&
- attrValue === undefined) {
- issueWarning('undefined-attribute-value', `The attribute value for the ${name} property is ` +
- `undefined on element ${this.localName}. The attribute will be ` +
- `removed, but in the previous version of \`ReactiveElement\`, ` +
- `the attribute would not have changed.`);
- }
- // Track if the property is being reflected to avoid
- // setting the property again via `attributeChangedCallback`. Note:
- // 1. this takes advantage of the fact that the callback is synchronous.
- // 2. will behave incorrectly if multiple attributes are in the reaction
- // stack at time of calling. However, since we process attributes
- // in `update` this should not be possible (or an extreme corner case
- // that we'd like to discover).
- // mark state reflecting
- this.__reflectingProperty = name;
- if (attrValue == null) {
- this.removeAttribute(attr);
- }
- else {
- this.setAttribute(attr, attrValue);
- }
- // mark state not reflecting
- this.__reflectingProperty = null;
- }
- }
- /** @internal */
- _$attributeToProperty(name, value) {
- var _a, _b, _c;
- const ctor = this.constructor;
- // Note, hint this as an `AttributeMap` so closure clearly understands
- // the type; it has issues with tracking types through statics
- const propName = ctor.__attributeToPropertyMap.get(name);
- // Use tracking info to avoid reflecting a property value to an attribute
- // if it was just set because the attribute changed.
- if (propName !== undefined && this.__reflectingProperty !== propName) {
- const options = ctor.getPropertyOptions(propName);
- const converter = options.converter;
- const fromAttribute = (_c = (_b = (_a = converter) === null || _a === void 0 ? void 0 : _a.fromAttribute) !== null && _b !== void 0 ? _b : (typeof converter === 'function'
- ? converter
- : null)) !== null && _c !== void 0 ? _c : defaultConverter.fromAttribute;
- // mark state reflecting
- this.__reflectingProperty = propName;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- this[propName] = fromAttribute(value, options.type);
- // mark state not reflecting
- this.__reflectingProperty = null;
- }
- }
- /**
- * Requests an update which is processed asynchronously. This should be called
- * when an element should update based on some state not triggered by setting
- * a reactive property. In this case, pass no arguments. It should also be
- * called when manually implementing a property setter. In this case, pass the
- * property `name` and `oldValue` to ensure that any configured property
- * options are honored.
- *
- * @param name name of requesting property
- * @param oldValue old value of requesting property
- * @param options property options to use instead of the previously
- * configured options
- * @category updates
- */
- requestUpdate(name, oldValue, options) {
- let shouldRequestUpdate = true;
- // If we have a property key, perform property update steps.
- if (name !== undefined) {
- options =
- options ||
- this.constructor.getPropertyOptions(name);
- const hasChanged = options.hasChanged || notEqual;
- if (hasChanged(this[name], oldValue)) {
- if (!this._$changedProperties.has(name)) {
- this._$changedProperties.set(name, oldValue);
- }
- // Add to reflecting properties set.
- // Note, it's important that every change has a chance to add the
- // property to `_reflectingProperties`. This ensures setting
- // attribute + property reflects correctly.
- if (options.reflect === true && this.__reflectingProperty !== name) {
- if (this.__reflectingProperties === undefined) {
- this.__reflectingProperties = new Map();
- }
- this.__reflectingProperties.set(name, options);
- }
- }
- else {
- // Abort the request if the property should not be considered changed.
- shouldRequestUpdate = false;
- }
- }
- if (!this.isUpdatePending && shouldRequestUpdate) {
- this.__updatePromise = this.__enqueueUpdate();
- }
- // Note, since this no longer returns a promise, in dev mode we return a
- // thenable which warns if it's called.
- return DEV_MODE
- ? requestUpdateThenable(this.localName)
- : undefined;
- }
- /**
- * Sets up the element to asynchronously update.
- */
- async __enqueueUpdate() {
- this.isUpdatePending = true;
- try {
- // Ensure any previous update has resolved before updating.
- // This `await` also ensures that property changes are batched.
- await this.__updatePromise;
- }
- catch (e) {
- // Refire any previous errors async so they do not disrupt the update
- // cycle. Errors are refired so developers have a chance to observe
- // them, and this can be done by implementing
- // `window.onunhandledrejection`.
- Promise.reject(e);
- }
- const result = this.scheduleUpdate();
- // If `scheduleUpdate` returns a Promise, we await it. This is done to
- // enable coordinating updates with a scheduler. Note, the result is
- // checked to avoid delaying an additional microtask unless we need to.
- if (result != null) {
- await result;
- }
- return !this.isUpdatePending;
- }
- /**
- * Schedules an element update. You can override this method to change the
- * timing of updates by returning a Promise. The update will await the
- * returned Promise, and you should resolve the Promise to allow the update
- * to proceed. If this method is overridden, `super.scheduleUpdate()`
- * must be called.
- *
- * For instance, to schedule updates to occur just before the next frame:
- *
- * ```ts
- * override protected async scheduleUpdate(): Promise<unknown> {
- * await new Promise((resolve) => requestAnimationFrame(() => resolve()));
- * super.scheduleUpdate();
- * }
- * ```
- * @category updates
- */
- scheduleUpdate() {
- return this.performUpdate();
- }
- /**
- * Performs an element update. Note, if an exception is thrown during the
- * update, `firstUpdated` and `updated` will not be called.
- *
- * Call `performUpdate()` to immediately process a pending update. This should
- * generally not be needed, but it can be done in rare cases when you need to
- * update synchronously.
- *
- * Note: To ensure `performUpdate()` synchronously completes a pending update,
- * it should not be overridden. In LitElement 2.x it was suggested to override
- * `performUpdate()` to also customizing update scheduling. Instead, you should now
- * override `scheduleUpdate()`. For backwards compatibility with LitElement 2.x,
- * scheduling updates via `performUpdate()` continues to work, but will make
- * also calling `performUpdate()` to synchronously process updates difficult.
- *
- * @category updates
- */
- performUpdate() {
- var _a, _b;
- // Abort any update if one is not pending when this is called.
- // This can happen if `performUpdate` is called early to "flush"
- // the update.
- if (!this.isUpdatePending) {
- return;
- }
- debugLogEvent === null || debugLogEvent === void 0 ? void 0 : debugLogEvent({ kind: 'update' });
- // create renderRoot before first update.
- if (!this.hasUpdated) {
- // Produce warning if any class properties are shadowed by class fields
- if (DEV_MODE) {
- const shadowedProperties = [];
- (_a = this.constructor.__reactivePropertyKeys) === null || _a === void 0 ? void 0 : _a.forEach((p) => {
- var _a;
- if (this.hasOwnProperty(p) && !((_a = this.__instanceProperties) === null || _a === void 0 ? void 0 : _a.has(p))) {
- shadowedProperties.push(p);
- }
- });
- if (shadowedProperties.length) {
- throw new Error(`The following properties on element ${this.localName} will not ` +
- `trigger updates as expected because they are set using class ` +
- `fields: ${shadowedProperties.join(', ')}. ` +
- `Native class fields and some compiled output will overwrite ` +
- `accessors used for detecting changes. See ` +
- `https://lit.dev/msg/class-field-shadowing ` +
- `for more information.`);
- }
- }
- }
- // Mixin instance properties once, if they exist.
- if (this.__instanceProperties) {
- // Use forEach so this works even if for/of loops are compiled to for loops
- // expecting arrays
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- this.__instanceProperties.forEach((v, p) => (this[p] = v));
- this.__instanceProperties = undefined;
- }
- let shouldUpdate = false;
- const changedProperties = this._$changedProperties;
- try {
- shouldUpdate = this.shouldUpdate(changedProperties);
- if (shouldUpdate) {
- this.willUpdate(changedProperties);
- (_b = this.__controllers) === null || _b === void 0 ? void 0 : _b.forEach((c) => { var _a; return (_a = c.hostUpdate) === null || _a === void 0 ? void 0 : _a.call(c); });
- this.update(changedProperties);
- }
- else {
- this.__markUpdated();
- }
- }
- catch (e) {
- // Prevent `firstUpdated` and `updated` from running when there's an
- // update exception.
- shouldUpdate = false;
- // Ensure element can accept additional updates after an exception.
- this.__markUpdated();
- throw e;
- }
- // The update is no longer considered pending and further updates are now allowed.
- if (shouldUpdate) {
- this._$didUpdate(changedProperties);
- }
- }
- /**
- * Invoked before `update()` to compute values needed during the update.
- *
- * Implement `willUpdate` to compute property values that depend on other
- * properties and are used in the rest of the update process.
- *
- * ```ts
- * willUpdate(changedProperties) {
- * // only need to check changed properties for an expensive computation.
- * if (changedProperties.has('firstName') || changedProperties.has('lastName')) {
- * this.sha = computeSHA(`${this.firstName} ${this.lastName}`);
- * }
- * }
- *
- * render() {
- * return html`SHA: ${this.sha}`;
- * }
- * ```
- *
- * @category updates
- */
- willUpdate(_changedProperties) { }
- // Note, this is an override point for polyfill-support.
- // @internal
- _$didUpdate(changedProperties) {
- var _a;
- (_a = this.__controllers) === null || _a === void 0 ? void 0 : _a.forEach((c) => { var _a; return (_a = c.hostUpdated) === null || _a === void 0 ? void 0 : _a.call(c); });
- if (!this.hasUpdated) {
- this.hasUpdated = true;
- this.firstUpdated(changedProperties);
- }
- this.updated(changedProperties);
- if (DEV_MODE &&
- this.isUpdatePending &&
- this.constructor.enabledWarnings.indexOf('change-in-update') >= 0) {
- issueWarning('change-in-update', `Element ${this.localName} scheduled an update ` +
- `(generally because a property was set) ` +
- `after an update completed, causing a new update to be scheduled. ` +
- `This is inefficient and should be avoided unless the next update ` +
- `can only be scheduled as a side effect of the previous update.`);
- }
- }
- __markUpdated() {
- this._$changedProperties = new Map();
- this.isUpdatePending = false;
- }
- /**
- * Returns a Promise that resolves when the element has completed updating.
- * The Promise value is a boolean that is `true` if the element completed the
- * update without triggering another update. The Promise result is `false` if
- * a property was set inside `updated()`. If the Promise is rejected, an
- * exception was thrown during the update.
- *
- * To await additional asynchronous work, override the `getUpdateComplete`
- * method. For example, it is sometimes useful to await a rendered element
- * before fulfilling this Promise. To do this, first await
- * `super.getUpdateComplete()`, then any subsequent state.
- *
- * @return A promise of a boolean that resolves to true if the update completed
- * without triggering another update.
- * @category updates
- */
- get updateComplete() {
- return this.getUpdateComplete();
- }
- /**
- * Override point for the `updateComplete` promise.
- *
- * It is not safe to override the `updateComplete` getter directly due to a
- * limitation in TypeScript which means it is not possible to call a
- * superclass getter (e.g. `super.updateComplete.then(...)`) when the target
- * language is ES5 (https://github.com/microsoft/TypeScript/issues/338).
- * This method should be overridden instead. For example:
- *
- * ```ts
- * class MyElement extends LitElement {
- * override async getUpdateComplete() {
- * const result = await super.getUpdateComplete();
- * await this._myChild.updateComplete;
- * return result;
- * }
- * }
- * ```
- *
- * @return A promise of a boolean that resolves to true if the update completed
- * without triggering another update.
- * @category updates
- */
- getUpdateComplete() {
- return this.__updatePromise;
- }
- /**
- * Controls whether or not `update()` should be called when the element requests
- * an update. By default, this method always returns `true`, but this can be
- * customized to control when to update.
- *
- * @param _changedProperties Map of changed properties with old values
- * @category updates
- */
- shouldUpdate(_changedProperties) {
- return true;
- }
- /**
- * Updates the element. This method reflects property values to attributes.
- * It can be overridden to render and keep updated element DOM.
- * Setting properties inside this method will *not* trigger
- * another update.
- *
- * @param _changedProperties Map of changed properties with old values
- * @category updates
- */
- update(_changedProperties) {
- if (this.__reflectingProperties !== undefined) {
- // Use forEach so this works even if for/of loops are compiled to for
- // loops expecting arrays
- this.__reflectingProperties.forEach((v, k) => this.__propertyToAttribute(k, this[k], v));
- this.__reflectingProperties = undefined;
- }
- this.__markUpdated();
- }
- /**
- * Invoked whenever the element is updated. Implement to perform
- * post-updating tasks via DOM APIs, for example, focusing an element.
- *
- * Setting properties inside this method will trigger the element to update
- * again after this update cycle completes.
- *
- * @param _changedProperties Map of changed properties with old values
- * @category updates
- */
- updated(_changedProperties) { }
- /**
- * Invoked when the element is first updated. Implement to perform one time
- * work on the element after update.
- *
- * ```ts
- * firstUpdated() {
- * this.renderRoot.getElementById('my-text-area').focus();
- * }
- * ```
- *
- * Setting properties inside this method will trigger the element to update
- * again after this update cycle completes.
- *
- * @param _changedProperties Map of changed properties with old values
- * @category updates
- */
- firstUpdated(_changedProperties) { }
- }
- _d = finalized;
- /**
- * Marks class as having finished creating properties.
- */
- ReactiveElement[_d] = true;
- /**
- * Memoized list of all element properties, including any superclass properties.
- * Created lazily on user subclasses when finalizing the class.
- * @nocollapse
- * @category properties
- */
- ReactiveElement.elementProperties = new Map();
- /**
- * Memoized list of all element styles.
- * Created lazily on user subclasses when finalizing the class.
- * @nocollapse
- * @category styles
- */
- ReactiveElement.elementStyles = [];
- /**
- * Options used when calling `attachShadow`. Set this property to customize
- * the options for the shadowRoot; for example, to create a closed
- * shadowRoot: `{mode: 'closed'}`.
- *
- * Note, these options are used in `createRenderRoot`. If this method
- * is customized, options should be respected if possible.
- * @nocollapse
- * @category rendering
- */
- ReactiveElement.shadowRootOptions = { mode: 'open' };
- // Apply polyfills if available
- polyfillSupport === null || polyfillSupport === void 0 ? void 0 : polyfillSupport({ ReactiveElement });
- // Dev mode warnings...
- if (DEV_MODE) {
- // Default warning set.
- ReactiveElement.enabledWarnings = ['change-in-update'];
- const ensureOwnWarnings = function (ctor) {
- if (!ctor.hasOwnProperty(JSCompiler_renameProperty('enabledWarnings', ctor))) {
- ctor.enabledWarnings = ctor.enabledWarnings.slice();
- }
- };
- ReactiveElement.enableWarning = function (warning) {
- ensureOwnWarnings(this);
- if (this.enabledWarnings.indexOf(warning) < 0) {
- this.enabledWarnings.push(warning);
- }
- };
- ReactiveElement.disableWarning = function (warning) {
- ensureOwnWarnings(this);
- const i = this.enabledWarnings.indexOf(warning);
- if (i >= 0) {
- this.enabledWarnings.splice(i, 1);
- }
- };
- }
- // IMPORTANT: do not change the property name or the assignment expression.
- // This line will be used in regexes to search for ReactiveElement usage.
- ((_c = globalThis.reactiveElementVersions) !== null && _c !== void 0 ? _c : (globalThis.reactiveElementVersions = [])).push('1.3.0');
- if (DEV_MODE && globalThis.reactiveElementVersions.length > 1) {
- issueWarning('multiple-versions', `Multiple versions of Lit loaded. Loading multiple versions ` +
- `is not recommended.`);
- }
- //# sourceMappingURL=reactive-element.js.map
|