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

203 lines
4.9 KiB
JavaScript
Raw Normal View History

2017-09-04 02:20:56 +00:00
import PropTypes from 'prop-types';
import React, { Component } from 'react';
2020-09-07 01:33:10 +00:00
import { Grid, WindowScroller } from 'react-virtualized';
import Measure from 'Components/Measure';
2017-09-04 02:20:56 +00:00
import Scroller from 'Components/Scroller/Scroller';
2020-09-07 01:33:10 +00:00
import { scrollDirections } from 'Helpers/Props';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
2017-09-04 02:20:56 +00:00
import styles from './VirtualTable.css';
const ROW_HEIGHT = 38;
2017-09-04 02:20:56 +00:00
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,
scrollRestored: false
2017-09-04 02:20:56 +00:00
};
this._grid = null;
2017-09-04 02:20:56 +00:00
}
componentDidUpdate(prevProps, prevState) {
const {
items,
2020-08-26 21:38:48 +00:00
scrollIndex,
scrollTop
} = this.props;
2017-09-04 02:20:56 +00:00
const {
width,
scrollRestored
} = this.state;
2018-03-15 01:28:46 +00:00
if (this._grid && (prevState.width !== width || hasDifferentItemsOrOrder(prevProps.items, items))) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
2018-03-15 01:28:46 +00:00
if (this._grid && scrollTop !== undefined && scrollTop !== 0 && !scrollRestored) {
this.setState({ scrollRestored: true });
this._grid.scrollToPosition({ scrollTop });
}
if (scrollIndex != null && scrollIndex !== prevProps.scrollIndex) {
this._grid.scrollToCell({
rowIndex: scrollIndex,
columnIndex: 0
});
2018-03-15 01:28:46 +00:00
}
}
2017-09-04 02:20:56 +00:00
//
// Control
setGridRef = (ref) => {
this._grid = ref;
2021-12-24 18:18:14 +00:00
};
2017-09-04 02:20:56 +00:00
//
// Listeners
onMeasure = ({ width }) => {
this.setState({
width
});
2021-12-24 18:18:14 +00:00
};
2017-09-04 02:20:56 +00:00
//
// Render
render() {
const {
isSmallScreen,
2017-09-04 02:20:56 +00:00
className,
items,
scroller,
2017-09-04 02:20:56 +00:00
header,
headerHeight,
2020-08-26 21:38:48 +00:00
rowHeight,
2017-09-04 02:20:56 +00:00
rowRenderer,
...otherProps
} = this.props;
const {
width
} = this.state;
const gridStyle = {
boxSizing: undefined,
direction: undefined,
height: undefined,
position: undefined,
willChange: undefined,
overflow: undefined,
width: undefined
};
const containerStyle = {
position: undefined
};
2017-09-04 02:20:56 +00:00
return (
<WindowScroller
scrollElement={isSmallScreen ? undefined : scroller}
>
{({ height, registerChild, onChildScroll, scrollTop }) => {
if (!height) {
return null;
}
return (
<Measure
whitelist={['width']}
onMeasure={this.onMeasure}
>
2017-09-04 02:20:56 +00:00
<Scroller
className={className}
scrollDirection={scrollDirections.HORIZONTAL}
>
{header}
<div ref={registerChild}>
<Grid
{...otherProps}
ref={this.setGridRef}
autoContainerWidth={true}
autoHeight={true}
autoWidth={true}
width={width}
height={height}
headerHeight={height - headerHeight}
2020-08-26 21:38:48 +00:00
rowHeight={rowHeight}
rowCount={items.length}
columnCount={1}
columnWidth={width}
scrollTop={scrollTop}
onScroll={onChildScroll}
overscanRowCount={2}
cellRenderer={rowRenderer}
overscanIndicesGetter={overscanIndicesGetter}
scrollToAlignment={'start'}
isScrollingOptout={true}
className={styles.tableBodyContainer}
style={gridStyle}
containerStyle={containerStyle}
/>
</div>
2017-09-04 02:20:56 +00:00
</Scroller>
</Measure>
);
}
}
</WindowScroller>
2017-09-04 02:20:56 +00:00
);
}
}
VirtualTable.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
2017-09-04 02:20:56 +00:00
className: PropTypes.string.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
2018-03-15 01:28:46 +00:00
scrollIndex: PropTypes.number,
scrollTop: PropTypes.number,
scroller: PropTypes.instanceOf(Element).isRequired,
2017-09-04 02:20:56 +00:00
header: PropTypes.node.isRequired,
headerHeight: PropTypes.number.isRequired,
2020-08-26 21:38:48 +00:00
rowRenderer: PropTypes.func.isRequired,
rowHeight: PropTypes.number.isRequired
2017-09-04 02:20:56 +00:00
};
VirtualTable.defaultProps = {
className: styles.tableContainer,
2020-08-26 21:38:48 +00:00
headerHeight: 38,
rowHeight: ROW_HEIGHT
2017-09-04 02:20:56 +00:00
};
export default VirtualTable;