-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Pull Request ## π€¨ Rationale Related Issue: - #865 ## π©βπ» Implementation Uses TanStack Virtualizer as mentioned in the spec, and as done in [the prototype branch](https://github.com/ni/nimble/tree/table-custom-columns/packages/nimble-components/src/table). Major differences from the prototype: - Prototype and TanStack Virtual examples set the virtualizer's "total size" (height of all rows) on the row container, and added translate transforms on each individual row. We noticed some flashing during scrolling with that approach in Firefox. Instead, this version sets the height of all rows on a sibling div, and applies a single translate transform on the row container. This is more similar to what Perspective/regular-table do, and it got rid of the flashing in Firefox. - Prototype used a `ResizeObserver` in the Nimble table code. TanStack Virtualizer is already using a ResizeObserver internally (due to us using its provided `observeElementOffset` and `observeElementRect` functions as inputs in our `VirtualizerOptions` object), so I concluded we didn't need a separate resize observer. ## π§ͺ Testing - Added a couple of autotests dealing with virtualization (that the # of rows rendered is less than the total; that we can scroll the element to view the end rows; that the # of rendered rows increases when the element height increases) - Updated [table Storybook to add a 2nd option for table data](https://60e89457a987cf003efc0a5b-wjfhtkxgzq.chromatic.com/?path=/story/table--table&args=data:LargeDataSet), a data set of 10,000 rows ## β Checklist - [x] I have updated the project documentation to reflect my changes or determined no changes are needed.
- Loading branch information
Showing
14 changed files
with
314 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
change/@ni-nimble-components-15e8f878-a870-4090-8bf6-7c45c65195ba.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "Add virtualization to table", | ||
"packageName": "@ni/nimble-components", | ||
"email": "20709258+msmithNI@users.noreply.github.com", | ||
"dependentChangeType": "patch" | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
packages/nimble-components/src/table/models/virtualizer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { observable } from '@microsoft/fast-element'; | ||
import { | ||
Virtualizer as TanStackVirtualizer, | ||
VirtualizerOptions, | ||
elementScroll, | ||
observeElementOffset, | ||
observeElementRect, | ||
VirtualItem | ||
} from '@tanstack/virtual-core'; | ||
import { controlHeight } from '../../theme-provider/design-tokens'; | ||
import type { Table } from '..'; | ||
import type { TableRecord } from '../types'; | ||
|
||
/** | ||
* Helper class for the nimble-table for row virtualization. | ||
* | ||
* @internal | ||
*/ | ||
export class Virtualizer<TData extends TableRecord = TableRecord> { | ||
@observable | ||
public visibleItems: VirtualItem[] = []; | ||
|
||
@observable | ||
public allRowsHeight = 0; | ||
|
||
@observable | ||
public headerContainerMarginRight = 0; | ||
|
||
@observable | ||
public rowContainerYOffset = 0; | ||
|
||
private readonly table: Table<TData>; | ||
private readonly viewportResizeObserver: ResizeObserver; | ||
private virtualizer?: TanStackVirtualizer<HTMLElement, HTMLElement>; | ||
|
||
public constructor(table: Table<TData>) { | ||
this.table = table; | ||
this.viewportResizeObserver = new ResizeObserver(entries => { | ||
const borderBoxSize = entries[0]?.borderBoxSize[0]; | ||
if (borderBoxSize) { | ||
// If we have enough rows that a vertical scrollbar is shown, we need to offset the header widths | ||
// by the same margin so the column headers align with the corresponding rendered cells | ||
const viewportBoundingWidth = borderBoxSize.inlineSize; | ||
this.headerContainerMarginRight = viewportBoundingWidth - this.table.viewport.scrollWidth; | ||
} | ||
}); | ||
} | ||
|
||
public connectedCallback(): void { | ||
this.viewportResizeObserver.observe(this.table.viewport); | ||
this.updateVirtualizer(); | ||
} | ||
|
||
public disconnectedCallback(): void { | ||
this.viewportResizeObserver.disconnect(); | ||
} | ||
|
||
public dataChanged(): void { | ||
if (this.table.$fastController.isConnected) { | ||
this.updateVirtualizer(); | ||
} | ||
} | ||
|
||
private updateVirtualizer(): void { | ||
const options = this.createVirtualizerOptions(); | ||
if (this.virtualizer) { | ||
this.virtualizer.setOptions(options); | ||
} else { | ||
this.virtualizer = new TanStackVirtualizer(options); | ||
} | ||
this.virtualizer._willUpdate(); | ||
this.handleVirtualizerChange(); | ||
} | ||
|
||
private createVirtualizerOptions(): VirtualizerOptions< | ||
HTMLElement, | ||
HTMLElement | ||
> { | ||
const rowHeight = parseFloat(controlHeight.getValueFor(this.table)); | ||
return { | ||
count: this.table.tableData.length, | ||
getScrollElement: () => { | ||
return this.table.viewport; | ||
}, | ||
estimateSize: (_: number) => rowHeight, | ||
enableSmoothScroll: true, | ||
overscan: 3, | ||
scrollingDelay: 5, | ||
scrollToFn: elementScroll, | ||
observeElementOffset, | ||
observeElementRect, | ||
onChange: () => this.handleVirtualizerChange() | ||
} as VirtualizerOptions<HTMLElement, HTMLElement>; | ||
} | ||
|
||
private handleVirtualizerChange(): void { | ||
const virtualizer = this.virtualizer!; | ||
this.visibleItems = virtualizer.getVirtualItems(); | ||
this.allRowsHeight = virtualizer.getTotalSize(); | ||
// We're using a separate div ('table-scroll') to represent the full height of all rows, and | ||
// the row container's height is only big enough to hold the virtualized rows. So we don't | ||
// use the TanStackVirtual-provided 'start' offset (which is in terms of the full height) | ||
// to translate every individual row, we just translate the row container. | ||
let rowContainerYOffset = 0; | ||
if (this.visibleItems.length > 0) { | ||
const firstItem = this.visibleItems[0]!; | ||
const lastItem = this.visibleItems[this.visibleItems.length - 1]!; | ||
if (lastItem.end < this.allRowsHeight) { | ||
rowContainerYOffset = firstItem.start - virtualizer.scrollOffset; | ||
} | ||
} | ||
this.rowContainerYOffset = rowContainerYOffset; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.