UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

125 lines (122 loc) 5.61 kB
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: "19.2.0", ngImport: i0, type: SharedResizeObserver, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: SharedResizeObserver, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: SharedResizeObserver, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [] }); export { SharedResizeObserver }; //# sourceMappingURL=private.mjs.map