From 6bf3e9acfde20092e987d94bcf295b9e9282e7af Mon Sep 17 00:00:00 2001 From: pradeepnschrodinger Date: Mon, 3 Jun 2019 21:25:32 +0000 Subject: [PATCH 1/7] Fix cells having stale props --- src/FixedDataTableRow.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/FixedDataTableRow.js b/src/FixedDataTableRow.js index 8387289b..c1f535f4 100644 --- a/src/FixedDataTableRow.js +++ b/src/FixedDataTableRow.js @@ -153,6 +153,11 @@ class FixedDataTableRowImpl extends React.Component { touchEnabled: PropTypes.bool, }; + shouldComponentUpdate(nextProps) { + // only render if row is visible + return nextProps.visible; + } + render() /*object*/ { if (this.props.fake) { return null; @@ -461,7 +466,7 @@ class FixedDataTableRow extends React.Component { }; FixedDataTableTranslateDOMPosition(style, 0, this.props.offsetTop, this._initialRender); - const { offsetTop, zIndex, visible, ...rowProps } = this.props; + const { offsetTop, zIndex, ...rowProps } = this.props; return (
Date: Tue, 4 Jun 2019 06:00:14 +0000 Subject: [PATCH 2/7] Refactor FixedDataTableBufferedRows --- src/FixedDataTableBufferedRows.js | 145 +++++++++++++----------------- src/FixedDataTableCell.js | 9 ++ 2 files changed, 70 insertions(+), 84 deletions(-) diff --git a/src/FixedDataTableBufferedRows.js b/src/FixedDataTableBufferedRows.js index ea15a42a..9d74db9b 100644 --- a/src/FixedDataTableBufferedRows.js +++ b/src/FixedDataTableBufferedRows.js @@ -77,119 +77,96 @@ class FixedDataTableBufferedRows extends React.Component { } render() /*object*/ { - var props = this.props; - var rowClassNameGetter = props.rowClassNameGetter || emptyFunction; - var rowsToRender = this.props.rowsToRender || []; - - if (props.isScrolling) { - // We are scrolling, so there's no need to display any rows which lie outside the viewport. - // We still need to render them though, so as to not cause any mounts/unmounts. - this._staticRowArray.forEach((row, i) => { - const rowOutsideViewport = !this.isRowInsideViewport(row.props.index); - if (rowOutsideViewport) { - this._staticRowArray[i] = this.getStubRow(i, false, row.props.index); - } - }); + let { offsetTop, scrollTop, isScrolling, rowsToRender } = this.props; + const baseOffsetTop = offsetTop - scrollTop; + rowsToRender = rowsToRender || []; + + if (isScrolling) { + // allow static array to grow while scrolling + this._staticRowArray.length = Math.max(this._staticRowArray.length, rowsToRender.length); } else { + // when scrolling is done, static array can shrink to fit the buffer this._staticRowArray.length = rowsToRender.length; } - var baseOffsetTop = props.offsetTop - props.scrollTop; - - for (let i = 0; i < rowsToRender.length; i++) { - const rowIndex = rowsToRender[i]; - - // if the row doesn't exist in the buffer, assign a fake row to it. - // this is so that we can get rid of unnecessary row mounts/unmounts + // render each row from the buffer into the static row array + for (let i = 0; i < this._staticRowArray.length; i++) { + let rowIndex = rowsToRender[i]; + // if the row doesn't exist in the buffer set, then take the previous one if (rowIndex === undefined) { - // if a previous row existed, let's just make use of that - if (this._staticRowArray[i] === undefined) { - this._staticRowArray[i] = this.getStubRow(i, true, -1); - } - continue; + rowIndex = this._staticRowArray[i] && this._staticRowArray[i].props.index; } - const currentRowHeight = this.props.rowSettings.rowHeightGetter(rowIndex); - const currentSubRowHeight = this.props.rowSettings.subRowHeightGetter(rowIndex); - const rowOffsetTop = baseOffsetTop + props.rowOffsets[rowIndex]; - const rowKey = props.rowKeyGetter ? props.rowKeyGetter(rowIndex) : i; - const hasBottomBorder = (rowIndex === props.rowSettings.rowsCount - 1) && - props.showLastRowBorder; - const visible = this.isRowInsideViewport(rowIndex); - - this._staticRowArray[i] = - ; + this._staticRowArray[i] = this.renderRow({ + rowIndex, + key: i, + baseOffsetTop, + }); } return
{this._staticRowArray}
; } - /** - * Returns a stub row which won't be visible to the user. - * This allows us to still render a row and React won't unmount it. - * + * @param {number} rowIndex * @param {number} key - * @param {boolean} fake - * @param {number} index + * @param {number} baseOffsetTop * @return {!Object} */ - getStubRow(key, fake, index) /*object*/ { + renderRow({ rowIndex, key, baseOffsetTop }) /*object*/ { const props = this.props; + const rowClassNameGetter = props.rowClassNameGetter || emptyFunction; + const fake = rowIndex === undefined; + let rowProps = {}; + + // if row exists, then calculate row specific props + if (!fake) { + rowProps.height = this.props.rowSettings.rowHeightGetter(rowIndex); + rowProps.subRowHeight = this.props.rowSettings.subRowHeightGetter(rowIndex); + rowProps.offsetTop = Math.round(baseOffsetTop + props.rowOffsets[rowIndex]); + rowProps.key = props.rowKeyGetter ? props.rowKeyGetter(rowIndex) : key; + + const hasBottomBorder = (rowIndex === props.rowSettings.rowsCount - 1) && props.showLastRowBorder; + rowProps.className = joinClasses( + rowClassNameGetter(rowIndex), + cx('public/fixedDataTable/bodyRow'), + cx({ + 'fixedDataTableLayout/hasBottomBorder': hasBottomBorder, + 'public/fixedDataTable/hasBottomBorder': hasBottomBorder, + }) + ); + } + + const visible = inRange(rowIndex, this.props.firstViewportRowIndex, this.props.endViewportRowIndex); + return ( ); } - - isRowInsideViewport(/*number*/rowIndex) { - return inRange(rowIndex, this.props.firstViewportRowIndex, this.props.endViewportRowIndex); - } }; module.exports = FixedDataTableBufferedRows; diff --git a/src/FixedDataTableCell.js b/src/FixedDataTableCell.js index e8ab9718..1c7ed658 100644 --- a/src/FixedDataTableCell.js +++ b/src/FixedDataTableCell.js @@ -90,6 +90,15 @@ class FixedDataTableCell extends React.Component { reorderingDisplacement: 0 } + componentDidMount() { + "use strict"; + console.log("cell mounted"); + } + componentWillUnmount() { + "use strict"; + console.log("cell unmounted"); + } + shouldComponentUpdate(nextProps) { if (nextProps.isScrolling && this.props.rowIndex === nextProps.rowIndex) { return false; From d9f342003a0c3664ecae3e18c6cebb646ee4fc77 Mon Sep 17 00:00:00 2001 From: pradeepnschrodinger Date: Tue, 4 Jun 2019 06:20:15 +0000 Subject: [PATCH 3/7] Remove shouldComponentUpdate --- src/FixedDataTableRow.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/FixedDataTableRow.js b/src/FixedDataTableRow.js index c1f535f4..223e3e32 100644 --- a/src/FixedDataTableRow.js +++ b/src/FixedDataTableRow.js @@ -153,11 +153,6 @@ class FixedDataTableRowImpl extends React.Component { touchEnabled: PropTypes.bool, }; - shouldComponentUpdate(nextProps) { - // only render if row is visible - return nextProps.visible; - } - render() /*object*/ { if (this.props.fake) { return null; From 22de3eb0c00a3c6b2b037774c861faf88fa38ed0 Mon Sep 17 00:00:00 2001 From: pradeepnschrodinger Date: Tue, 4 Jun 2019 18:59:45 +0000 Subject: [PATCH 4/7] Remove debug statements --- src/FixedDataTableCell.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/FixedDataTableCell.js b/src/FixedDataTableCell.js index 1c7ed658..e8ab9718 100644 --- a/src/FixedDataTableCell.js +++ b/src/FixedDataTableCell.js @@ -90,15 +90,6 @@ class FixedDataTableCell extends React.Component { reorderingDisplacement: 0 } - componentDidMount() { - "use strict"; - console.log("cell mounted"); - } - componentWillUnmount() { - "use strict"; - console.log("cell unmounted"); - } - shouldComponentUpdate(nextProps) { if (nextProps.isScrolling && this.props.rowIndex === nextProps.rowIndex) { return false; From 250ac3702bf05ef945d314c0d7584619b8c477d0 Mon Sep 17 00:00:00 2001 From: pradeepnschrodinger Date: Wed, 5 Jun 2019 22:19:17 +0000 Subject: [PATCH 5/7] Add 'AutoScroll' Example --- examples/AutoScrollExample.js | 156 +++++++++++++++++++++++++++ site/Constants.js | 6 ++ site/examples/ExamplesPage.js | 1 + site/examples/examplesPageStyle.less | 6 ++ 4 files changed, 169 insertions(+) create mode 100644 examples/AutoScrollExample.js diff --git a/examples/AutoScrollExample.js b/examples/AutoScrollExample.js new file mode 100644 index 00000000..d7b4c5a3 --- /dev/null +++ b/examples/AutoScrollExample.js @@ -0,0 +1,156 @@ +/** + * Copyright Schrodinger, LLC + */ + +"use strict"; + +const FakeObjectDataListStore = require('./helpers/FakeObjectDataListStore'); +const { ImageCell, LinkCell } = require('./helpers/cells'); +const { Table, Column, Cell } = require('fixed-data-table-2'); +const React = require('react'); + +class AutoScrollExample extends React.Component { + constructor(props) { + super(props); + + this.state = { + dataList: new FakeObjectDataListStore(1000000), + scrollTop: 0, + scrollLeft: 0, + autoScrollEnabled: true, + horizontalScrollDelta: 0, + verticalScrollDelta: 0, + }; + + this.columns = []; + const cellRenderer = ({ columnKey, rowIndex }) => (`${rowIndex}, ${columnKey}`); + + for (let i = 0; i < 100; i++) { + this.columns[i] = ( + {i}
} + cell={cellRenderer} + width={100} + allowCellsRecycling={true} + /> + ) + } + + this.onVerticalScroll = this.onVerticalScroll.bind(this); + this.onHorizontalScroll = this.onHorizontalScroll.bind(this); + this.toggleAutoScroll = this.toggleAutoScroll.bind(this); + this.setHorizontalScrollDelta = this.setHorizontalScrollDelta.bind(this); + this.setVerticalScrollDelta = this.setVerticalScrollDelta.bind(this); + } + + componentDidMount() { + setInterval(() => { + if (!this.state.autoScrollEnabled) { + return; + } + this.setState((prevState) => ({ + scrollTop: prevState.scrollTop + (prevState.verticalScrollDelta || 0), + scrollLeft: prevState.scrollLeft + (prevState.horizontalScrollDelta || 0), + })); + }, 16); + } + + render() { + return ( +
+ {this.renderControls()} + {this.renderTable()} +
+ ); + } + + renderControls() { + return ( +
+ + + +
+ ) + } + + renderTable() { + var { dataList, scrollLeft, scrollTop } = this.state; + return ( + + } + fixed={true} + width={50} + /> + First Name} + cell={} + fixed={true} + width={100} + /> + {this.columns} +
+ ); + } + + onVerticalScroll(scrollTop) { + this.setState({ scrollTop }); + } + + onHorizontalScroll(scrollLeft) { + this.setState({ scrollLeft }); + } + + toggleAutoScroll() { + this.setState((prevState) => ({ + autoScrollEnabled: !prevState.autoScrollEnabled, + })); + } + + setHorizontalScrollDelta(event) { + const { value } = event.target; + if (isNaN(value)) { + return; + } + this.setState({ + horizontalScrollDelta: parseInt(value), + }); + } + + setVerticalScrollDelta(event) { + const { value } = event.target; + if (isNaN(value)) { + return; + } + this.setState({ + verticalScrollDelta: parseInt(value), + }); + } +} + +module.exports = AutoScrollExample; diff --git a/site/Constants.js b/site/Constants.js index f84a6e1c..f502f5ee 100644 --- a/site/Constants.js +++ b/site/Constants.js @@ -169,6 +169,12 @@ exports.ExamplePages = { title: 'Fixed Rows', description: 'An example using multiple tables to mimic fixed rows.', }, + AUTO_SCROLL_EXAMPLE: { + location: 'example-auto-scroll.html', + fileName: 'AutoScrollExample.js', + title: 'Auto Scroll', + description: 'An example using Controlled Scrolling to mimic auto scrolling', + }, }; Object.keys(exports.ExamplePages).forEach( diff --git a/site/examples/ExamplesPage.js b/site/examples/ExamplesPage.js index 150c3b51..beea3c8b 100644 --- a/site/examples/ExamplesPage.js +++ b/site/examples/ExamplesPage.js @@ -50,6 +50,7 @@ var EXAMPLE_COMPONENTS = { [ExamplePages.CONTEXT_EXAMPLE.location]: require('../../examples/ContextExample'), [ExamplePages.FIXED_RIGHT_COLUMNS_EXAMPLE.location]: require('../../examples/FixedRightColumnsExample'), [ExamplePages.FIXED_ROWS_EXAMPLE.location]: require('../../examples/FixedRowsExample'), + [ExamplePages.AUTO_SCROLL_EXAMPLE.location]: require('../../examples/AutoScrollExample'), }; class ExamplesPage extends React.Component { diff --git a/site/examples/examplesPageStyle.less b/site/examples/examplesPageStyle.less index 613c0074..b17d7234 100644 --- a/site/examples/examplesPageStyle.less +++ b/site/examples/examplesPageStyle.less @@ -326,3 +326,9 @@ height: 50px; width: 50px; } + +.autoScrollControlContainer { + display: flex; + justify-content: space-around; + align-items: baseline; +} From e4c3d0a5ffd848a22a43b5afd61af9b407bd927d Mon Sep 17 00:00:00 2001 From: pradeepnschrodinger Date: Wed, 5 Jun 2019 22:41:23 +0000 Subject: [PATCH 6/7] Fix stale props --- examples/AutoScrollExample.js | 7 ++++--- site/examples/examplesPageStyle.less | 16 ++++++++++++---- src/FixedDataTableBufferedRows.js | 4 ++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/AutoScrollExample.js b/examples/AutoScrollExample.js index d7b4c5a3..54b72421 100644 --- a/examples/AutoScrollExample.js +++ b/examples/AutoScrollExample.js @@ -23,7 +23,8 @@ class AutoScrollExample extends React.Component { }; this.columns = []; - const cellRenderer = ({ columnKey, rowIndex }) => (`${rowIndex}, ${columnKey}`); + const cellRenderer = ({ columnKey, rowIndex }) => + (
`${rowIndex}, ${columnKey}`
); for (let i = 0; i < 100; i++) { this.columns[i] = ( @@ -59,7 +60,7 @@ class AutoScrollExample extends React.Component { render() { return ( -
+
{this.renderControls()} {this.renderTable()}
@@ -68,7 +69,7 @@ class AutoScrollExample extends React.Component { renderControls() { return ( -
+