/* eslint-disable */ import { Component } from 'react'; import { NoContentRenderer, Alignment, CellSize, CellPosition, OverscanIndicesGetter, RenderedSection, CellRendererParams, Scroll as VirtualGridScroll, Scroll } from './types'; import accessibilityOverscanIndicesGetter from './accessibilityOverscanIndicesGetter'; import { VirtualGrid, VirtualGridProps } from './VirtualGrid'; import { css } from '@patternfly/react-styles'; /** * It is inefficient to create and manage a large list of DOM elements within a scrolling container * if only a few of those elements are visible. The primary purpose of this component is to improve * performance by only rendering the DOM nodes that a user is able to see based on their current * scroll position. * * This component renders a virtualized list of elements with either fixed or dynamic heights. */ interface VirtualTableBodyProps extends Omit< VirtualGridProps, | 'style' | 'containerStyle' | 'aria-label' | 'aria-readonly' | 'tabIndex' | 'autoContainerWidth' | 'cellRenderer' | 'className' | 'columnWidth' | 'columnCount' | 'noContentRenderer' | 'onScroll' | 'onSectionRendered' | 'ref' | 'scrollToRow' > { 'aria-label'?: string; /** * Removes fixed height from the scrollingContainer so that the total height * of rows can stretch the window. Intended for use with WindowScroller */ autoHeight?: boolean; /** Optional CSS class name */ className?: string; /** * Used to estimate the total height of a List before all of its rows have actually been measured. * The estimated total height is adjusted as rows are rendered. */ estimatedRowSize?: number; /** Height constraint for list (determines how many actual rows are rendered) */ height: number; /** Optional renderer to be used in place of rows when rowCount is 0 */ noRowsRenderer?: NoContentRenderer; /** Callback invoked with information about the slice of rows that were just rendered. */ onRowsRendered?: (params: any) => void; /** * Callback invoked whenever the scroll offset changes within the inner scrollable region. * This callback can be used to sync scrolling between lists, tables, or grids. */ onScroll?: (params: Scroll) => void; /** See VirtualGrid#overscanIndicesGetter */ overscanIndicesGetter?: OverscanIndicesGetter; /** * Number of rows to render above/below the visible bounds of the list. * These rows can help for smoother scrolling on touch devices. */ overscanRowCount?: number; /** Either a fixed row height (number) or a function that returns the height of a row given its index. */ rowHeight: CellSize; /** Responsible for rendering a row given an index; ({ index: number }): node */ rowRenderer: any; /** Number of rows in list. */ rowCount: number; /** See VirtualGrid#scrollToAlignment */ scrollToAlignment?: Alignment; /** Row index to ensure visible (by forcefully scrolling if necessary) */ scrollToIndex?: number; /** Vertical offset. */ scrollTop?: number; style?: Object; /** Tab index for focus */ tabIndex?: number; /** Width of list */ width: number; columns?: any[]; columnCount?: number; rows: any[]; } export class VirtualTableBody extends Component { static defaultProps = { autoHeight: false, estimatedRowSize: 30, onScroll: () => {}, noRowsRenderer: () => null as any, onRowsRendered: () => {}, overscanIndicesGetter: accessibilityOverscanIndicesGetter, overscanRowCount: 10, scrollToAlignment: 'auto', scrollToIndex: -1, style: {}, scrollContainerComponent: 'table', innerScrollContainerComponent: 'tbody' }; VirtualGrid: any; forceUpdateVirtualGrid() { if (this.VirtualGrid) { this.VirtualGrid.forceUpdate(); } } /** See VirtualGrid#getOffsetForCell */ getOffsetForRow({ alignment, index }: { alignment: Alignment; index: number }) { if (this.VirtualGrid) { const { scrollTop } = this.VirtualGrid.getOffsetForCell({ alignment, rowIndex: index, columnIndex: 0 }); return scrollTop; } return 0; } /** CellMeasurer compatibility */ invalidateCellSizeAfterRender({ columnIndex, rowIndex }: CellPosition) { if (this.VirtualGrid) { this.VirtualGrid.invalidateCellSizeAfterRender({ rowIndex, columnIndex }); } } /** See VirtualGrid#measureAllCells */ measureAllRows() { if (this.VirtualGrid) { this.VirtualGrid.measureAllCells(); } } /** CellMeasurer compatibility */ recomputeVirtualGridSize({ columnIndex = 0, rowIndex = 0 }: CellPosition = {}) { if (this.VirtualGrid) { this.VirtualGrid.recomputeVirtualGridSize({ rowIndex, columnIndex }); } } /** See VirtualGrid#recomputeVirtualGridSize */ recomputeRowHeights(index: number = 0) { if (this.VirtualGrid) { this.VirtualGrid.recomputeVirtualGridSize({ rowIndex: index, columnIndex: 0 }); } } /** See VirtualGrid#scrollToPosition */ scrollToPosition(scrollTop: number = 0) { if (this.VirtualGrid) { this.VirtualGrid.scrollToPosition({ scrollTop }); } } /** See VirtualGrid#scrollToCell */ scrollToRow(index: number = 0) { if (this.VirtualGrid) { this.VirtualGrid.scrollToCell({ columnIndex: 0, rowIndex: index }); } } render() { const { className, noRowsRenderer, scrollToIndex, width, columns, columnCount, style, scrollContainerComponent, innerScrollContainerComponent } = this.props; const classNames = css('ReactVirtualized__List', className); return ( // note: these aria props if rendered will break a11y for role="presentation" // this approach attempts to fix non standard table grids // see: https://www.html5accessibility.com/tests/aria-table-fix.html () ); } _cellRenderer = ({ parent, rowIndex, style, isScrolling, isVisible, key }: CellRendererParams) => { const { rowRenderer } = this.props; // TRICKY The style object is sometimes cached by VirtualGrid. // This prevents new style objects from bypassing shallowCompare(). // However as of React 16, style props are auto-frozen (at least in dev mode) // Check to make sure we can still modify the style before proceeding. // https://github.com/facebook/react/commit/977357765b44af8ff0cfea327866861073095c12#commitcomment-20648713 const { writable } = Object.getOwnPropertyDescriptor(style, 'width'); if (writable) { // By default, List cells should be 100% width. // This prevents them from flowing under a scrollbar (if present). style.width = '100%'; } return rowRenderer({ index: rowIndex, style, isScrolling, isVisible, key, parent }); }; _setRef = (ref: any) => { this.VirtualGrid = ref; }; _onScroll = ({ clientHeight, scrollHeight, scrollTop }: VirtualGridScroll) => { const { onScroll } = this.props; onScroll({ clientHeight, scrollHeight, scrollTop } as VirtualGridScroll); }; _onSectionRendered = ({ rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex }: RenderedSection) => { const { onRowsRendered } = this.props; onRowsRendered({ overscanStartIndex: rowOverscanStartIndex, overscanStopIndex: rowOverscanStopIndex, startIndex: rowStartIndex, stopIndex: rowStopIndex }); }; }