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' ;
17
+ import { of as observableOf } from 'rxjs/observable/of' ;
11
18
19
+ /** Time in ms to throttle the resize events by default. */
20
+ export const DEFAULT_RESIZE_TIME = 20 ;
12
21
13
22
/**
14
23
* Simple utility for getting the bounds of the browser viewport.
15
24
* @docs -private
16
25
*/
17
26
@Injectable ( )
18
- export class ViewportRuler {
27
+ export class ViewportRuler implements OnDestroy {
19
28
20
29
/** Cached document client rectangle. */
21
30
private _documentRect ?: ClientRect ;
22
31
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
+
24
43
// 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 ( ) ) ;
26
52
}
27
53
28
54
/** Gets a ClientRect for the viewport's bounds. */
@@ -56,7 +82,6 @@ export class ViewportRuler {
56
82
} ;
57
83
}
58
84
59
-
60
85
/**
61
86
* Gets the (top, left) scroll position of the viewport.
62
87
* @param documentRect
@@ -75,31 +100,40 @@ export class ViewportRuler {
75
100
// `document.documentElement` works consistently, where the `top` and `left` values will
76
101
// equal negative the scroll position.
77
102
const top = - documentRect ! . top || document . body . scrollTop || window . scrollY ||
78
- document . documentElement . scrollTop || 0 ;
103
+ document . documentElement . scrollTop || 0 ;
79
104
80
105
const left = - documentRect ! . left || document . body . scrollLeft || window . scrollX ||
81
106
document . documentElement . scrollLeft || 0 ;
82
107
83
108
return { top, left} ;
84
109
}
85
110
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
+
86
119
/** Caches the latest client rectangle of the document element. */
87
120
_cacheViewportGeometry ( ) {
88
121
this . _documentRect = document . documentElement . getBoundingClientRect ( ) ;
89
122
}
90
-
91
123
}
92
124
93
125
/** @docs -private */
94
126
export function VIEWPORT_RULER_PROVIDER_FACTORY ( parentRuler : ViewportRuler ,
127
+ platform : Platform ,
128
+ ngZone : NgZone ,
95
129
scrollDispatcher : ScrollDispatcher ) {
96
- return parentRuler || new ViewportRuler ( scrollDispatcher ) ;
130
+ return parentRuler || new ViewportRuler ( platform , ngZone , scrollDispatcher ) ;
97
131
}
98
132
99
133
/** @docs -private */
100
134
export const VIEWPORT_RULER_PROVIDER = {
101
135
// If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
102
136
provide : ViewportRuler ,
103
- deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , ScrollDispatcher ] ,
137
+ deps : [ [ new Optional ( ) , new SkipSelf ( ) , ViewportRuler ] , Platform , NgZone , ScrollDispatcher ] ,
104
138
useFactory : VIEWPORT_RULER_PROVIDER_FACTORY
105
139
} ;
0 commit comments