private.mjs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import * as i0 from '@angular/core';
  2. import { inject, NgZone, RendererFactory2, Injectable } from '@angular/core';
  3. import { Subject, Observable } from 'rxjs';
  4. import { filter, shareReplay, takeUntil } from 'rxjs/operators';
  5. /**
  6. * Handler that logs "ResizeObserver loop limit exceeded" errors.
  7. * These errors are not shown in the Chrome console, so we log them to ensure developers are aware.
  8. * @param e The error
  9. */
  10. const loopLimitExceededErrorHandler = (e) => {
  11. if (e instanceof ErrorEvent && e.message === 'ResizeObserver loop limit exceeded') {
  12. console.error(`${e.message}. This could indicate a performance issue with your app. See https://github.com/WICG/resize-observer/blob/master/explainer.md#error-handling`);
  13. }
  14. };
  15. /**
  16. * A shared ResizeObserver to be used for a particular box type (content-box, border-box, or
  17. * device-pixel-content-box)
  18. */
  19. class SingleBoxSharedResizeObserver {
  20. _box;
  21. /** Stream that emits when the shared observer is destroyed. */
  22. _destroyed = new Subject();
  23. /** Stream of all events from the ResizeObserver. */
  24. _resizeSubject = new Subject();
  25. /** ResizeObserver used to observe element resize events. */
  26. _resizeObserver;
  27. /** A map of elements to streams of their resize events. */
  28. _elementObservables = new Map();
  29. constructor(
  30. /** The box type to observe for resizes. */
  31. _box) {
  32. this._box = _box;
  33. if (typeof ResizeObserver !== 'undefined') {
  34. this._resizeObserver = new ResizeObserver(entries => this._resizeSubject.next(entries));
  35. }
  36. }
  37. /**
  38. * Gets a stream of resize events for the given element.
  39. * @param target The element to observe.
  40. * @return The stream of resize events for the element.
  41. */
  42. observe(target) {
  43. if (!this._elementObservables.has(target)) {
  44. this._elementObservables.set(target, new Observable(observer => {
  45. const subscription = this._resizeSubject.subscribe(observer);
  46. this._resizeObserver?.observe(target, { box: this._box });
  47. return () => {
  48. this._resizeObserver?.unobserve(target);
  49. subscription.unsubscribe();
  50. this._elementObservables.delete(target);
  51. };
  52. }).pipe(filter(entries => entries.some(entry => entry.target === target)),
  53. // Share a replay of the last event so that subsequent calls to observe the same element
  54. // receive initial sizing info like the first one. Also enable ref counting so the
  55. // element will be automatically unobserved when there are no more subscriptions.
  56. shareReplay({ bufferSize: 1, refCount: true }), takeUntil(this._destroyed)));
  57. }
  58. return this._elementObservables.get(target);
  59. }
  60. /** Destroys this instance. */
  61. destroy() {
  62. this._destroyed.next();
  63. this._destroyed.complete();
  64. this._resizeSubject.complete();
  65. this._elementObservables.clear();
  66. }
  67. }
  68. /**
  69. * Allows observing resize events on multiple elements using a shared set of ResizeObserver.
  70. * Sharing a ResizeObserver instance is recommended for better performance (see
  71. * https://github.com/WICG/resize-observer/issues/59).
  72. *
  73. * Rather than share a single `ResizeObserver`, this class creates one `ResizeObserver` per type
  74. * of observed box ('content-box', 'border-box', and 'device-pixel-content-box'). This avoids
  75. * later calls to `observe` with a different box type from influencing the events dispatched to
  76. * earlier calls.
  77. */
  78. class SharedResizeObserver {
  79. _cleanupErrorListener;
  80. /** Map of box type to shared resize observer. */
  81. _observers = new Map();
  82. /** The Angular zone. */
  83. _ngZone = inject(NgZone);
  84. constructor() {
  85. if (typeof ResizeObserver !== 'undefined' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
  86. this._ngZone.runOutsideAngular(() => {
  87. const renderer = inject(RendererFactory2).createRenderer(null, null);
  88. this._cleanupErrorListener = renderer.listen('window', 'error', loopLimitExceededErrorHandler);
  89. });
  90. }
  91. }
  92. ngOnDestroy() {
  93. for (const [, observer] of this._observers) {
  94. observer.destroy();
  95. }
  96. this._observers.clear();
  97. this._cleanupErrorListener?.();
  98. }
  99. /**
  100. * Gets a stream of resize events for the given target element and box type.
  101. * @param target The element to observe for resizes.
  102. * @param options Options to pass to the `ResizeObserver`
  103. * @return The stream of resize events for the element.
  104. */
  105. observe(target, options) {
  106. const box = options?.box || 'content-box';
  107. if (!this._observers.has(box)) {
  108. this._observers.set(box, new SingleBoxSharedResizeObserver(box));
  109. }
  110. return this._observers.get(box).observe(target);
  111. }
  112. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SharedResizeObserver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
  113. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SharedResizeObserver, providedIn: 'root' });
  114. }
  115. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SharedResizeObserver, decorators: [{
  116. type: Injectable,
  117. args: [{
  118. providedIn: 'root',
  119. }]
  120. }], ctorParameters: () => [] });
  121. export { SharedResizeObserver };
  122. //# sourceMappingURL=private.mjs.map