66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { Injectable , Optional , SkipSelf } from '@angular/core' ;
9+ import { Injectable , Optional , SkipSelf , NgZone , OnDestroy } from '@angular/core' ;
10+ import { Platform } from '@angular/cdk/platform' ;
1011import { ScrollDispatcher } from './scroll-dispatcher' ;
12+ import { Observable } from 'rxjs/Observable' ;
13+ import { fromEvent } from 'rxjs/observable/fromEvent' ;
14+ import { merge } from 'rxjs/observable/merge' ;
15+ import { auditTime } from 'rxjs/operator/auditTime' ;
16+ import { Subscription } from 'rxjs/Subscription' ;
17+ import { of as observableOf } from 'rxjs/observable/of' ;
1118
19+ /** Time in ms to throttle the resize events by default. */
20+ export const DEFAULT_RESIZE_TIME = 20 ;
1221
1322/**
1423 * Simple utility for getting the bounds of the browser viewport.
1524 * @docs -private
1625 */
1726@Injectable ( )
18- export class ViewportRuler {
27+ export class ViewportRuler implements OnDestroy {
1928
2029 /** Cached document client rectangle. */
2130 private _documentRect ?: ClientRect ;
2231
23- constructor ( scrollDispatcher : ScrollDispatcher ) {
32+ /** Stream of viewport change events. */
33+ private _change : Observable < Event > ;
34+
35+ /** Subscriptions to streams that invalidate the cached viewport dimensions. */
36+ private _invalidateCacheSubscriptions : Subscription [ ] ;
37+
38+ constructor ( platform : Platform , ngZone : NgZone , scrollDispatcher : ScrollDispatcher ) {
39+ this . _change = platform . isBrowser ? ngZone . runOutsideAngular ( ( ) => {
40+ return merge < Event > ( fromEvent ( window , 'resize' ) , fromEvent ( window , 'orientationchange' ) ) ;
41+ } ) : observableOf ( ) ;
42+
2443 // Subscribe to scroll and resize events and update the document rectangle on changes.
25- scrollDispatcher . scrolled ( 0 , ( ) => this . _cacheViewportGeometry ( ) ) ;
44+ this . _invalidateCacheSubscriptions = [
45+ scrollDispatcher . scrolled ( 0 , ( ) => this . _cacheViewportGeometry ( ) ) ,
46+ this . change ( ) . subscribe ( ( ) => this . _cacheViewportGeometry ( ) )
47+ ] ;
48+ }
49+
50+ ngOnDestroy ( ) {
51+ this . _invalidateCacheSubscriptions . forEach ( subscription => subscription . unsubscribe ( ) ) ;
2652 }
2753
2854 /** Gets a ClientRect for the viewport's bounds. */
@@ -56,7 +82,6 @@ export class ViewportRuler {
5682 } ;
5783 }
5884
59-
6085 /**
6186 * Gets the (top, left) scroll position of the viewport.
6287 * @param documentRect
@@ -75,31 +100,40 @@ export class ViewportRuler {
75100 // `document.documentElement` works consistently, where the `top` and `left` values will
76101 // equal negative the scroll position.
77102 const top = - documentRect ! . top || document . body . scrollTop || window . scrollY ||
78- document . documentElement . scrollTop || 0 ;
103+ document . documentElement . scrollTop || 0 ;
79104
80105 const left = - documentRect ! . left || document . body . scrollLeft || window . scrollX ||
81106 document . documentElement . scrollLeft || 0 ;
82107
83108 return { top, left} ;
84109 }
85110
111+ /**
112+ * Returns a stream that emits whenever the size of the viewport changes.
113+ * @param throttle Time in milliseconds to throttle the stream.
114+ */
115+ change ( throttleTime : number = DEFAULT_RESIZE_TIME ) : Observable < string > {
116+ return throttleTime > 0 ? auditTime . call ( this . _change , throttleTime ) : this . _change ;
117+ }
118+
86119 /** Caches the latest client rectangle of the document element. */
87120 _cacheViewportGeometry ( ) {
88121 this . _documentRect = document . documentElement . getBoundingClientRect ( ) ;
89122 }
90-
91123}
92124
93125/** @docs -private */
94126export function VIEWPORT_RULER_PROVIDER_FACTORY ( parentRuler : ViewportRuler ,
127+ platform : Platform ,
128+ ngZone : NgZone ,
95129 scrollDispatcher : ScrollDispatcher ) {
96- return parentRuler || new ViewportRuler ( scrollDispatcher ) ;
130+ return parentRuler || new ViewportRuler ( platform , ngZone , scrollDispatcher ) ;
97131}
98132
99133/** @docs -private */
100134export const VIEWPORT_RULER_PROVIDER = {
101135 // If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
102136 provide : ViewportRuler ,
103- deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , ScrollDispatcher ] ,
137+ deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , Platform , NgZone , ScrollDispatcher ] ,
104138 useFactory : VIEWPORT_RULER_PROVIDER_FACTORY
105139} ;
0 commit comments