8
8
9
9
import { Directionality } from '@angular/cdk/bidi' ;
10
10
import { ListRange } from '@angular/cdk/collections' ;
11
- import { supportsScrollBehavior } from '@angular/cdk/platform' ;
12
11
import {
13
12
ChangeDetectionStrategy ,
14
13
ChangeDetectorRef ,
@@ -24,8 +23,10 @@ import {
24
23
ViewChild ,
25
24
ViewEncapsulation ,
26
25
} from '@angular/core' ;
27
- import { animationFrameScheduler , fromEvent , Observable , Subject } from 'rxjs' ;
26
+ import { animationFrameScheduler , Observable , Subject } from 'rxjs' ;
28
27
import { sampleTime , startWith , takeUntil } from 'rxjs/operators' ;
28
+ import { ScrollDispatcher } from './scroll-dispatcher' ;
29
+ import { CdkScrollable , ExtendedScrollToOptions } from './scrollable' ;
29
30
import { CdkVirtualForOf } from './virtual-for-of' ;
30
31
import { VIRTUAL_SCROLL_STRATEGY , VirtualScrollStrategy } from './virtual-scroll-strategy' ;
31
32
@@ -50,7 +51,7 @@ function rangesEqual(r1: ListRange, r2: ListRange): boolean {
50
51
encapsulation : ViewEncapsulation . None ,
51
52
changeDetection : ChangeDetectionStrategy . OnPush ,
52
53
} )
53
- export class CdkVirtualScrollViewport implements OnInit , OnDestroy {
54
+ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit , OnDestroy {
54
55
/** Emits when the viewport is detached from a CdkVirtualForOf. */
55
56
private _detachedSubject = new Subject < void > ( ) ;
56
57
@@ -67,7 +68,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
67
68
/** Emits when the index of the first element visible in the viewport changes. */
68
69
@Output ( ) scrolledIndexChange : Observable < number > = Observable . create ( observer =>
69
70
this . _scrollStrategy . scrolledIndexChange . subscribe ( index =>
70
- Promise . resolve ( ) . then ( ( ) => this . _ngZone . run ( ( ) => observer . next ( index ) ) ) ) ) ;
71
+ Promise . resolve ( ) . then ( ( ) => this . ngZone . run ( ( ) => observer . next ( index ) ) ) ) ) ;
71
72
72
73
/** The element that wraps the rendered content. */
73
74
@ViewChild ( 'contentWrapper' ) _contentWrapper : ElementRef < HTMLElement > ;
@@ -113,9 +114,6 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
113
114
*/
114
115
private _renderedContentOffsetNeedsRewrite = false ;
115
116
116
- /** Observable that emits when the viewport is destroyed. */
117
- private _destroyed = new Subject < void > ( ) ;
118
-
119
117
/** Whether there is a pending change detection cycle. */
120
118
private _isChangeDetectionPending = false ;
121
119
@@ -124,27 +122,31 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
124
122
125
123
constructor ( public elementRef : ElementRef < HTMLElement > ,
126
124
private _changeDetectorRef : ChangeDetectorRef ,
127
- private _ngZone : NgZone ,
125
+ ngZone : NgZone ,
128
126
@Inject ( VIRTUAL_SCROLL_STRATEGY ) private _scrollStrategy : VirtualScrollStrategy ,
129
- @Optional ( ) private _dir : Directionality ) { }
127
+ @Optional ( ) dir : Directionality ,
128
+ scrollDispatcher : ScrollDispatcher ) {
129
+ super ( elementRef , scrollDispatcher , ngZone , dir ) ;
130
+ }
130
131
131
132
ngOnInit ( ) {
133
+ super . ngOnInit ( ) ;
134
+
132
135
// It's still too early to measure the viewport at this point. Deferring with a promise allows
133
136
// the Viewport to be rendered with the correct size before we measure. We run this outside the
134
137
// zone to avoid causing more change detection cycles. We handle the change detection loop
135
138
// ourselves instead.
136
- this . _ngZone . runOutsideAngular ( ( ) => Promise . resolve ( ) . then ( ( ) => {
139
+ this . ngZone . runOutsideAngular ( ( ) => Promise . resolve ( ) . then ( ( ) => {
137
140
this . _measureViewportSize ( ) ;
138
141
this . _scrollStrategy . attach ( this ) ;
139
142
140
- fromEvent ( this . elementRef . nativeElement , 'scroll' )
143
+ this . elementScrolled ( )
141
144
. pipe (
142
145
// Start off with a fake scroll event so we properly detect our initial position.
143
146
startWith ( null ! ) ,
144
147
// Sample the scroll stream at every animation frame. This way if there are multiple
145
148
// scroll events in the same frame we only need to recheck our layout once.
146
- sampleTime ( 0 , animationFrameScheduler ) ,
147
- takeUntil ( this . _destroyed ) )
149
+ sampleTime ( 0 , animationFrameScheduler ) )
148
150
. subscribe ( ( ) => this . _scrollStrategy . onContentScrolled ( ) ) ;
149
151
150
152
this . _markChangeDetectionNeeded ( ) ;
@@ -154,12 +156,12 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
154
156
ngOnDestroy ( ) {
155
157
this . detach ( ) ;
156
158
this . _scrollStrategy . detach ( ) ;
157
- this . _destroyed . next ( ) ;
158
159
159
160
// Complete all subjects
160
161
this . _renderedRangeSubject . complete ( ) ;
161
162
this . _detachedSubject . complete ( ) ;
162
- this . _destroyed . complete ( ) ;
163
+
164
+ super . ngOnDestroy ( ) ;
163
165
}
164
166
165
167
/** Attaches a `CdkVirtualForOf` to this viewport. */
@@ -171,7 +173,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
171
173
// Subscribe to the data stream of the CdkVirtualForOf to keep track of when the data length
172
174
// changes. Run outside the zone to avoid triggering change detection, since we're managing the
173
175
// change detection loop ourselves.
174
- this . _ngZone . runOutsideAngular ( ( ) => {
176
+ this . ngZone . runOutsideAngular ( ( ) => {
175
177
this . _forOf = forOf ;
176
178
this . _forOf . dataStream . pipe ( takeUntil ( this . _detachedSubject ) ) . subscribe ( data => {
177
179
const newLength = data . length ;
@@ -244,7 +246,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
244
246
setRenderedContentOffset ( offset : number , to : 'to-start' | 'to-end' = 'to-start' ) {
245
247
// For a horizontal viewport in a right-to-left language we need to translate along the x-axis
246
248
// in the negative direction.
247
- const axisDirection = this . orientation == 'horizontal' && this . _dir && this . _dir . value == 'rtl'
249
+ const axisDirection = this . orientation == 'horizontal' && this . dir && this . dir . value == 'rtl'
248
250
? - 1 : 1 ;
249
251
const axis = this . orientation === 'horizontal' ? 'X' : 'Y' ;
250
252
let transform = `translate${ axis } (${ Number ( axisDirection * offset ) } px)` ;
@@ -280,23 +282,13 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
280
282
* @param behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`.
281
283
*/
282
284
scrollToOffset ( offset : number , behavior : ScrollBehavior = 'auto' ) {
283
- const viewportElement = this . elementRef . nativeElement ;
284
-
285
- // For a horizontal viewport in a right-to-left language we need to calculate what `scrollRight`
286
- // would be.
287
- offset = this . orientation == 'horizontal' && this . _dir && this . _dir . value == 'rtl' ?
288
- this . _getRightOffsetAsLeftOffset ( offset ) : offset ;
289
-
290
- if ( supportsScrollBehavior ( ) ) {
291
- const offsetDirection = this . orientation === 'horizontal' ? 'left' : 'top' ;
292
- viewportElement . scrollTo ( { [ offsetDirection ] : offset , behavior} ) ;
285
+ const options : ExtendedScrollToOptions = { behavior} ;
286
+ if ( this . orientation === 'horizontal' ) {
287
+ options . start = offset ;
293
288
} else {
294
- if ( this . orientation === 'horizontal' ) {
295
- viewportElement . scrollLeft = offset ;
296
- } else {
297
- viewportElement . scrollTop = offset ;
298
- }
289
+ options . top = offset ;
299
290
}
291
+ this . scrollTo ( options ) ;
300
292
}
301
293
302
294
/**
@@ -308,16 +300,14 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
308
300
this . _scrollStrategy . scrollToIndex ( index , behavior ) ;
309
301
}
310
302
311
- /** Gets the current scroll offset from the start of the viewport (in pixels). */
312
- measureScrollOffset ( ) : number {
313
- if ( this . orientation == 'horizontal' ) {
314
- const offset = this . elementRef . nativeElement . scrollLeft ;
315
- // For a horizontal viewport in a right-to-left language we need to calculate what
316
- // `scrollRight` would be.
317
- return this . _dir && this . _dir . value == 'rtl' ?
318
- this . _getRightOffsetAsLeftOffset ( offset ) : offset ;
319
- }
320
- return this . elementRef . nativeElement . scrollTop ;
303
+ /**
304
+ * Gets the current scroll offset from the start of the viewport (in pixels).
305
+ * @param from The edge to measure the offset from. Defaults to 'top' in vertical mode and 'start'
306
+ * in horizontal mode.
307
+ */
308
+ measureScrollOffset ( from ?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end' ) : number {
309
+ return super . measureScrollOffset (
310
+ from ? from : this . orientation === 'horizontal' ? 'start' : 'top' ) ;
321
311
}
322
312
323
313
/** Measure the combined size of all of the rendered items. */
@@ -361,7 +351,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
361
351
// properties sequentially we only have to run `_doChangeDetection` once at the end.
362
352
if ( ! this . _isChangeDetectionPending ) {
363
353
this . _isChangeDetectionPending = true ;
364
- this . _ngZone . runOutsideAngular ( ( ) => Promise . resolve ( ) . then ( ( ) => {
354
+ this . ngZone . runOutsideAngular ( ( ) => Promise . resolve ( ) . then ( ( ) => {
365
355
this . _doChangeDetection ( ) ;
366
356
} ) ) ;
367
357
}
@@ -374,7 +364,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
374
364
// Apply changes to Angular bindings. Note: We must call `markForCheck` to run change detection
375
365
// from the root, since the repeated items are content projected in. Calling `detectChanges`
376
366
// instead does not properly check the projected content.
377
- this . _ngZone . run ( ( ) => this . _changeDetectorRef . markForCheck ( ) ) ;
367
+ this . ngZone . run ( ( ) => this . _changeDetectorRef . markForCheck ( ) ) ;
378
368
// Apply the content transform. The transform can't be set via an Angular binding because
379
369
// bypassSecurityTrustStyle is banned in Google. However the value is safe, it's composed of
380
370
// string literals, a variable that can only be 'X' or 'Y', and user input that is run through
@@ -387,9 +377,4 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
387
377
fn ( ) ;
388
378
}
389
379
}
390
-
391
- /** Expresses an offset from the right as the equivalent offset from the left. */
392
- private _getRightOffsetAsLeftOffset ( offset : number ) : number {
393
- return Math . max ( 0 , this . _totalContentSize - this . _viewportSize - offset ) ;
394
- }
395
380
}
0 commit comments