New: Improved Series list performance

This commit is contained in:
ta264 2020-01-05 21:49:08 +00:00 committed by Mark McDowall
parent 466d4fba9e
commit 5a79b8502e
14 changed files with 257 additions and 418 deletions

View File

@ -21,17 +21,15 @@ class ImportSeries extends Component {
allSelected: false,
allUnselected: false,
lastToggled: null,
selectedState: {},
contentBody: null,
scrollTop: 0
selectedState: {}
};
}
//
// Control
setContentBodyRef = (ref) => {
this.setState({ contentBody: ref });
setScrollerRef = (ref) => {
this.setState({ scroller: ref });
}
//
@ -94,13 +92,13 @@ class ImportSeries extends Component {
allSelected,
allUnselected,
selectedState,
contentBody
scroller
} = this.state;
return (
<PageContent title="Import Series">
<PageContentBodyConnector
ref={this.setContentBodyRef}
registerScroller={this.setScrollerRef}
onScroll={this.onScroll}
>
{
@ -121,20 +119,18 @@ class ImportSeries extends Component {
}
{
!rootFoldersError && rootFoldersPopulated && !!unmappedFolders.length && contentBody &&
!rootFoldersError && rootFoldersPopulated && !!unmappedFolders.length && scroller &&
<ImportSeriesTableConnector
rootFolderId={rootFolderId}
unmappedFolders={unmappedFolders}
allSelected={allSelected}
allUnselected={allUnselected}
selectedState={selectedState}
contentBody={contentBody}
scroller={scroller}
showLanguageProfile={showLanguageProfile}
scrollTop={this.state.scrollTop}
onSelectAllChange={this.onSelectAllChange}
onSelectedChange={this.onSelectedChange}
onRemoveSelectedStateItem={this.onRemoveSelectedStateItem}
onScroll={this.onScroll}
/>
}
</PageContentBodyConnector>

View File

@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import { inputTypes } from 'Helpers/Props';
import FormInputGroup from 'Components/Form/FormInputGroup';
import VirtualTableRow from 'Components/Table/VirtualTableRow';
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
import ImportSeriesSelectSeriesConnector from './SelectSeries/ImportSeriesSelectSeriesConnector';
@ -10,7 +9,6 @@ import styles from './ImportSeriesRow.css';
function ImportSeriesRow(props) {
const {
style,
id,
monitor,
qualityProfileId,
@ -26,7 +24,7 @@ function ImportSeriesRow(props) {
} = props;
return (
<VirtualTableRow style={style}>
<>
<VirtualTableSelectCell
inputClassName={styles.selectInput}
id={id}
@ -93,12 +91,11 @@ function ImportSeriesRow(props) {
onInputChange={onInputChange}
/>
</VirtualTableRowCell>
</VirtualTableRow>
</>
);
}
ImportSeriesRow.propTypes = {
style: PropTypes.object.isRequired,
id: PropTypes.string.isRequired,
monitor: PropTypes.string.isRequired,
qualityProfileId: PropTypes.number.isRequired,

View File

@ -2,6 +2,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import VirtualTable from 'Components/Table/VirtualTable';
import VirtualTableRow from 'Components/Table/VirtualTableRow';
import ImportSeriesHeader from './ImportSeriesHeader';
import ImportSeriesRowConnector from './ImportSeriesRowConnector';
@ -112,15 +113,19 @@ class ImportSeriesTable extends Component {
const item = items[rowIndex];
return (
<ImportSeriesRowConnector
<VirtualTableRow
key={key}
style={style}
rootFolderId={rootFolderId}
showLanguageProfile={showLanguageProfile}
isSelected={selectedState[item.id]}
onSelectedChange={onSelectedChange}
id={item.id}
/>
>
<ImportSeriesRowConnector
key={item.id}
rootFolderId={rootFolderId}
showLanguageProfile={showLanguageProfile}
isSelected={selectedState[item.id]}
onSelectedChange={onSelectedChange}
id={item.id}
/>
</VirtualTableRow>
);
}
@ -133,12 +138,10 @@ class ImportSeriesTable extends Component {
allSelected,
allUnselected,
isSmallScreen,
contentBody,
scroller,
showLanguageProfile,
scrollTop,
selectedState,
onSelectAllChange,
onScroll
onSelectAllChange
} = this.props;
if (!items.length) {
@ -148,10 +151,9 @@ class ImportSeriesTable extends Component {
return (
<VirtualTable
items={items}
contentBody={contentBody}
scroller={scroller}
isSmallScreen={isSmallScreen}
rowHeight={52}
scrollTop={scrollTop}
overscanRowCount={2}
rowRenderer={this.rowRenderer}
header={
@ -163,7 +165,6 @@ class ImportSeriesTable extends Component {
/>
}
selectedState={selectedState}
onScroll={onScroll}
/>
);
}
@ -183,15 +184,13 @@ ImportSeriesTable.propTypes = {
selectedState: PropTypes.object.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
allSeries: PropTypes.arrayOf(PropTypes.object),
contentBody: PropTypes.object.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
scrollTop: PropTypes.number.isRequired,
onSelectAllChange: PropTypes.func.isRequired,
onSelectedChange: PropTypes.func.isRequired,
onRemoveSelectedStateItem: PropTypes.func.isRequired,
onSeriesLookup: PropTypes.func.isRequired,
onSetImportSeriesValue: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
onSetImportSeriesValue: PropTypes.func.isRequired
};
export default ImportSeriesTable;

View File

@ -37,6 +37,10 @@ class OverlayScroller extends Component {
_setScrollRef = (ref) => {
this._scroller = ref;
if (ref) {
this.props.registerScroller(ref.view);
}
}
_renderThumb = (props) => {
@ -157,7 +161,8 @@ OverlayScroller.propTypes = {
autoHide: PropTypes.bool.isRequired,
autoScroll: PropTypes.bool.isRequired,
children: PropTypes.node,
onScroll: PropTypes.func
onScroll: PropTypes.func,
registerScroller: PropTypes.func
};
OverlayScroller.defaultProps = {
@ -165,7 +170,8 @@ OverlayScroller.defaultProps = {
trackClassName: styles.thumb,
scrollDirection: scrollDirections.VERTICAL,
autoHide: false,
autoScroll: true
autoScroll: true,
registerScroller: () => {}
};
export default OverlayScroller;

View File

@ -30,6 +30,8 @@ class Scroller extends Component {
_setScrollerRef = (ref) => {
this._scroller = ref;
this.props.registerScroller(ref);
}
//
@ -43,6 +45,7 @@ class Scroller extends Component {
children,
scrollTop,
onScroll,
registerScroller,
...otherProps
} = this.props;
@ -70,12 +73,14 @@ Scroller.propTypes = {
autoScroll: PropTypes.bool.isRequired,
scrollTop: PropTypes.number,
children: PropTypes.node,
onScroll: PropTypes.func
onScroll: PropTypes.func,
registerScroller: PropTypes.func
};
Scroller.defaultProps = {
scrollDirection: scrollDirections.VERTICAL,
autoScroll: true
autoScroll: true,
registerScroller: () => {}
};
export default Scroller;

View File

@ -1,3 +1,7 @@
.tableContainer {
width: 100%;
}
.tableBodyContainer {
position: relative;
}

View File

@ -1,12 +1,10 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { WindowScroller } from 'react-virtualized';
import { isLocked } from 'Utilities/scrollLock';
import { scrollDirections } from 'Helpers/Props';
import Measure from 'Components/Measure';
import Scroller from 'Components/Scroller/Scroller';
import VirtualTableBody from './VirtualTableBody';
import { WindowScroller, Grid } from 'react-virtualized';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import styles from './VirtualTable.css';
const ROW_HEIGHT = 38;
@ -44,28 +42,37 @@ class VirtualTable extends Component {
width: 0
};
this._isInitialized = false;
this._grid = null;
}
componentDidMount() {
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
}
componentDidUpdate(prevProps, prevState) {
const {
items,
scrollIndex
} = this.props;
componentDidUpdate(prevProps, preState) {
const { scrollIndex, rowHeight } = this.props;
const {
width
} = this.state;
if (this._grid && (prevState.width !== width || hasDifferentItemsOrOrder(prevProps.items, items))) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
if (scrollIndex != null && scrollIndex !== prevProps.scrollIndex) {
const scrollTop = (scrollIndex + 1) * rowHeight + 20;
this.props.onScroll({ scrollTop });
this._grid.scrollToCell({
rowIndex: scrollIndex,
columnIndex: 0
});
}
}
//
// Control
rowGetter = ({ index }) => {
return this.props.items[index];
setGridRef = (ref) => {
this._grid = ref;
}
//
@ -77,36 +84,18 @@ class VirtualTable extends Component {
});
}
onSectionRendered = () => {
if (!this._isInitialized && this._contentBodyNode) {
this.props.onRender();
this._isInitialized = true;
}
}
onScroll = (props) => {
if (isLocked()) {
return;
}
const { onScroll } = this.props;
onScroll(props);
}
//
// Render
render() {
const {
isSmallScreen,
className,
items,
isSmallScreen,
scroller,
header,
headerHeight,
scrollTop,
rowRenderer,
onScroll,
...otherProps
} = this.props;
@ -114,66 +103,89 @@ class VirtualTable extends Component {
width
} = this.state;
const gridStyle = {
boxSizing: undefined,
direction: undefined,
height: undefined,
position: undefined,
willChange: undefined,
overflow: undefined,
width: undefined
};
const containerStyle = {
position: undefined
};
return (
<Measure onMeasure={this.onMeasure}>
<WindowScroller
scrollElement={isSmallScreen ? undefined : this._contentBodyNode}
onScroll={this.onScroll}
>
{({ height, isScrolling }) => {
return (
<WindowScroller
scrollElement={isSmallScreen ? undefined : scroller}
>
{({ height, registerChild, onChildScroll, scrollTop }) => {
if (!height) {
return null;
}
return (
<Measure
whitelist={['width']}
onMeasure={this.onMeasure}
>
<Scroller
className={className}
scrollDirection={scrollDirections.HORIZONTAL}
>
{header}
<VirtualTableBody
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}
/>
<div ref={registerChild}>
<Grid
ref={this.setGridRef}
autoContainerWidth={true}
autoHeight={true}
autoWidth={true}
width={width}
height={height}
headerHeight={height - headerHeight}
rowHeight={ROW_HEIGHT}
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}
{...otherProps}
/>
</div>
</Scroller>
);
}
}
</WindowScroller>
</Measure>
</Measure>
);
}
}
</WindowScroller>
);
}
}
VirtualTable.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
className: PropTypes.string.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
scrollTop: PropTypes.number.isRequired,
scrollIndex: PropTypes.number,
contentBody: PropTypes.object.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
header: PropTypes.node.isRequired,
headerHeight: PropTypes.number.isRequired,
rowRenderer: PropTypes.func.isRequired,
rowHeight: PropTypes.number.isRequired,
onRender: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
rowHeight: PropTypes.number.isRequired
};
VirtualTable.defaultProps = {
className: styles.tableContainer,
headerHeight: 38,
onRender: () => {}
headerHeight: 38
};
export default VirtualTable;

View File

@ -1,3 +0,0 @@
.tableBodyContainer {
position: relative;
}

View File

@ -1,40 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Grid } from 'react-virtualized';
import styles from './VirtualTableBody.css';
class VirtualTableBody extends Component {
//
// Render
render() {
return (
<Grid
{...this.props}
style={{
boxSizing: undefined,
direction: undefined,
height: undefined,
position: undefined,
willChange: undefined,
overflow: undefined,
width: undefined
}}
containerStyle={{
position: undefined
}}
/>
);
}
}
VirtualTableBody.propTypes = {
className: PropTypes.string.isRequired
};
VirtualTableBody.defaultProps = {
className: styles.tableBodyContainer
};
export default VirtualTableBody;

View File

@ -1,12 +1,9 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Grid, WindowScroller } from 'react-virtualized';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import dimensions from 'Styles/Variables/dimensions';
import { sortDirections } from 'Helpers/Props';
import Measure from 'Components/Measure';
import SeriesIndexItemConnector from 'Series/Index/SeriesIndexItemConnector';
import SeriesIndexOverview from './SeriesIndexOverview';
@ -66,56 +63,44 @@ class SeriesIndexOverviews extends Component {
rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {})
};
this._isInitialized = false;
this._grid = null;
}
componentDidMount() {
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps, prevState) {
const {
items,
filters,
sortKey,
sortDirection,
overviewOptions,
jumpToCharacter
} = this.props;
const itemsChanged = hasDifferentItemsOrOrder(prevProps.items, items);
const overviewOptionsChanged = !_.isMatch(prevProps.overviewOptions, overviewOptions);
const {
width,
rowHeight
} = this.state;
if (
prevProps.sortKey !== sortKey ||
prevProps.overviewOptions !== overviewOptions ||
itemsChanged
) {
if (prevProps.sortKey !== sortKey ||
prevProps.overviewOptions !== overviewOptions) {
this.calculateGrid();
}
if (
prevProps.filters !== filters ||
prevProps.sortKey !== sortKey ||
prevProps.sortDirection !== sortDirection ||
itemsChanged ||
overviewOptionsChanged
) {
if (this._grid &&
(prevState.width !== width ||
prevState.rowHeight !== rowHeight ||
hasDifferentItemsOrOrder(prevProps.items, items))) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const index = getIndexOfFirstCharacter(items, jumpToCharacter);
if (index != null) {
const {
rowHeight
} = this.state;
if (this._grid && index != null) {
const scrollTop = rowHeight * index;
this.props.onScroll({ scrollTop });
this._grid.scrollToCell({
rowIndex: index,
columnIndex: 0
});
}
}
}
@ -123,21 +108,6 @@ class SeriesIndexOverviews extends Component {
//
// Control
scrollToFirstCharacter(character) {
const items = this.props.items;
const {
rowHeight
} = this.state;
const index = getIndexOfFirstCharacter(items, character);
if (index != null) {
const scrollTop = rowHeight * index;
this.props.onScroll({ scrollTop });
}
}
setGridRef = (ref) => {
this._grid = ref;
}
@ -219,22 +189,14 @@ class SeriesIndexOverviews extends Component {
this.calculateGrid(width, this.props.isSmallScreen);
}
onSectionRendered = () => {
if (!this._isInitialized && this._contentBodyNode) {
this.props.onRender();
this._isInitialized = true;
}
}
//
// Render
render() {
const {
scroller,
items,
scrollTop,
isSmallScreen,
onScroll
isSmallScreen
} = this.props;
const {
@ -243,29 +205,38 @@ class SeriesIndexOverviews extends Component {
} = this.state;
return (
<Measure onMeasure={this.onMeasure}>
<Measure
whitelist={['width']}
onMeasure={this.onMeasure}
>
<WindowScroller
scrollElement={isSmallScreen ? undefined : this._contentBodyNode}
onScroll={onScroll}
scrollElement={isSmallScreen ? undefined : scroller}
>
{({ height, isScrolling }) => {
{({ height, registerChild, onChildScroll, scrollTop }) => {
if (!height) {
return <div />;
}
return (
<Grid
ref={this.setGridRef}
className={styles.grid}
autoHeight={true}
height={height}
columnCount={1}
columnWidth={width}
rowCount={items.length}
rowHeight={rowHeight}
width={width}
scrollTop={scrollTop}
overscanRowCount={2}
cellRenderer={this.cellRenderer}
onSectionRendered={this.onSectionRendered}
isScrollingOptOut={true}
/>
<div ref={registerChild}>
<Grid
ref={this.setGridRef}
className={styles.grid}
autoHeight={true}
height={height}
columnCount={1}
columnWidth={width}
rowCount={items.length}
rowHeight={rowHeight}
width={width}
onScroll={onChildScroll}
scrollTop={scrollTop}
overscanRowCount={2}
cellRenderer={this.cellRenderer}
scrollToAlignment={'start'}
isScrollingOptOut={true}
/>
</div>
);
}
}
@ -277,20 +248,15 @@ class SeriesIndexOverviews extends Component {
SeriesIndexOverviews.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
overviewOptions: PropTypes.object.isRequired,
scrollTop: PropTypes.number.isRequired,
jumpToCharacter: PropTypes.string,
contentBody: PropTypes.object.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
longDateFormat: PropTypes.string.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
timeFormat: PropTypes.string.isRequired,
onRender: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
timeFormat: PropTypes.string.isRequired
};
export default SeriesIndexOverviews;

View File

@ -1,11 +1,9 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Grid, WindowScroller } from 'react-virtualized';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import dimensions from 'Styles/Variables/dimensions';
import { sortDirections } from 'Helpers/Props';
import Measure from 'Components/Measure';
import SeriesIndexItemConnector from 'Series/Index/SeriesIndexItemConnector';
import SeriesIndexPoster from './SeriesIndexPoster';
@ -110,52 +108,46 @@ class SeriesIndexPosters extends Component {
this._grid = null;
}
componentDidMount() {
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps, prevState) {
const {
items,
filters,
sortKey,
sortDirection,
posterOptions,
jumpToCharacter
} = this.props;
const itemsChanged = hasDifferentItemsOrOrder(prevProps.items, items);
const {
width,
columnWidth,
columnCount,
rowHeight
} = this.state;
if (
prevProps.sortKey !== sortKey ||
prevProps.posterOptions !== posterOptions ||
itemsChanged
) {
if (prevProps.sortKey !== sortKey ||
prevProps.posterOptions !== posterOptions) {
this.calculateGrid();
}
if (
prevProps.filters !== filters ||
prevProps.sortKey !== sortKey ||
prevProps.sortDirection !== sortDirection ||
itemsChanged
) {
if (this._grid &&
(prevState.width !== width ||
prevState.columnWidth !== columnWidth ||
prevState.columnCount !== columnCount ||
prevState.rowHeight !== rowHeight ||
hasDifferentItemsOrOrder(prevProps.items, items))) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const index = getIndexOfFirstCharacter(items, jumpToCharacter);
if (index != null) {
const {
columnCount,
rowHeight
} = this.state;
if (this._grid && index != null) {
const row = Math.floor(index / columnCount);
const scrollTop = rowHeight * row;
this.props.onScroll({ scrollTop });
this._grid.scrollToCell({
rowIndex: row,
columnIndex: 0
});
}
}
}
@ -254,22 +246,14 @@ class SeriesIndexPosters extends Component {
this.calculateGrid(width, this.props.isSmallScreen);
}
onSectionRendered = () => {
if (!this._isInitialized && this._contentBodyNode) {
this.props.onRender();
this._isInitialized = true;
}
}
//
// Render
render() {
const {
scroller,
items,
scrollTop,
isSmallScreen,
onScroll
isSmallScreen
} = this.props;
const {
@ -282,29 +266,38 @@ class SeriesIndexPosters extends Component {
const rowCount = Math.ceil(items.length / columnCount);
return (
<Measure onMeasure={this.onMeasure}>
<Measure
whitelist={['width']}
onMeasure={this.onMeasure}
>
<WindowScroller
scrollElement={isSmallScreen ? undefined : this._contentBodyNode}
onScroll={onScroll}
scrollElement={isSmallScreen ? undefined : scroller}
>
{({ height, isScrolling }) => {
{({ height, registerChild, onChildScroll, scrollTop }) => {
if (!height) {
return <div />;
}
return (
<Grid
ref={this.setGridRef}
className={styles.grid}
autoHeight={true}
height={height}
columnCount={columnCount}
columnWidth={columnWidth}
rowCount={rowCount}
rowHeight={rowHeight}
width={width}
scrollTop={scrollTop}
overscanRowCount={2}
cellRenderer={this.cellRenderer}
onSectionRendered={this.onSectionRendered}
isScrollingOptOut={true}
/>
<div ref={registerChild}>
<Grid
ref={this.setGridRef}
className={styles.grid}
autoHeight={true}
height={height}
columnCount={columnCount}
columnWidth={columnWidth}
rowCount={rowCount}
rowHeight={rowHeight}
width={width}
onScroll={onChildScroll}
scrollTop={scrollTop}
overscanRowCount={2}
cellRenderer={this.cellRenderer}
scrollToAlignment={'start'}
isScrollingOptOut={true}
/>
</div>
);
}
}
@ -316,19 +309,14 @@ class SeriesIndexPosters extends Component {
SeriesIndexPosters.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
posterOptions: PropTypes.object.isRequired,
scrollTop: PropTypes.number.isRequired,
jumpToCharacter: PropTypes.string,
contentBody: PropTypes.object.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
timeFormat: PropTypes.string.isRequired,
onRender: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
timeFormat: PropTypes.string.isRequired
};
export default SeriesIndexPosters;

View File

@ -46,12 +46,11 @@ class SeriesIndex extends Component {
super(props, context);
this.state = {
contentBody: null,
scroller: null,
jumpBarItems: { order: [] },
jumpToCharacter: null,
isPosterOptionsModalOpen: false,
isOverviewOptionsModalOpen: false,
isRendered: false
isOverviewOptionsModalOpen: false
};
}
@ -63,8 +62,7 @@ class SeriesIndex extends Component {
const {
items,
sortKey,
sortDirection,
scrollTop
sortDirection
} = this.props;
if (sortKey !== prevProps.sortKey ||
@ -74,7 +72,7 @@ class SeriesIndex extends Component {
this.setJumpBarItems();
}
if (this.state.jumpToCharacter != null && scrollTop !== prevProps.scrollTop) {
if (this.state.jumpToCharacter != null) {
this.setState({ jumpToCharacter: null });
}
}
@ -82,8 +80,8 @@ class SeriesIndex extends Component {
//
// Control
setContentBodyRef = (ref) => {
this.setState({ contentBody: ref });
setScrollerRef = (ref) => {
this.setState({ scroller: ref });
}
setJumpBarItems() {
@ -153,27 +151,6 @@ class SeriesIndex extends Component {
this.setState({ jumpToCharacter });
}
onRender = () => {
this.setState({ isRendered: true }, () => {
const {
scrollTop,
isSmallScreen
} = this.props;
if (isSmallScreen) {
// Seems to result in the view being off by 125px (distance to the top of the page)
// document.documentElement.scrollTop = document.body.scrollTop = scrollTop;
// This works, but then jumps another 1px after scrolling
document.documentElement.scrollTop = scrollTop;
}
});
}
onScroll = ({ scrollTop }) => {
this.props.onScroll({ scrollTop });
}
//
// Render
@ -193,7 +170,7 @@ class SeriesIndex extends Component {
view,
isRefreshingSeries,
isRssSyncExecuting,
scrollTop,
onScroll,
onSortSelect,
onFilterSelect,
onViewSelect,
@ -203,16 +180,15 @@ class SeriesIndex extends Component {
} = this.props;
const {
contentBody,
scroller,
jumpBarItems,
jumpToCharacter,
isPosterOptionsModalOpen,
isOverviewOptionsModalOpen,
isRendered
isOverviewOptionsModalOpen
} = this.state;
const ViewComponent = getViewComponent(view);
const isLoaded = !!(!error && isPopulated && items.length && contentBody);
const isLoaded = !!(!error && isPopulated && items.length && scroller);
const hasNoSeries = !totalItems;
return (
@ -309,11 +285,10 @@ class SeriesIndex extends Component {
<div className={styles.pageContentBodyWrapper}>
<PageContentBodyConnector
ref={this.setContentBodyRef}
registerScroller={this.setScrollerRef}
className={styles.contentBody}
innerClassName={styles[`${view}InnerContentBody`]}
scrollTop={isRendered ? scrollTop : 0}
onScroll={this.onScroll}
onScroll={onScroll}
>
{
isFetching && !isPopulated &&
@ -329,14 +304,12 @@ class SeriesIndex extends Component {
isLoaded &&
<div className={styles.contentBodyContainer}>
<ViewComponent
contentBody={contentBody}
scroller={scroller}
items={items}
filters={filters}
sortKey={sortKey}
sortDirection={sortDirection}
scrollTop={scrollTop}
jumpToCharacter={jumpToCharacter}
onRender={this.onRender}
{...otherProps}
/>
@ -388,7 +361,6 @@ SeriesIndex.propTypes = {
view: PropTypes.string.isRequired,
isRefreshingSeries: PropTypes.bool.isRequired,
isRssSyncExecuting: PropTypes.bool.isRequired,
scrollTop: PropTypes.number.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
onSortSelect: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired,

View File

@ -3,7 +3,6 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createSeriesClientSideCollectionItemsSelector from 'Store/Selectors/createSeriesClientSideCollectionItemsSelector';
import dimensions from 'Styles/Variables/dimensions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import scrollPositions from 'Store/scrollPositions';
@ -13,29 +12,6 @@ import * as commandNames from 'Commands/commandNames';
import withScrollPosition from 'Components/withScrollPosition';
import SeriesIndex from './SeriesIndex';
const POSTERS_PADDING = 15;
const POSTERS_PADDING_SMALL_SCREEN = 5;
const TABLE_PADDING = parseInt(dimensions.pageContentBodyPadding);
const TABLE_PADDING_SMALL_SCREEN = parseInt(dimensions.pageContentBodyPaddingSmallScreen);
// If the scrollTop is greater than zero it needs to be offset
// by the padding so when it is set initially so it is correct
// after React Virtualized takes the padding into account.
function getScrollTop(view, scrollTop, isSmallScreen) {
if (scrollTop === 0) {
return 0;
}
let padding = isSmallScreen ? TABLE_PADDING_SMALL_SCREEN : TABLE_PADDING;
if (view === 'posters') {
padding = isSmallScreen ? POSTERS_PADDING_SMALL_SCREEN : POSTERS_PADDING;
}
return scrollTop + padding;
}
function createMapStateToProps() {
return createSelector(
createSeriesClientSideCollectionItemsSelector('seriesIndex'),
@ -92,39 +68,15 @@ function createMapDispatchToProps(dispatch, props) {
class SeriesIndexConnector extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
const {
view,
scrollTop,
isSmallScreen
} = props;
this.state = {
scrollTop: getScrollTop(view, scrollTop, isSmallScreen)
};
}
//
// Listeners
onViewSelect = (view) => {
// Reset the scroll position before changing the view
this.setState({ scrollTop: 0 }, () => {
this.props.dispatchSetSeriesView(view);
});
this.props.dispatchSetSeriesView(view);
}
onScroll = ({ scrollTop }) => {
this.setState({
scrollTop
}, () => {
scrollPositions.seriesIndex = scrollTop;
});
scrollPositions.seriesIndex = scrollTop;
}
//
@ -134,7 +86,6 @@ class SeriesIndexConnector extends Component {
return (
<SeriesIndex
{...this.props}
scrollTop={this.state.scrollTop}
onViewSelect={this.onViewSelect}
onScroll={this.onScroll}
/>
@ -145,7 +96,6 @@ class SeriesIndexConnector extends Component {
SeriesIndexConnector.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
view: PropTypes.string.isRequired,
scrollTop: PropTypes.number.isRequired,
dispatchSetSeriesView: PropTypes.func.isRequired
};

View File

@ -23,10 +23,12 @@ class SeriesIndexTable extends Component {
}
componentDidUpdate(prevProps) {
const jumpToCharacter = this.props.jumpToCharacter;
const {
items,
jumpToCharacter
} = this.props;
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const items = this.props.items;
const scrollIndex = getIndexOfFirstCharacter(items, jumpToCharacter);
@ -75,25 +77,20 @@ class SeriesIndexTable extends Component {
const {
items,
columns,
filters,
sortKey,
sortDirection,
showBanners,
isSmallScreen,
scrollTop,
contentBody,
onSortPress,
onRender,
onScroll
scroller
} = this.props;
return (
<VirtualTable
className={styles.tableContainer}
items={items}
scrollTop={scrollTop}
scrollIndex={this.state.scrollIndex}
contentBody={contentBody}
scroller={scroller}
isSmallScreen={isSmallScreen}
rowHeight={showBanners ? 70 : 38}
overscanRowCount={2}
@ -108,12 +105,6 @@ class SeriesIndexTable extends Component {
/>
}
columns={columns}
filters={filters}
sortKey={sortKey}
sortDirection={sortDirection}
onRender={onRender}
onScroll={onScroll}
isScrollingOptOut={true}
/>
);
}
@ -122,17 +113,13 @@ class SeriesIndexTable extends Component {
SeriesIndexTable.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
showBanners: PropTypes.bool.isRequired,
scrollTop: PropTypes.number.isRequired,
jumpToCharacter: PropTypes.string,
contentBody: PropTypes.object.isRequired,
scroller: PropTypes.instanceOf(Element).isRequired,
isSmallScreen: PropTypes.bool.isRequired,
onSortPress: PropTypes.func.isRequired,
onRender: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
onSortPress: PropTypes.func.isRequired
};
export default SeriesIndexTable;