UNPKG

@zywave/zui-bundle

Version:

ZUI, out of the box, provides ES modules with bare path modifiers (e.g. `import '@zywave/zui-foo-bar'`). This is great as that's the way browsers are _going_, but they aren't there quite yet. Tooling exists to help solve this problem like webpack or rollu

1,282 lines (1,279 loc) 132 kB
/* eslint-disable prefer-rest-params */ const proxiedMethods = []; /** * Because we cannot observe push state routing coming from external frameworks (e.g. React's router), we need to * proxy pushState/replaceState invocation to emit events to be observed by us to properly track component utilization * in client-side routed apps. */ function configureHistory() { const emitHistoryEvent = function (type, eventName) { if (proxiedMethods.includes(type)) { return window.history[type]; } const orig = window.history[type]; proxiedMethods.push(type); return function () { const rv = orig.apply(this, arguments); const e = new CustomEvent(eventName ?? type, { detail: { args: arguments } }); window.dispatchEvent(e); return rv; }; }; window.history.pushState = emitHistoryEvent('pushState', 'zuiPushState'); window.history.replaceState = emitHistoryEvent('replaceState', 'zuiReplaceState'); } class UtilizationTracker extends EventTarget { static #instance; static get instance() { return this.#instance ?? (this.#instance = new this()); } #utilization; constructor() { super(); this.#utilization = new Map(); configureHistory(); window.addEventListener('zuiPushState', () => this.#reset()); window.addEventListener('zuiReplaceState', () => this.#reset()); } track(reportType, componentName) { let report = this.#utilization.get(reportType); if (!report) { report = new Map(); this.#utilization.set(reportType, report); } const count = report.get(componentName) ?? 0; report.set(componentName, count + 1); } untrack(reportType, componentName) { const report = this.#utilization.get(reportType); if (!report) { return; } const count = report.get(componentName) ?? 0; if (count > 1) { report.set(componentName, count - 1); } else { report.delete(componentName); } } getReport(reportType) { const report = this.#utilization.get(reportType); return report; } #reset() { for (const key of this.#utilization.keys()) { this.#utilization.set(key, new Map()); } this.dispatchEvent(new CustomEvent('reset')); } } const instance = UtilizationTracker.instance; /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const NODE_MODE = false; // Allows minifiers to rename references to globalThis const global$2 = globalThis; /** * Whether the current browser supports `adoptedStyleSheets`. */ const supportsAdoptingStyleSheets = global$2.ShadowRoot && (global$2.ShadyCSS === undefined || global$2.ShadyCSS.nativeShadow) && 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype; const constructionToken = Symbol(); const cssTagCache = new WeakMap(); /** * A container for a string of CSS text, that may be used to create a CSSStyleSheet. * * CSSResult is the return value of `css`-tagged template literals and * `unsafeCSS()`. In order to ensure that CSSResults are only created via the * `css` tag and `unsafeCSS()`, CSSResult cannot be constructed directly. */ class CSSResult { constructor(cssText, strings, safeToken) { // This property needs to remain unminified. this['_$cssResult$'] = true; if (safeToken !== constructionToken) { throw new Error('CSSResult is not constructable. Use `unsafeCSS` or `css` instead.'); } this.cssText = cssText; this._strings = strings; } // This is a getter so that it's lazy. In practice, this means stylesheets // are not created until the first element instance is made. get styleSheet() { // If `supportsAdoptingStyleSheets` is true then we assume CSSStyleSheet is // constructable. let styleSheet = this._styleSheet; const strings = this._strings; if (supportsAdoptingStyleSheets && styleSheet === undefined) { const cacheable = strings !== undefined && strings.length === 1; if (cacheable) { styleSheet = cssTagCache.get(strings); } if (styleSheet === undefined) { (this._styleSheet = styleSheet = new CSSStyleSheet()).replaceSync(this.cssText); if (cacheable) { cssTagCache.set(strings, styleSheet); } } } return styleSheet; } toString() { return this.cssText; } } const textFromCSSResult = value => { // This property needs to remain unminified. if (value['_$cssResult$'] === true) { return value.cssText; } else if (typeof value === 'number') { return value; } else { throw new Error(`Value passed to 'css' function must be a 'css' function result: ` + `${value}. Use 'unsafeCSS' to pass non-literal values, but take care ` + `to ensure page security.`); } }; /** * Wrap a value for interpolation in a {@linkcode css} tagged template literal. * * This is unsafe because untrusted CSS text can be used to phone home * or exfiltrate data to an attacker controlled site. Take care to only use * this with trusted input. */ const unsafeCSS = value => new CSSResult(typeof value === 'string' ? value : String(value), undefined, constructionToken); /** * A template literal tag which can be used with LitElement's * {@linkcode LitElement.styles} property to set element styles. * * For security reasons, only literal string values and number may be used in * embedded expressions. To incorporate non-literal values {@linkcode unsafeCSS} * may be used inside an expression. */ const css = (strings, ...values) => { const cssText = strings.length === 1 ? strings[0] : values.reduce((acc, v, idx) => acc + textFromCSSResult(v) + strings[idx + 1], strings[0]); return new CSSResult(cssText, strings, constructionToken); }; /** * Applies the given styles to a `shadowRoot`. When Shadow DOM is * available but `adoptedStyleSheets` is not, styles are appended to the * `shadowRoot` to [mimic spec behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets). * Note, when shimming is used, any styles that are subsequently placed into * the shadowRoot should be placed *before* any shimmed adopted styles. This * will match spec behavior that gives adopted sheets precedence over styles in * shadowRoot. */ const adoptStyles = (renderRoot, styles) => { if (supportsAdoptingStyleSheets) { renderRoot.adoptedStyleSheets = styles.map(s => s instanceof CSSStyleSheet ? s : s.styleSheet); } else { for (const s of styles) { const style = document.createElement('style'); // eslint-disable-next-line @typescript-eslint/no-explicit-any const nonce = global$2['litNonce']; if (nonce !== undefined) { style.setAttribute('nonce', nonce); } style.textContent = s.cssText; renderRoot.appendChild(style); } } }; const cssResultFromStyleSheet = sheet => { let cssText = ''; for (const rule of sheet.cssRules) { cssText += rule.cssText; } return unsafeCSS(cssText); }; const getCompatibleStyle = supportsAdoptingStyleSheets || NODE_MODE ? s => s : s => s instanceof CSSStyleSheet ? cssResultFromStyleSheet(s) : s; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ // TODO (justinfagnani): Add `hasOwn` here when we ship ES2022 const { is, defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols, getPrototypeOf } = Object; // Lets a minifier replace globalThis references with a minified name const global$1 = globalThis; let issueWarning$4; const trustedTypes$1 = global$1.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$1 ? trustedTypes$1.emptyScript : ''; const polyfillSupport$2 = global$1.reactiveElementPolyfillSupportDevMode ; { // Ensure warnings are issued only 1x, even if multiple versions of Lit // are loaded. const issuedWarnings = global$1.litIssuedWarnings ??= new Set(); // Issue a warning, if we haven't already. issueWarning$4 = (code, warning) => { warning += ` See https://lit.dev/msg/${code} for more information.`; if (!issuedWarnings.has(warning)) { console.warn(warning); issuedWarnings.add(warning); } }; issueWarning$4('dev-mode', `Lit is in dev mode. Not recommended for production!`); // Issue polyfill support warning. if (global$1.ShadyDOM?.inUse && polyfillSupport$2 === undefined) { issueWarning$4('polyfill-support-missing', `Shadow DOM is being polyfilled via \`ShadyDOM\` but ` + `the \`polyfill-support\` module has not been loaded.`); } } /** * Useful for visualizing and logging insights into what the Lit template system is doing. * * Compiled out of prod mode builds. */ const debugLogEvent$1 = event => { const shouldEmit = global$1.emitLitDebugLogEvents; if (!shouldEmit) { return; } global$1.dispatchEvent(new CustomEvent('lit-debug', { detail: event })); } ; /* * 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$1 = (prop, _obj) => prop; 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. */ const notEqual = (value, old) => !is(value, old); const defaultPropertyDeclaration$1 = { attribute: true, type: String, converter: defaultConverter, reflect: false, hasChanged: notEqual }; // Ensure metadata is enabled. TypeScript does not polyfill // Symbol.metadata, so we must ensure that it exists. Symbol.metadata ??= Symbol('metadata'); // Map from a class's metadata object to property options // Note that we must use nullish-coalescing assignment so that we only use one // map even if we load multiple version of this module. global$1.litPropertyMetadata ??= new WeakMap(); /** * Base element class which manages element properties and attributes. When * properties change, the `update` method is asynchronously called. This method * should be supplied by subclasses to render updates as desired. * @noInheritDoc */ class ReactiveElement // In the Node build, this `extends` clause will be substituted with // `(globalThis.HTMLElement ?? HTMLElement)`. // // This way, we will first prefer any global `HTMLElement` polyfill that the // user has assigned, and then fall back to the `HTMLElement` shim which has // been imported (see note at the top of this file about how this import is // generated by Rollup). Note that the `HTMLElement` variable has been // shadowed by this import, so it no longer refers to the global. extends HTMLElement { /** * 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) { this.__prepare(); (this._initializers ??= []).push(initializer); } /** * Returns a list of attributes corresponding to the registered properties. * @nocollapse * @category attributes */ static get observedAttributes() { // Ensure we've created all properties this.finalize(); // this.__attributeToPropertyMap is only undefined after finalize() in // ReactiveElement itself. ReactiveElement.observedAttributes is only // accessed with ReactiveElement as the receiver when a subclass or mixin // calls super.observedAttributes return this.__attributeToPropertyMap && [...this.__attributeToPropertyMap.keys()]; } /** * 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$1) { // If this is a state property, force the attribute to false. if (options.state) { options.attribute = false; } this.__prepare(); this.elementProperties.set(name, options); if (!options.noAccessor) { const key = // Use Symbol.for in dev mode to make it easier to maintain state // when doing HMR. Symbol.for(`${String(name)} (@property() cache)`) ; const descriptor = this.getPropertyDescriptor(name, key, options); if (descriptor !== undefined) { defineProperty(this.prototype, name, descriptor); } } } /** * 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) { const { get, set } = getOwnPropertyDescriptor(this.prototype, name) ?? { get() { return this[key]; }, set(v) { this[key] = v; } }; if (get == null) { if ('value' in (getOwnPropertyDescriptor(this.prototype, name) ?? {})) { throw new Error(`Field ${JSON.stringify(String(name))} on ` + `${this.name} was declared as a reactive property ` + `but it's actually declared as a value on the prototype. ` + `Usually this is due to using @property or @state on a method.`); } issueWarning$4('reactive-property-without-getter', `Field ${JSON.stringify(String(name))} on ` + `${this.name} was declared as a reactive property ` + `but it does not have a getter. This will be an error in a ` + `future version of Lit.`); } return { get() { return get?.call(this); }, set(value) { const oldValue = get?.call(this); set.call(this, 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$1; } /** * Initializes static own properties of the class used in bookkeeping * for element properties, initializers, etc. * * Can be called multiple times by code that needs to ensure these * properties exist before using them. * * This method ensures the superclass is finalized so that inherited * property metadata can be copied down. * @nocollapse */ static __prepare() { if (this.hasOwnProperty(JSCompiler_renameProperty$1('elementProperties'))) { // Already prepared return; } // Finalize any superclasses const superCtor = getPrototypeOf(this); superCtor.finalize(); // Create own set of initializers for this class if any exist on the // superclass and copy them down. Note, for a small perf boost, avoid // creating initializers unless needed. if (superCtor._initializers !== undefined) { this._initializers = [...superCtor._initializers]; } // Initialize elementProperties from the superclass this.elementProperties = new Map(superCtor.elementProperties); } /** * Finishes setting up the class so that it's ready to be registered * as a custom element and instantiated. * * This method is called by the ReactiveElement.observedAttributes getter. * If you override the observedAttributes getter, you must either call * super.observedAttributes to trigger finalization, or call finalize() * yourself. * * @nocollapse */ static finalize() { if (this.hasOwnProperty(JSCompiler_renameProperty$1('finalized'))) { return; } this.finalized = true; this.__prepare(); // Create properties from the static properties block: if (this.hasOwnProperty(JSCompiler_renameProperty$1('properties'))) { const props = this.properties; const propKeys = [...getOwnPropertyNames(props), ...getOwnPropertySymbols(props)]; for (const p of propKeys) { this.createProperty(p, props[p]); } } // Create properties from standard decorator metadata: const metadata = this[Symbol.metadata]; if (metadata !== null) { const properties = litPropertyMetadata.get(metadata); if (properties !== undefined) { for (const [p, options] of properties) { this.elementProperties.set(p, options); } } } // Create the attribute-to-property map this.__attributeToPropertyMap = new Map(); for (const [p, options] of this.elementProperties) { const attr = this.__attributeNameForProperty(p, options); if (attr !== undefined) { this.__attributeToPropertyMap.set(attr, p); } } this.elementStyles = this.finalizeStyles(this.styles); { if (this.hasOwnProperty('createProperty')) { issueWarning$4('no-override-create-property', 'Overriding ReactiveElement.createProperty() is deprecated. ' + 'The override will not be called with standard decorators'); } if (this.hasOwnProperty('getPropertyDescriptor')) { issueWarning$4('no-override-get-property-descriptor', 'Overriding ReactiveElement.getPropertyDescriptor() is deprecated. ' + 'The override will not be called with standard decorators'); } } } /** * 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; } constructor() { super(); this.__instanceProperties = undefined; /** * 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(); } /** * Internal only override point for customizing work done when elements * are constructed. */ __initialize() { this.__updatePromise = new Promise(res => this.enableUpdating = res); this._$changedProperties = new Map(); // This enqueues a microtask that ust run before the first update, so it // must be called before requestUpdate() this.__saveInstanceProperties(); // ensures first update will be caught by an early access of // `updateComplete` this.requestUpdate(); this.constructor._initializers?.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) { (this.__controllers ??= new Set()).add(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) { controller.hostConnected?.(); } } /** * Removes a `ReactiveController` from the element. * @category controllers */ removeController(controller) { this.__controllers?.delete(controller); } /** * 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() { const instanceProperties = new Map(); const elementProperties = this.constructor.elementProperties; for (const p of elementProperties.keys()) { if (this.hasOwnProperty(p)) { instanceProperties.set(p, this[p]); delete this[p]; } } if (instanceProperties.size > 0) { this.__instanceProperties = instanceProperties; } } /** * 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() { const renderRoot = this.shadowRoot ?? 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() { // Create renderRoot before controllers `hostConnected` this.renderRoot ??= this.createRenderRoot(); this.enableUpdating(true); this.__controllers?.forEach(c => c.hostConnected?.()); } /** * 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() { this.__controllers?.forEach(c => c.hostDisconnected?.()); } /** * 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) { const elemProperties = this.constructor.elementProperties; const options = elemProperties.get(name); const attr = this.constructor.__attributeNameForProperty(name, options); if (attr !== undefined && options.reflect === true) { const converter = options.converter?.toAttribute !== undefined ? options.converter : defaultConverter; const attrValue = converter.toAttribute(value, options.type); if (this.constructor.enabledWarnings.includes('migration') && attrValue === undefined) { issueWarning$4('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) { 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 = typeof options.converter === 'function' ? { fromAttribute: options.converter } : options.converter?.fromAttribute !== undefined ? options.converter : defaultConverter; // mark state reflecting this.__reflectingProperty = propName; this[propName] = converter.fromAttribute(value, options.type // eslint-disable-next-line @typescript-eslint/no-explicit-any ); // 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) { // If we have a property key, perform property update steps. if (name !== undefined) { if (name instanceof Event) { issueWarning$4(``, `The requestUpdate() method was called with an Event as the property name. This is probably a mistake caused by binding this.requestUpdate as an event listener. Instead bind a function that will call it with no arguments: () => this.requestUpdate()`); } options ??= this.constructor.getPropertyOptions(name); const hasChanged = options.hasChanged ?? notEqual; const newValue = this[name]; if (hasChanged(newValue, oldValue)) { this._$changeProperty(name, oldValue, options); } else { // Abort the request if the property should not be considered changed. return; } } if (this.isUpdatePending === false) { this.__updatePromise = this.__enqueueUpdate(); } } /** * @internal */ _$changeProperty(name, oldValue, options) { // TODO (justinfagnani): Create a benchmark of Map.has() + Map.set( // vs just Map.set() 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) { (this.__reflectingProperties ??= new Set()).add(name); } } /** * 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() { const result = this.performUpdate(); if (this.constructor.enabledWarnings.includes('async-perform-update') && typeof result?.then === 'function') { issueWarning$4('async-perform-update', `Element ${this.localName} returned a Promise from performUpdate(). ` + `This behavior is deprecated and will be removed in a future ` + `version of ReactiveElement.`); } return result; } /** * 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. * * @category updates */ performUpdate() { // 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$1?.({ kind: 'update' }); if (!this.hasUpdated) { // Create renderRoot before first update. This occurs in `connectedCallback` // but is done here to support out of tree calls to `enableUpdating`/`performUpdate`. this.renderRoot ??= this.createRenderRoot(); { // Produce warning if any reactive properties on the prototype are // shadowed by class fields. Instance fields set before upgrade are // deleted by this point, so any own property is caused by class field // initialization in the constructor. const ctor = this.constructor; const shadowedProperties = [...ctor.elementProperties.keys()].filter(p => this.hasOwnProperty(p) && p in getPrototypeOf(this)); 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) { // TODO (justinfagnani): should we use the stored value? Could a new value // have been set since we stored the own property value? for (const [p, value] of this.__instanceProperties) { this[p] = value; } this.__instanceProperties = undefined; } // Trigger initial value reflection and populate the initial // changedProperties map, but only for the case of experimental // decorators on accessors, which will not have already populated the // changedProperties map. We can't know if these accessors had // initializers, so we just set them anyway - a difference from // experimental decorators on fields and standard decorators on // auto-accessors. // For context why experimentalDecorators with auto accessors are handled // specifically also see: // https://github.com/lit/lit/pull/4183#issuecomment-1711959635 const elementProperties = this.constructor.elementProperties; if (elementProperties.size > 0) { for (const [p, options] of elementProperties) { if (options.wrapped === true && !this._$changedProperties.has(p) && this[p] !== undefined) { this._$changeProperty(p, this[p], options); } } } } let shouldUpdate = false; const changedProperties = this._$changedProperties; try { shouldUpdate = this.shouldUpdate(changedProperties); if (shouldUpdate) { this.willUpdate(changedProperties); this.__controllers?.forEach(c => c.hostUpdate?.()); 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) { this.__controllers?.forEach(c => c.hostUpdated?.()); if (!this.hasUpdated) { this.hasUpdated = true; this.firstUpdated(changedProperties); } this.updated(changedProperties); if (this.isUpdatePending && this.constructor.enabledWarnings.includes('change-in-update')) { issueWarning$4('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) { // The forEach() expression will only run when when __reflectingProperties is // defined, and it returns undefined, setting __reflectingProperties to // undefined this.__reflectingProperties &&= this.__reflectingProperties.forEach(p => this.__propertyToAttribute(p, this[p])); 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) {} } /** * 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' }; // Assigned here to work around a jscompiler bug with static fields // when compiling to ES5. // https://github.com/google/closure-compiler/issues/3177 ReactiveElement[JSCompiler_renameProperty$1('elementProperties')] = new Map(); ReactiveElement[JSCompiler_renameProperty$1('finalized')] = new Map(); // Apply polyfills if available polyfillSupport$2?.({ ReactiveElement }); // Dev mode warnings... { // Default warning set. ReactiveElement.enabledWarnings = ['change-in-update', 'async-perform-update']; const ensureOwnWarnings = function (ctor) { if (!ctor.hasOwnProperty(JSCompiler_renameProperty$1('enabledWarnings'))) { ctor.enabledWarnings = ctor.enabledWarnings.slice(); } }; ReactiveElement.enableWarning = function (warning) { ensureOwnWarnings(this); if (!this.enabledWarnings.includes(warning)) { 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. (global$1.reactiveElementVersions ??= []).push('2.0.4'); if (global$1.reactiveElementVersions.length > 1) { issueWarning$4('multiple-versions', `Multiple versions of Lit loaded. Loading multiple versions ` + `is not recommended.`); } /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ // Allows minifiers to rename references to globalThis const global = globalThis; /** * Useful for visualizing and logging insights into what the Lit template system is doing. * * Compiled out of prod mode builds. */ const debugLogEvent = event => { const shouldEmit = global.emitLitDebugLogEvents; if (!shouldEmit) { return; } global.dispatchEvent(new CustomEvent('lit-debug', { detail: event })); } ; // Used for connecting beginRender and endRender events when there are nested // renders when errors are thrown preventing an endRender event from being // called. let debugLogRenderId = 0; let issueWarning$3; { global.litIssuedWarnings ??= new Set(); // Issue a warning, if we haven't already. issueWarning$3 = (code, warning) => { warning += code ? ` See https://lit.dev/msg/${code} for more information.` : ''; if (!global.litIssuedWarnings.has(warning)) { console.warn(warning); global.litIssuedWarnings.add(warning); } }; issueWarning$3('dev-mode', `Lit is in dev mode. Not recommended for production!`); } const wrap = global.ShadyDOM?.inUse && global.ShadyDOM?.noPatch === true ? global.ShadyDOM.wrap : node => node; const trustedTypes = global.trustedTypes; /** * Our TrustedTypePolicy for HTML which is declared using the html template * tag function. * * That HTML is a developer-authored constant, and is parsed with innerHTML * before any untrusted expressions have been mixed in. Therefor it is * considered safe by construction. */ const policy = trustedTypes ? trustedTypes.createPolicy('lit-html', { createHTML: s => s }) : undefined; const identityFunction = value => value; const noopSanitizer = (_node, _name, _type) => identityFunction; /** Sets the global sanitizer factory. */ const setSanitizer = newSanitizer => { if (sanitizerFactoryInternal !== noopSanitizer) { throw new Error(`Attempted to overwrite existing lit-html security policy.` + ` setSanitizeDOMValueFactory should be called at most once.`); } sanitizerFactoryInternal = newSanitizer; }; /** * Only used in internal tests, not a part of the public API. */ const _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => { sanitizerFactoryInternal = noopSanitizer; }; const createSanitizer = (node, name, type) => { return sanitizerFactoryInternal(node, name, type); }; // Added to an attribute name to mark the attribute as bound so we can find // it easily. const boundAttributeSuffix = '$lit$'; // This marker is used in many syntactic positions in HTML, so it must be // a valid element name and attribute name. We don't support dynamic names (yet) // but this at least ensures that the parse tree is closer to the template // intention. const marker = `lit$${Math.random().toFixed(9).slice(2)}$`; // String used to tell if a comment is a marker comment const markerMatch = '?' + marker; // Text used to insert a comment marker node. We use processing instruction // syntax because it's slightly smaller, but parses as a comment node. const nodeMarker = `<${mar