timepiece/node_modules/lit-html/development/directives/async-replace.js

100 lines
4.2 KiB
JavaScript
Raw Normal View History

2024-05-14 14:54:12 +00:00
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
import { noChange } from '../lit-html.js';
import { AsyncDirective, directive, } from '../async-directive.js';
import { Pauser, PseudoWeakRef, forAwaitOf } from './private-async-helpers.js';
export class AsyncReplaceDirective extends AsyncDirective {
constructor() {
super(...arguments);
this.__weakThis = new PseudoWeakRef(this);
this.__pauser = new Pauser();
}
// @ts-expect-error value not used, but we want a nice parameter for docs
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(value, _mapper) {
return noChange;
}
update(_part, [value, mapper]) {
// If our initial render occurs while disconnected, ensure that the pauser
// and weakThis are in the disconnected state
if (!this.isConnected) {
this.disconnected();
}
// If we've already set up this particular iterable, we don't need
// to do anything.
if (value === this.__value) {
return;
}
this.__value = value;
let i = 0;
const { __weakThis: weakThis, __pauser: pauser } = this;
// 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
forAwaitOf(value, async (v) => {
// 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) {
// Check to make sure that value is the still the current value of
// the part, and if not bail because a new value owns this part
if (_this.__value !== value) {
return false;
}
// As a convenience, because functional-programming-style
// transforms of iterables and async iterables requires a library,
// we accept a mapper function. This is especially convenient for
// rendering a template for each item.
if (mapper !== undefined) {
v = mapper(v, i);
}
_this.commitValue(v, i);
i++;
}
return true;
});
return noChange;
}
// Override point for AsyncAppend to append rather than replace
commitValue(value, _index) {
this.setValue(value);
}
disconnected() {
this.__weakThis.disconnect();
this.__pauser.pause();
}
reconnected() {
this.__weakThis.reconnect(this);
this.__pauser.resume();
}
}
/**
* A directive that renders the items of an async iterable[1], replacing
* previous values with new values, so that only one value is ever rendered
* at a time. This directive may be used in any expression type.
*
* Async iterables are objects with a `[Symbol.asyncIterator]` method, which
* returns an iterator who's `next()` method returns a Promise. When a new
* value is available, the Promise resolves and the value is rendered to the
* Part controlled by the directive. If another value other than this
* directive has been set on the Part, the iterable will no longer be listened
* to and new values won't be written to the Part.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
*
* @param value An async iterable
* @param mapper An optional function that maps from (value, index) to another
* value. Useful for generating templates for each item in the iterable.
*/
export const asyncReplace = directive(AsyncReplaceDirective);
//# sourceMappingURL=async-replace.js.map