From 176213d705f25def682644e29824b340ca1637b6 Mon Sep 17 00:00:00 2001 From: Maxi Date: Wed, 8 Jun 2022 09:03:32 +0200 Subject: [PATCH] feat(cdk/scrolling): make scroller element configurable for virtual scrolling (#24394) --- src/cdk/scrolling/public-api.ts | 3 + src/cdk/scrolling/scrollable.ts | 4 +- src/cdk/scrolling/scrolling-module.ts | 12 +- .../scrolling/virtual-scroll-viewport.scss | 17 +- .../scrolling/virtual-scroll-viewport.spec.ts | 161 +++++++++++++++++- src/cdk/scrolling/virtual-scroll-viewport.ts | 79 +++++++-- .../scrolling/virtual-scrollable-element.ts | 42 +++++ .../scrolling/virtual-scrollable-window.ts | 40 +++++ src/cdk/scrolling/virtual-scrollable.ts | 48 ++++++ .../virtual-scroll/virtual-scroll-demo.html | 22 +++ tools/public_api_guard/cdk/scrolling.md | 57 ++++++- 11 files changed, 452 insertions(+), 33 deletions(-) create mode 100644 src/cdk/scrolling/virtual-scrollable-element.ts create mode 100644 src/cdk/scrolling/virtual-scrollable-window.ts create mode 100644 src/cdk/scrolling/virtual-scrollable.ts diff --git a/src/cdk/scrolling/public-api.ts b/src/cdk/scrolling/public-api.ts index f4a89ab4032c..3153cd2845e7 100644 --- a/src/cdk/scrolling/public-api.ts +++ b/src/cdk/scrolling/public-api.ts @@ -15,3 +15,6 @@ export * from './virtual-for-of'; export * from './virtual-scroll-strategy'; export * from './virtual-scroll-viewport'; export * from './virtual-scroll-repeater'; +export * from './virtual-scrollable'; +export * from './virtual-scrollable-element'; +export * from './virtual-scrollable-window'; diff --git a/src/cdk/scrolling/scrollable.ts b/src/cdk/scrolling/scrollable.ts index d095e97efed3..7655f2c7314d 100644 --- a/src/cdk/scrolling/scrollable.ts +++ b/src/cdk/scrolling/scrollable.ts @@ -45,9 +45,9 @@ export type ExtendedScrollToOptions = _XAxis & _YAxis & ScrollOptions; selector: '[cdk-scrollable], [cdkScrollable]', }) export class CdkScrollable implements OnInit, OnDestroy { - private readonly _destroyed = new Subject(); + protected readonly _destroyed = new Subject(); - private _elementScrolled: Observable = new Observable((observer: Observer) => + protected _elementScrolled: Observable = new Observable((observer: Observer) => this.ngZone.runOutsideAngular(() => fromEvent(this.elementRef.nativeElement, 'scroll') .pipe(takeUntil(this._destroyed)) diff --git a/src/cdk/scrolling/scrolling-module.ts b/src/cdk/scrolling/scrolling-module.ts index 0f3dcecdc8f0..213c2711df77 100644 --- a/src/cdk/scrolling/scrolling-module.ts +++ b/src/cdk/scrolling/scrolling-module.ts @@ -12,6 +12,8 @@ import {CdkFixedSizeVirtualScroll} from './fixed-size-virtual-scroll'; import {CdkScrollable} from './scrollable'; import {CdkVirtualForOf} from './virtual-for-of'; import {CdkVirtualScrollViewport} from './virtual-scroll-viewport'; +import {CdkVirtualScrollableElement} from './virtual-scrollable-element'; +import {CdkVirtualScrollableWindow} from './virtual-scrollable-window'; @NgModule({ exports: [CdkScrollable], @@ -30,7 +32,15 @@ export class CdkScrollableModule {} CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, + CdkVirtualScrollableWindow, + CdkVirtualScrollableElement, + ], + declarations: [ + CdkFixedSizeVirtualScroll, + CdkVirtualForOf, + CdkVirtualScrollViewport, + CdkVirtualScrollableWindow, + CdkVirtualScrollableElement, ], - declarations: [CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport], }) export class ScrollingModule {} diff --git a/src/cdk/scrolling/virtual-scroll-viewport.scss b/src/cdk/scrolling/virtual-scroll-viewport.scss index c24aa19ed3ef..0fc8853165da 100644 --- a/src/cdk/scrolling/virtual-scroll-viewport.scss +++ b/src/cdk/scrolling/virtual-scroll-viewport.scss @@ -27,14 +27,18 @@ } -// Scrolling container. +// viewport cdk-virtual-scroll-viewport { display: block; position: relative; - overflow: auto; - contain: strict; transform: translateZ(0); +} + +// Scrolling container. +.cdk-virtual-scrollable { + overflow: auto; will-change: scroll-position; + contain: strict; -webkit-overflow-scrolling: touch; } @@ -69,19 +73,14 @@ cdk-virtual-scroll-viewport { // set if it were rendered all at once. This ensures that the scrollable content region is the // correct size. .cdk-virtual-scroll-spacer { - position: absolute; - top: 0; - left: 0; height: 1px; - width: 1px; transform-origin: 0 0; + flex: 0 0 auto; // prevents spacer from collapsing if display: flex is applied // Note: We can't put `will-change: transform;` here because it causes Safari to not update the // viewport's `scrollHeight` when the spacer's transform changes. [dir='rtl'] & { - right: 0; - left: auto; transform-origin: 100% 0; } } diff --git a/src/cdk/scrolling/virtual-scroll-viewport.spec.ts b/src/cdk/scrolling/virtual-scroll-viewport.spec.ts index 223cb0802674..44366f6f51c4 100644 --- a/src/cdk/scrolling/virtual-scroll-viewport.spec.ts +++ b/src/cdk/scrolling/virtual-scroll-viewport.spec.ts @@ -772,9 +772,9 @@ describe('CdkVirtualScrollViewport', () => { spyOn(dispatcher, 'register').and.callThrough(); spyOn(dispatcher, 'deregister').and.callThrough(); finishInit(fixture); - expect(dispatcher.register).toHaveBeenCalledWith(testComponent.viewport); + expect(dispatcher.register).toHaveBeenCalledWith(testComponent.viewport.scrollable); fixture.destroy(); - expect(dispatcher.deregister).toHaveBeenCalledWith(testComponent.viewport); + expect(dispatcher.deregister).toHaveBeenCalledWith(testComponent.viewport.scrollable); }), )); @@ -1086,6 +1086,76 @@ describe('CdkVirtualScrollViewport', () => { expect(viewport.getOffsetToRenderedContentStart()).toBe(0); })); }); + + describe('with custom scrolling element', () => { + let fixture: ComponentFixture; + let testComponent: VirtualScrollWithCustomScrollingElement; + let viewport: CdkVirtualScrollViewport; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ScrollingModule], + declarations: [VirtualScrollWithCustomScrollingElement], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(VirtualScrollWithCustomScrollingElement); + testComponent = fixture.componentInstance; + viewport = testComponent.viewport; + }); + + it('should measure viewport offset', fakeAsync(() => { + finishInit(fixture); + + expect(viewport.measureViewportOffset('top')) + .withContext('with scrolling-element padding-top: 50 offset should be 50') + .toBe(50); + })); + + it('should measure scroll offset', fakeAsync(() => { + finishInit(fixture); + triggerScroll(viewport, 100); + fixture.detectChanges(); + flush(); + + expect(viewport.measureScrollOffset('top')) + .withContext('should be 50 (actual scroll offset - viewport offset)') + .toBe(50); + })); + }); + + describe('with scrollable window', () => { + let fixture: ComponentFixture; + let testComponent: VirtualScrollWithScrollableWindow; + let viewport: CdkVirtualScrollViewport; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ScrollingModule], + declarations: [VirtualScrollWithScrollableWindow], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(VirtualScrollWithScrollableWindow); + testComponent = fixture.componentInstance; + viewport = testComponent.viewport; + }); + + it('should measure scroll offset', fakeAsync(() => { + finishInit(fixture); + viewport.scrollToOffset(100 + 8); // the +8 is due to a horizontal scrollbar + dispatchFakeEvent(window, 'scroll', true); + tick(); + fixture.detectChanges(); + flush(); + + expect(viewport.measureScrollOffset('top')) + .withContext('should be 50 (actual scroll offset - viewport offset)') + .toBe(50); + })); + }); }); /** Finish initializing the virtual scroll component at the beginning of a test. */ @@ -1109,7 +1179,7 @@ function triggerScroll(viewport: CdkVirtualScrollViewport, offset?: number) { if (offset !== undefined) { viewport.scrollToOffset(offset); } - dispatchFakeEvent(viewport.elementRef.nativeElement, 'scroll'); + dispatchFakeEvent(viewport.scrollable.getElementRef().nativeElement, 'scroll'); animationFrameScheduler.flush(); } @@ -1391,3 +1461,88 @@ class VirtualScrollWithAppendOnly { .fill(0) .map((_, i) => i); } + +@Component({ + template: ` +
+ +
{{item}}
+
+
+ `, + styles: [ + ` + .cdk-virtual-scroll-content-wrapper { + display: flex; + flex-direction: column; + } + + .cdk-virtual-scroll-viewport { + width: 200px; + height: 200px; + background-color: #f5f5f5; + } + + .item { + width: 100%; + height: 50px; + box-sizing: border-box; + border: 1px dashed #ccc; + } + + .scrolling-element { + padding-top: 50px; + } + `, + ], + encapsulation: ViewEncapsulation.None, +}) +class VirtualScrollWithCustomScrollingElement { + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + itemSize = 50; + items = Array(20000) + .fill(0) + .map((_, i) => i); +} + +@Component({ + template: ` +
+ +
{{item}}
+
+ `, + styles: [ + ` + .cdk-virtual-scroll-content-wrapper { + display: flex; + flex-direction: column; + } + + .cdk-virtual-scroll-viewport { + width: 200px; + height: 200px; + background-color: #f5f5f5; + } + + .item { + width: 100%; + height: 50px; + box-sizing: border-box; + border: 1px dashed #ccc; + } + + .before-virtual-viewport { + height: 50px; + } + `, + ], + encapsulation: ViewEncapsulation.None, +}) +class VirtualScrollWithScrollableWindow { + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + itemSize = 50; + items = Array(20000) + .fill(0) + .map((_, i) => i); +} diff --git a/src/cdk/scrolling/virtual-scroll-viewport.ts b/src/cdk/scrolling/virtual-scroll-viewport.ts index d9181fbf8569..0f5257a25a1e 100644 --- a/src/cdk/scrolling/virtual-scroll-viewport.ts +++ b/src/cdk/scrolling/virtual-scroll-viewport.ts @@ -38,6 +38,7 @@ import {VIRTUAL_SCROLL_STRATEGY, VirtualScrollStrategy} from './virtual-scroll-s import {ViewportRuler} from './viewport-ruler'; import {CdkVirtualScrollRepeater} from './virtual-scroll-repeater'; import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion'; +import {CdkVirtualScrollable, VIRTUAL_SCROLLABLE} from './virtual-scrollable'; /** Checks if the given ranges are equal. */ function rangesEqual(r1: ListRange, r2: ListRange): boolean { @@ -67,11 +68,15 @@ const SCROLL_SCHEDULER = providers: [ { provide: CdkScrollable, - useExisting: CdkVirtualScrollViewport, + useFactory: ( + virtualScrollable: CdkVirtualScrollable | null, + viewport: CdkVirtualScrollViewport, + ) => virtualScrollable || viewport, + deps: [CdkVirtualScrollable, CdkVirtualScrollViewport], }, ], }) -export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy { +export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements OnInit, OnDestroy { /** Emits when the viewport is detached from a CdkVirtualForOf. */ private readonly _detachedSubject = new Subject(); @@ -83,6 +88,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O get orientation() { return this._orientation; } + set orientation(orientation: 'horizontal' | 'vertical') { if (this._orientation !== orientation) { this._orientation = orientation; @@ -179,6 +185,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O @Optional() dir: Directionality, scrollDispatcher: ScrollDispatcher, viewportRuler: ViewportRuler, + @Optional() @Inject(VIRTUAL_SCROLLABLE) public scrollable: CdkVirtualScrollable, ) { super(elementRef, scrollDispatcher, ngZone, dir); @@ -189,11 +196,18 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O this._viewportChanges = viewportRuler.change().subscribe(() => { this.checkViewportSize(); }); + + if (!this.scrollable) { + // No scrollable is provided, so the virtual-scroll-viewport needs to become a scrollable + this.elementRef.nativeElement.classList.add('cdk-virtual-scrollable'); + this.scrollable = this; + } } override ngOnInit() { - super.ngOnInit(); - + if (this.scrollable === this) { + super.ngOnInit(); + } // It's still too early to measure the viewport at this point. Deferring with a promise allows // the Viewport to be rendered with the correct size before we measure. We run this outside the // zone to avoid causing more change detection cycles. We handle the change detection loop @@ -203,7 +217,8 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O this._measureViewportSize(); this._scrollStrategy.attach(this); - this.elementScrolled() + this.scrollable + .elementScrolled() .pipe( // Start off with a fake scroll event so we properly detect our initial position. startWith(null), @@ -279,6 +294,10 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O return this._renderedRange; } + measureBoundingClientRectWithScrollOffset(from: 'left' | 'top' | 'right' | 'bottom'): number { + return this.getElementRef().nativeElement.getBoundingClientRect()[from]; + } + /** * Sets the total size of all content (in pixels), including content that is not currently * rendered. @@ -361,7 +380,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O } else { options.top = offset; } - this.scrollTo(options); + this.scrollable.scrollTo(options); } /** @@ -374,16 +393,52 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O } /** - * Gets the current scroll offset from the start of the viewport (in pixels). + * Gets the current scroll offset from the start of the scrollable (in pixels). * @param from The edge to measure the offset from. Defaults to 'top' in vertical mode and 'start' * in horizontal mode. */ override measureScrollOffset( from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end', ): number { - return from - ? super.measureScrollOffset(from) - : super.measureScrollOffset(this.orientation === 'horizontal' ? 'start' : 'top'); + // This is to break the call cycle + let measureScrollOffset: InstanceType['measureScrollOffset']; + if (this.scrollable == this) { + measureScrollOffset = (_from: NonNullable) => super.measureScrollOffset(_from); + } else { + measureScrollOffset = (_from: NonNullable) => + this.scrollable.measureScrollOffset(_from); + } + + return Math.max( + 0, + measureScrollOffset(from ?? (this.orientation === 'horizontal' ? 'start' : 'top')) - + this.measureViewportOffset(), + ); + } + + /** + * Measures the offset of the viewport from the scrolling container + * @param from The edge to measure from. + */ + measureViewportOffset(from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end') { + let fromRect: 'left' | 'top' | 'right' | 'bottom'; + const LEFT = 'left'; + const RIGHT = 'right'; + const isRtl = this.dir?.value == 'rtl'; + if (from == 'start') { + fromRect = isRtl ? RIGHT : LEFT; + } else if (from == 'end') { + fromRect = isRtl ? LEFT : RIGHT; + } else if (from) { + fromRect = from; + } else { + fromRect = this.orientation === 'horizontal' ? 'left' : 'top'; + } + + const scrollerClientRect = this.scrollable.measureBoundingClientRectWithScrollOffset(fromRect); + const viewportClientRect = this.elementRef.nativeElement.getBoundingClientRect()[fromRect]; + + return viewportClientRect - scrollerClientRect; } /** Measure the combined size of all of the rendered items. */ @@ -412,9 +467,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O /** Measure the viewport size. */ private _measureViewportSize() { - const viewportEl = this.elementRef.nativeElement; - this._viewportSize = - this.orientation === 'horizontal' ? viewportEl.clientWidth : viewportEl.clientHeight; + this._viewportSize = this.scrollable.measureViewportSize(this.orientation); } /** Queue up change detection to run. */ diff --git a/src/cdk/scrolling/virtual-scrollable-element.ts b/src/cdk/scrolling/virtual-scrollable-element.ts new file mode 100644 index 000000000000..7d7c74a5f604 --- /dev/null +++ b/src/cdk/scrolling/virtual-scrollable-element.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directionality} from '@angular/cdk/bidi'; +import {Directive, ElementRef, NgZone, Optional} from '@angular/core'; +import {ScrollDispatcher} from './scroll-dispatcher'; +import {CdkVirtualScrollable, VIRTUAL_SCROLLABLE} from './virtual-scrollable'; + +/** + * Provides a virtual scrollable for the element it is attached to. + */ +@Directive({ + selector: '[cdkVirtualScrollingElement]', + providers: [{provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableElement}], + host: { + 'class': 'cdk-virtual-scrollable', + }, +}) +export class CdkVirtualScrollableElement extends CdkVirtualScrollable { + constructor( + elementRef: ElementRef, + scrollDispatcher: ScrollDispatcher, + ngZone: NgZone, + @Optional() dir: Directionality, + ) { + super(elementRef, scrollDispatcher, ngZone, dir); + } + + override measureBoundingClientRectWithScrollOffset( + from: 'left' | 'top' | 'right' | 'bottom', + ): number { + return ( + this.getElementRef().nativeElement.getBoundingClientRect()[from] - + this.measureScrollOffset(from) + ); + } +} diff --git a/src/cdk/scrolling/virtual-scrollable-window.ts b/src/cdk/scrolling/virtual-scrollable-window.ts new file mode 100644 index 000000000000..44644fc6b257 --- /dev/null +++ b/src/cdk/scrolling/virtual-scrollable-window.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directionality} from '@angular/cdk/bidi'; +import {Directive, ElementRef, NgZone, Optional} from '@angular/core'; +import {fromEvent, Observable, Observer} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; +import {ScrollDispatcher} from './scroll-dispatcher'; +import {CdkVirtualScrollable, VIRTUAL_SCROLLABLE} from './virtual-scrollable'; + +/** + * Provides as virtual scrollable for the global / window scrollbar. + */ +@Directive({ + selector: 'cdk-virtual-scroll-viewport[scrollWindow]', + providers: [{provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableWindow}], +}) +export class CdkVirtualScrollableWindow extends CdkVirtualScrollable { + protected override _elementScrolled: Observable = new Observable( + (observer: Observer) => + this.ngZone.runOutsideAngular(() => + fromEvent(document, 'scroll').pipe(takeUntil(this._destroyed)).subscribe(observer), + ), + ); + + constructor(scrollDispatcher: ScrollDispatcher, ngZone: NgZone, @Optional() dir: Directionality) { + super(new ElementRef(document.documentElement), scrollDispatcher, ngZone, dir); + } + + override measureBoundingClientRectWithScrollOffset( + from: 'left' | 'top' | 'right' | 'bottom', + ): number { + return this.getElementRef().nativeElement.getBoundingClientRect()[from]; + } +} diff --git a/src/cdk/scrolling/virtual-scrollable.ts b/src/cdk/scrolling/virtual-scrollable.ts new file mode 100644 index 000000000000..4ded6984948c --- /dev/null +++ b/src/cdk/scrolling/virtual-scrollable.ts @@ -0,0 +1,48 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directionality} from '@angular/cdk/bidi'; +import {Directive, ElementRef, InjectionToken, NgZone, Optional} from '@angular/core'; +import {ScrollDispatcher} from './scroll-dispatcher'; +import {CdkScrollable} from './scrollable'; + +export const VIRTUAL_SCROLLABLE = new InjectionToken('VIRTUAL_SCROLLABLE'); + +/** + * Extending the {@link CdkScrollable} to be used as scrolling container for virtual scrolling. + */ +@Directive() +export abstract class CdkVirtualScrollable extends CdkScrollable { + constructor( + elementRef: ElementRef, + scrollDispatcher: ScrollDispatcher, + ngZone: NgZone, + @Optional() dir?: Directionality, + ) { + super(elementRef, scrollDispatcher, ngZone, dir); + } + + /** + * Measure the viewport size for the provided orientation. + * + * @param orientation The orientation to measure the size from. + */ + measureViewportSize(orientation: 'horizontal' | 'vertical') { + const viewportEl = this.elementRef.nativeElement; + return orientation === 'horizontal' ? viewportEl.clientWidth : viewportEl.clientHeight; + } + + /** + * Measure the bounding ClientRect size including the scroll offset. + * + * @param from The edge to measure from. + */ + abstract measureBoundingClientRectWithScrollOffset( + from: 'left' | 'top' | 'right' | 'bottom', + ): number; +} diff --git a/src/dev-app/virtual-scroll/virtual-scroll-demo.html b/src/dev-app/virtual-scroll/virtual-scroll-demo.html index 7ada2fe5a921..c70ddd7c9ed1 100644 --- a/src/dev-app/virtual-scroll/virtual-scroll-demo.html +++ b/src/dev-app/virtual-scroll/virtual-scroll-demo.html @@ -178,3 +178,25 @@

Append only

Item #{{i}} - ({{size}}px) + +

Custom virtual scroller

+ +
+

Content before virtual scrolling items

+ +
+ Item #{{i}} - ({{size}}px) +
+
+

Content after virtual scrolling items

+
+ +

Window virtual scroller

+ + +
+ Item #{{i}} - ({{size}}px) +
+
diff --git a/tools/public_api_guard/cdk/scrolling.md b/tools/public_api_guard/cdk/scrolling.md index 3b11cd941653..be44ac03facc 100644 --- a/tools/public_api_guard/cdk/scrolling.md +++ b/tools/public_api_guard/cdk/scrolling.md @@ -12,7 +12,7 @@ import { Directionality } from '@angular/cdk/bidi'; import { DoCheck } from '@angular/core'; import { ElementRef } from '@angular/core'; import * as i0 from '@angular/core'; -import * as i5 from '@angular/cdk/bidi'; +import * as i7 from '@angular/cdk/bidi'; import { InjectionToken } from '@angular/core'; import { IterableDiffers } from '@angular/core'; import { ListRange } from '@angular/cdk/collections'; @@ -63,10 +63,14 @@ export class CdkFixedSizeVirtualScroll implements OnChanges { export class CdkScrollable implements OnInit, OnDestroy { constructor(elementRef: ElementRef, scrollDispatcher: ScrollDispatcher, ngZone: NgZone, dir?: Directionality | undefined); // (undocumented) + protected readonly _destroyed: Subject; + // (undocumented) protected dir?: Directionality | undefined; // (undocumented) protected elementRef: ElementRef; elementScrolled(): Observable; + // (undocumented) + protected _elementScrolled: Observable; getElementRef(): ElementRef; measureScrollOffset(from: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number; // (undocumented) @@ -136,6 +140,41 @@ export type CdkVirtualForOfContext = { odd: boolean; }; +// @public +export abstract class CdkVirtualScrollable extends CdkScrollable { + constructor(elementRef: ElementRef, scrollDispatcher: ScrollDispatcher, ngZone: NgZone, dir?: Directionality); + abstract measureBoundingClientRectWithScrollOffset(from: 'left' | 'top' | 'right' | 'bottom'): number; + measureViewportSize(orientation: 'horizontal' | 'vertical'): number; + // (undocumented) + static ɵdir: i0.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; +} + +// @public +export class CdkVirtualScrollableElement extends CdkVirtualScrollable { + constructor(elementRef: ElementRef, scrollDispatcher: ScrollDispatcher, ngZone: NgZone, dir: Directionality); + // (undocumented) + measureBoundingClientRectWithScrollOffset(from: 'left' | 'top' | 'right' | 'bottom'): number; + // (undocumented) + static ɵdir: i0.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; +} + +// @public +export class CdkVirtualScrollableWindow extends CdkVirtualScrollable { + constructor(scrollDispatcher: ScrollDispatcher, ngZone: NgZone, dir: Directionality); + // (undocumented) + protected _elementScrolled: Observable; + // (undocumented) + measureBoundingClientRectWithScrollOffset(from: 'left' | 'top' | 'right' | 'bottom'): number; + // (undocumented) + static ɵdir: i0.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; +} + // @public export interface CdkVirtualScrollRepeater { // (undocumented) @@ -145,8 +184,8 @@ export interface CdkVirtualScrollRepeater { } // @public -export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy { - constructor(elementRef: ElementRef, _changeDetectorRef: ChangeDetectorRef, ngZone: NgZone, _scrollStrategy: VirtualScrollStrategy, dir: Directionality, scrollDispatcher: ScrollDispatcher, viewportRuler: ViewportRuler); +export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements OnInit, OnDestroy { + constructor(elementRef: ElementRef, _changeDetectorRef: ChangeDetectorRef, ngZone: NgZone, _scrollStrategy: VirtualScrollStrategy, dir: Directionality, scrollDispatcher: ScrollDispatcher, viewportRuler: ViewportRuler, scrollable: CdkVirtualScrollable); get appendOnly(): boolean; set appendOnly(value: BooleanInput); attach(forOf: CdkVirtualScrollRepeater): void; @@ -159,9 +198,12 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O getOffsetToRenderedContentStart(): number | null; getRenderedRange(): ListRange; getViewportSize(): number; + // (undocumented) + measureBoundingClientRectWithScrollOffset(from: 'left' | 'top' | 'right' | 'bottom'): number; measureRangeSize(range: ListRange): number; measureRenderedContentSize(): number; measureScrollOffset(from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number; + measureViewportOffset(from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number; // (undocumented) ngOnDestroy(): void; // (undocumented) @@ -169,6 +211,8 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O get orientation(): 'horizontal' | 'vertical'; set orientation(orientation: 'horizontal' | 'vertical'); readonly renderedRangeStream: Observable; + // (undocumented) + scrollable: CdkVirtualScrollable; readonly scrolledIndexChange: Observable; scrollToIndex(index: number, behavior?: ScrollBehavior): void; scrollToOffset(offset: number, behavior?: ScrollBehavior): void; @@ -180,7 +224,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵfac: i0.ɵɵFactoryDeclaration; } // @public @@ -250,7 +294,7 @@ export class ScrollingModule { // (undocumented) static ɵinj: i0.ɵɵInjectorDeclaration; // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public (undocumented) @@ -300,6 +344,9 @@ export interface ViewportScrollPosition { // @public export const VIRTUAL_SCROLL_STRATEGY: InjectionToken; +// @public (undocumented) +export const VIRTUAL_SCROLLABLE: InjectionToken; + // @public export interface VirtualScrollStrategy { attach(viewport: CdkVirtualScrollViewport): void;