mirror of https://github.com/lidarr/Lidarr
174 lines
4.0 KiB
JavaScript
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;
|