diff --git a/client/scripts/components/General/ListViewport.js b/client/scripts/components/General/ListViewport.js index fbc80967..b115bade 100644 --- a/client/scripts/components/General/ListViewport.js +++ b/client/scripts/components/General/ListViewport.js @@ -10,20 +10,19 @@ const methodsToBind = [ 'handleScrollStart', 'handleScrollStop', 'measureItemHeight', + 'scrollToTop', 'setScrollPosition', 'setViewportHeight' ]; -let cachedList = null; - class ListViewport extends React.Component { constructor() { super(); + this.isScrolling = false; this.lastScrollTop = 0; this.nodeRefs = {}; this.state = { - isScrolling: false, itemHeight: null, listVerticalPadding: null, scrollTop: 0, @@ -35,9 +34,11 @@ class ListViewport extends React.Component { }); this.setViewportHeight = _.debounce(this.setViewportHeight, 250); - this.setScrollPosition = _.throttle(this.setScrollPosition, 100, { + this.updateAfterScrolling = _.debounce(this.updateAfterScrolling, 500, { + leading: true, trailing: true }); + this.setScrollPosition = _.throttle(this.setScrollPosition, 100); } componentDidMount() { @@ -70,12 +71,10 @@ class ListViewport extends React.Component { } shouldComponentUpdate(nextProps, nextState) { - const scrollDelta = nextState.scrollTop - this.lastScrollTop; + const scrollDelta = Math.abs(this.state.scrollTop - nextState.scrollTop); const {outerScrollbar} = this.nodeRefs; - if ((nextState.isScrolling && (scrollDelta > this.state.viewportHeight - || scrollDelta < this.state.viewportHeight * -1)) - || (outerScrollbar != null && outerScrollbar.refs.scrollbar.dragging)) { + if (this.isScrolling && scrollDelta > 20) { return false; } @@ -93,19 +92,13 @@ class ListViewport extends React.Component { // Calculate the number of items that should be rendered based on the height // of the viewport. We offset this to render a few more outide of the // container's dimensions, which looks nicer when the user scrolls. - let offsetBottom = 1; - let offsetTop = 1; - - if (!this.nodeRefs.outerScrollbar.refs.scrollbar.dragging) { - if (scrollDelta < 0) { - offsetTop = 18; - } else if (scrollDelta > 0) { - offsetBottom = 18; - } else { - offsetBottom = 10; - offsetTop = 10; - } - } + const {itemScrollOffset} = this.props; + const offsetBottom = scrollDelta > 0 + ? itemScrollOffset * 2 + : itemScrollOffset / 2; + const offsetTop = scrollDelta < 0 + ? itemScrollOffset * 2 + : itemScrollOffset / 2; let { itemHeight, @@ -143,11 +136,12 @@ class ListViewport extends React.Component { } handleScrollStart() { - this.setState({isScrolling: true}); + this.isScrolling = true; } handleScrollStop() { - this.setState({isScrolling: false}); + this.isScrolling = false; + this.updateAfterScrolling(); } measureItemHeight() { @@ -187,6 +181,17 @@ class ListViewport extends React.Component { return {bottom, top}; } + scrollToTop() { + if (this.state.scrollTop !== 0) { + if (this.nodeRefs.outerScrollbar != null) { + this.nodeRefs.outerScrollbar.refs.scrollbar.scrollToTop(); + } + + this.lastScrollTop = 0; + this.setState({scrollTop: 0}); + } + } + setScrollPosition(scrollValues) { this.lastScrollTop = this.state.scrollTop; this.setState({scrollTop: scrollValues.scrollTop}); @@ -202,6 +207,10 @@ class ListViewport extends React.Component { } } + updateAfterScrolling() { + this.forceUpdate(); + } + render() { const {lastScrollTop, nodeRefs, props, state} = this; const {minItemIndex, maxItemIndex} = this.getViewportLimits( @@ -214,7 +223,7 @@ class ListViewport extends React.Component { // For loops are fast, and performance matters here. for (let index = minItemIndex; index < maxItemIndex; index++) { - list.push(props.itemRenderer(index, props.itemRendererProps)); + list.push(props.itemRenderer(index)); } const listContent = ( @@ -251,12 +260,14 @@ class ListViewport extends React.Component { ListViewport.defaultProps = { bottomSpacerClass: 'list__spacer list__spacer--bottom', + itemScrollOffset: 10, topSpacerClass: 'list__spacer list__spacer--top' }; ListViewport.propTypes = { bottomSpacerClass: React.PropTypes.string, itemRenderer: React.PropTypes.func.isRequired, + itemScrollOffset: React.PropTypes.number, listClass: React.PropTypes.string, listLength: React.PropTypes.number.isRequired, scrollContainerClass: React.PropTypes.string, diff --git a/client/scripts/components/TorrentList/index.js b/client/scripts/components/TorrentList/index.js index b2972a9c..702056f8 100644 --- a/client/scripts/components/TorrentList/index.js +++ b/client/scripts/components/TorrentList/index.js @@ -382,7 +382,9 @@ class TorrentListContainer extends React.Component { } onTorrentFilterChange() { - this.forceUpdate(); + if (this.listViewportRef != null) { + this.listViewportRef.scrollToTop(); + } } onTorrentSelectionChange() { @@ -495,9 +497,9 @@ class TorrentListContainer extends React.Component { this.setState({torrentListColumnWidths: nextPropWidths}); } - renderListItem(index, options) { + renderListItem(index) { const selectedTorrents = TorrentStore.getSelectedTorrents(); - const {displayedProperties, torrents} = this.state; + const {displayedProperties, torrentListViewSize, torrents} = this.state; const torrent = torrents[index]; const {hash} = torrent; @@ -509,7 +511,7 @@ class TorrentListContainer extends React.Component { handleDoubleClick={this.handleDoubleClick} handleRightClick={this.handleContextMenuClick} index={index} - isCondensed={options.isCondensed} + isCondensed={torrentListViewSize === 'condensed'} key={hash} columns={this.state.displayedProperties} propWidths={this.state.torrentListColumnWidths} @@ -552,7 +554,6 @@ class TorrentListContainer extends React.Component { content = ( this.listViewportRef = ref} diff --git a/client/scripts/stores/TorrentFilterStore.js b/client/scripts/stores/TorrentFilterStore.js index ea3adcd3..aad9c513 100644 --- a/client/scripts/stores/TorrentFilterStore.js +++ b/client/scripts/stores/TorrentFilterStore.js @@ -97,6 +97,7 @@ class TorrentFilterStoreClass extends BaseStore { setSearchFilter(filter) { this.searchFilter = filter; + this.emit(EventTypes.UI_TORRENTS_FILTER_CHANGE); this.emit(EventTypes.UI_TORRENTS_FILTER_SEARCH_CHANGE); }