recycle-view-repeater-strategy-DoWdPqVw.mjs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { isObservable, of } from 'rxjs';
  2. import { D as DataSource } from './data-source-D34wiQZj.mjs';
  3. import { InjectionToken } from '@angular/core';
  4. /** DataSource wrapper for a native array. */
  5. class ArrayDataSource extends DataSource {
  6. _data;
  7. constructor(_data) {
  8. super();
  9. this._data = _data;
  10. }
  11. connect() {
  12. return isObservable(this._data) ? this._data : of(this._data);
  13. }
  14. disconnect() { }
  15. }
  16. /** Indicates how a view was changed by a {@link _ViewRepeater}. */
  17. var _ViewRepeaterOperation;
  18. (function (_ViewRepeaterOperation) {
  19. /** The content of an existing view was replaced with another item. */
  20. _ViewRepeaterOperation[_ViewRepeaterOperation["REPLACED"] = 0] = "REPLACED";
  21. /** A new view was created with `createEmbeddedView`. */
  22. _ViewRepeaterOperation[_ViewRepeaterOperation["INSERTED"] = 1] = "INSERTED";
  23. /** The position of a view changed, but the content remains the same. */
  24. _ViewRepeaterOperation[_ViewRepeaterOperation["MOVED"] = 2] = "MOVED";
  25. /** A view was detached from the view container. */
  26. _ViewRepeaterOperation[_ViewRepeaterOperation["REMOVED"] = 3] = "REMOVED";
  27. })(_ViewRepeaterOperation || (_ViewRepeaterOperation = {}));
  28. /**
  29. * Injection token for {@link _ViewRepeater}. This token is for use by Angular Material only.
  30. * @docs-private
  31. */
  32. const _VIEW_REPEATER_STRATEGY = new InjectionToken('_ViewRepeater');
  33. /**
  34. * A repeater that caches views when they are removed from a
  35. * {@link ViewContainerRef}. When new items are inserted into the container,
  36. * the repeater will reuse one of the cached views instead of creating a new
  37. * embedded view. Recycling cached views reduces the quantity of expensive DOM
  38. * inserts.
  39. *
  40. * @template T The type for the embedded view's $implicit property.
  41. * @template R The type for the item in each IterableDiffer change record.
  42. * @template C The type for the context passed to each embedded view.
  43. */
  44. class _RecycleViewRepeaterStrategy {
  45. /**
  46. * The size of the cache used to store unused views.
  47. * Setting the cache size to `0` will disable caching. Defaults to 20 views.
  48. */
  49. viewCacheSize = 20;
  50. /**
  51. * View cache that stores embedded view instances that have been previously stamped out,
  52. * but don't are not currently rendered. The view repeater will reuse these views rather than
  53. * creating brand new ones.
  54. *
  55. * TODO(michaeljamesparsons) Investigate whether using a linked list would improve performance.
  56. */
  57. _viewCache = [];
  58. /** Apply changes to the DOM. */
  59. applyChanges(changes, viewContainerRef, itemContextFactory, itemValueResolver, itemViewChanged) {
  60. // Rearrange the views to put them in the right location.
  61. changes.forEachOperation((record, adjustedPreviousIndex, currentIndex) => {
  62. let view;
  63. let operation;
  64. if (record.previousIndex == null) {
  65. // Item added.
  66. const viewArgsFactory = () => itemContextFactory(record, adjustedPreviousIndex, currentIndex);
  67. view = this._insertView(viewArgsFactory, currentIndex, viewContainerRef, itemValueResolver(record));
  68. operation = view ? _ViewRepeaterOperation.INSERTED : _ViewRepeaterOperation.REPLACED;
  69. }
  70. else if (currentIndex == null) {
  71. // Item removed.
  72. this._detachAndCacheView(adjustedPreviousIndex, viewContainerRef);
  73. operation = _ViewRepeaterOperation.REMOVED;
  74. }
  75. else {
  76. // Item moved.
  77. view = this._moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, itemValueResolver(record));
  78. operation = _ViewRepeaterOperation.MOVED;
  79. }
  80. if (itemViewChanged) {
  81. itemViewChanged({
  82. context: view?.context,
  83. operation,
  84. record,
  85. });
  86. }
  87. });
  88. }
  89. detach() {
  90. for (const view of this._viewCache) {
  91. view.destroy();
  92. }
  93. this._viewCache = [];
  94. }
  95. /**
  96. * Inserts a view for a new item, either from the cache or by creating a new
  97. * one. Returns `undefined` if the item was inserted into a cached view.
  98. */
  99. _insertView(viewArgsFactory, currentIndex, viewContainerRef, value) {
  100. const cachedView = this._insertViewFromCache(currentIndex, viewContainerRef);
  101. if (cachedView) {
  102. cachedView.context.$implicit = value;
  103. return undefined;
  104. }
  105. const viewArgs = viewArgsFactory();
  106. return viewContainerRef.createEmbeddedView(viewArgs.templateRef, viewArgs.context, viewArgs.index);
  107. }
  108. /** Detaches the view at the given index and inserts into the view cache. */
  109. _detachAndCacheView(index, viewContainerRef) {
  110. const detachedView = viewContainerRef.detach(index);
  111. this._maybeCacheView(detachedView, viewContainerRef);
  112. }
  113. /** Moves view at the previous index to the current index. */
  114. _moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, value) {
  115. const view = viewContainerRef.get(adjustedPreviousIndex);
  116. viewContainerRef.move(view, currentIndex);
  117. view.context.$implicit = value;
  118. return view;
  119. }
  120. /**
  121. * Cache the given detached view. If the cache is full, the view will be
  122. * destroyed.
  123. */
  124. _maybeCacheView(view, viewContainerRef) {
  125. if (this._viewCache.length < this.viewCacheSize) {
  126. this._viewCache.push(view);
  127. }
  128. else {
  129. const index = viewContainerRef.indexOf(view);
  130. // The host component could remove views from the container outside of
  131. // the view repeater. It's unlikely this will occur, but just in case,
  132. // destroy the view on its own, otherwise destroy it through the
  133. // container to ensure that all the references are removed.
  134. if (index === -1) {
  135. view.destroy();
  136. }
  137. else {
  138. viewContainerRef.remove(index);
  139. }
  140. }
  141. }
  142. /** Inserts a recycled view from the cache at the given index. */
  143. _insertViewFromCache(index, viewContainerRef) {
  144. const cachedView = this._viewCache.pop();
  145. if (cachedView) {
  146. viewContainerRef.insert(cachedView, index);
  147. }
  148. return cachedView || null;
  149. }
  150. }
  151. export { ArrayDataSource as A, _RecycleViewRepeaterStrategy as _, _ViewRepeaterOperation as a, _VIEW_REPEATER_STRATEGY as b };
  152. //# sourceMappingURL=recycle-view-repeater-strategy-DoWdPqVw.mjs.map