react-virtualized
Version:
React components for efficiently rendering large, scrollable lists and tabular data
266 lines (257 loc) • 10.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _objectDestructuringEmpty2 = _interopRequireDefault(require("@babel/runtime/helpers/objectDestructuringEmpty"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var React = _interopRequireWildcard(require("react"));
var _CollectionView = _interopRequireDefault(require("./CollectionView"));
var _calculateSizeAndPositionData2 = _interopRequireDefault(require("./utils/calculateSizeAndPositionData"));
var _getUpdatedOffsetForIndex = _interopRequireDefault(require("../utils/getUpdatedOffsetForIndex"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/*:: import type {ScrollPosition, SizeInfo} from './types';*/
/**
* Renders scattered or non-linear data.
* Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data.
*/
var Collection = exports["default"] = /*#__PURE__*/function (_React$PureComponent) {
function Collection(props, context) {
var _this;
(0, _classCallCheck2["default"])(this, Collection);
_this = _callSuper(this, Collection, [props, context]);
_this._cellMetadata = [];
_this._lastRenderedCellIndices = [];
// Cell cache during scroll (for performance)
_this._cellCache = [];
_this._isScrollingChange = _this._isScrollingChange.bind(_this);
_this._setCollectionViewRef = _this._setCollectionViewRef.bind(_this);
return _this;
}
(0, _inherits2["default"])(Collection, _React$PureComponent);
return (0, _createClass2["default"])(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 = (0, _extends2["default"])({}, ((0, _objectDestructuringEmpty2["default"])(this.props), this.props));
return /*#__PURE__*/React.createElement(_CollectionView["default"], (0, _extends2["default"])({
cellLayoutManager: this,
isScrollingChange: this._isScrollingChange,
ref: this._setCollectionViewRef
}, props));
}
/** CellLayoutManager interface */
}, {
key: "calculateSizeAndPositionData",
value: function calculateSizeAndPositionData() {
var _this$props = this.props,
cellCount = _this$props.cellCount,
cellSizeAndPositionGetter = _this$props.cellSizeAndPositionGetter,
sectionSize = _this$props.sectionSize;
var data = (0, _calculateSizeAndPositionData2["default"])({
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) /*: ScrollPosition*/{
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 = (0, _getUpdatedOffsetForIndex["default"])({
align: align,
cellOffset: cellMetadata.x,
cellSize: cellMetadata.width,
containerSize: width,
currentOffset: scrollLeft,
targetIndex: cellIndex
});
scrollTop = (0, _getUpdatedOffsetForIndex["default"])({
align: align,
cellOffset: cellMetadata.y,
cellSize: cellMetadata.height,
containerSize: height,
currentOffset: scrollTop,
targetIndex: cellIndex
});
}
return {
scrollLeft: scrollLeft,
scrollTop: scrollTop
};
}
}, {
key: "getTotalSize",
value: function getTotalSize() /*: SizeInfo*/{
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 _this$props2 = this.props,
cellGroupRenderer = _this$props2.cellGroupRenderer,
cellRenderer = _this$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;
}
}]);
}(React.PureComponent);
(0, _defineProperty2["default"])(Collection, "defaultProps", {
'aria-label': 'grid',
cellGroupRenderer: defaultCellGroupRenderer
});
Collection.propTypes = process.env.NODE_ENV !== "production" ? {
'aria-label': _propTypes["default"].string,
/**
* Number of cells in Collection.
*/
cellCount: _propTypes["default"].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["default"].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["default"].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["default"].func.isRequired,
/**
* Optionally override the size of the sections a Collection's cells are split into.
*/
sectionSize: _propTypes["default"].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;
});
}