UNPKG

react-recycled-list

Version:

A React library for efficiently rendering large list with expensive component

1,217 lines (1,024 loc) 46.5 kB
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = _interopDefault(require('react')); var reactResizeDetector = require('react-resize-detector'); function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function calculateRowPositions(rowHeights) { var prev = 0; var rowPositions = []; var rowCount = rowHeights.length; for (var i = 0; i < rowCount; i++) { if (i === 0) rowPositions[i] = 0;else rowPositions[i] = prev; prev += rowHeights[i]; } return rowPositions; } function mapRowIndexToDataIndex(rowColumns, totalNumOfData) { var rowCount = rowColumns.length; var prevEndDataIndex = 0; var map = {}; for (var i = 0; i < rowCount; i++) { var newEndDataIndex = Math.min(prevEndDataIndex + rowColumns[i], totalNumOfData); map[i] = [prevEndDataIndex, newEndDataIndex]; prevEndDataIndex = newEndDataIndex; } return map; } function validateScrollTo(result) { if (result === -1 || result === undefined) { throw Error('Invalid input to ScrollTo, make sure your input data index or row is correct'); } } function sortedLastIndex(array, target) { var start = 0; var end = array.length - 1; while (start <= end) { var mid = Math.floor((start + end) / 2); if (array[mid] <= target) { start = mid + 1; } else { end = mid - 1; } } return start; } function sortedFirstIndex(array, target) { var start = 0; var end = array.length - 1; while (start <= end) { var mid = Math.floor((start + end) / 2); if (array[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return start; } var noRowRenderInfo = { firstRenderedRowIndex: -1, firstRenderedDataIndex: -1, lastRenderedRowIndex: -1, lastRenderedDataIndex: -1, lastRowIndex: -1 }; var noRowVisibilityInfo = { firstVisibleRowIndex: -1, firstVisibleDataIndex: -1, lastVisibleRowIndex: -1, lastVisibleDataIndex: -1, lastRowIndex: -1 }; var General = /*#__PURE__*/function (_React$PureComponent) { _inheritsLoose(General, _React$PureComponent); function General(props) { var _this; _this = _React$PureComponent.call(this, props) || this; _this.onListWillRecycle = function (newRenderedRowIndex, newScrollState, newTopRenderedRowRelativeIndex) { var _this$props = _this.props, useScrollIndicator = _this$props.useScrollIndicator, onRenderedRowChange = _this$props.onRenderedRowChange; if (useScrollIndicator) { _this.setState({ scrollState: newScrollState }); _this._debounceScrollState(); } if (onRenderedRowChange) { if (_this.totalNumOfRenderedRows === 0) { onRenderedRowChange(noRowRenderInfo); return; } var lastRenderedRowIndex = newRenderedRowIndex[_this.mod(newTopRenderedRowRelativeIndex - 1)]; if (lastRenderedRowIndex === _this.prevLastRenderedRow) return; var topRowIndex = newRenderedRowIndex[newTopRenderedRowRelativeIndex]; onRenderedRowChange({ firstRenderedRowIndex: topRowIndex, firstRenderedDataIndex: _this.rowToDataIndexMap[topRowIndex][0], lastRenderedRowIndex: lastRenderedRowIndex, lastRenderedDataIndex: _this.rowToDataIndexMap[lastRenderedRowIndex][1] - 1, lastRowIndex: _this.totalRows - 1 }); _this.prevLastRenderedRow = lastRenderedRowIndex; } }; _this.onScrollChange = function (scrollTop) { var onVisibleRowChange = _this.props.onVisibleRowChange; if (!onVisibleRowChange) return; if (_this.totalNumOfRenderedRows === 0) { onVisibleRowChange(noRowVisibilityInfo); _this.prevLastVisibleRow = -1; return; } var lastVisibleRowIndex = _this.getBottomViewportRowIndex(scrollTop + _this.windowHeight); if (_this.fullHeight < _this.windowHeight) { lastVisibleRowIndex = _this.getBottomViewportRowIndex(scrollTop + _this.fullHeight); } if (lastVisibleRowIndex === _this.prevLastVisibleRow) return; var firstVisibleRowIndex = _this.getTopViewportRowIndex(scrollTop); var firstVisibleDataIndex = _this.rowToDataIndexMap[firstVisibleRowIndex][0]; var lastVisibleDataIndex = _this.rowToDataIndexMap[lastVisibleRowIndex][1] - 1; onVisibleRowChange({ firstVisibleRowIndex: firstVisibleRowIndex, firstVisibleDataIndex: firstVisibleDataIndex, lastVisibleRowIndex: lastVisibleRowIndex, lastVisibleDataIndex: lastVisibleDataIndex, lastRowIndex: _this.totalRows - 1 }); _this.prevLastVisibleRow = lastVisibleRowIndex; }; _this.recycle = function (scrollTop) { var _this$state = _this.state, renderedRowIndex = _this$state.renderedRowIndex, topRenderedRowRelativeIndex = _this$state.topRenderedRowRelativeIndex, scrollState = _this$state.scrollState; var topScroll = scrollTop - _this.prevScroll > 0 ? false : true; _this.prevScroll = scrollTop; _this.onScrollChange(scrollTop); var rowsToRecycle = 0; if (topScroll) { var topRenderedRowIndex = renderedRowIndex[topRenderedRowRelativeIndex]; var newTopRenderedRowIndex = Math.max(_this.getTopViewportRowIndex(scrollTop) - _this.numOfInvisibleRowOnEachDirection, 0); rowsToRecycle = topRenderedRowIndex - newTopRenderedRowIndex; } else { var bottomRenderedRowIndex = renderedRowIndex[_this.mod(topRenderedRowRelativeIndex - 1)]; var viewportBottom = scrollTop + _this.windowHeight; var newBottomRenderedRowIndex = Math.min(_this.getBottomViewportRowIndex(viewportBottom) + _this.numOfInvisibleRowOnEachDirection, _this.totalRows - 1); rowsToRecycle = newBottomRenderedRowIndex - bottomRenderedRowIndex; } if (rowsToRecycle > 0) { var newRenderedRowIndex = [].concat(renderedRowIndex); var newScrollState = [].concat(scrollState); var cycle = 0; while (cycle < rowsToRecycle) { var _newTopRenderedRowRelativeIndex = _this.mod(topRenderedRowRelativeIndex + (topScroll ? -cycle - 1 : cycle)); newRenderedRowIndex[_newTopRenderedRowRelativeIndex] += topScroll ? -_this.totalNumOfRenderedRows : _this.totalNumOfRenderedRows; newScrollState[_newTopRenderedRowRelativeIndex] = true; cycle++; } var newTopRenderedRowRelativeIndex = _this.mod(topRenderedRowRelativeIndex + (topScroll ? -rowsToRecycle : rowsToRecycle)); _this.onListWillRecycle(newRenderedRowIndex, newScrollState, newTopRenderedRowRelativeIndex); _this.setState({ renderedRowIndex: newRenderedRowIndex, topRenderedRowRelativeIndex: newTopRenderedRowRelativeIndex }); } }; _this.resetList = function () { var bottomRenderedRowIndex = _this.totalNumOfRenderedRows - 1; var viewportBottom = _this.getResetViewportBottom(); var newBottomRenderedRowIndex = Math.min(_this.getBottomViewportRowIndex(viewportBottom) + _this.numOfInvisibleRowOnEachDirection, _this.totalRows - 1); var rowsToRecycle = newBottomRenderedRowIndex - bottomRenderedRowIndex; var newRenderedRowIndex = _this.initialArrayTemplate.map(function (_, index) { return index; }); var newScrollState = _this.initialArrayTemplate.map(function () { return false; }); var newTopRenderedRowRelativeIndex = 0; if (rowsToRecycle > 0) { var cycle = 0; while (cycle < rowsToRecycle) { var _newTopRenderedRowRelativeIndex2 = _this.mod(cycle); newRenderedRowIndex[_newTopRenderedRowRelativeIndex2] += _this.totalNumOfRenderedRows; newScrollState[_newTopRenderedRowRelativeIndex2] = true; cycle++; } newTopRenderedRowRelativeIndex = _this.mod(rowsToRecycle); } _this.onListWillRecycle(newRenderedRowIndex, newScrollState, newTopRenderedRowRelativeIndex); _this.onScrollChange(_this.prevScroll); _this.setState({ renderedRowIndex: newRenderedRowIndex, topRenderedRowRelativeIndex: newTopRenderedRowRelativeIndex }); }; _this.onScroll = function (event) { _this.recycle(event.currentTarget.scrollTop); }; _this.scrollToDataIndex = function (targetIndex) { if (targetIndex === -1) { _this.manualScroll(targetIndex); return; } var targetRow = Object.values(_this.rowToDataIndexMap).findIndex(function (value) { return targetIndex >= value[0] && targetIndex < value[1]; }); validateScrollTo(targetRow); var targetPosition = _this.rowPositions[targetRow]; _this.manualScroll(targetPosition); }; _this.scrollToRow = function (targetRow) { if (targetRow === -1) { _this.manualScroll(targetRow); return; } var targetPosition = _this.rowPositions[targetRow]; validateScrollTo(targetPosition); _this.manualScroll(targetPosition); }; _this.scrollTo = function (scrollTop) { _this.manualScroll(scrollTop); }; _this.manualScroll = function (targetPosition) { if (_this.listWindowRef.current) { if (targetPosition === -1) _this.listWindowRef.current.scrollTop = _this.fullHeight;else _this.listWindowRef.current.scrollTop = targetPosition; } }; _this.mod = function (n, m) { if (m === void 0) { m = _this.totalNumOfRenderedRows; } return (n % m + m) % m; }; _this.getResetViewportBottom = function () { return _this.prevScroll + _this.windowHeight; }; _this._debounceScrollState = function () { clearTimeout(_this.timeOut); _this.timeOut = setTimeout(function () { _this.setState({ scrollState: _this.initialArrayTemplate.map(function () { return false; }) }); }, _this.props.scrollInterval || 250); }; _this.prevScroll = 0; _this.prevLastVisibleRow = -1; _this.prevLastRenderedRow = -1; return _this; } var _proto = General.prototype; _proto.componentDidMount = function componentDidMount() { var initialScrollTop = this.props.initialScrollTop; if (initialScrollTop) { this.manualScroll(initialScrollTop); } }; _proto.componentWillUnmount = function componentWillUnmount() { var onUnmount = this.props.onUnmount; if (onUnmount) onUnmount(this.prevScroll); }; _proto.componentDidUpdate = function componentDidUpdate(prevProps) { if (this.shouldResetList(prevProps)) { var _this$initializePrope = this.initializeProperties(), rowToDataIndexMap = _this$initializePrope.rowToDataIndexMap, rowPositions = _this$initializePrope.rowPositions, totalRows = _this$initializePrope.totalRows, initialArrayTemplate = _this$initializePrope.initialArrayTemplate, fullHeight = _this$initializePrope.fullHeight, totalNumOfRenderedRows = _this$initializePrope.totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection = _this$initializePrope.numOfInvisibleRowOnEachDirection, windowHeight = _this$initializePrope.windowHeight, rowHeights = _this$initializePrope.rowHeights; this.rowToDataIndexMap = rowToDataIndexMap; this.rowPositions = rowPositions; this.totalRows = totalRows; this.initialArrayTemplate = initialArrayTemplate; this.fullHeight = fullHeight; this.totalNumOfRenderedRows = totalNumOfRenderedRows; this.numOfInvisibleRowOnEachDirection = numOfInvisibleRowOnEachDirection; this.rowHeights = rowHeights; this.windowHeight = windowHeight; this.resetList(); } }; _proto.render = function render() { var _this2 = this; var _this$props2 = this.props, listTagName = _this$props2.listTagName, _this$props2$listProp = _this$props2.listProps, listProps = _this$props2$listProp === void 0 ? {} : _this$props2$listProp, _this$props2$listWind = _this$props2.listWindowProps, listWindowProps = _this$props2$listWind === void 0 ? {} : _this$props2$listWind, listWindowTagName = _this$props2.listWindowTagName, data = _this$props2.data, width = _this$props2.width, rowComponent = _this$props2.rowComponent; var _this$state2 = this.state, renderedRowIndex = _this$state2.renderedRowIndex, scrollState = _this$state2.scrollState; var ListTag = listTagName || 'div'; var WindowTag = listWindowTagName || 'div'; var RowComponent = rowComponent; return React.createElement(WindowTag, Object.assign({}, listWindowProps, { style: { height: this.windowHeight, overflowY: 'auto', width: width || '100%' }, onScroll: this.onScroll, ref: this.listWindowRef }), React.createElement(ListTag, Object.assign({}, listProps, { style: { height: this.fullHeight, position: 'relative' } }), renderedRowIndex.map(function (absoluteRowIndex, index) { var dataIndexInfo = _this2.rowToDataIndexMap[absoluteRowIndex]; var startDataIndex = dataIndexInfo[0]; var endDataIndex = dataIndexInfo[1]; return React.createElement(RowComponent, { key: index, data: data, dataIndex: startDataIndex, dataEndIndex: endDataIndex, row: absoluteRowIndex, column: endDataIndex - startDataIndex, isScrolling: scrollState[index], top: _this2.rowPositions[absoluteRowIndex], height: _this2.rowHeights[absoluteRowIndex] }); }))); }; return General; }(React.PureComponent); var FixedList = /*#__PURE__*/function (_GeneralList) { _inheritsLoose(FixedList, _GeneralList); function FixedList(props) { var _this; _this = _GeneralList.call(this, props) || this; _this.initializeProperties = function () { var _this$props = _this.props, rowHeight = _this$props.rowHeight, column = _this$props.column, rowColumns = _this$props.rowColumns, data = _this$props.data, height = _this$props.height, offScreenRow = _this$props.offScreenRow; if (rowColumns) { if (rowColumns.reduce(function (acc, current) { return acc + current; }, 0) !== data.length) { throw Error('The total number of data item calculated from rowColumns does not match the length of your input data'); } } var calculatedRowColumns = rowColumns ? rowColumns : column ? Array(Math.ceil(data.length / column)).fill(column) : Array(data.length).fill(1); var rowHeights = calculatedRowColumns.map(function () { return rowHeight; }); var rowToDataIndexMap = mapRowIndexToDataIndex(calculatedRowColumns, data.length); var rowPositions = calculateRowPositions(rowHeights); var totalRows = rowHeights.length; var numOfVisibleRow = Math.ceil(height / rowHeight); var numOfInvisibleRowOnEachDirection = offScreenRow || 1; var totalNumOfRenderedRows = numOfVisibleRow + numOfInvisibleRowOnEachDirection * 2; if (totalNumOfRenderedRows > totalRows) totalNumOfRenderedRows = totalRows; var initialArrayTemplate = Array(totalNumOfRenderedRows).fill(null); var fullHeight = rowHeights.reduce(function (acc, current) { return acc + current; }, 0); var windowHeight = height; return { rowToDataIndexMap: rowToDataIndexMap, rowPositions: rowPositions, totalRows: totalRows, initialArrayTemplate: initialArrayTemplate, fullHeight: fullHeight, totalNumOfRenderedRows: totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection: numOfInvisibleRowOnEachDirection, rowHeights: rowHeights, windowHeight: windowHeight }; }; _this.shouldResetList = function (prevProps) { var currentProp = _this.props; if (prevProps === currentProp) return false; var rowHeight = currentProp.rowHeight, column = currentProp.column, rowColumns = currentProp.rowColumns, height = currentProp.height, data = currentProp.data, offScreenRow = currentProp.offScreenRow; return prevProps.rowHeight !== rowHeight || prevProps.column !== column || prevProps.rowColumns !== rowColumns || prevProps.height !== height || prevProps.data !== data || prevProps.offScreenRow !== offScreenRow; }; _this.getTopViewportRowIndex = function (scrollTop) { return Math.floor(scrollTop / _this.props.rowHeight); }; _this.getBottomViewportRowIndex = function (viewportBottom) { var viewportBottomRow = viewportBottom / _this.props.rowHeight; if (Number.isInteger(viewportBottomRow)) viewportBottomRow -= 1;else viewportBottomRow = Math.floor(viewportBottomRow); return viewportBottomRow; }; var _this$initializePrope = _this.initializeProperties(), rowToDataIndexMap = _this$initializePrope.rowToDataIndexMap, rowPositions = _this$initializePrope.rowPositions, totalRows = _this$initializePrope.totalRows, initialArrayTemplate = _this$initializePrope.initialArrayTemplate, fullHeight = _this$initializePrope.fullHeight, totalNumOfRenderedRows = _this$initializePrope.totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection = _this$initializePrope.numOfInvisibleRowOnEachDirection, rowHeights = _this$initializePrope.rowHeights, windowHeight = _this$initializePrope.windowHeight; _this.rowToDataIndexMap = rowToDataIndexMap; _this.rowPositions = rowPositions; _this.totalRows = totalRows; _this.initialArrayTemplate = initialArrayTemplate; _this.fullHeight = fullHeight; _this.totalNumOfRenderedRows = totalNumOfRenderedRows; _this.numOfInvisibleRowOnEachDirection = numOfInvisibleRowOnEachDirection; _this.rowHeights = rowHeights; _this.windowHeight = windowHeight; _this.listWindowRef = React.createRef(); var initialRenderedRowIndex = _this.initialArrayTemplate.map(function (_, index) { return index; }); var initialScrollState = _this.initialArrayTemplate.map(function () { return false; }); if (!props.initialScrollTop) { _this.onListWillRecycle(initialRenderedRowIndex, initialScrollState, 0); _this.onScrollChange(0); } _this.state = { renderedRowIndex: initialRenderedRowIndex, scrollState: initialScrollState, topRenderedRowRelativeIndex: 0 }; return _this; } return FixedList; }(General); var VariableList = /*#__PURE__*/function (_GeneralList) { _inheritsLoose(VariableList, _GeneralList); function VariableList(props) { var _this; _this = _GeneralList.call(this, props) || this; _this.initializeProperties = function () { var _this$props = _this.props, rowHeight = _this$props.rowHeight, rowHeights = _this$props.rowHeights, column = _this$props.column, rowColumns = _this$props.rowColumns, data = _this$props.data, height = _this$props.height, offScreenRow = _this$props.offScreenRow; if (rowColumns) { if (rowColumns.reduce(function (acc, current) { return acc + current; }, 0) !== data.length) { throw Error('The total number of data item calculated from rowColumns does not match the length of your input data'); } if (rowColumns.length !== rowHeights.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows provided from rowColumns'); } } else if (column) { var rows = Math.ceil(data.length / column); if (rows !== rowHeights.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows calculated from column'); } } else if (rowHeights.length !== data.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows calculated from your input data'); } var calculatedRowColumns = rowColumns ? rowColumns : column ? Array(rowHeights.length).fill(column) : Array(rowHeights.length).fill(1); var rowToDataIndexMap = mapRowIndexToDataIndex(calculatedRowColumns, data.length); var rowPositions = calculateRowPositions(rowHeights); var totalRows = rowHeights.length; var numOfVisibleRow = Math.ceil(height / rowHeight); var numOfInvisibleRowOnEachDirection = offScreenRow || 1; var totalNumOfRenderedRows = numOfVisibleRow + numOfInvisibleRowOnEachDirection * 2; if (totalNumOfRenderedRows > totalRows) totalNumOfRenderedRows = totalRows; var initialArrayTemplate = Array(totalNumOfRenderedRows).fill(null); var fullHeight = rowHeights.reduce(function (acc, current) { return acc + current; }, 0); var windowHeight = height; return { rowToDataIndexMap: rowToDataIndexMap, rowPositions: rowPositions, totalRows: totalRows, initialArrayTemplate: initialArrayTemplate, fullHeight: fullHeight, totalNumOfRenderedRows: totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection: numOfInvisibleRowOnEachDirection, rowHeights: rowHeights, windowHeight: windowHeight }; }; _this.shouldResetList = function (prevProps) { var currentProp = _this.props; if (prevProps === currentProp) return false; var rowHeight = currentProp.rowHeight, rowHeights = currentProp.rowHeights, column = currentProp.column, rowColumns = currentProp.rowColumns, height = currentProp.height, data = currentProp.data, offScreenRow = currentProp.offScreenRow; return prevProps.rowHeight !== rowHeight || prevProps.rowHeights !== rowHeights || prevProps.column !== column || prevProps.rowColumns !== rowColumns || prevProps.height !== height || prevProps.data !== data || prevProps.offScreenRow !== offScreenRow; }; _this.getTopViewportRowIndex = function (scrollTop) { return sortedLastIndex(_this.rowPositions, scrollTop) - 1; }; _this.getBottomViewportRowIndex = function (viewportBottom) { return sortedFirstIndex(_this.rowPositions, viewportBottom) - 1; }; var _this$initializePrope = _this.initializeProperties(), rowToDataIndexMap = _this$initializePrope.rowToDataIndexMap, rowPositions = _this$initializePrope.rowPositions, totalRows = _this$initializePrope.totalRows, initialArrayTemplate = _this$initializePrope.initialArrayTemplate, fullHeight = _this$initializePrope.fullHeight, totalNumOfRenderedRows = _this$initializePrope.totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection = _this$initializePrope.numOfInvisibleRowOnEachDirection, rowHeights = _this$initializePrope.rowHeights, windowHeight = _this$initializePrope.windowHeight; _this.rowToDataIndexMap = rowToDataIndexMap; _this.rowPositions = rowPositions; _this.totalRows = totalRows; _this.initialArrayTemplate = initialArrayTemplate; _this.fullHeight = fullHeight; _this.totalNumOfRenderedRows = totalNumOfRenderedRows; _this.numOfInvisibleRowOnEachDirection = numOfInvisibleRowOnEachDirection; _this.rowHeights = rowHeights; _this.windowHeight = windowHeight; _this.listWindowRef = React.createRef(); var initialRenderedRowIndex = _this.initialArrayTemplate.map(function (_, index) { return index; }); var initialScrollState = _this.initialArrayTemplate.map(function () { return false; }); _this.onListWillRecycle(initialRenderedRowIndex, initialScrollState, 0); _this.onScrollChange(0); _this.state = { renderedRowIndex: initialRenderedRowIndex, scrollState: initialScrollState, topRenderedRowRelativeIndex: 0 }; return _this; } return VariableList; }(General); var FullWindowFixedList = /*#__PURE__*/function (_GeneralList) { _inheritsLoose(FullWindowFixedList, _GeneralList); function FullWindowFixedList(props) { var _this; _this = _GeneralList.call(this, props) || this; _this.initializeProperties = function (constructor) { if (constructor === void 0) { constructor = false; } var _this$props = _this.props, rowHeight = _this$props.rowHeight, column = _this$props.column, rowColumns = _this$props.rowColumns, data = _this$props.data, offScreenRow = _this$props.offScreenRow, serverSideHeight = _this$props.serverSideHeight, scrollRef = _this$props.scrollRef, _this$props$rootMargi = _this$props.rootMarginTop, rootMarginTop = _this$props$rootMargi === void 0 ? 0 : _this$props$rootMargi, _this$props$rootMargi2 = _this$props.rootMarginBottom, rootMarginBottom = _this$props$rootMargi2 === void 0 ? 0 : _this$props$rootMargi2; if (rowColumns) { if (rowColumns.reduce(function (acc, current) { return acc + current; }, 0) !== data.length) { throw Error('The total number of data item calculated from rowColumns does not match the length of your input data'); } } var calculatedWindowHeight = 0; var scrollListener; if (constructor && serverSideHeight !== undefined) { calculatedWindowHeight = serverSideHeight; } else if ('scrollRef' in _this.props) { if (scrollRef !== null && scrollRef !== void 0 && scrollRef.current) { calculatedWindowHeight = parseInt(window.getComputedStyle(scrollRef.current).height); scrollListener = scrollRef.current; } else calculatedWindowHeight = 0; } else { calculatedWindowHeight = window.innerHeight; scrollListener = window; } calculatedWindowHeight = Math.max(0, calculatedWindowHeight - rootMarginTop - rootMarginBottom); var calculatedRowColumns = rowColumns ? rowColumns : column ? Array(Math.ceil(data.length / column)).fill(column) : Array(data.length).fill(1); var rowHeights = calculatedRowColumns.map(function () { return rowHeight; }); var rowToDataIndexMap = mapRowIndexToDataIndex(calculatedRowColumns, data.length); var rowPositions = calculateRowPositions(rowHeights); var totalRows = rowHeights.length; var numOfVisibleRow = Math.ceil(calculatedWindowHeight / rowHeight); var numOfInvisibleRowOnEachDirection = offScreenRow || numOfVisibleRow ? 1 : 0; var totalNumOfRenderedRows = numOfVisibleRow + numOfInvisibleRowOnEachDirection * 2; if (totalNumOfRenderedRows > totalRows) totalNumOfRenderedRows = totalRows; var initialArrayTemplate = Array(totalNumOfRenderedRows).fill(null); var fullHeight = rowHeights.reduce(function (acc, current) { return acc + current; }, 0); return { rowToDataIndexMap: rowToDataIndexMap, rowPositions: rowPositions, totalRows: totalRows, initialArrayTemplate: initialArrayTemplate, fullHeight: fullHeight, totalNumOfRenderedRows: totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection: numOfInvisibleRowOnEachDirection, rowHeights: rowHeights, windowHeight: calculatedWindowHeight, scrollListener: scrollListener }; }; _this.attachScrollListener = function () { if (_this.scrollListener) { _this.scrollListener.removeEventListener('scroll', _this.onScroll); _this.scrollListener.addEventListener('scroll', _this.onScroll); } }; _this.getScrollTop = function () { var _this$props$rootMargi3 = _this.props.rootMarginTop, rootMarginTop = _this$props$rootMargi3 === void 0 ? 0 : _this$props$rootMargi3; var recycledList = _this.fullListRef.current; if (!recycledList) return window.scrollY; var distanceBetweenScrollContainerAndWindow = _this.scrollListener === window ? 0 : _this.scrollListener.getBoundingClientRect().top; return -(recycledList.getBoundingClientRect().top - distanceBetweenScrollContainerAndWindow - rootMarginTop); }; _this.onScroll = function () { if (_this.fullListRef) { var scrollTop = _this.getScrollTop(); _this.recycle(scrollTop); } }; _this.manualScroll = function (targetPosition) { var parsedTargetPosition = targetPosition; if (targetPosition === -1) { var _this$props2 = _this.props, _this$props2$rootMarg = _this$props2.rootMarginBottom, rootMarginBottom = _this$props2$rootMarg === void 0 ? 0 : _this$props2$rootMarg, _this$props2$rootMarg2 = _this$props2.rootMarginTop, _rootMarginTop = _this$props2$rootMarg2 === void 0 ? 0 : _this$props2$rootMarg2; var fullWindowHeight = _this.windowHeight + _rootMarginTop + rootMarginBottom; parsedTargetPosition = _this.fullHeight + rootMarginBottom + _rootMarginTop - fullWindowHeight; } var _this$props$rootMargi4 = _this.props.rootMarginTop, rootMarginTop = _this$props$rootMargi4 === void 0 ? 0 : _this$props$rootMargi4; if (_this.scrollListener) { var recycledList = _this.fullListRef.current; if (_this.scrollListener === window) { var distanceToWindowTopFromTopOfList = recycledList.getBoundingClientRect().top + window.scrollY; window.scrollTo({ top: distanceToWindowTopFromTopOfList + parsedTargetPosition - rootMarginTop }); } else { var customElement = _this.scrollListener; var distanceToElementTopFromTopOfList = recycledList.getBoundingClientRect().top - customElement.getBoundingClientRect().top; customElement.scrollTop = distanceToElementTopFromTopOfList + parsedTargetPosition - rootMarginTop; } } }; _this.shouldResetList = function (prevProps) { var _this$props3 = _this.props, rowHeight = _this$props3.rowHeight, column = _this$props3.column, rowColumns = _this$props3.rowColumns, windowHeight = _this$props3.windowHeight, data = _this$props3.data, offScreenRow = _this$props3.offScreenRow, scrollRef = _this$props3.scrollRef, rootMarginBottom = _this$props3.rootMarginBottom, rootMarginTop = _this$props3.rootMarginTop; return prevProps.data !== data || prevProps.windowHeight !== windowHeight || scrollRef && scrollRef.current !== _this.scrollListener || prevProps.rowHeight !== rowHeight || prevProps.column !== column || prevProps.rowColumns !== rowColumns || prevProps.offScreenRow !== offScreenRow || prevProps.rootMarginBottom !== rootMarginBottom || prevProps.rootMarginTop !== rootMarginTop; }; _this.resetListAndRef = function () { var _this$initializePrope = _this.initializeProperties(), rowToDataIndexMap = _this$initializePrope.rowToDataIndexMap, rowPositions = _this$initializePrope.rowPositions, totalRows = _this$initializePrope.totalRows, initialArrayTemplate = _this$initializePrope.initialArrayTemplate, fullHeight = _this$initializePrope.fullHeight, totalNumOfRenderedRows = _this$initializePrope.totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection = _this$initializePrope.numOfInvisibleRowOnEachDirection, rowHeights = _this$initializePrope.rowHeights, windowHeight = _this$initializePrope.windowHeight, scrollListener = _this$initializePrope.scrollListener; var scrollRef = _this.props.scrollRef; _this.rowToDataIndexMap = rowToDataIndexMap; _this.rowPositions = rowPositions; _this.totalRows = totalRows; _this.initialArrayTemplate = initialArrayTemplate; _this.fullHeight = fullHeight; _this.totalNumOfRenderedRows = totalNumOfRenderedRows; _this.numOfInvisibleRowOnEachDirection = numOfInvisibleRowOnEachDirection; _this.rowHeights = rowHeights; _this.windowHeight = windowHeight; if (scrollRef && scrollRef.current !== _this.scrollListener) { _this.scrollListener = scrollListener; _this.attachScrollListener(); } _this.resetList(); }; _this.setCustomScrollRef = function () { _this.resetListAndRef(); }; _this.getTopViewportRowIndex = function (scrollTop) { return Math.max(Math.floor(scrollTop / _this.props.rowHeight), 0); }; _this.getBottomViewportRowIndex = function (viewportBottom) { var viewportBottomRow = viewportBottom / _this.props.rowHeight; if (Number.isInteger(viewportBottomRow)) viewportBottomRow -= 1;else viewportBottomRow = Math.floor(viewportBottomRow); return Math.min(viewportBottomRow, _this.totalRows - 1); }; _this.getResetViewportBottom = function () { var _this$props4 = _this.props, _this$props4$rootMarg = _this$props4.rootMarginBottom, rootMarginBottom = _this$props4$rootMarg === void 0 ? 0 : _this$props4$rootMarg, _this$props4$rootMarg2 = _this$props4.rootMarginTop, rootMarginTop = _this$props4$rootMarg2 === void 0 ? 0 : _this$props4$rootMarg2; var scrollTop = _this.getScrollTop(); var fullWindowHeight = _this.windowHeight + rootMarginTop + rootMarginBottom; return scrollTop + fullWindowHeight - rootMarginBottom; }; var _this$initializePrope2 = _this.initializeProperties(true), rowToDataIndexMap = _this$initializePrope2.rowToDataIndexMap, rowPositions = _this$initializePrope2.rowPositions, totalRows = _this$initializePrope2.totalRows, initialArrayTemplate = _this$initializePrope2.initialArrayTemplate, fullHeight = _this$initializePrope2.fullHeight, totalNumOfRenderedRows = _this$initializePrope2.totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection = _this$initializePrope2.numOfInvisibleRowOnEachDirection, rowHeights = _this$initializePrope2.rowHeights, windowHeight = _this$initializePrope2.windowHeight, scrollListener = _this$initializePrope2.scrollListener; _this.fullListRef = React.createRef(); _this.listWindowRef = null; _this.rowToDataIndexMap = rowToDataIndexMap; _this.rowPositions = rowPositions; _this.totalRows = totalRows; _this.initialArrayTemplate = initialArrayTemplate; _this.fullHeight = fullHeight; _this.totalNumOfRenderedRows = totalNumOfRenderedRows; _this.numOfInvisibleRowOnEachDirection = numOfInvisibleRowOnEachDirection; _this.rowHeights = rowHeights; _this.windowHeight = windowHeight; _this.scrollListener = scrollListener; _this.initialScrolling = false; var initialRenderedRowIndex = _this.initialArrayTemplate.map(function (_, index) { return index; }); var initialScrollState = _this.initialArrayTemplate.map(function () { return false; }); if (!props.initialScrollTop && _this.scrollListener) { _this.onListWillRecycle(initialRenderedRowIndex, initialScrollState, 0); _this.onScrollChange(0); } _this.state = { renderedRowIndex: initialRenderedRowIndex, scrollState: initialScrollState, topRenderedRowRelativeIndex: 0 }; return _this; } var _proto = FullWindowFixedList.prototype; _proto.componentDidMount = function componentDidMount() { this.attachScrollListener(); var initialScrollTop = this.props.initialScrollTop; if (initialScrollTop) { this.manualScroll(initialScrollTop); } }; _proto.componentWillUnmount = function componentWillUnmount() { if (this.scrollListener) { this.scrollListener.removeEventListener('scroll', this.onScroll); } var onUnmount = this.props.onUnmount; if (onUnmount) onUnmount(this.prevScroll); }; _proto.componentDidUpdate = function componentDidUpdate(prevProps) { if (this.shouldResetList(prevProps)) { this.resetListAndRef(); } }; _proto.render = function render() { var _this2 = this; var _this$props5 = this.props, listTagName = _this$props5.listTagName, _this$props5$listProp = _this$props5.listProps, listProps = _this$props5$listProp === void 0 ? {} : _this$props5$listProp, data = _this$props5.data, width = _this$props5.width, rowComponent = _this$props5.rowComponent; var _this$state = this.state, renderedRowIndex = _this$state.renderedRowIndex, scrollState = _this$state.scrollState; console.log('render'); var ListTag = listTagName || 'div'; var RowComponent = rowComponent; return React.createElement(ListTag, Object.assign({}, listProps, { style: { height: this.fullHeight, position: 'relative', width: width }, ref: this.fullListRef }), renderedRowIndex.map(function (absoluteRowIndex, index) { var dataIndexInfo = _this2.rowToDataIndexMap[absoluteRowIndex]; var startDataIndex = dataIndexInfo[0]; var endDataIndex = dataIndexInfo[1]; return React.createElement(RowComponent, { key: index, data: data, dataIndex: startDataIndex, dataEndIndex: endDataIndex, row: absoluteRowIndex, column: endDataIndex - startDataIndex, isScrolling: scrollState[index], top: _this2.rowPositions[absoluteRowIndex], height: _this2.rowHeights[absoluteRowIndex] }); })); }; return FullWindowFixedList; }(General); var FullWindowVariableList = /*#__PURE__*/function (_FullWindowFixedList) { _inheritsLoose(FullWindowVariableList, _FullWindowFixedList); function FullWindowVariableList(props) { var _this; _this = _FullWindowFixedList.call(this, props) || this; _this.initializeProperties = function (constructor) { if (constructor === void 0) { constructor = false; } var _this$props = _this.props, rowHeight = _this$props.rowHeight, rowHeights = _this$props.rowHeights, column = _this$props.column, rowColumns = _this$props.rowColumns, data = _this$props.data, offScreenRow = _this$props.offScreenRow, serverSideHeight = _this$props.serverSideHeight, scrollRef = _this$props.scrollRef, _this$props$rootMargi = _this$props.rootMarginTop, rootMarginTop = _this$props$rootMargi === void 0 ? 0 : _this$props$rootMargi, _this$props$rootMargi2 = _this$props.rootMarginBottom, rootMarginBottom = _this$props$rootMargi2 === void 0 ? 0 : _this$props$rootMargi2; if (rowColumns) { if (rowColumns.reduce(function (acc, current) { return acc + current; }, 0) !== data.length) { throw Error('The total number of data item calculated from rowColumns does not match the length of your input data'); } if (rowColumns.length !== rowHeights.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows provided from rowColumns'); } } else if (column) { var rows = Math.ceil(data.length / column); if (rows !== rowHeights.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows calculated from column'); } } else if (rowHeights.length !== data.length) { throw Error('The number of rows provided from rowHeights does not match the number of rows calculated from your input data'); } var calculatedWindowHeight = 0; var scrollListener; if (constructor && serverSideHeight !== undefined) { calculatedWindowHeight = serverSideHeight; } else if ('scrollRef' in _this.props) { if (scrollRef !== null && scrollRef !== void 0 && scrollRef.current) { calculatedWindowHeight = parseInt(window.getComputedStyle(scrollRef.current).height); scrollListener = scrollRef; } else calculatedWindowHeight = 0; } else { calculatedWindowHeight = window.innerHeight; scrollListener = window; } calculatedWindowHeight = Math.max(0, calculatedWindowHeight - rootMarginTop - rootMarginBottom); var calculatedRowColumns = rowColumns ? rowColumns : column ? Array(rowHeights.length).fill(column) : Array(rowHeights.length).fill(1); var rowToDataIndexMap = mapRowIndexToDataIndex(calculatedRowColumns, data.length); var rowPositions = calculateRowPositions(rowHeights); var totalRows = rowHeights.length; var numOfVisibleRow = Math.ceil(calculatedWindowHeight / rowHeight); var numOfInvisibleRowOnEachDirection = offScreenRow || numOfVisibleRow ? 1 : 0; var totalNumOfRenderedRows = numOfVisibleRow + numOfInvisibleRowOnEachDirection * 2; if (totalNumOfRenderedRows > totalRows) totalNumOfRenderedRows = totalRows; var initialArrayTemplate = Array(totalNumOfRenderedRows).fill(null); var fullHeight = rowHeights.reduce(function (acc, current) { return acc + current; }, 0); return { rowToDataIndexMap: rowToDataIndexMap, rowPositions: rowPositions, totalRows: totalRows, initialArrayTemplate: initialArrayTemplate, fullHeight: fullHeight, totalNumOfRenderedRows: totalNumOfRenderedRows, numOfInvisibleRowOnEachDirection: numOfInvisibleRowOnEachDirection, rowHeights: rowHeights, windowHeight: calculatedWindowHeight, scrollListener: scrollListener }; }; _this.getTopViewportRowIndex = function (scrollTop) { return sortedLastIndex(_this.rowPositions, scrollTop) - 1; }; _this.getBottomViewportRowIndex = function (viewportBottom) { return sortedFirstIndex(_this.rowPositions, viewportBottom) - 1; }; _this.shouldResetList = function (prevProps) { var _this$props2 = _this.props, rowHeight = _this$props2.rowHeight, rowHeights = _this$props2.rowHeights, column = _this$props2.column, rowColumns = _this$props2.rowColumns, windowHeight = _this$props2.windowHeight, data = _this$props2.data, offScreenRow = _this$props2.offScreenRow, scrollRef = _this$props2.scrollRef, rootMarginBottom = _this$props2.rootMarginBottom, rootMarginTop = _this$props2.rootMarginTop; return prevProps.data !== data || prevProps.windowHeight !== windowHeight || scrollRef && scrollRef.current !== _this.scrollListener || prevProps.rowHeight !== rowHeight || prevProps.rowHeights !== rowHeights || prevProps.column !== column || prevProps.rowColumns !== rowColumns || prevProps.offScreenRow !== offScreenRow || prevProps.rootMarginBottom !== rootMarginBottom || prevProps.rootMarginTop !== rootMarginTop; }; return _this; } return FullWindowVariableList; }(FullWindowFixedList); function ResponsiveContainer(props) { var render = props.render, debounceResize = props.debounceResize, debounceInterval = props.debounceInterval, serverSideHeight = props.serverSideHeight; var _useResizeDetector = reactResizeDetector.useResizeDetector({ refreshMode: debounceResize ? 'debounce' : undefined, refreshRate: debounceInterval ? debounceInterval : 100 }), width = _useResizeDetector.width, height = _useResizeDetector.height, ref = _useResizeDetector.ref; var _React$useState = React.useState(false), hasMounted = _React$useState[0], setHasMounted = _React$useState[1]; React.useLayoutEffect(function () { if (serverSideHeight !== undefined) { setHasMounted(true); } }, []); return React.createElement("div", { ref: ref, style: { height: '100%', width: '100%' } }, render({ width: width || 0, height: height || !hasMounted && serverSideHeight || 0 })); } function ResponsiveWindowContainer(props) { var render = props.render, debounceResize = props.debounceResize, debounceInterval = props.debounceInterval, serverSideHeight = props.serverSideHeight, scrollRef = props.scrollRef; var targetRef = React.useRef(); var _useResizeDetector = reactResizeDetector.useResizeDetector({ refreshMode: debounceResize ? 'debounce' : undefined, refreshRate: debounceInterval ? debounceInterval : 100, targetRef: 'scrollRef' in props ? scrollRef : targetRef }), width = _useResizeDetector.width, height = _useResizeDetector.height; var _React$useState = React.useState(false), hasMounted = _React$useState[0], setHasMounted = _React$useState[1]; var _React$useState2 = React.useState(true), reRender = _React$useState2[0], forceRerender = _React$useState2[1]; React.useLayoutEffect(function () { if (serverSideHeight !== undefined) { setHasMounted(!hasMounted); } }, []); React.useLayoutEffect(function () { forceRerender(!reRender); }, [scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current, scrollRef]); return React.createElement(React.Fragment, null, render({ width: width || 0, height: height || !hasMounted && serverSideHeight || 0 }), React.createElement("div", { ref: targetRef, style: { position: 'fixed', height: '100vh', width: '100vw' } })); } exports.FixedList = FixedList; exports.FullWindowFixedList = FullWindowFixedList; exports.FullWindowVariableList = FullWindowVariableList; exports.ResponsiveContainer = ResponsiveContainer; exports.ResponsiveWindowContainer = ResponsiveWindowContainer; exports.VariableList = VariableList; //# sourceMappingURL=index.js.map