6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
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' ;
10
11
import { 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' ;
11
17
18
+ /** Time in ms to throttle the resize events by default. */
19
+ export const DEFAULT_RESIZE_TIME = 20 ;
12
20
13
21
/**
14
22
* Simple utility for getting the bounds of the browser viewport.
15
23
* @docs -private
16
24
*/
17
25
@Injectable ( )
18
- export class ViewportRuler {
26
+ export class ViewportRuler implements OnDestroy {
19
27
20
28
/** Cached document client rectangle. */
21
29
private _documentRect ?: ClientRect ;
22
30
23
- constructor ( scrollDispatcher : ScrollDispatcher ) {
31
+ /** Stream of viewport change events. */
32
+ private _change : Observable < Event > ;
33
+
34
+ /** Subscriptions to streams that invalidate the cached viewport dimensions. */
35
+ private _invalidateCacheSubscriptions : Subscription [ ] ;
36
+
37
+ constructor ( platform : Platform , ngZone : NgZone , scrollDispatcher : ScrollDispatcher ) {
38
+ if ( platform . isBrowser ) {
39
+ this . _change = ngZone . runOutsideAngular ( ( ) => {
40
+ return merge < Event > ( fromEvent ( window , 'resize' ) , fromEvent ( window , 'orientationchange' ) ) ;
41
+ } ) ;
42
+ }
43
+
24
44
// Subscribe to scroll and resize events and update the document rectangle on changes.
25
- scrollDispatcher . scrolled ( 0 , ( ) => this . _cacheViewportGeometry ( ) ) ;
45
+ this . _invalidateCacheSubscriptions = [
46
+ scrollDispatcher . scrolled ( 0 , ( ) => this . _cacheViewportGeometry ( ) ) ,
47
+ this . change ( ) . subscribe ( ( ) => this . _cacheViewportGeometry ( ) )
48
+ ] ;
49
+ }
50
+
51
+ ngOnDestroy ( ) {
52
+ this . _invalidateCacheSubscriptions . forEach ( subscription => subscription . unsubscribe ( ) ) ;
26
53
}
27
54
28
55
/** Gets a ClientRect for the viewport's bounds. */
@@ -56,7 +83,6 @@ export class ViewportRuler {
56
83
} ;
57
84
}
58
85
59
-
60
86
/**
61
87
* Gets the (top, left) scroll position of the viewport.
62
88
* @param documentRect
@@ -75,31 +101,40 @@ export class ViewportRuler {
75
101
// `document.documentElement` works consistently, where the `top` and `left` values will
76
102
// equal negative the scroll position.
77
103
const top = - documentRect ! . top || document . body . scrollTop || window . scrollY ||
78
- document . documentElement . scrollTop || 0 ;
104
+ document . documentElement . scrollTop || 0 ;
79
105
80
106
const left = - documentRect ! . left || document . body . scrollLeft || window . scrollX ||
81
107
document . documentElement . scrollLeft || 0 ;
82
108
83
109
return { top, left} ;
84
110
}
85
111
112
+ /**
113
+ * Returns a stream that emits whenever the size of the viewport changes.
114
+ * @param throttle Time in milliseconds to throttle the stream.
115
+ */
116
+ change ( throttleTime : number = DEFAULT_RESIZE_TIME ) : Observable < string > {
117
+ return throttleTime > 0 ? auditTime . call ( this . _change , throttleTime ) : this . _change ;
118
+ }
119
+
86
120
/** Caches the latest client rectangle of the document element. */
87
121
_cacheViewportGeometry ( ) {
88
122
this . _documentRect = document . documentElement . getBoundingClientRect ( ) ;
89
123
}
90
-
91
124
}
92
125
93
126
/** @docs -private */
94
127
export function VIEWPORT_RULER_PROVIDER_FACTORY ( parentRuler : ViewportRuler ,
128
+ platform : Platform ,
129
+ ngZone : NgZone ,
95
130
scrollDispatcher : ScrollDispatcher ) {
96
- return parentRuler || new ViewportRuler ( scrollDispatcher ) ;
131
+ return parentRuler || new ViewportRuler ( platform , ngZone , scrollDispatcher ) ;
97
132
}
98
133
99
134
/** @docs -private */
100
135
export const VIEWPORT_RULER_PROVIDER = {
101
136
// If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
102
137
provide : ViewportRuler ,
103
- deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , ScrollDispatcher ] ,
138
+ deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , Platform , NgZone , ScrollDispatcher ] ,
104
139
useFactory : VIEWPORT_RULER_PROVIDER_FACTORY
105
140
} ;
0 commit comments