100 lines
4.2 KiB
JavaScript
100 lines
4.2 KiB
JavaScript
/**
|
|
* @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 noChange;
|
|
}
|
|
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
|