diff --git a/CHANGELOG.md b/CHANGELOG.md index 18684147..e66046a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## NEXT VERSION +- feat: add the ability to pass function in `estimatedRowHeight` to determine the initial height of rows + ## v1.11.3 (2020-08-24) - fix: remove propTypes for Column.key diff --git a/src/BaseTable.js b/src/BaseTable.js index 54a39f3c..12a35143 100644 --- a/src/BaseTable.js +++ b/src/BaseTable.js @@ -19,6 +19,7 @@ import { renderElement, normalizeColumns, getScrollbarSize as defaultGetScrollbarSize, + getEstimatedTotalRowsHeight, isObjectEqual, callOrReturn, hasChildren, @@ -120,6 +121,7 @@ class BaseTable extends React.PureComponent { this._mainRowHeightMap = {}; this._leftRowHeightMap = {}; this._rightRowHeightMap = {}; + this._getEstimatedTotalRowsHeight = memoize(getEstimatedTotalRowsHeight); this._getRowHeight = this._getRowHeight.bind(this); this._updateRowHeights = debounce(() => { this._isResetting = true; @@ -185,7 +187,9 @@ class BaseTable extends React.PureComponent { const { rowHeight, estimatedRowHeight } = this.props; if (estimatedRowHeight) { - return this.table ? this.table.getTotalRowsHeight() : this._data.length * estimatedRowHeight; + return this.table + ? this.table.getTotalRowsHeight() + : this._getEstimatedTotalRowsHeight(this._data, estimatedRowHeight); } return this._data.length * rowHeight; } @@ -716,7 +720,7 @@ class BaseTable extends React.PureComponent { [`${classPrefix}--has-frozen-rows`]: frozenData.length > 0, [`${classPrefix}--has-frozen-columns`]: this.columnManager.hasFrozenColumns(), [`${classPrefix}--disabled`]: disabled, - [`${classPrefix}--dynamic`]: estimatedRowHeight > 0, + [`${classPrefix}--dynamic`]: !!estimatedRowHeight, }); return (
@@ -785,7 +789,10 @@ class BaseTable extends React.PureComponent { // for dynamic row height _getRowHeight(rowIndex) { const { estimatedRowHeight, rowKey } = this.props; - return this._rowHeightMap[this._data[rowIndex][rowKey]] || estimatedRowHeight; + return ( + this._rowHeightMap[this._data[rowIndex][rowKey]] || + callOrReturn(estimatedRowHeight, { rowData: this._data[rowIndex], rowIndex }) + ); } _getIsResetting() { @@ -1103,8 +1110,9 @@ BaseTable.propTypes = { rowHeight: PropTypes.number, /** * Estimated row height, the real height will be measure dynamically according to the content + * The callback is of the shape of `({ rowData, rowIndex }) => number` */ - estimatedRowHeight: PropTypes.number, + estimatedRowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), /** * The height of the table header, set to 0 to hide the header, could be an array to render multi headers. */ diff --git a/src/GridTable.js b/src/GridTable.js index a0216b78..b23ec257 100644 --- a/src/GridTable.js +++ b/src/GridTable.js @@ -5,6 +5,7 @@ import { FixedSizeGrid, VariableSizeGrid } from 'react-window'; import memoize from 'memoize-one'; import Header from './TableHeader'; +import { getEstimatedTotalRowsHeight } from './utils'; /** * A wrapper of the Grid for internal only @@ -23,6 +24,7 @@ class GridTable extends React.PureComponent { if (!this.props.estimatedRowHeight) return; this.bodyRef && this.bodyRef.resetAfterColumnIndex(0, false); }); + this._getEstimatedTotalRowsHeight = memoize(getEstimatedTotalRowsHeight); this.renderRow = this.renderRow.bind(this); } @@ -59,7 +61,9 @@ class GridTable extends React.PureComponent { const { data, rowHeight, estimatedRowHeight } = this.props; if (estimatedRowHeight) { - return (this.innerRef && this.innerRef.clientHeight) || data.length * estimatedRowHeight; + return ( + (this.innerRef && this.innerRef.clientHeight) || this._getEstimatedTotalRowsHeight(data, estimatedRowHeight) + ); } return data.length * rowHeight; } @@ -114,7 +118,7 @@ class GridTable extends React.PureComponent { width={width} height={Math.max(height - headerHeight - frozenRowsHeight, 0)} rowHeight={estimatedRowHeight ? getRowHeight : rowHeight} - estimatedRowHeight={estimatedRowHeight} + estimatedRowHeight={typeof estimatedRowHeight === 'function' ? undefined : estimatedRowHeight} rowCount={data.length} overscanRowCount={overscanRowCount} columnWidth={estimatedRowHeight ? this._getBodyWidth : bodyWidth} @@ -198,7 +202,7 @@ GridTable.propTypes = { headerWidth: PropTypes.number.isRequired, bodyWidth: PropTypes.number.isRequired, rowHeight: PropTypes.number.isRequired, - estimatedRowHeight: PropTypes.number, + estimatedRowHeight: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), getRowHeight: PropTypes.func, columns: PropTypes.arrayOf(PropTypes.object).isRequired, data: PropTypes.array.isRequired, diff --git a/src/TableRow.js b/src/TableRow.js index d7d44402..be5e8ff0 100644 --- a/src/TableRow.js +++ b/src/TableRow.js @@ -183,7 +183,7 @@ TableRow.propTypes = { rowRenderer: PropTypes.oneOfType([PropTypes.func, PropTypes.element]), cellRenderer: PropTypes.func, expandIconRenderer: PropTypes.func, - estimatedRowHeight: PropTypes.number, + estimatedRowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), getIsResetting: PropTypes.func, onRowHover: PropTypes.func, onRowExpand: PropTypes.func, diff --git a/src/utils.js b/src/utils.js index 0d98b19a..94cacc6e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -251,3 +251,9 @@ export function removeClassName(el, className) { el.className = el.className.replace(new RegExp(`(?:^|\\s)${className}(?!\\S)`, 'g'), ''); } } + +export function getEstimatedTotalRowsHeight(data, estimatedRowHeight) { + return typeof estimatedRowHeight === 'function' + ? data.reduce((height, rowData, rowIndex) => height + estimatedRowHeight({ rowData, rowIndex }), 0) + : data.length * estimatedRowHeight; +} diff --git a/types/index.d.ts b/types/index.d.ts index 1248ad51..bbadb93f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -208,8 +208,15 @@ declare module 'react-base-table' { rowHeight?: number; /** * Estimated row height, the real height will be measure dynamically according to the content + * The callback is of the shape of `({ rowData, rowIndex }) => number` */ - estimatedRowHeight?: number; + estimatedRowHeight?: CallOrReturn< + number, + { + rowData: T; + rowIndex: number; + } + >; /** * The height of the table header, set to 0 to hide the header, could be an array to render multi headers. */