From 12be786bcd7208918cd10c9187dd27b58fafe0f9 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 13 Jul 2018 15:24:22 -0700 Subject: [PATCH 1/3] virtual-scroll: add support for user-provided content wrapper --- .../scrolling/virtual-for-of.ts | 12 ++++-- .../scrolling/virtual-scroll-viewport.scss | 38 +++++++++++++++++ .../scrolling/virtual-scroll-viewport.ts | 11 +++-- .../scrolling/virtual-scroll.md | 18 ++++++++ .../virtual-scroll/virtual-scroll-demo.html | 42 +++++++++++++++++++ .../virtual-scroll/virtual-scroll-demo.scss | 18 ++++++++ 6 files changed, 131 insertions(+), 8 deletions(-) diff --git a/src/cdk-experimental/scrolling/virtual-for-of.ts b/src/cdk-experimental/scrolling/virtual-for-of.ts index 22c5b4ee5a0d..f5fa0dca3fe3 100644 --- a/src/cdk-experimental/scrolling/virtual-for-of.ts +++ b/src/cdk-experimental/scrolling/virtual-for-of.ts @@ -49,8 +49,13 @@ export type CdkVirtualForOfContext = { }; -/** Helper to extract size from a ClientRect. */ -function getSize(orientation: 'horizontal' | 'vertical', rect: ClientRect): number { +/** Helper to extract size from a DOM Node. */ +function getSize(orientation: 'horizontal' | 'vertical', node: Node): number { + const el = node as Element; + if (!el.getBoundingClientRect) { + return 0; + } + const rect = el.getBoundingClientRect(); return orientation == 'horizontal' ? rect.width : rect.height; } @@ -193,7 +198,6 @@ export class CdkVirtualForOf implements CollectionViewer, DoCheck, OnDestroy const rangeLen = range.end - range.start; // Loop over all root nodes for all items in the range and sum up their size. - // TODO(mmalerba): Make this work with non-element nodes. let totalSize = 0; let i = rangeLen; while (i--) { @@ -201,7 +205,7 @@ export class CdkVirtualForOf implements CollectionViewer, DoCheck, OnDestroy EmbeddedViewRef> | null; let j = view ? view.rootNodes.length : 0; while (j--) { - totalSize += getSize(orientation, (view!.rootNodes[j] as Element).getBoundingClientRect()); + totalSize += getSize(orientation, view!.rootNodes[j]); } } diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss index b68799167b1e..b2f2da0cde49 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss @@ -16,10 +16,48 @@ cdk-virtual-scroll-viewport { .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { bottom: 0; + + & > dl, + & > ol, + & > table, + & > ul { + padding: { + left: 0; + right: 0 + }; + margin: { + left: 0; + right: 0 + }; + border: { + left-width: 0; + right-width: 0 + }; + outline: none; + } } .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper { right: 0; + + & > dl, + & > ol, + & > table, + & > ul { + padding: { + top: 0; + bottom: 0 + }; + margin: { + top: 0; + bottom: 0 + }; + border: { + top-width: 0; + bottom-width: 0 + }; + outline: none; + } } // Spacer element that whose width or height will be adjusted to match the size of the entire data diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts index d6e456a262b6..a529847e4965 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts @@ -57,7 +57,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { @Input() orientation: 'horizontal' | 'vertical' = 'vertical'; /** The element that wraps the rendered content. */ - @ViewChild('contentWrapper') _contentWrapper: ElementRef; + @ViewChild('contentWrapper') _contentWrapper: ElementRef; /** A stream that emits whenever the rendered range changes. */ renderedRangeStream: Observable = this._renderedRangeSubject.asObservable(); @@ -67,7 +67,10 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { */ _totalContentSize = 0; - /** The the rendered content transform. */ + /** + * The the rendered content transform, used to move the rendered portion of the content to the + * correct place in the viewport. + */ private _renderedContentTransform: string; /** The currently rendered range of indices. */ @@ -103,7 +106,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { /** A list of functions to run after the next change detection cycle. */ private _runAfterChangeDetection: Function[] = []; - constructor(public elementRef: ElementRef, + constructor(public elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef, private _ngZone: NgZone, @Inject(VIRTUAL_SCROLL_STRATEGY) private _scrollStrategy: VirtualScrollStrategy) {} @@ -327,7 +330,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { } } - for (let fn of this._runAfterChangeDetection) { + for (const fn of this._runAfterChangeDetection) { fn(); } this._runAfterChangeDetection = []; diff --git a/src/cdk-experimental/scrolling/virtual-scroll.md b/src/cdk-experimental/scrolling/virtual-scroll.md index 12f275d88f65..d5d008f706d4 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll.md +++ b/src/cdk-experimental/scrolling/virtual-scroll.md @@ -142,3 +142,21 @@ content. ... ``` + +### Elements with parent tag requirements +Some HTML elements such as `` and `
  • ` have limitations on the kinds of parent elements they +can be placed inside. To enable virtual scrolling over these type of elements, place the elements in +their proper parent, and then wrap the whole thing in a `cdk-virtual-scroll-viewport`. Be careful +that the parent does not introduce additional space (e.g. via `margin` or `padding`) as it may +interfere with the scrolling. + +```html + + + + + + +
    {{row.first}}{{row.second}}
    +
    +``` diff --git a/src/demo-app/virtual-scroll/virtual-scroll-demo.html b/src/demo-app/virtual-scroll/virtual-scroll-demo.html index 7c8b754eee04..f479593e64bb 100644 --- a/src/demo-app/virtual-scroll/virtual-scroll-demo.html +++ b/src/demo-app/virtual-scroll/virtual-scroll-demo.html @@ -94,3 +94,45 @@

    trackBy state name

    {{state.capital}}
    + +

    Use with <ol>

    + + +
      +
    1. + {{state.name}} - {{state.capital}} +
    2. +
    +
    + +

    Use with <ul>

    + + +
      +
    • + {{state.name}} - {{state.capital}} +
    • +
    +
    + +

    Use with <dl>

    + + +
    + +
    {{state.name}}
    +
    {{state.capital}}
    +
    +
    +
    + +

    Use with <table>

    + + + + + + + +
    {{state.name}}{{state.capital}}
    +
    diff --git a/src/demo-app/virtual-scroll/virtual-scroll-demo.scss b/src/demo-app/virtual-scroll/virtual-scroll-demo.scss index 58e9e1741b3c..0aa6a2a48e6f 100644 --- a/src/demo-app/virtual-scroll/virtual-scroll-demo.scss +++ b/src/demo-app/virtual-scroll/virtual-scroll-demo.scss @@ -37,3 +37,21 @@ .demo-capital { font-size: 14px; } + +.demo-dt { + height: 30px; + font-weight: bold; +} + +.demo-dd { + height: 30px; +} + +.demo-li, +.demo-td { + height: 50px; +} + +.demo-td { + border: 1px solid gray; +} From cbfacdda8378bfaafc77c8608e844b0dc17be7bc Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 16 Jul 2018 17:07:01 -0700 Subject: [PATCH 2/3] address comments --- .../scrolling/virtual-scroll-viewport.scss | 69 +++++++++---------- .../scrolling/virtual-scroll-viewport.ts | 4 +- .../scrolling/virtual-scroll.md | 2 +- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss index b2f2da0cde49..998eb6dd0780 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss @@ -1,3 +1,32 @@ +// When elements such as `` or `
  • ` are repeated inside the cdk-virtual-scroll-viewport, +// their container element (e.g. ``, `
      `, etc.) needs to be placed in the viewport as +// well. We reset some properties here to prevent these container elements from introducing +// additional space that would throw off the the scrolling calculations. +@mixin _cdk-virtual-scroll-clear-container-space($direction) { + $start: if($direction == horizontal, 'left', 'top'); + $end: if($direction == horizontal, 'right', 'bottom'); + + & > dl:not([cdkVirtualFor]), + & > ol:not([cdkVirtualFor]), + & > table:not([cdkVirtualFor]), + & > ul:not([cdkVirtualFor]) { + padding: { + #{$start}: 0; + #{$end}: 0 + }; + margin: { + #{$start}: 0; + #{$end}: 0 + }; + border: { + #{$start}-width: 0; + #{$end}-width: 0 + }; + outline: none; + } +} + + // Scrolling container. cdk-virtual-scroll-viewport { display: block; @@ -16,48 +45,12 @@ cdk-virtual-scroll-viewport { .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { bottom: 0; - - & > dl, - & > ol, - & > table, - & > ul { - padding: { - left: 0; - right: 0 - }; - margin: { - left: 0; - right: 0 - }; - border: { - left-width: 0; - right-width: 0 - }; - outline: none; - } + @include _cdk-virtual-scroll-clear-container-space(horizontal); } .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper { right: 0; - - & > dl, - & > ol, - & > table, - & > ul { - padding: { - top: 0; - bottom: 0 - }; - margin: { - top: 0; - bottom: 0 - }; - border: { - top-width: 0; - bottom-width: 0 - }; - outline: none; - } + @include _cdk-virtual-scroll-clear-container-space(vertical); } // Spacer element that whose width or height will be adjusted to match the size of the entire data diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts index a529847e4965..670946ca305c 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts @@ -68,8 +68,8 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { _totalContentSize = 0; /** - * The the rendered content transform, used to move the rendered portion of the content to the - * correct place in the viewport. + * The CSS transform applied to the rendered subset of items so that they appear within the bounds + * of the visible viewport. */ private _renderedContentTransform: string; diff --git a/src/cdk-experimental/scrolling/virtual-scroll.md b/src/cdk-experimental/scrolling/virtual-scroll.md index d5d008f706d4..9f892254573e 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll.md +++ b/src/cdk-experimental/scrolling/virtual-scroll.md @@ -147,7 +147,7 @@ content. Some HTML elements such as `
    ` and `
  • ` have limitations on the kinds of parent elements they can be placed inside. To enable virtual scrolling over these type of elements, place the elements in their proper parent, and then wrap the whole thing in a `cdk-virtual-scroll-viewport`. Be careful -that the parent does not introduce additional space (e.g. via `margin` or `padding`) as it may +that the parent does not introduce additional space (e.g. via `margin` or `padding`) as it will interfere with the scrolling. ```html From 661eda7e7e56806aae6ecd78b79bb0ebea08fe82 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 17 Jul 2018 10:42:24 -0700 Subject: [PATCH 3/3] fix type issue --- .../scrolling/virtual-scroll-viewport.scss | 12 ++++++------ .../scrolling/virtual-scroll-viewport.spec.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss index 998eb6dd0780..9b7c5f0377ed 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss @@ -12,16 +12,16 @@ & > ul:not([cdkVirtualFor]) { padding: { #{$start}: 0; - #{$end}: 0 - }; + #{$end}: 0; + } margin: { #{$start}: 0; - #{$end}: 0 - }; + #{$end}: 0; + } border: { #{$start}-width: 0; - #{$end}-width: 0 - }; + #{$end}-width: 0; + } outline: none; } } diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts b/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts index 2aea398549de..6fe679a20a54 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts @@ -27,7 +27,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length) .toBe(4, 'should render 4 50px items to fill 200px space'); })); @@ -507,7 +507,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length) .toBe(4, 'should render 4 50px items to fill 200px space'); })); @@ -517,7 +517,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length).toBe(4, 'should render 4 items to fill 200px space based on 50px estimate from first item'); }));