diff --git a/CHANGELOG.md b/CHANGELOG.md index 7272bf9f..e313f8c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## NEXT VERSION - chore: stop relying on `onScrollbarPresenceChange` +- feat: replace `react-virtualized` with `react-window` +- feat: add scroll direction to `onScroll` +- feat: add `align` to `scrollToRow` # v1.1.1 (2019-04-27) diff --git a/package.json b/package.json index e230c349..74a2fc2a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "memoize-one": "^5.0.0", "prop-types": "^15.6.0", "react-draggable": "^3.0.5", - "react-virtualized": "^9.18.5" + "react-virtualized-auto-sizer": "^1.0.2", + "react-window": "^1.8.1" }, "peerDependencies": { "react": "^16.0.0", diff --git a/src/AutoResizer.js b/src/AutoResizer.js index 7d3ba939..9a627b2d 100644 --- a/src/AutoResizer.js +++ b/src/AutoResizer.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; +import AutoSizer from 'react-virtualized-auto-sizer'; /** * Decorator component that automatically adjusts the width and height of a single child diff --git a/src/BaseTable.js b/src/BaseTable.js index fccc77c8..c5b068b2 100644 --- a/src/BaseTable.js +++ b/src/BaseTable.js @@ -176,15 +176,26 @@ class BaseTable extends React.PureComponent { } /** - * Ensure row is visible. - * This method can be used to safely scroll back to a row that a user has scrolled away from even if it was previously scrolled to. - * - * @param {number} rowIndex + * Scroll to the specified row. + * By default, the table will scroll as little as possible to ensure the row is visible. + * You can control the alignment of the row though by specifying an align property. Acceptable values are: + * + * - `auto` (default) - Scroll as little as possible to ensure the row is visible. + * (If the row is already visible, it won't scroll at all.) + * - `smart` - If the row is already visible, don't scroll at all. If it is less than one viewport away, + * scroll as little as possible so that it becomes visible. + * If it is more than one viewport away, scroll so that it is centered within the grid. + * - `center` - Center align the row within the table. + * - `end` - Align the row to the bottom, right hand side of the table. + * - `start` - Align the row to the top, left hand of the table. + + * @param {number} rowIndex + * @param {string} align */ - scrollToRow(rowIndex = 0) { - this.table && this.table.scrollToRow(rowIndex); - this.leftTable && this.leftTable.scrollToRow(rowIndex); - this.rightTable && this.rightTable.scrollToRow(rowIndex); + scrollToRow(rowIndex = 0, align = 'auto') { + this.table && this.table.scrollToRow(rowIndex, align); + this.leftTable && this.leftTable.scrollToRow(rowIndex, align); + this.rightTable && this.rightTable.scrollToRow(rowIndex, align); } /** @@ -1044,7 +1055,14 @@ BaseTable.propTypes = { overscanRowCount: PropTypes.number, /** * A callback function when scrolling the table - * The handler is of the shape of `({ scrollLeft, scrollTop }) => *` + * The handler is of the shape of `({ scrollLeft, scrollTop, horizontalScrollDirection, verticalScrollDirection, scrollUpdateWasRequested }) => *` + * + * `scrollLeft` and `scrollTop` are numbers. + * + * `horizontalDirection` and `verticalDirection` are either `forward` or `backward`. + * + * `scrollUpdateWasRequested` is a boolean. This value is true if the scroll was caused by `scrollTo*`, + * and false if it was the result of a user interaction in the browser. */ onScroll: PropTypes.func, /** diff --git a/src/GridTable.js b/src/GridTable.js index c3be429b..a618adda 100644 --- a/src/GridTable.js +++ b/src/GridTable.js @@ -1,10 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import cn from 'classnames'; -import Grid from 'react-virtualized/dist/commonjs/Grid'; +import { FixedSizeGrid as Grid } from 'react-window'; import Header from './TableHeader'; -import cellRangeRenderer from './cellRangeRenderer'; /** * A wrapper of the Grid for internal only @@ -15,10 +14,10 @@ class GridTable extends React.PureComponent { this._setHeaderRef = this._setHeaderRef.bind(this); this._setBodyRef = this._setBodyRef.bind(this); - this._handleSectionRendered = this._handleSectionRendered.bind(this); + this._itemKey = this._itemKey.bind(this); + this._handleItemsRendered = this._handleItemsRendered.bind(this); this.renderRow = this.renderRow.bind(this); - this.renderCellRange = this.renderCellRange.bind(this); } forceUpdateTable() { @@ -28,11 +27,11 @@ class GridTable extends React.PureComponent { scrollToPosition(args) { this.headerRef && this.headerRef.scrollTo(args.scrollLeft); - this.bodyRef && this.bodyRef.scrollToPosition(args); + this.bodyRef && this.bodyRef.scrollTo(args); } scrollToTop(scrollTop) { - this.bodyRef && this.bodyRef.scrollToPosition({ scrollTop }); + this.bodyRef && this.bodyRef.scrollTo({ scrollTop }); } scrollToLeft(scrollLeft) { @@ -40,12 +39,8 @@ class GridTable extends React.PureComponent { this.bodyRef && this.bodyRef.scrollToPosition({ scrollLeft }); } - scrollToRow(rowIndex = 0) { - this.bodyRef && - this.bodyRef.scrollToCell({ - rowIndex, - columnIndex: 0, - }); + scrollToRow(rowIndex = 0, align = 'auto') { + this.bodyRef && this.bodyRef.scrollToItem({ rowIndex, columnIndex: 0, align }); } renderRow(args) { @@ -54,12 +49,6 @@ class GridTable extends React.PureComponent { return rowRenderer({ ...args, columns, rowData }); } - renderCellRange(args) { - const { useIsScrolling } = this.props; - const isScrolling = useIsScrolling ? args.isScrolling : undefined; - return cellRangeRenderer({ ...args, useIsScrolling, isScrolling }); - } - render() { const { containerStyle, @@ -75,6 +64,7 @@ class GridTable extends React.PureComponent { useIsScrolling, onScroll, hoveredRowKey, + overscanRowCount, // omit from rest style, onScrollbarPresenceChange, @@ -92,19 +82,21 @@ class GridTable extends React.PureComponent { className={`${classPrefix}__body`} ref={this._setBodyRef} data={data} + itemKey={this._itemKey} frozenData={frozenData} width={width} height={Math.max(height - headerHeight - frozenRowsHeight, 0)} rowHeight={rowHeight} rowCount={data.length} + overscanRowsCount={overscanRowCount} columnWidth={bodyWidth} columnCount={1} - isScrollingOptOut={!useIsScrolling} - cellRenderer={this.renderRow} - cellRangeRenderer={this.renderCellRange} + overscanColumnsCount={0} + useIsScrolling={useIsScrolling} hoveredRowKey={hoveredRowKey} onScroll={onScroll} - onSectionRendered={this._handleSectionRendered} + onItemsRendered={this._handleItemsRendered} + children={this.renderRow} /> {headerHeight + frozenRowsHeight > 0 && ( // put header after body and reverse the display order via css @@ -137,6 +129,11 @@ class GridTable extends React.PureComponent { this.bodyRef = ref; } + _itemKey({ rowIndex }) { + const { data, rowKey } = this.props; + return data[rowIndex][rowKey]; + } + _getHeaderHeight() { const { headerHeight } = this.props; if (Array.isArray(headerHeight)) { @@ -145,14 +142,12 @@ class GridTable extends React.PureComponent { return headerHeight; } - _handleSectionRendered({ rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex }) { - const { onRowsRendered } = this.props; - - onRowsRendered({ - overscanStartIndex: rowOverscanStartIndex, - overscanStopIndex: rowOverscanStopIndex, - startIndex: rowStartIndex, - stopIndex: rowStopIndex, + _handleItemsRendered({ overscanRowStartIndex, overscanRowStopIndex, visibleRowStartIndex, visibleRowStopIndex }) { + this.props.onRowsRendered({ + overscanStartIndex: overscanRowStartIndex, + overscanStopIndex: overscanRowStopIndex, + startIndex: visibleRowStartIndex, + stopIndex: visibleRowStopIndex, }); } } @@ -169,8 +164,10 @@ GridTable.propTypes = { rowHeight: PropTypes.number.isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired, data: PropTypes.arrayOf(PropTypes.object).isRequired, + rowKey: PropTypes.string.isRequired, frozenData: PropTypes.arrayOf(PropTypes.object), useIsScrolling: PropTypes.bool, + overscanRowCount: PropTypes.number, hoveredRowKey: PropTypes.string, style: PropTypes.object, onScrollbarPresenceChange: PropTypes.func, diff --git a/src/__snapshots__/BaseTable.spec.js.snap b/src/__snapshots__/BaseTable.spec.js.snap index 71449a05..bf15b1a7 100644 --- a/src/__snapshots__/BaseTable.spec.js.snap +++ b/src/__snapshots__/BaseTable.spec.js.snap @@ -16,37 +16,25 @@ exports[`Table renders correctly 1`] = ` role="table" >
+ > +
+
+
+
+ 1 +
+
+
+
+ 1 +
+
+
+
+
+
+ 2 +
+
+
+
+ 2 +
+
+
+
+
+ > +
+
=3.1.1 <6": + version "5.0.4" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc" + integrity sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA== + memoize-one@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.0.tgz#d55007dffefb8de7546659a1722a5d42e128286e" @@ -6536,11 +6534,6 @@ react-is@^16.8.1, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2" integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA== -react-lifecycles-compat@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== - react-test-renderer@^16.4.2: version "16.8.4" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.4.tgz#abee4c2c3bf967a8892a7b37f77370c5570d5329" @@ -6551,17 +6544,18 @@ react-test-renderer@^16.4.2: react-is "^16.8.4" scheduler "^0.13.4" -react-virtualized@^9.18.5: - version "9.21.0" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.0.tgz#8267c40ffb48db35b242a36dea85edcf280a6506" - integrity sha512-duKD2HvO33mqld4EtQKm9H9H0p+xce1c++2D5xn59Ma7P8VT7CprfAe5hwjd1OGkyhqzOZiTMlTal7LxjH5yBQ== +react-virtualized-auto-sizer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd" + integrity sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg== + +react-window@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.1.tgz#12f1db71d91552ed2263790e57cc77d950803db1" + integrity sha512-iNzekymggL9zAnil3QbmRG74RDMfIbO+plE/soP3M/zskicA1DwoLthC6/QA6xu9dr+A5UoawCTsEYcva2mfeA== dependencies: - babel-runtime "^6.26.0" - classnames "^2.2.3" - dom-helpers "^2.4.0 || ^3.0.0" - loose-envify "^1.3.0" - prop-types "^15.6.0" - react-lifecycles-compat "^3.0.4" + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" react@^16.4.2: version "16.8.4"