86 lines
3.4 KiB
JavaScript
86 lines
3.4 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
* This code may only be used under the BSD style license found at
|
|
* http://polymer.github.io/LICENSE.txt
|
|
* The complete set of authors may be found at
|
|
* http://polymer.github.io/AUTHORS.txt
|
|
* The complete set of contributors may be found at
|
|
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
* Code distributed by Google as part of the polymer project is also
|
|
* subject to an additional IP rights grant found at
|
|
* http://polymer.github.io/PATENTS.txt
|
|
*/
|
|
import { isPrimitive } from '../lib/parts.js';
|
|
import { directive } from '../lit-html.js';
|
|
const _state = new WeakMap();
|
|
// Effectively infinity, but a SMI.
|
|
const _infinity = 0x7fffffff;
|
|
/**
|
|
* 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:
|
|
*
|
|
* const content = fetch('./content.txt').then(r => r.text());
|
|
* html`${until(content, html`<span>Loading...</span>`)}`
|
|
*/
|
|
export const until = directive((...args) => (part) => {
|
|
let state = _state.get(part);
|
|
if (state === undefined) {
|
|
state = {
|
|
lastRenderedIndex: _infinity,
|
|
values: [],
|
|
};
|
|
_state.set(part, state);
|
|
}
|
|
const previousValues = state.values;
|
|
let previousLength = previousValues.length;
|
|
state.values = args;
|
|
for (let i = 0; i < args.length; i++) {
|
|
// If we've rendered a higher-priority value already, stop.
|
|
if (i > state.lastRenderedIndex) {
|
|
break;
|
|
}
|
|
const value = args[i];
|
|
// Render non-Promise values immediately
|
|
if (isPrimitive(value) ||
|
|
typeof value.then !== 'function') {
|
|
part.setValue(value);
|
|
state.lastRenderedIndex = i;
|
|
// Since a lower-priority value will never overwrite a higher-priority
|
|
// synchronous value, we can stop processsing now.
|
|
break;
|
|
}
|
|
// 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.
|
|
state.lastRenderedIndex = _infinity;
|
|
previousLength = 0;
|
|
Promise.resolve(value).then((resolvedValue) => {
|
|
const index = state.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 < state.lastRenderedIndex) {
|
|
state.lastRenderedIndex = index;
|
|
part.setValue(resolvedValue);
|
|
part.commit();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
//# sourceMappingURL=until.js.map
|