diff --git a/docs/CellMeasurer.md b/docs/CellMeasurer.md index 7b49d0c69..4ef789df6 100644 --- a/docs/CellMeasurer.md +++ b/docs/CellMeasurer.md @@ -51,7 +51,10 @@ class CellSizeCache { ``` The [default caching strategy](https://github.com/bvaughn/react-virtualized/blob/master/source/CellMeasurer/defaultCellSizeCache.js) is exported as `defaultCellMeasurerCellSizeCache` should you wish to decorate it. -You can also use [an alternative caching strategy](https://github.com/bvaughn/react-virtualized/blob/master/source/CellMeasurer/uniformSizeCellSizeCache.js) for lists with a uniform (yet unknown) row height, exported as `uniformSizeCellMeasurerCellSizeCache`. +You can also pass `uniformRowHeight` and/or `uniformColumnWidth` named parameters to the constructor for lists with a uniform (yet unknown) cell sizes. + +An [id-based caching strategy](https://github.com/bvaughn/react-virtualized/blob/master/source/CellMeasurer/idCellSizeCache.js) is also available for data that may be sorted. +This strategy maps data ids to cell sizes rathe than index so that the sorting order of the data does not invalidate sizes. ### Examples diff --git a/source/CellMeasurer/defaultCellSizeCache.js b/source/CellMeasurer/defaultCellSizeCache.js index 60a8a0371..3fc05d60a 100644 --- a/source/CellMeasurer/defaultCellSizeCache.js +++ b/source/CellMeasurer/defaultCellSizeCache.js @@ -3,7 +3,7 @@ * Permanently caches all cell sizes (identified by column and row index) unless explicitly cleared. * Can be configured to handle uniform cell widths and/or heights as a way of optimizing certain use cases. */ -export default class CellSizeCache { +export default class DefaultCellSizeCache { constructor ({ uniformRowHeight = false, uniformColumnWidth = false @@ -28,36 +28,36 @@ export default class CellSizeCache { this._cachedRowHeights = {} } - clearColumnWidth (index: number) { + clearColumnWidth (index: any) { this._cachedColumnWidth = undefined delete this._cachedColumnWidths[index] } - clearRowHeight (index: number) { + clearRowHeight (index: any) { this._cachedRowHeight = undefined delete this._cachedRowHeights[index] } - getColumnWidth (index: number): ?number { + getColumnWidth (index: any): ?number { return this._uniformColumnWidth ? this._cachedColumnWidth : this._cachedColumnWidths[index] } - getRowHeight (index: number): ?number { + getRowHeight (index: any): ?number { return this._uniformRowHeight ? this._cachedRowHeight : this._cachedRowHeights[index] } - setColumnWidth (index: number, width: number) { + setColumnWidth (index: any, width: number) { this._cachedColumnWidth = width this._cachedColumnWidths[index] = width } - setRowHeight (index: number, height: number) { + setRowHeight (index: any, height: number) { this._cachedRowHeight = height this._cachedRowHeights[index] = height } diff --git a/source/CellMeasurer/idCellSizeCache.js b/source/CellMeasurer/idCellSizeCache.js new file mode 100644 index 000000000..d6f6e59f0 --- /dev/null +++ b/source/CellMeasurer/idCellSizeCache.js @@ -0,0 +1,72 @@ +/** @flow */ +import DefaultCellSizeCache from './defaultCellSizeCache' + +type IdCellSizeCacheConstructorParams = { + indexToIdMap : Function, + uniformRowHeight ?: boolean, + uniformColumnWidth ?: boolean +}; + +/** + * Alternate CellMeasurer `cellSizeCache` implementation. + * Similar to `defaultCellSizeCache` except that sizes are tied to data id rather than index. + * Requires an index-to-id map function (passed in externally) to operate. + */ +export default function idCellSizeCache ({ + indexToIdMap, + uniformColumnWidth = false, + uniformRowHeight = false +} : IdCellSizeCacheConstructorParams) { + const cellSizeCache = new DefaultCellSizeCache({ + uniformColumnWidth, + uniformRowHeight + }) + + return { + clearAllColumnWidths () { + cellSizeCache.clearAllColumnWidths() + }, + + clearAllRowHeights () { + cellSizeCache.clearAllRowHeights() + }, + + clearColumnWidth (index: number) { + cellSizeCache.clearColumnWidth( + indexToIdMap(index) + ) + }, + + clearRowHeight (index: number) { + cellSizeCache.clearRowHeight( + indexToIdMap(index) + ) + }, + + getColumnWidth (index: number): ?number { + return cellSizeCache.getColumnWidth( + indexToIdMap(index) + ) + }, + + getRowHeight (index: number): ?number { + return cellSizeCache.getRowHeight( + indexToIdMap(index) + ) + }, + + setColumnWidth (index: number, width: number) { + cellSizeCache.setColumnWidth( + indexToIdMap(index), + width + ) + }, + + setRowHeight (index: number, height: number) { + cellSizeCache.setRowHeight( + indexToIdMap(index), + height + ) + } + } +} diff --git a/source/CellMeasurer/idCellSizeCache.test.js b/source/CellMeasurer/idCellSizeCache.test.js new file mode 100644 index 000000000..04846c1c8 --- /dev/null +++ b/source/CellMeasurer/idCellSizeCache.test.js @@ -0,0 +1,68 @@ +import idCellSizeCache from './idCellSizeCache' + +describe('idCellSizeCache', () => { + it('should track width and height using id instead of index', () => { + const indexToIdMap = { + 0: 'foo', + 1: 'bar', + 2: 'baz' + } + const indexToIdMapCalls = [] + const cache = idCellSizeCache({ + indexToIdMap: (index) => { + indexToIdMapCalls.push(index) + return indexToIdMap[index] + } + }) + + // Set values with initial indices + cache.setColumnWidth(0, 100) + cache.setRowHeight(0, 25) + cache.setColumnWidth(1, 200) + cache.setRowHeight(1, 50) + cache.setColumnWidth(2, 300) + cache.setRowHeight(2, 75) + + // Mimic a sort operation + indexToIdMap[0] = 'baz' + indexToIdMap[1] = 'bar' + indexToIdMap[2] = 'foo' + + // Previous widths and heights should still be available + expect(cache.getColumnWidth(0)).toBe(300) + expect(cache.getRowHeight(0)).toBe(75) + expect(cache.getColumnWidth(1)).toBe(200) + expect(cache.getRowHeight(1)).toBe(50) + expect(cache.getColumnWidth(2)).toBe(100) + expect(cache.getRowHeight(2)).toBe(25) + + // Clear a specific item + cache.clearColumnWidth(0) + cache.clearRowHeight(0) + + // Mimic a sort operation + indexToIdMap[0] = 'bar' + indexToIdMap[1] = 'foo' + indexToIdMap[2] = 'baz' + + // Previous widths and heights should still be available + expect(cache.getColumnWidth(0)).toBe(200) + expect(cache.getRowHeight(0)).toBe(50) + expect(cache.getColumnWidth(1)).toBe(100) + expect(cache.getRowHeight(1)).toBe(25) + expect(cache.getColumnWidth(2)).toBeUndefined() + expect(cache.getRowHeight(2)).toBeUndefined() + + // Clear all items + cache.clearAllColumnWidths() + cache.clearAllRowHeights() + + // No sizes should be available + expect(cache.getColumnWidth(0)).toBeUndefined() + expect(cache.getRowHeight(0)).toBeUndefined() + expect(cache.getColumnWidth(1)).toBeUndefined() + expect(cache.getRowHeight(1)).toBeUndefined() + expect(cache.getColumnWidth(2)).toBeUndefined() + expect(cache.getRowHeight(2)).toBeUndefined() + }) +}) diff --git a/source/CellMeasurer/index.js b/source/CellMeasurer/index.js index e226f3544..f2ba0dc78 100644 --- a/source/CellMeasurer/index.js +++ b/source/CellMeasurer/index.js @@ -1,3 +1,4 @@ export default from './CellMeasurer' export CellMeasurer from './CellMeasurer' export defaultCellSizeCache from './defaultCellSizeCache' +export idCellSizeCache from './idCellSizeCache' diff --git a/source/index.js b/source/index.js index 6932798b2..83e6fbc59 100644 --- a/source/index.js +++ b/source/index.js @@ -4,7 +4,8 @@ export { AutoSizer } from './AutoSizer' export { CellMeasurer, defaultCellSizeCache as defaultCellMeasurerCellSizeCache, - defaultCellSizeCache as uniformSizeCellMeasurerCellSizeCache // 7.21 backwards compatible export + defaultCellSizeCache as uniformSizeCellMeasurerCellSizeCache, // 7.21 backwards compatible export + idCellSizeCache as idCellMeasurerCellSizeCache } from './CellMeasurer' export { Collection } from './Collection' export { ColumnSizer } from './ColumnSizer'