license import { Platform } from '@angular/cdk/platform' ; import { Injectable , NgZone , OnDestroy , Optional , SkipSelf } from '@angular/core' ; import { merge , of as observableOf , fromEvent , Observable , Subscription } from 'rxjs' ; import { auditTime } from 'rxjs/operators' ; /** Time in ms to throttle the resize events by default. */ export const DEFAULT_RESIZE_TIME = 20 ; /** Object that holds the scroll position of the viewport in each direction. */ export interface ViewportScrollPosition { top : number ; left : number ; } docs-private @ Injectable ( { providedIn : 'root' } ) export class ViewportRuler implements OnDestroy { /** Cached viewport dimensions. */ private _viewportSize : { width : number ; height : number ; } ; /** Stream of viewport change events. */ private _change : Observable < Event > ; /** Subscription to streams that invalidate the cached viewport dimensions. */ private _invalidateCache : Subscription ; constructor ( private _platform , ngZone ) { ngZone . runOutsideAngular ( ( ) => { this . _change = _platform . isBrowser ? merge < Event > ( fromEvent ( window , 'resize' ) , fromEvent ( window , 'orientationchange' ) ) : observableOf ( ) ; this . _invalidateCache = this . change ( ) . subscribe ( ( ) => this . _updateViewportSize ( ) ) ; } ) ; } ngOnDestroy ( ) { this . _invalidateCache . unsubscribe ( ) ; } /** Returns the viewport's width and height. */ getViewportSize ( ) { if ( ! this . _viewportSize ) { this . _updateViewportSize ( ) ; } const output = { width : this . _viewportSize . width , height : this . _viewportSize . height } ; if ( ! this . _platform . isBrowser ) { this . _viewportSize = null ! ; } return output ; } /** Gets a ClientRect for the viewport's bounds. */ getViewportRect ( ) { const scrollPosition = this . getViewportScrollPosition ( ) ; const { width , height } = this . getViewportSize ( ) ; return { top : scrollPosition . top , left : scrollPosition . left , bottom : scrollPosition . top + height , right : scrollPosition . left + width , height , width , } ; } /** Gets the (top, left) scroll position of the viewport. */ getViewportScrollPosition ( ) { if ( ! this . _platform . isBrowser ) { return { top : 0 , left : 0 } ; } const documentElement = document . documentElement ! ; const documentRect = documentElement . getBoundingClientRect ( ) ; const top = - documentRect . top || document . body . scrollTop || window . scrollY || documentElement . scrollTop || 0 ; const left = - documentRect . left || document . body . scrollLeft || window . scrollX || documentElement . scrollLeft || 0 ; return { top , left } ; } param throttleTime change ( throttleTime = DEFAULT_RESIZE_TIME ) { return throttleTime > 0 ? this . _change . pipe ( auditTime ( throttleTime ) ) : this . _change ; } /** Updates the cached viewport size. */ private _updateViewportSize ( ) { this . _viewportSize = this . _platform . isBrowser ? { width : window . innerWidth , height : window . innerHeight } : { width : 0 , height : 0 } ; } } docs-private deprecated breaking-change export function VIEWPORT_RULER_PROVIDER_FACTORY ( parentRuler , platform , ngZone ) { return parentRuler || new ViewportRuler ( platform , ngZone ) ; } docs-private deprecated breaking-change export const VIEWPORT_RULER_PROVIDER = { provide : ViewportRuler , deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , Platform , NgZone ] , useFactory : VIEWPORT_RULER_PROVIDER_FACTORY } ;