mirror of https://github.com/lidarr/Lidarr
Fixed: Rework Artist Index and VirtualTable
This commit is contained in:
parent
4036b2ade2
commit
9902434057
|
@ -22,16 +22,15 @@ class ImportArtist extends Component {
|
|||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
selectedState: {},
|
||||
contentBody: null,
|
||||
scrollTop: 0
|
||||
scroller: null
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
setContentBodyRef = (ref) => {
|
||||
this.setState({ contentBody: ref });
|
||||
setScrollerRef = (ref) => {
|
||||
this.setState({ scroller: ref });
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -72,10 +71,6 @@ class ImportArtist extends Component {
|
|||
this.props.onImportPress(this.getSelectedIds());
|
||||
}
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
this.setState({ scrollTop });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -94,13 +89,13 @@ class ImportArtist extends Component {
|
|||
allSelected,
|
||||
allUnselected,
|
||||
selectedState,
|
||||
contentBody
|
||||
scroller
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Import Artist">
|
||||
<PageContentBodyConnector
|
||||
ref={this.setContentBodyRef}
|
||||
registerScroller={this.setScrollerRef}
|
||||
onScroll={this.onScroll}
|
||||
>
|
||||
{
|
||||
|
@ -121,20 +116,18 @@ class ImportArtist extends Component {
|
|||
}
|
||||
|
||||
{
|
||||
!rootFoldersError && rootFoldersPopulated && !!unmappedFolders.length && contentBody &&
|
||||
!rootFoldersError && rootFoldersPopulated && !!unmappedFolders.length && scroller &&
|
||||
<ImportArtistTableConnector
|
||||
rootFolderId={rootFolderId}
|
||||
unmappedFolders={unmappedFolders}
|
||||
allSelected={allSelected}
|
||||
allUnselected={allUnselected}
|
||||
selectedState={selectedState}
|
||||
contentBody={contentBody}
|
||||
scroller={scroller}
|
||||
showMetadataProfile={showMetadataProfile}
|
||||
scrollTop={this.state.scrollTop}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
onRemoveSelectedStateItem={this.onRemoveSelectedStateItem}
|
||||
onScroll={this.onScroll}
|
||||
/>
|
||||
}
|
||||
</PageContentBodyConnector>
|
||||
|
|
|
@ -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 ImportArtistSelectArtistConnector from './SelectArtist/ImportArtistSelectArtistConnector';
|
||||
|
@ -10,7 +9,6 @@ import styles from './ImportArtistRow.css';
|
|||
|
||||
function ImportArtistRow(props) {
|
||||
const {
|
||||
style,
|
||||
id,
|
||||
monitor,
|
||||
qualityProfileId,
|
||||
|
@ -25,7 +23,7 @@ function ImportArtistRow(props) {
|
|||
} = props;
|
||||
|
||||
return (
|
||||
<VirtualTableRow style={style}>
|
||||
<>
|
||||
<VirtualTableSelectCell
|
||||
inputClassName={styles.selectInput}
|
||||
id={id}
|
||||
|
@ -82,12 +80,11 @@ function ImportArtistRow(props) {
|
|||
isExistingArtist={isExistingArtist}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
</VirtualTableRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ImportArtistRow.propTypes = {
|
||||
style: PropTypes.object.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
monitor: PropTypes.string.isRequired,
|
||||
qualityProfileId: PropTypes.number.isRequired,
|
||||
|
|
|
@ -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 ImportArtistHeader from './ImportArtistHeader';
|
||||
import ImportArtistRowConnector from './ImportArtistRowConnector';
|
||||
|
||||
|
@ -110,15 +111,20 @@ class ImportArtistTable extends Component {
|
|||
const item = items[rowIndex];
|
||||
|
||||
return (
|
||||
<ImportArtistRowConnector
|
||||
<VirtualTableRow
|
||||
key={key}
|
||||
style={style}
|
||||
rootFolderId={rootFolderId}
|
||||
showMetadataProfile={showMetadataProfile}
|
||||
isSelected={selectedState[item.id]}
|
||||
onSelectedChange={onSelectedChange}
|
||||
id={item.id}
|
||||
/>
|
||||
>
|
||||
<ImportArtistRowConnector
|
||||
key={item.id}
|
||||
style={style}
|
||||
rootFolderId={rootFolderId}
|
||||
showMetadataProfile={showMetadataProfile}
|
||||
isSelected={selectedState[item.id]}
|
||||
onSelectedChange={onSelectedChange}
|
||||
id={item.id}
|
||||
/>
|
||||
</VirtualTableRow>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -131,12 +137,10 @@ class ImportArtistTable extends Component {
|
|||
allSelected,
|
||||
allUnselected,
|
||||
isSmallScreen,
|
||||
contentBody,
|
||||
showMetadataProfile,
|
||||
scrollTop,
|
||||
scroller,
|
||||
selectedState,
|
||||
onSelectAllChange,
|
||||
onScroll
|
||||
onSelectAllChange
|
||||
} = this.props;
|
||||
|
||||
if (!items.length) {
|
||||
|
@ -146,10 +150,9 @@ class ImportArtistTable extends Component {
|
|||
return (
|
||||
<VirtualTable
|
||||
items={items}
|
||||
contentBody={contentBody}
|
||||
isSmallScreen={isSmallScreen}
|
||||
scroller={scroller}
|
||||
rowHeight={52}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={2}
|
||||
rowRenderer={this.rowRenderer}
|
||||
header={
|
||||
|
@ -161,7 +164,6 @@ class ImportArtistTable extends Component {
|
|||
/>
|
||||
}
|
||||
selectedState={selectedState}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -180,15 +182,14 @@ ImportArtistTable.propTypes = {
|
|||
selectedState: PropTypes.object.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
allArtists: PropTypes.arrayOf(PropTypes.object),
|
||||
contentBody: PropTypes.object.isRequired,
|
||||
scroller: PropTypes.instanceOf(Element).isRequired,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
onSelectAllChange: PropTypes.func.isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
onRemoveSelectedStateItem: PropTypes.func.isRequired,
|
||||
onArtistLookup: PropTypes.func.isRequired,
|
||||
onSetImportArtistValue: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired
|
||||
onSetImportArtistValue: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ImportArtistTable;
|
||||
|
|
|
@ -53,13 +53,12 @@ class ArtistIndex extends Component {
|
|||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
contentBody: null,
|
||||
scroller: null,
|
||||
jumpBarItems: { order: [] },
|
||||
jumpToCharacter: null,
|
||||
isPosterOptionsModalOpen: false,
|
||||
isBannerOptionsModalOpen: false,
|
||||
isOverviewOptionsModalOpen: false,
|
||||
isRendered: false
|
||||
isOverviewOptionsModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,8 +70,7 @@ class ArtistIndex extends Component {
|
|||
const {
|
||||
items,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
scrollTop
|
||||
sortDirection
|
||||
} = this.props;
|
||||
|
||||
if (sortKey !== prevProps.sortKey ||
|
||||
|
@ -82,7 +80,7 @@ class ArtistIndex extends Component {
|
|||
this.setJumpBarItems();
|
||||
}
|
||||
|
||||
if (this.state.jumpToCharacter != null && scrollTop !== prevProps.scrollTop) {
|
||||
if (this.state.jumpToCharacter != null) {
|
||||
this.setState({ jumpToCharacter: null });
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +88,8 @@ class ArtistIndex extends Component {
|
|||
//
|
||||
// Control
|
||||
|
||||
setContentBodyRef = (ref) => {
|
||||
this.setState({ contentBody: ref });
|
||||
setScrollerRef = (ref) => {
|
||||
this.setState({ scroller: ref });
|
||||
}
|
||||
|
||||
setJumpBarItems() {
|
||||
|
@ -169,27 +167,6 @@ class ArtistIndex 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
|
||||
|
||||
|
@ -209,7 +186,7 @@ class ArtistIndex extends Component {
|
|||
view,
|
||||
isRefreshingArtist,
|
||||
isRssSyncExecuting,
|
||||
scrollTop,
|
||||
onScroll,
|
||||
onSortSelect,
|
||||
onFilterSelect,
|
||||
onViewSelect,
|
||||
|
@ -219,17 +196,16 @@ class ArtistIndex extends Component {
|
|||
} = this.props;
|
||||
|
||||
const {
|
||||
contentBody,
|
||||
scroller,
|
||||
jumpBarItems,
|
||||
jumpToCharacter,
|
||||
isPosterOptionsModalOpen,
|
||||
isBannerOptionsModalOpen,
|
||||
isOverviewOptionsModalOpen,
|
||||
isRendered
|
||||
isOverviewOptionsModalOpen
|
||||
} = this.state;
|
||||
|
||||
const ViewComponent = getViewComponent(view);
|
||||
const isLoaded = !!(!error && isPopulated && items.length && contentBody);
|
||||
const isLoaded = !!(!error && isPopulated && items.length && scroller);
|
||||
const hasNoArtist = !totalItems;
|
||||
|
||||
return (
|
||||
|
@ -338,11 +314,10 @@ class ArtistIndex 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 &&
|
||||
|
@ -360,14 +335,12 @@ class ArtistIndex 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}
|
||||
/>
|
||||
|
||||
|
@ -426,7 +399,6 @@ ArtistIndex.propTypes = {
|
|||
view: PropTypes.string.isRequired,
|
||||
isRefreshingArtist: PropTypes.bool.isRequired,
|
||||
isRssSyncExecuting: PropTypes.bool.isRequired,
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
onSortSelect: PropTypes.func.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
|
|
|
@ -4,7 +4,6 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
|
@ -14,35 +13,6 @@ import * as commandNames from 'Commands/commandNames';
|
|||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import ArtistIndex from './ArtistIndex';
|
||||
|
||||
const POSTERS_PADDING = 15;
|
||||
const POSTERS_PADDING_SMALL_SCREEN = 5;
|
||||
const BANNERS_PADDING = 15;
|
||||
const BANNERS_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;
|
||||
}
|
||||
|
||||
if (view === 'banners') {
|
||||
padding = isSmallScreen ? BANNERS_PADDING_SMALL_SCREEN : BANNERS_PADDING;
|
||||
}
|
||||
|
||||
return scrollTop + padding;
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistClientSideCollectionItemsSelector('artistIndex'),
|
||||
|
@ -99,39 +69,15 @@ function createMapDispatchToProps(dispatch, props) {
|
|||
|
||||
class ArtistIndexConnector 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.dispatchSetArtistView(view);
|
||||
});
|
||||
this.props.dispatchSetArtistView(view);
|
||||
}
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
this.setState({
|
||||
scrollTop
|
||||
}, () => {
|
||||
scrollPositions.artistIndex = scrollTop;
|
||||
});
|
||||
scrollPositions.artistIndex = scrollTop;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -141,7 +87,6 @@ class ArtistIndexConnector extends Component {
|
|||
return (
|
||||
<ArtistIndex
|
||||
{...this.props}
|
||||
scrollTop={this.state.scrollTop}
|
||||
onViewSelect={this.onViewSelect}
|
||||
onScroll={this.onScroll}
|
||||
/>
|
||||
|
@ -152,7 +97,6 @@ class ArtistIndexConnector extends Component {
|
|||
ArtistIndexConnector.propTypes = {
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
view: PropTypes.string.isRequired,
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
dispatchSetArtistView: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -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 ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||
import ArtistIndexBanner from './ArtistIndexBanner';
|
||||
|
@ -109,52 +107,46 @@ class ArtistIndexBanners extends Component {
|
|||
this._grid = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const {
|
||||
items,
|
||||
filters,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
bannerOptions,
|
||||
jumpToCharacter
|
||||
} = this.props;
|
||||
|
||||
const itemsChanged = hasDifferentItemsOrOrder(prevProps.items, items);
|
||||
const {
|
||||
width,
|
||||
columnWidth,
|
||||
columnCount,
|
||||
rowHeight
|
||||
} = this.state;
|
||||
|
||||
if (
|
||||
prevProps.sortKey !== sortKey ||
|
||||
prevProps.bannerOptions !== bannerOptions ||
|
||||
itemsChanged
|
||||
) {
|
||||
if (prevProps.sortKey !== sortKey ||
|
||||
prevProps.bannerOptions !== bannerOptions) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,22 +243,14 @@ class ArtistIndexBanners extends Component {
|
|||
this.calculateGrid(width, this.props.isSmallScreen);
|
||||
}
|
||||
|
||||
onSectionRendered = () => {
|
||||
if (!this._isInitialized && this._contentBodyNode) {
|
||||
this.props.onRender();
|
||||
this._isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
scrollTop,
|
||||
isSmallScreen,
|
||||
onScroll
|
||||
scroller
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
@ -279,12 +263,18 @@ class ArtistIndexBanners 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}
|
||||
|
@ -296,10 +286,11 @@ class ArtistIndexBanners extends Component {
|
|||
rowCount={rowCount}
|
||||
rowHeight={rowHeight}
|
||||
width={width}
|
||||
onScroll={onChildScroll}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={2}
|
||||
cellRenderer={this.cellRenderer}
|
||||
onSectionRendered={this.onSectionRendered}
|
||||
scrollToAlignment={'start'}
|
||||
isScrollingOptOut={true}
|
||||
/>
|
||||
);
|
||||
|
@ -313,19 +304,14 @@ class ArtistIndexBanners extends Component {
|
|||
|
||||
ArtistIndexBanners.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||
bannerOptions: 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 ArtistIndexBanners;
|
||||
|
|
|
@ -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 ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||
import ArtistIndexOverview from './ArtistIndexOverview';
|
||||
|
@ -66,56 +63,44 @@ class ArtistIndexOverviews 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 ArtistIndexOverviews 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;
|
||||
}
|
||||
|
@ -217,22 +187,14 @@ class ArtistIndexOverviews extends Component {
|
|||
this.calculateGrid(width, this.props.isSmallScreen);
|
||||
}
|
||||
|
||||
onSectionRendered = () => {
|
||||
if (!this._isInitialized && this._contentBodyNode) {
|
||||
this.props.onRender();
|
||||
this._isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
scrollTop,
|
||||
isSmallScreen,
|
||||
onScroll
|
||||
scroller
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
@ -241,29 +203,39 @@ class ArtistIndexOverviews 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}
|
||||
onSectionRendered={this.onSectionRendered}
|
||||
scrollToAlignment={'start'}
|
||||
isScrollingOptOut={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -275,20 +247,16 @@ class ArtistIndexOverviews extends Component {
|
|||
|
||||
ArtistIndexOverviews.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 ArtistIndexOverviews;
|
||||
|
|
|
@ -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 ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||
import ArtistIndexPoster from './ArtistIndexPoster';
|
||||
|
@ -109,52 +107,46 @@ class ArtistIndexPosters 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -252,22 +244,14 @@ class ArtistIndexPosters extends Component {
|
|||
this.calculateGrid(width, this.props.isSmallScreen);
|
||||
}
|
||||
|
||||
onSectionRendered = () => {
|
||||
if (!this._isInitialized && this._contentBodyNode) {
|
||||
this.props.onRender();
|
||||
this._isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
scrollTop,
|
||||
isSmallScreen,
|
||||
onScroll
|
||||
scroller
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
@ -280,29 +264,38 @@ class ArtistIndexPosters 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -314,19 +307,14 @@ class ArtistIndexPosters extends Component {
|
|||
|
||||
ArtistIndexPosters.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 ArtistIndexPosters;
|
||||
|
|
|
@ -23,10 +23,12 @@ class ArtistIndexTable 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);
|
||||
|
||||
|
@ -76,26 +78,21 @@ class ArtistIndexTable 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}
|
||||
isSmallScreen={isSmallScreen}
|
||||
scroller={scroller}
|
||||
rowHeight={showBanners ? 70 : 38}
|
||||
overscanRowCount={2}
|
||||
rowRenderer={this.rowRenderer}
|
||||
|
@ -109,12 +106,8 @@ class ArtistIndexTable extends Component {
|
|||
/>
|
||||
}
|
||||
columns={columns}
|
||||
filters={filters}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onRender={onRender}
|
||||
onScroll={onScroll}
|
||||
isScrollingOptOut={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -123,17 +116,13 @@ class ArtistIndexTable extends Component {
|
|||
ArtistIndexTable.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 ArtistIndexTable;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
.tableContainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tableBodyContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -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,39 @@ 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 = this.props.scrollIndex;
|
||||
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) * ROW_HEIGHT + 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 +86,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,65 +105,88 @@ 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,
|
||||
onRender: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired
|
||||
rowRenderer: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
VirtualTable.defaultProps = {
|
||||
className: styles.tableContainer,
|
||||
headerHeight: 38,
|
||||
onRender: () => {}
|
||||
headerHeight: 38
|
||||
};
|
||||
|
||||
export default VirtualTable;
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.tableBodyContainer {
|
||||
position: relative;
|
||||
}
|
|
@ -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;
|
|
@ -3,6 +3,7 @@ import React, { Component } from 'react';
|
|||
import { align, icons, sortDirections } from 'Helpers/Props';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import VirtualTable from 'Components/Table/VirtualTable';
|
||||
import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
||||
|
@ -21,16 +22,15 @@ class UnmappedFilesTable extends Component {
|
|||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
contentBody: null,
|
||||
scrollTop: 0
|
||||
scroller: null
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
setContentBodyRef = (ref) => {
|
||||
this.setState({ contentBody: ref });
|
||||
setScrollerRef = (ref) => {
|
||||
this.setState({ scroller: ref });
|
||||
}
|
||||
|
||||
rowRenderer = ({ key, rowIndex, style }) => {
|
||||
|
@ -43,23 +43,20 @@ class UnmappedFilesTable extends Component {
|
|||
const item = items[rowIndex];
|
||||
|
||||
return (
|
||||
<UnmappedFilesTableRow
|
||||
style={style}
|
||||
<VirtualTableRow
|
||||
key={key}
|
||||
columns={columns}
|
||||
deleteUnmappedFile={deleteUnmappedFile}
|
||||
{...item}
|
||||
/>
|
||||
style={style}
|
||||
>
|
||||
<UnmappedFilesTableRow
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
deleteUnmappedFile={deleteUnmappedFile}
|
||||
{...item}
|
||||
/>
|
||||
</VirtualTableRow>
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
this.setState({ scrollTop });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const {
|
||||
|
@ -77,8 +74,7 @@ class UnmappedFilesTable extends Component {
|
|||
} = this.props;
|
||||
|
||||
const {
|
||||
scrollTop,
|
||||
contentBody
|
||||
scroller
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
|
@ -100,8 +96,7 @@ class UnmappedFilesTable extends Component {
|
|||
</PageToolbar>
|
||||
|
||||
<PageContentBodyConnector
|
||||
ref={this.setContentBodyRef}
|
||||
onScroll={this.onScroll}
|
||||
registerScroller={this.setScrollerRef}
|
||||
>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
|
@ -116,14 +111,12 @@ class UnmappedFilesTable extends Component {
|
|||
}
|
||||
|
||||
{
|
||||
isPopulated && !error && !!items.length && contentBody &&
|
||||
isPopulated && !error && !!items.length && scroller &&
|
||||
<VirtualTable
|
||||
items={items}
|
||||
columns={columns}
|
||||
contentBody={contentBody}
|
||||
scroller={scroller}
|
||||
isSmallScreen={false}
|
||||
scrollTop={scrollTop}
|
||||
onScroll={this.onScroll}
|
||||
overscanRowCount={10}
|
||||
rowRenderer={this.rowRenderer}
|
||||
header={
|
||||
|
|
|
@ -5,7 +5,6 @@ import formatBytes from 'Utilities/Number/formatBytes';
|
|||
import IconButton from 'Components/Link/IconButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import TrackQuality from 'Album/TrackQuality';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
|
@ -64,7 +63,6 @@ class UnmappedFilesTableRow extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
style,
|
||||
id,
|
||||
path,
|
||||
size,
|
||||
|
@ -82,9 +80,7 @@ class UnmappedFilesTableRow extends Component {
|
|||
} = this.state;
|
||||
|
||||
return (
|
||||
<VirtualTableRow
|
||||
style={style}
|
||||
>
|
||||
<>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
|
@ -198,14 +194,13 @@ class UnmappedFilesTableRow extends Component {
|
|||
onCancel={this.onConfirmDeleteModalClose}
|
||||
/>
|
||||
|
||||
</VirtualTableRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UnmappedFilesTableRow.propTypes = {
|
||||
style: PropTypes.object.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
|
|
Loading…
Reference in New Issue