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 } from '@angular/core' ;
10+ import { Platform } from '@angular/cdk/platform' ;
1011import { ScrollDispatcher } from './scroll-dispatcher' ;
12+ import { Observable } from 'rxjs/Observable' ;
13+ import { Subject } from 'rxjs/Subject' ;
14+ import { fromEvent } from 'rxjs/observable/fromEvent' ;
15+ import { merge } from 'rxjs/observable/merge' ;
16+ import { auditTime } from 'rxjs/operator/auditTime' ;
1117
18+ /** Time in ms to throttle the resize events by default. */
19+ export const DEFAULT_RESIZE_TIME = 20 ;
1220
1321/**
1422 * Simple utility for getting the bounds of the browser viewport.
@@ -20,9 +28,22 @@ export class ViewportRuler {
2028 /** Cached document client rectangle. */
2129 private _documentRect ?: ClientRect ;
2230
23- constructor ( scrollDispatcher : ScrollDispatcher ) {
31+ /** Stream of viewport change events. */
32+ private _changed = new Subject < string > ( ) ;
33+
34+ constructor ( platform : Platform , ngZone : NgZone , scrollDispatcher : ScrollDispatcher ) {
35+ if ( platform . isBrowser ) {
36+ ngZone . runOutsideAngular ( ( ) => {
37+ merge < Event > (
38+ fromEvent ( window , 'resize' ) ,
39+ fromEvent ( window , 'orientationchange' )
40+ ) . subscribe ( event => this . _changed . next ( event . type ) ) ;
41+ } ) ;
42+ }
43+
2444 // Subscribe to scroll and resize events and update the document rectangle on changes.
2545 scrollDispatcher . scrolled ( 0 , ( ) => this . _cacheViewportGeometry ( ) ) ;
46+ this . change ( ) . subscribe ( ( ) => this . _cacheViewportGeometry ( ) ) ;
2647 }
2748
2849 /** Gets a ClientRect for the viewport's bounds. */
@@ -56,7 +77,6 @@ export class ViewportRuler {
5677 } ;
5778 }
5879
59-
6080 /**
6181 * Gets the (top, left) scroll position of the viewport.
6282 * @param documentRect
@@ -75,31 +95,40 @@ export class ViewportRuler {
7595 // `document.documentElement` works consistently, where the `top` and `left` values will
7696 // equal negative the scroll position.
7797 const top = - documentRect ! . top || document . body . scrollTop || window . scrollY ||
78- document . documentElement . scrollTop || 0 ;
98+ document . documentElement . scrollTop || 0 ;
7999
80100 const left = - documentRect ! . left || document . body . scrollLeft || window . scrollX ||
81101 document . documentElement . scrollLeft || 0 ;
82102
83103 return { top, left} ;
84104 }
85105
106+ /**
107+ * Returns a stream that emits whenever the size of the viewport changes.
108+ * @param throttle Time in milliseconds to throttle the stream.
109+ */
110+ change ( throttleTime : number = DEFAULT_RESIZE_TIME ) : Observable < string > {
111+ return throttleTime > 0 ? auditTime . call ( this . _changed , throttleTime ) : this . _changed ;
112+ }
113+
86114 /** Caches the latest client rectangle of the document element. */
87115 _cacheViewportGeometry ( ) {
88116 this . _documentRect = document . documentElement . getBoundingClientRect ( ) ;
89117 }
90-
91118}
92119
93120/** @docs -private */
94121export function VIEWPORT_RULER_PROVIDER_FACTORY ( parentRuler : ViewportRuler ,
122+ platform : Platform ,
123+ ngZone : NgZone ,
95124 scrollDispatcher : ScrollDispatcher ) {
96- return parentRuler || new ViewportRuler ( scrollDispatcher ) ;
125+ return parentRuler || new ViewportRuler ( platform , ngZone , scrollDispatcher ) ;
97126}
98127
99128/** @docs -private */
100129export const VIEWPORT_RULER_PROVIDER = {
101130 // If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
102131 provide : ViewportRuler ,
103- deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , ScrollDispatcher ] ,
132+ deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , Platform , NgZone , ScrollDispatcher ] ,
104133 useFactory : VIEWPORT_RULER_PROVIDER_FACTORY
105134} ;
0 commit comments