Lidarr/frontend/src/Components/Table/VirtualTable.js

174 lines
4.0 KiB
JavaScript

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Measure from 'react-measure';
import { WindowScroller } from 'react-virtualized';
import { scrollDirections } from 'Helpers/Props';
import Scroller from 'Components/Scroller/Scroller';
import VirtualTableBody from './VirtualTableBody';
import styles from './VirtualTable.css';
const ROW_HEIGHT = 38;
function overscanIndicesGetter(options) {
const {
cellCount,
overscanCellsCount,
startIndex,
stopIndex
} = options;
// The default getter takes the scroll direction into account,
// but that can cause issues. Ignore the scroll direction and
// always over return more items.
const overscanStartIndex = startIndex - overscanCellsCount;
const overscanStopIndex = stopIndex + overscanCellsCount;
return {
overscanStartIndex: Math.max(0, overscanStartIndex),
overscanStopIndex: Math.min(cellCount - 1, overscanStopIndex)
};
}
class VirtualTable extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
width: 0
};
this._isInitialized = false;
this._table = null;
}
componentDidMount() {
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
}
//
// Control
rowGetter = ({ index }) => {
return this.props.items[index];
}
setTableRef = (ref) => {
this._table = ref;
}
forceUpdateGrid = () => {
this._table.recomputeGridSize();
}
scrollToRow = (rowIndex) => {
const scrollTop = (rowIndex + 1) * ROW_HEIGHT + 20;
// this._table.scrollToCell({ columnIndex: 0, rowIndex });
this.props.onScroll({ scrollTop });
}
//
// Listeners
onMeasure = ({ width }) => {
this.setState({
width
});
}
onSectionRendered = () => {
if (!this._isInitialized && this._contentBodyNode) {
this.props.onRender();
this._isInitialized = true;
}
}
//
// Render
render() {
const {
className,
items,
isSmallScreen,
header,
headerHeight,
scrollTop,
rowRenderer,
onScroll,
...otherProps
} = this.props;
const {
width
} = this.state;
return (
<Measure onMeasure={this.onMeasure}>
<WindowScroller
scrollElement={isSmallScreen ? null : this._contentBodyNode}
onScroll={onScroll}
>
{({ height, isScrolling }) => {
return (
<Scroller
className={className}
scrollDirection={scrollDirections.HORIZONTAL}
>
{header}
<VirtualTableBody
ref={this.setTableRef}
autoContainerWidth={true}
width={width}
height={height}
headerHeight={height - headerHeight}
rowHeight={ROW_HEIGHT}
rowCount={items.length}
columnCount={1}
scrollTop={scrollTop}
autoHeight={true}
overscanRowCount={2}
cellRenderer={rowRenderer}
columnWidth={width}
overscanIndicesGetter={overscanIndicesGetter}
onSectionRendered={this.onSectionRendered}
{...otherProps}
/>
</Scroller>
);
}
}
</WindowScroller>
</Measure>
);
}
}
VirtualTable.propTypes = {
className: PropTypes.string.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
scrollTop: PropTypes.number.isRequired,
contentBody: PropTypes.object.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
header: PropTypes.node.isRequired,
headerHeight: PropTypes.number.isRequired,
rowRenderer: PropTypes.func.isRequired,
onRender: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
};
VirtualTable.defaultProps = {
className: styles.tableContainer,
headerHeight: 38,
onRender: () => {}
};
export default VirtualTable;