157 lines
6.4 KiB
JavaScript
Executable File
157 lines
6.4 KiB
JavaScript
Executable File
import { isObservable, of } from 'rxjs';
|
|
import { DataSource } from './data-source.mjs';
|
|
import { InjectionToken } from '@angular/core';
|
|
|
|
/** DataSource wrapper for a native array. */
|
|
class ArrayDataSource extends DataSource {
|
|
_data;
|
|
constructor(_data) {
|
|
super();
|
|
this._data = _data;
|
|
}
|
|
connect() {
|
|
return isObservable(this._data) ? this._data : of(this._data);
|
|
}
|
|
disconnect() { }
|
|
}
|
|
|
|
/** Indicates how a view was changed by a `_ViewRepeater`. */
|
|
var _ViewRepeaterOperation;
|
|
(function (_ViewRepeaterOperation) {
|
|
/** The content of an existing view was replaced with another item. */
|
|
_ViewRepeaterOperation[_ViewRepeaterOperation["REPLACED"] = 0] = "REPLACED";
|
|
/** A new view was created with `createEmbeddedView`. */
|
|
_ViewRepeaterOperation[_ViewRepeaterOperation["INSERTED"] = 1] = "INSERTED";
|
|
/** The position of a view changed, but the content remains the same. */
|
|
_ViewRepeaterOperation[_ViewRepeaterOperation["MOVED"] = 2] = "MOVED";
|
|
/** A view was detached from the view container. */
|
|
_ViewRepeaterOperation[_ViewRepeaterOperation["REMOVED"] = 3] = "REMOVED";
|
|
})(_ViewRepeaterOperation || (_ViewRepeaterOperation = {}));
|
|
/**
|
|
* Injection token for `_ViewRepeater`. This token is for use by Angular Material only.
|
|
* @docs-private
|
|
*/
|
|
const _VIEW_REPEATER_STRATEGY = new InjectionToken('_ViewRepeater');
|
|
|
|
/**
|
|
* A repeater that caches views when they are removed from a
|
|
* `ViewContainerRef`. When new items are inserted into the container,
|
|
* the repeater will reuse one of the cached views instead of creating a new
|
|
* embedded view. Recycling cached views reduces the quantity of expensive DOM
|
|
* inserts.
|
|
*
|
|
* @template T The type for the embedded view's $implicit property.
|
|
* @template R The type for the item in each IterableDiffer change record.
|
|
* @template C The type for the context passed to each embedded view.
|
|
*/
|
|
class _RecycleViewRepeaterStrategy {
|
|
/**
|
|
* The size of the cache used to store unused views.
|
|
* Setting the cache size to `0` will disable caching. Defaults to 20 views.
|
|
*/
|
|
viewCacheSize = 20;
|
|
/**
|
|
* View cache that stores embedded view instances that have been previously stamped out,
|
|
* but don't are not currently rendered. The view repeater will reuse these views rather than
|
|
* creating brand new ones.
|
|
*
|
|
* TODO(michaeljamesparsons) Investigate whether using a linked list would improve performance.
|
|
*/
|
|
_viewCache = [];
|
|
/** Apply changes to the DOM. */
|
|
applyChanges(changes, viewContainerRef, itemContextFactory, itemValueResolver, itemViewChanged) {
|
|
// Rearrange the views to put them in the right location.
|
|
changes.forEachOperation((record, adjustedPreviousIndex, currentIndex) => {
|
|
let view;
|
|
let operation;
|
|
if (record.previousIndex == null) {
|
|
// Item added.
|
|
const viewArgsFactory = () => itemContextFactory(record, adjustedPreviousIndex, currentIndex);
|
|
view = this._insertView(viewArgsFactory, currentIndex, viewContainerRef, itemValueResolver(record));
|
|
operation = view ? _ViewRepeaterOperation.INSERTED : _ViewRepeaterOperation.REPLACED;
|
|
}
|
|
else if (currentIndex == null) {
|
|
// Item removed.
|
|
this._detachAndCacheView(adjustedPreviousIndex, viewContainerRef);
|
|
operation = _ViewRepeaterOperation.REMOVED;
|
|
}
|
|
else {
|
|
// Item moved.
|
|
view = this._moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, itemValueResolver(record));
|
|
operation = _ViewRepeaterOperation.MOVED;
|
|
}
|
|
if (itemViewChanged) {
|
|
itemViewChanged({
|
|
context: view?.context,
|
|
operation,
|
|
record,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
detach() {
|
|
for (const view of this._viewCache) {
|
|
view.destroy();
|
|
}
|
|
this._viewCache = [];
|
|
}
|
|
/**
|
|
* Inserts a view for a new item, either from the cache or by creating a new
|
|
* one. Returns `undefined` if the item was inserted into a cached view.
|
|
*/
|
|
_insertView(viewArgsFactory, currentIndex, viewContainerRef, value) {
|
|
const cachedView = this._insertViewFromCache(currentIndex, viewContainerRef);
|
|
if (cachedView) {
|
|
cachedView.context.$implicit = value;
|
|
return undefined;
|
|
}
|
|
const viewArgs = viewArgsFactory();
|
|
return viewContainerRef.createEmbeddedView(viewArgs.templateRef, viewArgs.context, viewArgs.index);
|
|
}
|
|
/** Detaches the view at the given index and inserts into the view cache. */
|
|
_detachAndCacheView(index, viewContainerRef) {
|
|
const detachedView = viewContainerRef.detach(index);
|
|
this._maybeCacheView(detachedView, viewContainerRef);
|
|
}
|
|
/** Moves view at the previous index to the current index. */
|
|
_moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, value) {
|
|
const view = viewContainerRef.get(adjustedPreviousIndex);
|
|
viewContainerRef.move(view, currentIndex);
|
|
view.context.$implicit = value;
|
|
return view;
|
|
}
|
|
/**
|
|
* Cache the given detached view. If the cache is full, the view will be
|
|
* destroyed.
|
|
*/
|
|
_maybeCacheView(view, viewContainerRef) {
|
|
if (this._viewCache.length < this.viewCacheSize) {
|
|
this._viewCache.push(view);
|
|
}
|
|
else {
|
|
const index = viewContainerRef.indexOf(view);
|
|
// The host component could remove views from the container outside of
|
|
// the view repeater. It's unlikely this will occur, but just in case,
|
|
// destroy the view on its own, otherwise destroy it through the
|
|
// container to ensure that all the references are removed.
|
|
if (index === -1) {
|
|
view.destroy();
|
|
}
|
|
else {
|
|
viewContainerRef.remove(index);
|
|
}
|
|
}
|
|
}
|
|
/** Inserts a recycled view from the cache at the given index. */
|
|
_insertViewFromCache(index, viewContainerRef) {
|
|
const cachedView = this._viewCache.pop();
|
|
if (cachedView) {
|
|
viewContainerRef.insert(cachedView, index);
|
|
}
|
|
return cachedView || null;
|
|
}
|
|
}
|
|
|
|
export { ArrayDataSource, _RecycleViewRepeaterStrategy, _VIEW_REPEATER_STRATEGY, _ViewRepeaterOperation };
|
|
//# sourceMappingURL=recycle-view-repeater-strategy.mjs.map
|