/** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ import { noChange } from '../lit-html.js'; import { isPrimitive } from '../directive-helpers.js'; import { directive, AsyncDirective } from '../async-directive.js'; import { Pauser, PseudoWeakRef } from './private-async-helpers.js'; const isPromise = (x) => { return !isPrimitive(x) && typeof x.then === 'function'; }; // Effectively infinity, but a SMI. const _infinity = 0x3fffffff; export class UntilDirective extends AsyncDirective { constructor() { super(...arguments); this.__lastRenderedIndex = _infinity; this.__values = []; this.__weakThis = new PseudoWeakRef(this); this.__pauser = new Pauser(); } render(...args) { var _a; return (_a = args.find((x) => !isPromise(x))) !== null && _a !== void 0 ? _a : noChange; } update(_part, args) { const previousValues = this.__values; let previousLength = previousValues.length; this.__values = args; const weakThis = this.__weakThis; const pauser = this.__pauser; // If our initial render occurs while disconnected, ensure that the pauser // and weakThis are in the disconnected state if (!this.isConnected) { this.disconnected(); } for (let i = 0; i < args.length; i++) { // If we've rendered a higher-priority value already, stop. if (i > this.__lastRenderedIndex) { break; } const value = args[i]; // Render non-Promise values immediately if (!isPromise(value)) { this.__lastRenderedIndex = i; // Since a lower-priority value will never overwrite a higher-priority // synchronous value, we can stop processing now. return value; } // If this is a Promise we've already handled, skip it. if (i < previousLength && value === previousValues[i]) { continue; } // We have a Promise that we haven't seen before, so priorities may have // changed. Forget what we rendered before. this.__lastRenderedIndex = _infinity; previousLength = 0; // Note, the callback avoids closing over `this` so that the directive // can be gc'ed before the promise resolves; instead `this` is retrieved // from `weakThis`, which can break the hard reference in the closure when // the directive disconnects Promise.resolve(value).then(async (result) => { // If we're disconnected, wait until we're (maybe) reconnected // The while loop here handles the case that the connection state // thrashes, causing the pauser to resume and then get re-paused while (pauser.get()) { await pauser.get(); } // If the callback gets here and there is no `this`, it means that the // directive has been disconnected and garbage collected and we don't // need to do anything else const _this = weakThis.deref(); if (_this !== undefined) { const index = _this.__values.indexOf(value); // If state.values doesn't contain the value, we've re-rendered without // the value, so don't render it. Then, only render if the value is // higher-priority than what's already been rendered. if (index > -1 && index < _this.__lastRenderedIndex) { _this.__lastRenderedIndex = index; _this.setValue(result); } } }); } return noChange; } disconnected() { this.__weakThis.disconnect(); this.__pauser.pause(); } reconnected() { this.__weakThis.reconnect(this); this.__pauser.resume(); } } /** * Renders one of a series of values, including Promises, to a Part. * * Values are rendered in priority order, with the first argument having the * highest priority and the last argument having the lowest priority. If a * value is a Promise, low-priority values will be rendered until it resolves. * * The priority of values can be used to create placeholder content for async * data. For example, a Promise with pending content can be the first, * highest-priority, argument, and a non_promise loading indicator template can * be used as the second, lower-priority, argument. The loading indicator will * render immediately, and the primary content will render when the Promise * resolves. * * Example: * * ```js * const content = fetch('./content.txt').then(r => r.text()); * html`${until(content, html`Loading...`)}` * ``` */ export const until = directive(UntilDirective); /** * The type of the class that powers this directive. Necessary for naming the * directive's return type. */ // export type {UntilDirective}; //# sourceMappingURL=until.js.map