UNPKG

react-virtualized

Version:

React components for efficiently rendering large, scrollable lists and tabular data

286 lines (243 loc) 8.67 kB
import _extends from 'babel-runtime/helpers/extends'; import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties'; import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; import PropTypes from 'prop-types'; import * as React from 'react'; import CollectionView from './CollectionView'; import _calculateSizeAndPositionData from './utils/calculateSizeAndPositionData'; import getUpdatedOffsetForIndex from '../utils/getUpdatedOffsetForIndex'; /** * Renders scattered or non-linear data. * Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data. */ var Collection = function (_React$PureComponent) { _inherits(Collection, _React$PureComponent); function Collection(props, context) { _classCallCheck(this, Collection); var _this = _possibleConstructorReturn(this, (Collection.__proto__ || _Object$getPrototypeOf(Collection)).call(this, props, context)); _this._cellMetadata = []; _this._lastRenderedCellIndices = []; // Cell cache during scroll (for perforamnce) _this._cellCache = []; _this._isScrollingChange = _this._isScrollingChange.bind(_this); _this._setCollectionViewRef = _this._setCollectionViewRef.bind(_this); return _this; } _createClass(Collection, [{ key: 'forceUpdate', value: function forceUpdate() { if (this._collectionView !== undefined) { this._collectionView.forceUpdate(); } } /** See Collection#recomputeCellSizesAndPositions */ }, { key: 'recomputeCellSizesAndPositions', value: function recomputeCellSizesAndPositions() { this._cellCache = []; this._collectionView.recomputeCellSizesAndPositions(); } /** React lifecycle methods */ }, { key: 'render', value: function render() { var props = _objectWithoutProperties(this.props, []); return React.createElement(CollectionView, _extends({ cellLayoutManager: this, isScrollingChange: this._isScrollingChange, ref: this._setCollectionViewRef }, props)); } /** CellLayoutManager interface */ }, { key: 'calculateSizeAndPositionData', value: function calculateSizeAndPositionData() { var _props = this.props, cellCount = _props.cellCount, cellSizeAndPositionGetter = _props.cellSizeAndPositionGetter, sectionSize = _props.sectionSize; var data = _calculateSizeAndPositionData({ cellCount: cellCount, cellSizeAndPositionGetter: cellSizeAndPositionGetter, sectionSize: sectionSize }); this._cellMetadata = data.cellMetadata; this._sectionManager = data.sectionManager; this._height = data.height; this._width = data.width; } /** * Returns the most recently rendered set of cell indices. */ }, { key: 'getLastRenderedIndices', value: function getLastRenderedIndices() { return this._lastRenderedCellIndices; } /** * Calculates the minimum amount of change from the current scroll position to ensure the specified cell is (fully) visible. */ }, { key: 'getScrollPositionForCell', value: function getScrollPositionForCell(_ref) { var align = _ref.align, cellIndex = _ref.cellIndex, height = _ref.height, scrollLeft = _ref.scrollLeft, scrollTop = _ref.scrollTop, width = _ref.width; var cellCount = this.props.cellCount; if (cellIndex >= 0 && cellIndex < cellCount) { var cellMetadata = this._cellMetadata[cellIndex]; scrollLeft = getUpdatedOffsetForIndex({ align: align, cellOffset: cellMetadata.x, cellSize: cellMetadata.width, containerSize: width, currentOffset: scrollLeft, targetIndex: cellIndex }); scrollTop = getUpdatedOffsetForIndex({ align: align, cellOffset: cellMetadata.y, cellSize: cellMetadata.height, containerSize: height, currentOffset: scrollTop, targetIndex: cellIndex }); } return { scrollLeft: scrollLeft, scrollTop: scrollTop }; } }, { key: 'getTotalSize', value: function getTotalSize() { return { height: this._height, width: this._width }; } }, { key: 'cellRenderers', value: function cellRenderers(_ref2) { var _this2 = this; var height = _ref2.height, isScrolling = _ref2.isScrolling, width = _ref2.width, x = _ref2.x, y = _ref2.y; var _props2 = this.props, cellGroupRenderer = _props2.cellGroupRenderer, cellRenderer = _props2.cellRenderer; // Store for later calls to getLastRenderedIndices() this._lastRenderedCellIndices = this._sectionManager.getCellIndices({ height: height, width: width, x: x, y: y }); return cellGroupRenderer({ cellCache: this._cellCache, cellRenderer: cellRenderer, cellSizeAndPositionGetter: function cellSizeAndPositionGetter(_ref3) { var index = _ref3.index; return _this2._sectionManager.getCellMetadata({ index: index }); }, indices: this._lastRenderedCellIndices, isScrolling: isScrolling }); } }, { key: '_isScrollingChange', value: function _isScrollingChange(isScrolling) { if (!isScrolling) { this._cellCache = []; } } }, { key: '_setCollectionViewRef', value: function _setCollectionViewRef(ref) { this._collectionView = ref; } }]); return Collection; }(React.PureComponent); Collection.defaultProps = { 'aria-label': 'grid', cellGroupRenderer: defaultCellGroupRenderer }; export default Collection; Collection.propTypes = process.env.NODE_ENV !== "production" ? { 'aria-label': PropTypes.string, /** * Number of cells in Collection. */ cellCount: PropTypes.number.isRequired, /** * Responsible for rendering a group of cells given their indices. * Should implement the following interface: ({ * cellSizeAndPositionGetter:Function, * indices: Array<number>, * cellRenderer: Function * }): Array<PropTypes.node> */ cellGroupRenderer: PropTypes.func.isRequired, /** * Responsible for rendering a cell given an row and column index. * Should implement the following interface: ({ index: number, key: string, style: object }): PropTypes.element */ cellRenderer: PropTypes.func.isRequired, /** * Callback responsible for returning size and offset/position information for a given cell (index). * ({ index: number }): { height: number, width: number, x: number, y: number } */ cellSizeAndPositionGetter: PropTypes.func.isRequired, /** * Optionally override the size of the sections a Collection's cells are split into. */ sectionSize: PropTypes.number } : {}; function defaultCellGroupRenderer(_ref4) { var cellCache = _ref4.cellCache, cellRenderer = _ref4.cellRenderer, cellSizeAndPositionGetter = _ref4.cellSizeAndPositionGetter, indices = _ref4.indices, isScrolling = _ref4.isScrolling; return indices.map(function (index) { var cellMetadata = cellSizeAndPositionGetter({ index: index }); var cellRendererProps = { index: index, isScrolling: isScrolling, key: index, style: { height: cellMetadata.height, left: cellMetadata.x, position: 'absolute', top: cellMetadata.y, width: cellMetadata.width } }; // Avoid re-creating cells while scrolling. // This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. // If a scroll is in progress- cache and reuse cells. // This cache will be thrown away once scrolling complets. if (isScrolling) { if (!(index in cellCache)) { cellCache[index] = cellRenderer(cellRendererProps); } return cellCache[index]; } else { return cellRenderer(cellRendererProps); } }).filter(function (renderedCell) { return !!renderedCell; }); } import { bpfrpt_proptype_ScrollPosition } from './types'; import { bpfrpt_proptype_SizeInfo } from './types';