125 lines
5.5 KiB
JavaScript
Executable File
125 lines
5.5 KiB
JavaScript
Executable File
import * as i0 from '@angular/core';
|
|
import { inject, NgZone, RendererFactory2, Injectable } from '@angular/core';
|
|
import { Subject, Observable } from 'rxjs';
|
|
import { filter, shareReplay, takeUntil } from 'rxjs/operators';
|
|
|
|
/**
|
|
* Handler that logs "ResizeObserver loop limit exceeded" errors.
|
|
* These errors are not shown in the Chrome console, so we log them to ensure developers are aware.
|
|
* @param e The error
|
|
*/
|
|
const loopLimitExceededErrorHandler = (e) => {
|
|
if (e instanceof ErrorEvent && e.message === 'ResizeObserver loop limit exceeded') {
|
|
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`);
|
|
}
|
|
};
|
|
/**
|
|
* A shared ResizeObserver to be used for a particular box type (content-box, border-box, or
|
|
* device-pixel-content-box)
|
|
*/
|
|
class SingleBoxSharedResizeObserver {
|
|
_box;
|
|
/** Stream that emits when the shared observer is destroyed. */
|
|
_destroyed = new Subject();
|
|
/** Stream of all events from the ResizeObserver. */
|
|
_resizeSubject = new Subject();
|
|
/** ResizeObserver used to observe element resize events. */
|
|
_resizeObserver;
|
|
/** A map of elements to streams of their resize events. */
|
|
_elementObservables = new Map();
|
|
constructor(
|
|
/** The box type to observe for resizes. */
|
|
_box) {
|
|
this._box = _box;
|
|
if (typeof ResizeObserver !== 'undefined') {
|
|
this._resizeObserver = new ResizeObserver(entries => this._resizeSubject.next(entries));
|
|
}
|
|
}
|
|
/**
|
|
* Gets a stream of resize events for the given element.
|
|
* @param target The element to observe.
|
|
* @return The stream of resize events for the element.
|
|
*/
|
|
observe(target) {
|
|
if (!this._elementObservables.has(target)) {
|
|
this._elementObservables.set(target, new Observable(observer => {
|
|
const subscription = this._resizeSubject.subscribe(observer);
|
|
this._resizeObserver?.observe(target, { box: this._box });
|
|
return () => {
|
|
this._resizeObserver?.unobserve(target);
|
|
subscription.unsubscribe();
|
|
this._elementObservables.delete(target);
|
|
};
|
|
}).pipe(filter(entries => entries.some(entry => entry.target === target)),
|
|
// Share a replay of the last event so that subsequent calls to observe the same element
|
|
// receive initial sizing info like the first one. Also enable ref counting so the
|
|
// element will be automatically unobserved when there are no more subscriptions.
|
|
shareReplay({ bufferSize: 1, refCount: true }), takeUntil(this._destroyed)));
|
|
}
|
|
return this._elementObservables.get(target);
|
|
}
|
|
/** Destroys this instance. */
|
|
destroy() {
|
|
this._destroyed.next();
|
|
this._destroyed.complete();
|
|
this._resizeSubject.complete();
|
|
this._elementObservables.clear();
|
|
}
|
|
}
|
|
/**
|
|
* Allows observing resize events on multiple elements using a shared set of ResizeObserver.
|
|
* Sharing a ResizeObserver instance is recommended for better performance (see
|
|
* https://github.com/WICG/resize-observer/issues/59).
|
|
*
|
|
* Rather than share a single `ResizeObserver`, this class creates one `ResizeObserver` per type
|
|
* of observed box ('content-box', 'border-box', and 'device-pixel-content-box'). This avoids
|
|
* later calls to `observe` with a different box type from influencing the events dispatched to
|
|
* earlier calls.
|
|
*/
|
|
class SharedResizeObserver {
|
|
_cleanupErrorListener;
|
|
/** Map of box type to shared resize observer. */
|
|
_observers = new Map();
|
|
/** The Angular zone. */
|
|
_ngZone = inject(NgZone);
|
|
constructor() {
|
|
if (typeof ResizeObserver !== 'undefined' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
this._ngZone.runOutsideAngular(() => {
|
|
const renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
this._cleanupErrorListener = renderer.listen('window', 'error', loopLimitExceededErrorHandler);
|
|
});
|
|
}
|
|
}
|
|
ngOnDestroy() {
|
|
for (const [, observer] of this._observers) {
|
|
observer.destroy();
|
|
}
|
|
this._observers.clear();
|
|
this._cleanupErrorListener?.();
|
|
}
|
|
/**
|
|
* Gets a stream of resize events for the given target element and box type.
|
|
* @param target The element to observe for resizes.
|
|
* @param options Options to pass to the `ResizeObserver`
|
|
* @return The stream of resize events for the element.
|
|
*/
|
|
observe(target, options) {
|
|
const box = options?.box || 'content-box';
|
|
if (!this._observers.has(box)) {
|
|
this._observers.set(box, new SingleBoxSharedResizeObserver(box));
|
|
}
|
|
return this._observers.get(box).observe(target);
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SharedResizeObserver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SharedResizeObserver, providedIn: 'root' });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SharedResizeObserver, decorators: [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: 'root',
|
|
}]
|
|
}], ctorParameters: () => [] });
|
|
|
|
export { SharedResizeObserver };
|
|
//# sourceMappingURL=private.mjs.map
|