import _extends from '@babel/runtime/helpers/esm/extends'; import _assertThisInitialized from '@babel/runtime/helpers/esm/assertThisInitialized'; import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose'; import memoizeOne from 'memoize-one'; import { createElement, PureComponent } from 'react'; import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose'; // Animation frame based implementation of setTimeout. // Inspired by Joe Lambert, https://gist.github.com/joelambert/1002116#file-requesttimeout-js var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; var now = hasNativePerformanceNow ? function () { return performance.now(); } : function () { return Date.now(); }; function cancelTimeout(timeoutID) { cancelAnimationFrame(timeoutID.id); } function requestTimeout(callback, delay) { var start = now(); function tick() { if (now() - start >= delay) { callback.call(null); } else { timeoutID.id = requestAnimationFrame(tick); } } var timeoutID = { id: requestAnimationFrame(tick) }; return timeoutID; } var size = -1; // This utility copied from "dom-helpers" package. function getScrollbarSize(recalculate) { if (recalculate === void 0) { recalculate = false; } if (size === -1 || recalculate) { var div = document.createElement('div'); var style = div.style; style.width = '50px'; style.height = '50px'; style.overflow = 'scroll'; document.body.appendChild(div); size = div.offsetWidth - div.clientWidth; document.body.removeChild(div); } return size; } var cachedRTLResult = null; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left). // Safari's elastic bounce makes detecting this even more complicated wrt potential false positives. // The safest way to check this is to intentionally set a negative offset, // and then verify that the subsequent "scroll" event matches the negative offset. // If it does not match, then we can assume a non-standard RTL scroll implementation. function getRTLOffsetType(recalculate) { if (recalculate === void 0) { recalculate = false; } if (cachedRTLResult === null || recalculate) { var outerDiv = document.createElement('div'); var outerStyle = outerDiv.style; outerStyle.width = '50px'; outerStyle.height = '50px'; outerStyle.overflow = 'scroll'; outerStyle.direction = 'rtl'; var innerDiv = document.createElement('div'); var innerStyle = innerDiv.style; innerStyle.width = '100px'; innerStyle.height = '100px'; outerDiv.appendChild(innerDiv); document.body.appendChild(outerDiv); if (outerDiv.scrollLeft > 0) { cachedRTLResult = 'positive-descending'; } else { outerDiv.scrollLeft = 1; if (outerDiv.scrollLeft === 0) { cachedRTLResult = 'negative'; } else { cachedRTLResult = 'positive-ascending'; } } document.body.removeChild(outerDiv); return cachedRTLResult; } return cachedRTLResult; } var IS_SCROLLING_DEBOUNCE_INTERVAL = 150; var defaultItemKey = function defaultItemKey(_ref) { var columnIndex = _ref.columnIndex, data = _ref.data, rowIndex = _ref.rowIndex; return rowIndex + ":" + columnIndex; }; // In DEV mode, this Set helps us only log a warning once per component instance. // This avoids spamming the console every time a render happens. var devWarningsOverscanCount = null; var devWarningsOverscanRowsColumnsCount = null; var devWarningsTagName = null; if (process.env.NODE_ENV !== 'production') { if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') { devWarningsOverscanCount = /*#__PURE__*/new WeakSet(); devWarningsOverscanRowsColumnsCount = /*#__PURE__*/new WeakSet(); devWarningsTagName = /*#__PURE__*/new WeakSet(); } } function createGridComponent(_ref2) { var _class; var getColumnOffset = _ref2.getColumnOffset, getColumnStartIndexForOffset = _ref2.getColumnStartIndexForOffset, getColumnStopIndexForStartIndex = _ref2.getColumnStopIndexForStartIndex, getColumnWidth = _ref2.getColumnWidth, getEstimatedTotalHeight = _ref2.getEstimatedTotalHeight, getEstimatedTotalWidth = _ref2.getEstimatedTotalWidth, getOffsetForColumnAndAlignment = _ref2.getOffsetForColumnAndAlignment, getOffsetForRowAndAlignment = _ref2.getOffsetForRowAndAlignment, getRowHeight = _ref2.getRowHeight, getRowOffset = _ref2.getRowOffset, getRowStartIndexForOffset = _ref2.getRowStartIndexForOffset, getRowStopIndexForStartIndex = _ref2.getRowStopIndexForStartIndex, initInstanceProps = _ref2.initInstanceProps, shouldResetStyleCacheOnItemSizeChange = _ref2.shouldResetStyleCacheOnItemSizeChange, validateProps = _ref2.validateProps; return _class = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(Grid, _PureComponent); // Always use explicit constructor for React components. // It produces less code after transpilation. (#26) // eslint-disable-next-line no-useless-constructor function Grid(props) { var _this; _this = _PureComponent.call(this, props) || this; _this._instanceProps = initInstanceProps(_this.props, _assertThisInitialized(_this)); _this._resetIsScrollingTimeoutId = null; _this._outerRef = void 0; _this.state = { instance: _assertThisInitialized(_this), isScrolling: false, horizontalScrollDirection: 'forward', scrollLeft: typeof _this.props.initialScrollLeft === 'number' ? _this.props.initialScrollLeft : 0, scrollTop: typeof _this.props.initialScrollTop === 'number' ? _this.props.initialScrollTop : 0, scrollUpdateWasRequested: false, verticalScrollDirection: 'forward' }; _this._callOnItemsRendered = void 0; _this._callOnItemsRendered = memoizeOne(function (overscanColumnStartIndex, overscanColumnStopIndex, overscanRowStartIndex, overscanRowStopIndex, visibleColumnStartIndex, visibleColumnStopIndex, visibleRowStartIndex, visibleRowStopIndex) { return _this.props.onItemsRendered({ overscanColumnStartIndex: overscanColumnStartIndex, overscanColumnStopIndex: overscanColumnStopIndex, overscanRowStartIndex: overscanRowStartIndex, overscanRowStopIndex: overscanRowStopIndex, visibleColumnStartIndex: visibleColumnStartIndex, visibleColumnStopIndex: visibleColumnStopIndex, visibleRowStartIndex: visibleRowStartIndex, visibleRowStopIndex: visibleRowStopIndex }); }); _this._callOnScroll = void 0; _this._callOnScroll = memoizeOne(function (scrollLeft, scrollTop, horizontalScrollDirection, verticalScrollDirection, scrollUpdateWasRequested) { return _this.props.onScroll({ horizontalScrollDirection: horizontalScrollDirection, scrollLeft: scrollLeft, scrollTop: scrollTop, verticalScrollDirection: verticalScrollDirection, scrollUpdateWasRequested: scrollUpdateWasRequested }); }); _this._getItemStyle = void 0; _this._getItemStyle = function (rowIndex, columnIndex) { var _this$props = _this.props, columnWidth = _this$props.columnWidth, direction = _this$props.direction, rowHeight = _this$props.rowHeight; var itemStyleCache = _this._getItemStyleCache(shouldResetStyleCacheOnItemSizeChange && columnWidth, shouldResetStyleCacheOnItemSizeChange && direction, shouldResetStyleCacheOnItemSizeChange && rowHeight); var key = rowIndex + ":" + columnIndex; var style; if (itemStyleCache.hasOwnProperty(key)) { style = itemStyleCache[key]; } else { var _offset = getColumnOffset(_this.props, columnIndex, _this._instanceProps); var isRtl = direction === 'rtl'; itemStyleCache[key] = style = { position: 'absolute', left: isRtl ? undefined : _offset, right: isRtl ? _offset : undefined, top: getRowOffset(_this.props, rowIndex, _this._instanceProps), height: getRowHeight(_this.props, rowIndex, _this._instanceProps), width: getColumnWidth(_this.props, columnIndex, _this._instanceProps) }; } return style; }; _this._getItemStyleCache = void 0; _this._getItemStyleCache = memoizeOne(function (_, __, ___) { return {}; }); _this._onScroll = function (event) { var _event$currentTarget = event.currentTarget, clientHeight = _event$currentTarget.clientHeight, clientWidth = _event$currentTarget.clientWidth, scrollLeft = _event$currentTarget.scrollLeft, scrollTop = _event$currentTarget.scrollTop, scrollHeight = _event$currentTarget.scrollHeight, scrollWidth = _event$currentTarget.scrollWidth; _this.setState(function (prevState) { if (prevState.scrollLeft === scrollLeft && prevState.scrollTop === scrollTop) { // Scroll position may have been updated by cDM/cDU, // In which case we don't need to trigger another render, // And we don't want to update state.isScrolling. return null; } var direction = _this.props.direction; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. var calculatedScrollLeft = scrollLeft; if (direction === 'rtl') { switch (getRTLOffsetType()) { case 'negative': calculatedScrollLeft = -scrollLeft; break; case 'positive-descending': calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft; break; } } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. calculatedScrollLeft = Math.max(0, Math.min(calculatedScrollLeft, scrollWidth - clientWidth)); var calculatedScrollTop = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)); return { isScrolling: true, horizontalScrollDirection: prevState.scrollLeft < scrollLeft ? 'forward' : 'backward', scrollLeft: calculatedScrollLeft, scrollTop: calculatedScrollTop, verticalScrollDirection: prevState.scrollTop < scrollTop ? 'forward' : 'backward', scrollUpdateWasRequested: false }; }, _this._resetIsScrollingDebounced); }; _this._outerRefSetter = function (ref) { var outerRef = _this.props.outerRef; _this._outerRef = ref; if (typeof outerRef === 'function') { outerRef(ref); } else if (outerRef != null && typeof outerRef === 'object' && outerRef.hasOwnProperty('current')) { outerRef.current = ref; } }; _this._resetIsScrollingDebounced = function () { if (_this._resetIsScrollingTimeoutId !== null) { cancelTimeout(_this._resetIsScrollingTimeoutId); } _this._resetIsScrollingTimeoutId = requestTimeout(_this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL); }; _this._resetIsScrolling = function () { _this._resetIsScrollingTimeoutId = null; _this.setState({ isScrolling: false }, function () { // Clear style cache after state update has been committed. // This way we don't break pure sCU for items that don't use isScrolling param. _this._getItemStyleCache(-1); }); }; return _this; } Grid.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { validateSharedProps(nextProps, prevState); validateProps(nextProps); return null; }; var _proto = Grid.prototype; _proto.scrollTo = function scrollTo(_ref3) { var scrollLeft = _ref3.scrollLeft, scrollTop = _ref3.scrollTop; if (scrollLeft !== undefined) { scrollLeft = Math.max(0, scrollLeft); } if (scrollTop !== undefined) { scrollTop = Math.max(0, scrollTop); } this.setState(function (prevState) { if (scrollLeft === undefined) { scrollLeft = prevState.scrollLeft; } if (scrollTop === undefined) { scrollTop = prevState.scrollTop; } if (prevState.scrollLeft === scrollLeft && prevState.scrollTop === scrollTop) { return null; } return { horizontalScrollDirection: prevState.scrollLeft < scrollLeft ? 'forward' : 'backward', scrollLeft: scrollLeft, scrollTop: scrollTop, scrollUpdateWasRequested: true, verticalScrollDirection: prevState.scrollTop < scrollTop ? 'forward' : 'backward' }; }, this._resetIsScrollingDebounced); }; _proto.scrollToItem = function scrollToItem(_ref4) { var _ref4$align = _ref4.align, align = _ref4$align === void 0 ? 'auto' : _ref4$align, columnIndex = _ref4.columnIndex, rowIndex = _ref4.rowIndex; var _this$props2 = this.props, columnCount = _this$props2.columnCount, height = _this$props2.height, rowCount = _this$props2.rowCount, width = _this$props2.width; var _this$state = this.state, scrollLeft = _this$state.scrollLeft, scrollTop = _this$state.scrollTop; var scrollbarSize = getScrollbarSize(); if (columnIndex !== undefined) { columnIndex = Math.max(0, Math.min(columnIndex, columnCount - 1)); } if (rowIndex !== undefined) { rowIndex = Math.max(0, Math.min(rowIndex, rowCount - 1)); } var estimatedTotalHeight = getEstimatedTotalHeight(this.props, this._instanceProps); var estimatedTotalWidth = getEstimatedTotalWidth(this.props, this._instanceProps); // The scrollbar size should be considered when scrolling an item into view, // to ensure it's fully visible. // But we only need to account for its size when it's actually visible. var horizontalScrollbarSize = estimatedTotalWidth > width ? scrollbarSize : 0; var verticalScrollbarSize = estimatedTotalHeight > height ? scrollbarSize : 0; this.scrollTo({ scrollLeft: columnIndex !== undefined ? getOffsetForColumnAndAlignment(this.props, columnIndex, align, scrollLeft, this._instanceProps, verticalScrollbarSize) : scrollLeft, scrollTop: rowIndex !== undefined ? getOffsetForRowAndAlignment(this.props, rowIndex, align, scrollTop, this._instanceProps, horizontalScrollbarSize) : scrollTop }); }; _proto.componentDidMount = function componentDidMount() { var _this$props3 = this.props, initialScrollLeft = _this$props3.initialScrollLeft, initialScrollTop = _this$props3.initialScrollTop; if (this._outerRef != null) { var outerRef = this._outerRef; if (typeof initialScrollLeft === 'number') { outerRef.scrollLeft = initialScrollLeft; } if (typeof initialScrollTop === 'number') { outerRef.scrollTop = initialScrollTop; } } this._callPropsCallbacks(); }; _proto.componentDidUpdate = function componentDidUpdate() { var direction = this.props.direction; var _this$state2 = this.state, scrollLeft = _this$state2.scrollLeft, scrollTop = _this$state2.scrollTop, scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested; if (scrollUpdateWasRequested && this._outerRef != null) { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // So we need to determine which browser behavior we're dealing with, and mimic it. var outerRef = this._outerRef; if (direction === 'rtl') { switch (getRTLOffsetType()) { case 'negative': outerRef.scrollLeft = -scrollLeft; break; case 'positive-ascending': outerRef.scrollLeft = scrollLeft; break; default: var clientWidth = outerRef.clientWidth, scrollWidth = outerRef.scrollWidth; outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft; break; } } else { outerRef.scrollLeft = Math.max(0, scrollLeft); } outerRef.scrollTop = Math.max(0, scrollTop); } this._callPropsCallbacks(); }; _proto.componentWillUnmount = function componentWillUnmount() { if (this._resetIsScrollingTimeoutId !== null) { cancelTimeout(this._resetIsScrollingTimeoutId); } }; _proto.render = function render() { var _this$props4 = this.props, children = _this$props4.children, className = _this$props4.className, columnCount = _this$props4.columnCount, direction = _this$props4.direction, height = _this$props4.height, innerRef = _this$props4.innerRef, innerElementType = _this$props4.innerElementType, innerTagName = _this$props4.innerTagName, itemData = _this$props4.itemData, _this$props4$itemKey = _this$props4.itemKey, itemKey = _this$props4$itemKey === void 0 ? defaultItemKey : _this$props4$itemKey, outerElementType = _this$props4.outerElementType, outerTagName = _this$props4.outerTagName, rowCount = _this$props4.rowCount, style = _this$props4.style, useIsScrolling = _this$props4.useIsScrolling, width = _this$props4.width; var isScrolling = this.state.isScrolling; var _this$_getHorizontalR = this._getHorizontalRangeToRender(), columnStartIndex = _this$_getHorizontalR[0], columnStopIndex = _this$_getHorizontalR[1]; var _this$_getVerticalRan = this._getVerticalRangeToRender(), rowStartIndex = _this$_getVerticalRan[0], rowStopIndex = _this$_getVerticalRan[1]; var items = []; if (columnCount > 0 && rowCount) { for (var _rowIndex = rowStartIndex; _rowIndex <= rowStopIndex; _rowIndex++) { for (var _columnIndex = columnStartIndex; _columnIndex <= columnStopIndex; _columnIndex++) { items.push(createElement(children, { columnIndex: _columnIndex, data: itemData, isScrolling: useIsScrolling ? isScrolling : undefined, key: itemKey({ columnIndex: _columnIndex, data: itemData, rowIndex: _rowIndex }), rowIndex: _rowIndex, style: this._getItemStyle(_rowIndex, _columnIndex) })); } } } // Read this value AFTER items have been created, // So their actual sizes (if variable) are taken into consideration. var estimatedTotalHeight = getEstimatedTotalHeight(this.props, this._instanceProps); var estimatedTotalWidth = getEstimatedTotalWidth(this.props, this._instanceProps); return createElement(outerElementType || outerTagName || 'div', { className: className, onScroll: this._onScroll, ref: this._outerRefSetter, style: _extends({ position: 'relative', height: height, width: width, overflow: 'auto', WebkitOverflowScrolling: 'touch', willChange: 'transform', direction: direction }, style) }, createElement(innerElementType || innerTagName || 'div', { children: items, ref: innerRef, style: { height: estimatedTotalHeight, pointerEvents: isScrolling ? 'none' : undefined, width: estimatedTotalWidth } })); }; _proto._callPropsCallbacks = function _callPropsCallbacks() { var _this$props5 = this.props, columnCount = _this$props5.columnCount, onItemsRendered = _this$props5.onItemsRendered, onScroll = _this$props5.onScroll, rowCount = _this$props5.rowCount; if (typeof onItemsRendered === 'function') { if (columnCount > 0 && rowCount > 0) { var _this$_getHorizontalR2 = this._getHorizontalRangeToRender(), _overscanColumnStartIndex = _this$_getHorizontalR2[0], _overscanColumnStopIndex = _this$_getHorizontalR2[1], _visibleColumnStartIndex = _this$_getHorizontalR2[2], _visibleColumnStopIndex = _this$_getHorizontalR2[3]; var _this$_getVerticalRan2 = this._getVerticalRangeToRender(), _overscanRowStartIndex = _this$_getVerticalRan2[0], _overscanRowStopIndex = _this$_getVerticalRan2[1], _visibleRowStartIndex = _this$_getVerticalRan2[2], _visibleRowStopIndex = _this$_getVerticalRan2[3]; this._callOnItemsRendered(_overscanColumnStartIndex, _overscanColumnStopIndex, _overscanRowStartIndex, _overscanRowStopIndex, _visibleColumnStartIndex, _visibleColumnStopIndex, _visibleRowStartIndex, _visibleRowStopIndex); } } if (typeof onScroll === 'function') { var _this$state3 = this.state, _horizontalScrollDirection = _this$state3.horizontalScrollDirection, _scrollLeft = _this$state3.scrollLeft, _scrollTop = _this$state3.scrollTop, _scrollUpdateWasRequested = _this$state3.scrollUpdateWasRequested, _verticalScrollDirection = _this$state3.verticalScrollDirection; this._callOnScroll(_scrollLeft, _scrollTop, _horizontalScrollDirection, _verticalScrollDirection, _scrollUpdateWasRequested); } } // Lazily create and cache item styles while scrolling, // So that pure component sCU will prevent re-renders. // We maintain this cache, and pass a style prop rather than index, // So that List can clear cached styles and force item re-render if necessary. ; _proto._getHorizontalRangeToRender = function _getHorizontalRangeToRender() { var _this$props6 = this.props, columnCount = _this$props6.columnCount, overscanColumnCount = _this$props6.overscanColumnCount, overscanColumnsCount = _this$props6.overscanColumnsCount, overscanCount = _this$props6.overscanCount, rowCount = _this$props6.rowCount; var _this$state4 = this.state, horizontalScrollDirection = _this$state4.horizontalScrollDirection, isScrolling = _this$state4.isScrolling, scrollLeft = _this$state4.scrollLeft; var overscanCountResolved = overscanColumnCount || overscanColumnsCount || overscanCount || 1; if (columnCount === 0 || rowCount === 0) { return [0, 0, 0, 0]; } var startIndex = getColumnStartIndexForOffset(this.props, scrollLeft, this._instanceProps); var stopIndex = getColumnStopIndexForStartIndex(this.props, startIndex, scrollLeft, this._instanceProps); // Overscan by one item in each direction so that tab/focus works. // If there isn't at least one extra item, tab loops back around. var overscanBackward = !isScrolling || horizontalScrollDirection === 'backward' ? Math.max(1, overscanCountResolved) : 1; var overscanForward = !isScrolling || horizontalScrollDirection === 'forward' ? Math.max(1, overscanCountResolved) : 1; return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(columnCount - 1, stopIndex + overscanForward)), startIndex, stopIndex]; }; _proto._getVerticalRangeToRender = function _getVerticalRangeToRender() { var _this$props7 = this.props, columnCount = _this$props7.columnCount, overscanCount = _this$props7.overscanCount, overscanRowCount = _this$props7.overscanRowCount, overscanRowsCount = _this$props7.overscanRowsCount, rowCount = _this$props7.rowCount; var _this$state5 = this.state, isScrolling = _this$state5.isScrolling, verticalScrollDirection = _this$state5.verticalScrollDirection, scrollTop = _this$state5.scrollTop; var overscanCountResolved = overscanRowCount || overscanRowsCount || overscanCount || 1; if (columnCount === 0 || rowCount === 0) { return [0, 0, 0, 0]; } var startIndex = getRowStartIndexForOffset(this.props, scrollTop, this._instanceProps); var stopIndex = getRowStopIndexForStartIndex(this.props, startIndex, scrollTop, this._instanceProps); // Overscan by one item in each direction so that tab/focus works. // If there isn't at least one extra item, tab loops back around. var overscanBackward = !isScrolling || verticalScrollDirection === 'backward' ? Math.max(1, overscanCountResolved) : 1; var overscanForward = !isScrolling || verticalScrollDirection === 'forward' ? Math.max(1, overscanCountResolved) : 1; return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(rowCount - 1, stopIndex + overscanForward)), startIndex, stopIndex]; }; return Grid; }(PureComponent), _class.defaultProps = { direction: 'ltr', itemData: undefined, useIsScrolling: false }, _class; } var validateSharedProps = function validateSharedProps(_ref5, _ref6) { var children = _ref5.children, direction = _ref5.direction, height = _ref5.height, innerTagName = _ref5.innerTagName, outerTagName = _ref5.outerTagName, overscanColumnsCount = _ref5.overscanColumnsCount, overscanCount = _ref5.overscanCount, overscanRowsCount = _ref5.overscanRowsCount, width = _ref5.width; var instance = _ref6.instance; if (process.env.NODE_ENV !== 'production') { if (typeof overscanCount === 'number') { if (devWarningsOverscanCount && !devWarningsOverscanCount.has(instance)) { devWarningsOverscanCount.add(instance); console.warn('The overscanCount prop has been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.'); } } if (typeof overscanColumnsCount === 'number' || typeof overscanRowsCount === 'number') { if (devWarningsOverscanRowsColumnsCount && !devWarningsOverscanRowsColumnsCount.has(instance)) { devWarningsOverscanRowsColumnsCount.add(instance); console.warn('The overscanColumnsCount and overscanRowsCount props have been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.'); } } if (innerTagName != null || outerTagName != null) { if (devWarningsTagName && !devWarningsTagName.has(instance)) { devWarningsTagName.add(instance); console.warn('The innerTagName and outerTagName props have been deprecated. ' + 'Please use the innerElementType and outerElementType props instead.'); } } if (children == null) { throw Error('An invalid "children" prop has been specified. ' + 'Value should be a React component. ' + ("\"" + (children === null ? 'null' : typeof children) + "\" was specified.")); } switch (direction) { case 'ltr': case 'rtl': // Valid values break; default: throw Error('An invalid "direction" prop has been specified. ' + 'Value should be either "ltr" or "rtl". ' + ("\"" + direction + "\" was specified.")); } if (typeof width !== 'number') { throw Error('An invalid "width" prop has been specified. ' + 'Grids must specify a number for width. ' + ("\"" + (width === null ? 'null' : typeof width) + "\" was specified.")); } if (typeof height !== 'number') { throw Error('An invalid "height" prop has been specified. ' + 'Grids must specify a number for height. ' + ("\"" + (height === null ? 'null' : typeof height) + "\" was specified.")); } } }; var DEFAULT_ESTIMATED_ITEM_SIZE = 50; var getEstimatedTotalHeight = function getEstimatedTotalHeight(_ref, _ref2) { var rowCount = _ref.rowCount; var rowMetadataMap = _ref2.rowMetadataMap, estimatedRowHeight = _ref2.estimatedRowHeight, lastMeasuredRowIndex = _ref2.lastMeasuredRowIndex; var totalSizeOfMeasuredRows = 0; // Edge case check for when the number of items decreases while a scroll is in progress. // https://github.com/bvaughn/react-window/pull/138 if (lastMeasuredRowIndex >= rowCount) { lastMeasuredRowIndex = rowCount - 1; } if (lastMeasuredRowIndex >= 0) { var itemMetadata = rowMetadataMap[lastMeasuredRowIndex]; totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size; } var numUnmeasuredItems = rowCount - lastMeasuredRowIndex - 1; var totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedRowHeight; return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems; }; var getEstimatedTotalWidth = function getEstimatedTotalWidth(_ref3, _ref4) { var columnCount = _ref3.columnCount; var columnMetadataMap = _ref4.columnMetadataMap, estimatedColumnWidth = _ref4.estimatedColumnWidth, lastMeasuredColumnIndex = _ref4.lastMeasuredColumnIndex; var totalSizeOfMeasuredRows = 0; // Edge case check for when the number of items decreases while a scroll is in progress. // https://github.com/bvaughn/react-window/pull/138 if (lastMeasuredColumnIndex >= columnCount) { lastMeasuredColumnIndex = columnCount - 1; } if (lastMeasuredColumnIndex >= 0) { var itemMetadata = columnMetadataMap[lastMeasuredColumnIndex]; totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size; } var numUnmeasuredItems = columnCount - lastMeasuredColumnIndex - 1; var totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedColumnWidth; return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems; }; var getItemMetadata = function getItemMetadata(itemType, props, index, instanceProps) { var itemMetadataMap, itemSize, lastMeasuredIndex; if (itemType === 'column') { itemMetadataMap = instanceProps.columnMetadataMap; itemSize = props.columnWidth; lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex; } else { itemMetadataMap = instanceProps.rowMetadataMap; itemSize = props.rowHeight; lastMeasuredIndex = instanceProps.lastMeasuredRowIndex; } if (index > lastMeasuredIndex) { var offset = 0; if (lastMeasuredIndex >= 0) { var itemMetadata = itemMetadataMap[lastMeasuredIndex]; offset = itemMetadata.offset + itemMetadata.size; } for (var i = lastMeasuredIndex + 1; i <= index; i++) { var size = itemSize(i); itemMetadataMap[i] = { offset: offset, size: size }; offset += size; } if (itemType === 'column') { instanceProps.lastMeasuredColumnIndex = index; } else { instanceProps.lastMeasuredRowIndex = index; } } return itemMetadataMap[index]; }; var findNearestItem = function findNearestItem(itemType, props, instanceProps, offset) { var itemMetadataMap, lastMeasuredIndex; if (itemType === 'column') { itemMetadataMap = instanceProps.columnMetadataMap; lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex; } else { itemMetadataMap = instanceProps.rowMetadataMap; lastMeasuredIndex = instanceProps.lastMeasuredRowIndex; } var lastMeasuredItemOffset = lastMeasuredIndex > 0 ? itemMetadataMap[lastMeasuredIndex].offset : 0; if (lastMeasuredItemOffset >= offset) { // If we've already measured items within this range just use a binary search as it's faster. return findNearestItemBinarySearch(itemType, props, instanceProps, lastMeasuredIndex, 0, offset); } else { // If we haven't yet measured this high, fallback to an exponential search with an inner binary search. // The exponential search avoids pre-computing sizes for the full set of items as a binary search would. // The overall complexity for this approach is O(log n). return findNearestItemExponentialSearch(itemType, props, instanceProps, Math.max(0, lastMeasuredIndex), offset); } }; var findNearestItemBinarySearch = function findNearestItemBinarySearch(itemType, props, instanceProps, high, low, offset) { while (low <= high) { var middle = low + Math.floor((high - low) / 2); var currentOffset = getItemMetadata(itemType, props, middle, instanceProps).offset; if (currentOffset === offset) { return middle; } else if (currentOffset < offset) { low = middle + 1; } else if (currentOffset > offset) { high = middle - 1; } } if (low > 0) { return low - 1; } else { return 0; } }; var findNearestItemExponentialSearch = function findNearestItemExponentialSearch(itemType, props, instanceProps, index, offset) { var itemCount = itemType === 'column' ? props.columnCount : props.rowCount; var interval = 1; while (index < itemCount && getItemMetadata(itemType, props, index, instanceProps).offset < offset) { index += interval; interval *= 2; } return findNearestItemBinarySearch(itemType, props, instanceProps, Math.min(index, itemCount - 1), Math.floor(index / 2), offset); }; var getOffsetForIndexAndAlignment = function getOffsetForIndexAndAlignment(itemType, props, index, align, scrollOffset, instanceProps, scrollbarSize) { var size = itemType === 'column' ? props.width : props.height; var itemMetadata = getItemMetadata(itemType, props, index, instanceProps); // Get estimated total size after ItemMetadata is computed, // To ensure it reflects actual measurements instead of just estimates. var estimatedTotalSize = itemType === 'column' ? getEstimatedTotalWidth(props, instanceProps) : getEstimatedTotalHeight(props, instanceProps); var maxOffset = Math.max(0, Math.min(estimatedTotalSize - size, itemMetadata.offset)); var minOffset = Math.max(0, itemMetadata.offset - size + scrollbarSize + itemMetadata.size); if (align === 'smart') { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset; case 'end': return minOffset; case 'center': return Math.round(minOffset + (maxOffset - minOffset) / 2); case 'auto': default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (minOffset > maxOffset) { // Because we only take into account the scrollbar size when calculating minOffset // this value can be larger than maxOffset when at the end of the list return minOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } }; var VariableSizeGrid = /*#__PURE__*/createGridComponent({ getColumnOffset: function getColumnOffset(props, index, instanceProps) { return getItemMetadata('column', props, index, instanceProps).offset; }, getColumnStartIndexForOffset: function getColumnStartIndexForOffset(props, scrollLeft, instanceProps) { return findNearestItem('column', props, instanceProps, scrollLeft); }, getColumnStopIndexForStartIndex: function getColumnStopIndexForStartIndex(props, startIndex, scrollLeft, instanceProps) { var columnCount = props.columnCount, width = props.width; var itemMetadata = getItemMetadata('column', props, startIndex, instanceProps); var maxOffset = scrollLeft + width; var offset = itemMetadata.offset + itemMetadata.size; var stopIndex = startIndex; while (stopIndex < columnCount - 1 && offset < maxOffset) { stopIndex++; offset += getItemMetadata('column', props, stopIndex, instanceProps).size; } return stopIndex; }, getColumnWidth: function getColumnWidth(props, index, instanceProps) { return instanceProps.columnMetadataMap[index].size; }, getEstimatedTotalHeight: getEstimatedTotalHeight, getEstimatedTotalWidth: getEstimatedTotalWidth, getOffsetForColumnAndAlignment: function getOffsetForColumnAndAlignment(props, index, align, scrollOffset, instanceProps, scrollbarSize) { return getOffsetForIndexAndAlignment('column', props, index, align, scrollOffset, instanceProps, scrollbarSize); }, getOffsetForRowAndAlignment: function getOffsetForRowAndAlignment(props, index, align, scrollOffset, instanceProps, scrollbarSize) { return getOffsetForIndexAndAlignment('row', props, index, align, scrollOffset, instanceProps, scrollbarSize); }, getRowOffset: function getRowOffset(props, index, instanceProps) { return getItemMetadata('row', props, index, instanceProps).offset; }, getRowHeight: function getRowHeight(props, index, instanceProps) { return instanceProps.rowMetadataMap[index].size; }, getRowStartIndexForOffset: function getRowStartIndexForOffset(props, scrollTop, instanceProps) { return findNearestItem('row', props, instanceProps, scrollTop); }, getRowStopIndexForStartIndex: function getRowStopIndexForStartIndex(props, startIndex, scrollTop, instanceProps) { var rowCount = props.rowCount, height = props.height; var itemMetadata = getItemMetadata('row', props, startIndex, instanceProps); var maxOffset = scrollTop + height; var offset = itemMetadata.offset + itemMetadata.size; var stopIndex = startIndex; while (stopIndex < rowCount - 1 && offset < maxOffset) { stopIndex++; offset += getItemMetadata('row', props, stopIndex, instanceProps).size; } return stopIndex; }, initInstanceProps: function initInstanceProps(props, instance) { var _ref5 = props, estimatedColumnWidth = _ref5.estimatedColumnWidth, estimatedRowHeight = _ref5.estimatedRowHeight; var instanceProps = { columnMetadataMap: {}, estimatedColumnWidth: estimatedColumnWidth || DEFAULT_ESTIMATED_ITEM_SIZE, estimatedRowHeight: estimatedRowHeight || DEFAULT_ESTIMATED_ITEM_SIZE, lastMeasuredColumnIndex: -1, lastMeasuredRowIndex: -1, rowMetadataMap: {} }; instance.resetAfterColumnIndex = function (columnIndex, shouldForceUpdate) { if (shouldForceUpdate === void 0) { shouldForceUpdate = true; } instance.resetAfterIndices({ columnIndex: columnIndex, shouldForceUpdate: shouldForceUpdate }); }; instance.resetAfterRowIndex = function (rowIndex, shouldForceUpdate) { if (shouldForceUpdate === void 0) { shouldForceUpdate = true; } instance.resetAfterIndices({ rowIndex: rowIndex, shouldForceUpdate: shouldForceUpdate }); }; instance.resetAfterIndices = function (_ref6) { var columnIndex = _ref6.columnIndex, rowIndex = _ref6.rowIndex, _ref6$shouldForceUpda = _ref6.shouldForceUpdate, shouldForceUpdate = _ref6$shouldForceUpda === void 0 ? true : _ref6$shouldForceUpda; if (typeof columnIndex === 'number') { instanceProps.lastMeasuredColumnIndex = Math.min(instanceProps.lastMeasuredColumnIndex, columnIndex - 1); } if (typeof rowIndex === 'number') { instanceProps.lastMeasuredRowIndex = Math.min(instanceProps.lastMeasuredRowIndex, rowIndex - 1); } // We could potentially optimize further by only evicting styles after this index, // But since styles are only cached while scrolling is in progress- // It seems an unnecessary optimization. // It's unlikely that resetAfterIndex() will be called while a user is scrolling. instance._getItemStyleCache(-1); if (shouldForceUpdate) { instance.forceUpdate(); } }; return instanceProps; }, shouldResetStyleCacheOnItemSizeChange: false, validateProps: function validateProps(_ref7) { var columnWidth = _ref7.columnWidth, rowHeight = _ref7.rowHeight; if (process.env.NODE_ENV !== 'production') { if (typeof columnWidth !== 'function') { throw Error('An invalid "columnWidth" prop has been specified. ' + 'Value should be a function. ' + ("\"" + (columnWidth === null ? 'null' : typeof columnWidth) + "\" was specified.")); } else if (typeof rowHeight !== 'function') { throw Error('An invalid "rowHeight" prop has been specified. ' + 'Value should be a function. ' + ("\"" + (rowHeight === null ? 'null' : typeof rowHeight) + "\" was specified.")); } } } }); var IS_SCROLLING_DEBOUNCE_INTERVAL$1 = 150; var defaultItemKey$1 = function defaultItemKey(index, data) { return index; }; // In DEV mode, this Set helps us only log a warning once per component instance. // This avoids spamming the console every time a render happens. var devWarningsDirection = null; var devWarningsTagName$1 = null; if (process.env.NODE_ENV !== 'production') { if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') { devWarningsDirection = /*#__PURE__*/new WeakSet(); devWarningsTagName$1 = /*#__PURE__*/new WeakSet(); } } function createListComponent(_ref) { var _class; var getItemOffset = _ref.getItemOffset, getEstimatedTotalSize = _ref.getEstimatedTotalSize, getItemSize = _ref.getItemSize, getOffsetForIndexAndAlignment = _ref.getOffsetForIndexAndAlignment, getStartIndexForOffset = _ref.getStartIndexForOffset, getStopIndexForStartIndex = _ref.getStopIndexForStartIndex, initInstanceProps = _ref.initInstanceProps, shouldResetStyleCacheOnItemSizeChange = _ref.shouldResetStyleCacheOnItemSizeChange, validateProps = _ref.validateProps; return _class = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(List, _PureComponent); // Always use explicit constructor for React components. // It produces less code after transpilation. (#26) // eslint-disable-next-line no-useless-constructor function List(props) { var _this; _this = _PureComponent.call(this, props) || this; _this._instanceProps = initInstanceProps(_this.props, _assertThisInitialized(_this)); _this._outerRef = void 0; _this._resetIsScrollingTimeoutId = null; _this.state = { instance: _assertThisInitialized(_this), isScrolling: false, scrollDirection: 'forward', scrollOffset: typeof _this.props.initialScrollOffset === 'number' ? _this.props.initialScrollOffset : 0, scrollUpdateWasRequested: false }; _this._callOnItemsRendered = void 0; _this._callOnItemsRendered = memoizeOne(function (overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex) { return _this.props.onItemsRendered({ overscanStartIndex: overscanStartIndex, overscanStopIndex: overscanStopIndex, visibleStartIndex: visibleStartIndex, visibleStopIndex: visibleStopIndex }); }); _this._callOnScroll = void 0; _this._callOnScroll = memoizeOne(function (scrollDirection, scrollOffset, scrollUpdateWasRequested) { return _this.props.onScroll({ scrollDirection: scrollDirection, scrollOffset: scrollOffset, scrollUpdateWasRequested: scrollUpdateWasRequested }); }); _this._getItemStyle = void 0; _this._getItemStyle = function (index) { var _this$props = _this.props, direction = _this$props.direction, itemSize = _this$props.itemSize, layout = _this$props.layout; var itemStyleCache = _this._getItemStyleCache(shouldResetStyleCacheOnItemSizeChange && itemSize, shouldResetStyleCacheOnItemSizeChange && layout, shouldResetStyleCacheOnItemSizeChange && direction); var style; if (itemStyleCache.hasOwnProperty(index)) { style = itemStyleCache[index]; } else { var _offset = getItemOffset(_this.props, index, _this._instanceProps); var size = getItemSize(_this.props, index, _this._instanceProps); // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var isRtl = direction === 'rtl'; var offsetHorizontal = isHorizontal ? _offset : 0; itemStyleCache[index] = style = { position: 'absolute', left: isRtl ? undefined : offsetHorizontal, right: isRtl ? offsetHorizontal : undefined, top: !isHorizontal ? _offset : 0, height: !isHorizontal ? size : '100%', width: isHorizontal ? size : '100%' }; } return style; }; _this._getItemStyleCache = void 0; _this._getItemStyleCache = memoizeOne(function (_, __, ___) { return {}; }); _this._onScrollHorizontal = function (event) { var _event$currentTarget = event.currentTarget, clientWidth = _event$currentTarget.clientWidth, scrollLeft = _event$currentTarget.scrollLeft, scrollWidth = _event$currentTarget.scrollWidth; _this.setState(function (prevState) { if (prevState.scrollOffset === scrollLeft) { // Scroll position may have been updated by cDM/cDU, // In which case we don't need to trigger another render, // And we don't want to update state.isScrolling. return null; } var direction = _this.props.direction; var scrollOffset = scrollLeft; if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. switch (getRTLOffsetType()) { case 'negative': scrollOffset = -scrollLeft; break; case 'positive-descending': scrollOffset = scrollWidth - clientWidth - scrollLeft; break; } } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollLeft ? 'forward' : 'backward', scrollOffset: scrollOffset, scrollUpdateWasRequested: false }; }, _this._resetIsScrollingDebounced); }; _this._onScrollVertical = function (event) { var _event$currentTarget2 = event.currentTarget, clientHeight = _event$currentTarget2.clientHeight, scrollHeight = _event$currentTarget2.scrollHeight, scrollTop = _event$currentTarget2.scrollTop; _this.setState(function (prevState) { if (prevState.scrollOffset === scrollTop) { // Scroll position may have been updated by cDM/cDU, // In which case we don't need to trigger another render, // And we don't want to update state.isScrolling. return null; } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. var scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward', scrollOffset: scrollOffset, scrollUpdateWasRequested: false }; }, _this._resetIsScrollingDebounced); }; _this._outerRefSetter = function (ref) { var outerRef = _this.props.outerRef; _this._outerRef = ref; if (typeof outerRef === 'function') { outerRef(ref); } else if (outerRef != null && typeof outerRef === 'object' && outerRef.hasOwnProperty('current')) { outerRef.current = ref; } }; _this._resetIsScrollingDebounced = function () { if (_this._resetIsScrollingTimeoutId !== null) { cancelTimeout(_this._resetIsScrollingTimeoutId); } _this._resetIsScrollingTimeoutId = requestTimeout(_this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL$1); }; _this._resetIsScrolling = function () { _this._resetIsScrollingTimeoutId = null; _this.setState({ isScrolling: false }, function () { // Clear style cache after state update has been committed. // This way we don't break pure sCU for items that don't use isScrolling param. _this._getItemStyleCache(-1, null); }); }; return _this; } List.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { validateSharedProps$1(nextProps, prevState); validateProps(nextProps); return null; }; var _proto = List.prototype; _proto.scrollTo = function scrollTo(scrollOffset) { scrollOffset = Math.max(0, scrollOffset); this.setState(function (prevState) { if (prevState.scrollOffset === scrollOffset) { return null; } return { scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward', scrollOffset: scrollOffset, scrollUpdateWasRequested: true }; }, this._resetIsScrollingDebounced); }; _proto.scrollToItem = function scrollToItem(index, align) { if (align === void 0) { align = 'auto'; } var _this$props2 = this.props, itemCount = _this$props2.itemCount, layout = _this$props2.layout; var scrollOffset = this.state.scrollOffset; index = Math.max(0, Math.min(index, itemCount - 1)); // The scrollbar size should be considered when scrolling an item into view, to ensure it's fully visible. // But we only need to account for its size when it's actually visible. // This is an edge case for lists; normally they only scroll in the dominant direction. var scrollbarSize = 0; if (this._outerRef) { var outerRef = this._outerRef; if (layout === 'vertical') { scrollbarSize = outerRef.scrollWidth > outerRef.clientWidth ? getScrollbarSize() : 0; } else { scrollbarSize = outerRef.scrollHeight > outerRef.clientHeight ? getScrollbarSize() : 0; } } this.scrollTo(getOffsetForIndexAndAlignment(this.props, index, align, scrollOffset, this._instanceProps, scrollbarSize)); }; _proto.componentDidMount = function componentDidMount() { var _this$props3 = this.props, direction = _this$props3.direction, initialScrollOffset = _this$props3.initialScrollOffset, layout = _this$props3.layout; if (typeof initialScrollOffset === 'number' && this._outerRef != null) { var outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { outerRef.scrollLeft = initialScrollOffset; } else { outerRef.scrollTop = initialScrollOffset; } } this._callPropsCallbacks(); }; _proto.componentDidUpdate = function componentDidUpdate() { var _this$props4 = this.props, direction = _this$props4.direction, layout = _this$props4.layout; var _this$state = this.state, scrollOffset = _this$state.scrollOffset, scrollUpdateWasRequested = _this$state.scrollUpdateWasRequested; if (scrollUpdateWasRequested && this._outerRef != null) { var outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // So we need to determine which browser behavior we're dealing with, and mimic it. switch (getRTLOffsetType()) { case 'negative': outerRef.scrollLeft = -scrollOffset; break; case 'positive-ascending': outerRef.scrollLeft = scrollOffset; break; default: var clientWidth = outerRef.clientWidth, scrollWidth = outerRef.scrollWidth; outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; break; } } else { outerRef.scrollLeft = scrollOffset; } } else { outerRef.scrollTop = scrollOffset; } } this._callPropsCallbacks(); }; _proto.componentWillUnmount = function componentWillUnmount() { if (this._resetIsScrollingTimeoutId !== null) { cancelTimeout(this._resetIsScrollingTimeoutId); } }; _proto.render = function render() { var _this$props5 = this.props, children = _this$props5.children, className = _this$props5.className, direction = _this$props5.direction, height = _this$props5.height, innerRef = _this$props5.innerRef, innerElementType = _this$props5.innerElementType, innerTagName = _this$props5.innerTagName, itemCount = _this$props5.itemCount, itemData = _this$props5.itemData, _this$props5$itemKey = _this$props5.itemKey, itemKey = _this$props5$itemKey === void 0 ? defaultItemKey$1 : _this$props5$itemKey, layout = _this$props5.layout, outerElementType = _this$props5.outerElementType, outerTagName = _this$props5.outerTagName, style = _this$props5.style, useIsScrolling = _this$props5.useIsScrolling, width = _this$props5.width; var isScrolling = this.state.isScrolling; // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var onScroll = isHorizontal ? this._onScrollHorizontal : this._onScrollVertical; var _this$_getRangeToRend = this._getRangeToRender(), startIndex = _this$_getRangeToRend[0], stopIndex = _this$_getRangeToRend[1]; var items = []; if (itemCount > 0) { for (var _index = startIndex; _index <= stopIndex; _index++) { items.push(createElement(children, { data: itemData, key: itemKey(_index, itemData), index: _index, isScrolling: useIsScrolling ? isScrolling : undefined, style: this._getItemStyle(_index) })); } } // Read this value AFTER items have been created, // So their actual sizes (if variable) are taken into consideration. var estimatedTotalSize = getEstimatedTotalSize(this.props, this._instanceProps); return createElement(outerElementType || outerTagName || 'div', { className: className, onScroll: onScroll, ref: this._outerRefSetter, style: _extends({ position: 'relative', height: height, width: width, overflow: 'auto', WebkitOverflowScrolling: 'touch', willChange: 'transform', direction: direction }, style) }, createElement(innerElementType || innerTagName || 'div', { children: items, ref: innerRef, style: { height: isHorizontal ? '100%' : estimatedTotalSize, pointerEvents: isScrolling ? 'none' : undefined, width: isHorizontal ? estimatedTotalSize : '100%' } })); }; _proto._callPropsCallbacks = function _callPropsCallbacks() { if (typeof this.props.onItemsRendered === 'function') { var itemCount = this.props.itemCount; if (itemCount > 0) { var _this$_getRangeToRend2 = this._getRangeToRender(), _overscanStartIndex = _this$_getRangeToRend2[0], _overscanStopIndex = _this$_getRangeToRend2[1], _visibleStartIndex = _this$_getRangeToRend2[2], _visibleStopIndex = _this$_getRangeToRend2[3]; this._callOnItemsRendered(_overscanStartIndex, _overscanStopIndex, _visibleStartIndex, _visibleStopIndex); } } if (typeof this.props.onScroll === 'function') { var _this$state2 = this.state, _scrollDirection = _this$state2.scrollDirection, _scrollOffset = _this$state2.scrollOffset, _scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested; this._callOnScroll(_scrollDirection, _scrollOffset, _scrollUpdateWasRequested); } } // Lazily create and cache item styles while scrolling, // So that pure component sCU will prevent re-renders. // We maintain this cache, and pass a style prop rather than index, // So that List can clear cached styles and force item re-render if necessary. ; _proto._getRangeToRender = function _getRangeToRender() { var _this$props6 = this.props, itemCount = _this$props6.itemCount, overscanCount = _this$props6.overscanCount; var _this$state3 = this.state, isScrolling = _this$state3.isScrolling, scrollDirection = _this$state3.scrollDirection, scrollOffset = _this$state3.scrollOffset; if (itemCount === 0) { return [0, 0, 0, 0]; } var startIndex = getStartIndexForOffset(this.props, scrollOffset, this._instanceProps); var stopIndex = getStopIndexForStartIndex(this.props, startIndex, scrollOffset, this._instanceProps); // Overscan by one item in each direction so that tab/focus works. // If there isn't at least one extra item, tab loops back around. var overscanBackward = !isScrolling || scrollDirection === 'backward' ? Math.max(1, overscanCount) : 1; var overscanForward = !isScrolling || scrollDirection === 'forward' ? Math.max(1, overscanCount) : 1; return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)), startIndex, stopIndex]; }; return List; }(PureComponent), _class.defaultProps = { direction: 'ltr', itemData: undefined, layout: 'vertical', overscanCount: 2, useIsScrolling: false }, _class; } // NOTE: I considered further wrapping individual items with a pure ListItem component. // This would avoid ever calling the render function for the same index more than once, // But it would also add the overhead of a lot of components/fibers. // I assume people already do this (render function returning a class component), // So my doing it would just unnecessarily double the wrappers. var validateSharedProps$1 = function validateSharedProps(_ref2, _ref3) { var children = _ref2.children, direction = _ref2.direction, height = _ref2.height, layout = _ref2.layout, innerTagName = _ref2.innerTagName, outerTagName = _ref2.outerTagName, width = _ref2.width; var instance = _ref3.instance; if (process.env.NODE_ENV !== 'production') { if (innerTagName != null || outerTagName != null) { if (devWarningsTagName$1 && !devWarningsTagName$1.has(instance)) { devWarningsTagName$1.add(instance); console.warn('The innerTagName and outerTagName props have been deprecated. ' + 'Please use the innerElementType and outerElementType props instead.'); } } // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; switch (direction) { case 'horizontal': case 'vertical': if (devWarningsDirection && !devWarningsDirection.has(instance)) { devWarningsDirection.add(instance); console.warn('The direction prop should be either "ltr" (default) or "rtl". ' + 'Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.'); } break; case 'ltr': case 'rtl': // Valid values break; default: throw Error('An invalid "direction" prop has been specified. ' + 'Value should be either "ltr" or "rtl". ' + ("\"" + direction + "\" was specified.")); } switch (layout) { case 'horizontal': case 'vertical': // Valid values break; default: throw Error('An invalid "layout" prop has been specified. ' + 'Value should be either "horizontal" or "vertical". ' + ("\"" + layout + "\" was specified.")); } if (children == null) { throw Error('An invalid "children" prop has been specified. ' + 'Value should be a React component. ' + ("\"" + (children === null ? 'null' : typeof children) + "\" was specified.")); } if (isHorizontal && typeof width !== 'number') { throw Error('An invalid "width" prop has been specified. ' + 'Horizontal lists must specify a number for width. ' + ("\"" + (width === null ? 'null' : typeof width) + "\" was specified.")); } else if (!isHorizontal && typeof height !== 'number') { throw Error('An invalid "height" prop has been specified. ' + 'Vertical lists must specify a number for height. ' + ("\"" + (height === null ? 'null' : typeof height) + "\" was specified.")); } } }; var DEFAULT_ESTIMATED_ITEM_SIZE$1 = 50; var getItemMetadata$1 = function getItemMetadata(props, index, instanceProps) { var _ref = props, itemSize = _ref.itemSize; var itemMetadataMap = instanceProps.itemMetadataMap, lastMeasuredIndex = instanceProps.lastMeasuredIndex; if (index > lastMeasuredIndex) { var offset = 0; if (lastMeasuredIndex >= 0) { var itemMetadata = itemMetadataMap[lastMeasuredIndex]; offset = itemMetadata.offset + itemMetadata.size; } for (var i = lastMeasuredIndex + 1; i <= index; i++) { var size = itemSize(i); itemMetadataMap[i] = { offset: offset, size: size }; offset += size; } instanceProps.lastMeasuredIndex = index; } return itemMetadataMap[index]; }; var findNearestItem$1 = function findNearestItem(props, instanceProps, offset) { var itemMetadataMap = instanceProps.itemMetadataMap, lastMeasuredIndex = instanceProps.lastMeasuredIndex; var lastMeasuredItemOffset = lastMeasuredIndex > 0 ? itemMetadataMap[lastMeasuredIndex].offset : 0; if (lastMeasuredItemOffset >= offset) { // If we've already measured items within this range just use a binary search as it's faster. return findNearestItemBinarySearch$1(props, instanceProps, lastMeasuredIndex, 0, offset); } else { // If we haven't yet measured this high, fallback to an exponential search with an inner binary search. // The exponential search avoids pre-computing sizes for the full set of items as a binary search would. // The overall complexity for this approach is O(log n). return findNearestItemExponentialSearch$1(props, instanceProps, Math.max(0, lastMeasuredIndex), offset); } }; var findNearestItemBinarySearch$1 = function findNearestItemBinarySearch(props, instanceProps, high, low, offset) { while (low <= high) { var middle = low + Math.floor((high - low) / 2); var currentOffset = getItemMetadata$1(props, middle, instanceProps).offset; if (currentOffset === offset) { return middle; } else if (currentOffset < offset) { low = middle + 1; } else if (currentOffset > offset) { high = middle - 1; } } if (low > 0) { return low - 1; } else { return 0; } }; var findNearestItemExponentialSearch$1 = function findNearestItemExponentialSearch(props, instanceProps, index, offset) { var itemCount = props.itemCount; var interval = 1; while (index < itemCount && getItemMetadata$1(props, index, instanceProps).offset < offset) { index += interval; interval *= 2; } return findNearestItemBinarySearch$1(props, instanceProps, Math.min(index, itemCount - 1), Math.floor(index / 2), offset); }; var getEstimatedTotalSize = function getEstimatedTotalSize(_ref2, _ref3) { var itemCount = _ref2.itemCount; var itemMetadataMap = _ref3.itemMetadataMap, estimatedItemSize = _ref3.estimatedItemSize, lastMeasuredIndex = _ref3.lastMeasuredIndex; var totalSizeOfMeasuredItems = 0; // Edge case check for when the number of items decreases while a scroll is in progress. // https://github.com/bvaughn/react-window/pull/138 if (lastMeasuredIndex >= itemCount) { lastMeasuredIndex = itemCount - 1; } if (lastMeasuredIndex >= 0) { var itemMetadata = itemMetadataMap[lastMeasuredIndex]; totalSizeOfMeasuredItems = itemMetadata.offset + itemMetadata.size; } var numUnmeasuredItems = itemCount - lastMeasuredIndex - 1; var totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedItemSize; return totalSizeOfMeasuredItems + totalSizeOfUnmeasuredItems; }; var VariableSizeList = /*#__PURE__*/createListComponent({ getItemOffset: function getItemOffset(props, index, instanceProps) { return getItemMetadata$1(props, index, instanceProps).offset; }, getItemSize: function getItemSize(props, index, instanceProps) { return instanceProps.itemMetadataMap[index].size; }, getEstimatedTotalSize: getEstimatedTotalSize, getOffsetForIndexAndAlignment: function getOffsetForIndexAndAlignment(props, index, align, scrollOffset, instanceProps, scrollbarSize) { var direction = props.direction, height = props.height, layout = props.layout, width = props.width; // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var size = isHorizontal ? width : height; var itemMetadata = getItemMetadata$1(props, index, instanceProps); // Get estimated total size after ItemMetadata is computed, // To ensure it reflects actual measurements instead of just estimates. var estimatedTotalSize = getEstimatedTotalSize(props, instanceProps); var maxOffset = Math.max(0, Math.min(estimatedTotalSize - size, itemMetadata.offset)); var minOffset = Math.max(0, itemMetadata.offset - size + itemMetadata.size + scrollbarSize); if (align === 'smart') { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset; case 'end': return minOffset; case 'center': return Math.round(minOffset + (maxOffset - minOffset) / 2); case 'auto': default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } }, getStartIndexForOffset: function getStartIndexForOffset(props, offset, instanceProps) { return findNearestItem$1(props, instanceProps, offset); }, getStopIndexForStartIndex: function getStopIndexForStartIndex(props, startIndex, scrollOffset, instanceProps) { var direction = props.direction, height = props.height, itemCount = props.itemCount, layout = props.layout, width = props.width; // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var size = isHorizontal ? width : height; var itemMetadata = getItemMetadata$1(props, startIndex, instanceProps); var maxOffset = scrollOffset + size; var offset = itemMetadata.offset + itemMetadata.size; var stopIndex = startIndex; while (stopIndex < itemCount - 1 && offset < maxOffset) { stopIndex++; offset += getItemMetadata$1(props, stopIndex, instanceProps).size; } return stopIndex; }, initInstanceProps: function initInstanceProps(props, instance) { var _ref4 = props, estimatedItemSize = _ref4.estimatedItemSize; var instanceProps = { itemMetadataMap: {}, estimatedItemSize: estimatedItemSize || DEFAULT_ESTIMATED_ITEM_SIZE$1, lastMeasuredIndex: -1 }; instance.resetAfterIndex = function (index, shouldForceUpdate) { if (shouldForceUpdate === void 0) { shouldForceUpdate = true; } instanceProps.lastMeasuredIndex = Math.min(instanceProps.lastMeasuredIndex, index - 1); // We could potentially optimize further by only evicting styles after this index, // But since styles are only cached while scrolling is in progress- // It seems an unnecessary optimization. // It's unlikely that resetAfterIndex() will be called while a user is scrolling. instance._getItemStyleCache(-1); if (shouldForceUpdate) { instance.forceUpdate(); } }; return instanceProps; }, shouldResetStyleCacheOnItemSizeChange: false, validateProps: function validateProps(_ref5) { var itemSize = _ref5.itemSize; if (process.env.NODE_ENV !== 'production') { if (typeof itemSize !== 'function') { throw Error('An invalid "itemSize" prop has been specified. ' + 'Value should be a function. ' + ("\"" + (itemSize === null ? 'null' : typeof itemSize) + "\" was specified.")); } } } }); var FixedSizeGrid = /*#__PURE__*/createGridComponent({ getColumnOffset: function getColumnOffset(_ref, index) { var columnWidth = _ref.columnWidth; return index * columnWidth; }, getColumnWidth: function getColumnWidth(_ref2, index) { var columnWidth = _ref2.columnWidth; return columnWidth; }, getRowOffset: function getRowOffset(_ref3, index) { var rowHeight = _ref3.rowHeight; return index * rowHeight; }, getRowHeight: function getRowHeight(_ref4, index) { var rowHeight = _ref4.rowHeight; return rowHeight; }, getEstimatedTotalHeight: function getEstimatedTotalHeight(_ref5) { var rowCount = _ref5.rowCount, rowHeight = _ref5.rowHeight; return rowHeight * rowCount; }, getEstimatedTotalWidth: function getEstimatedTotalWidth(_ref6) { var columnCount = _ref6.columnCount, columnWidth = _ref6.columnWidth; return columnWidth * columnCount; }, getOffsetForColumnAndAlignment: function getOffsetForColumnAndAlignment(_ref7, columnIndex, align, scrollLeft, instanceProps, scrollbarSize) { var columnCount = _ref7.columnCount, columnWidth = _ref7.columnWidth, width = _ref7.width; var lastColumnOffset = Math.max(0, columnCount * columnWidth - width); var maxOffset = Math.min(lastColumnOffset, columnIndex * columnWidth); var minOffset = Math.max(0, columnIndex * columnWidth - width + scrollbarSize + columnWidth); if (align === 'smart') { if (scrollLeft >= minOffset - width && scrollLeft <= maxOffset + width) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset; case 'end': return minOffset; case 'center': // "Centered" offset is usually the average of the min and max. // But near the edges of the list, this doesn't hold true. var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(width / 2)) { return 0; // near the beginning } else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) { return lastColumnOffset; // near the end } else { return middleOffset; } case 'auto': default: if (scrollLeft >= minOffset && scrollLeft <= maxOffset) { return scrollLeft; } else if (minOffset > maxOffset) { // Because we only take into account the scrollbar size when calculating minOffset // this value can be larger than maxOffset when at the end of the list return minOffset; } else if (scrollLeft < minOffset) { return minOffset; } else { return maxOffset; } } }, getOffsetForRowAndAlignment: function getOffsetForRowAndAlignment(_ref8, rowIndex, align, scrollTop, instanceProps, scrollbarSize) { var rowHeight = _ref8.rowHeight, height = _ref8.height, rowCount = _ref8.rowCount; var lastRowOffset = Math.max(0, rowCount * rowHeight - height); var maxOffset = Math.min(lastRowOffset, rowIndex * rowHeight); var minOffset = Math.max(0, rowIndex * rowHeight - height + scrollbarSize + rowHeight); if (align === 'smart') { if (scrollTop >= minOffset - height && scrollTop <= maxOffset + height) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset; case 'end': return minOffset; case 'center': // "Centered" offset is usually the average of the min and max. // But near the edges of the list, this doesn't hold true. var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(height / 2)) { return 0; // near the beginning } else if (middleOffset > lastRowOffset + Math.floor(height / 2)) { return lastRowOffset; // near the end } else { return middleOffset; } case 'auto': default: if (scrollTop >= minOffset && scrollTop <= maxOffset) { return scrollTop; } else if (minOffset > maxOffset) { // Because we only take into account the scrollbar size when calculating minOffset // this value can be larger than maxOffset when at the end of the list return minOffset; } else if (scrollTop < minOffset) { return minOffset; } else { return maxOffset; } } }, getColumnStartIndexForOffset: function getColumnStartIndexForOffset(_ref9, scrollLeft) { var columnWidth = _ref9.columnWidth, columnCount = _ref9.columnCount; return Math.max(0, Math.min(columnCount - 1, Math.floor(scrollLeft / columnWidth))); }, getColumnStopIndexForStartIndex: function getColumnStopIndexForStartIndex(_ref10, startIndex, scrollLeft) { var columnWidth = _ref10.columnWidth, columnCount = _ref10.columnCount, width = _ref10.width; var left = startIndex * columnWidth; var numVisibleColumns = Math.ceil((width + scrollLeft - left) / columnWidth); return Math.max(0, Math.min(columnCount - 1, startIndex + numVisibleColumns - 1 // -1 is because stop index is inclusive )); }, getRowStartIndexForOffset: function getRowStartIndexForOffset(_ref11, scrollTop) { var rowHeight = _ref11.rowHeight, rowCount = _ref11.rowCount; return Math.max(0, Math.min(rowCount - 1, Math.floor(scrollTop / rowHeight))); }, getRowStopIndexForStartIndex: function getRowStopIndexForStartIndex(_ref12, startIndex, scrollTop) { var rowHeight = _ref12.rowHeight, rowCount = _ref12.rowCount, height = _ref12.height; var top = startIndex * rowHeight; var numVisibleRows = Math.ceil((height + scrollTop - top) / rowHeight); return Math.max(0, Math.min(rowCount - 1, startIndex + numVisibleRows - 1 // -1 is because stop index is inclusive )); }, initInstanceProps: function initInstanceProps(props) {// Noop }, shouldResetStyleCacheOnItemSizeChange: true, validateProps: function validateProps(_ref13) { var columnWidth = _ref13.columnWidth, rowHeight = _ref13.rowHeight; if (process.env.NODE_ENV !== 'production') { if (typeof columnWidth !== 'number') { throw Error('An invalid "columnWidth" prop has been specified. ' + 'Value should be a number. ' + ("\"" + (columnWidth === null ? 'null' : typeof columnWidth) + "\" was specified.")); } if (typeof rowHeight !== 'number') { throw Error('An invalid "rowHeight" prop has been specified. ' + 'Value should be a number. ' + ("\"" + (rowHeight === null ? 'null' : typeof rowHeight) + "\" was specified.")); } } } }); var FixedSizeList = /*#__PURE__*/createListComponent({ getItemOffset: function getItemOffset(_ref, index) { var itemSize = _ref.itemSize; return index * itemSize; }, getItemSize: function getItemSize(_ref2, index) { var itemSize = _ref2.itemSize; return itemSize; }, getEstimatedTotalSize: function getEstimatedTotalSize(_ref3) { var itemCount = _ref3.itemCount, itemSize = _ref3.itemSize; return itemSize * itemCount; }, getOffsetForIndexAndAlignment: function getOffsetForIndexAndAlignment(_ref4, index, align, scrollOffset, instanceProps, scrollbarSize) { var direction = _ref4.direction, height = _ref4.height, itemCount = _ref4.itemCount, itemSize = _ref4.itemSize, layout = _ref4.layout, width = _ref4.width; // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var size = isHorizontal ? width : height; var lastItemOffset = Math.max(0, itemCount * itemSize - size); var maxOffset = Math.min(lastItemOffset, index * itemSize); var minOffset = Math.max(0, index * itemSize - size + itemSize + scrollbarSize); if (align === 'smart') { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset; case 'end': return minOffset; case 'center': { // "Centered" offset is usually the average of the min and max. // But near the edges of the list, this doesn't hold true. var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(size / 2)) { return 0; // near the beginning } else if (middleOffset > lastItemOffset + Math.floor(size / 2)) { return lastItemOffset; // near the end } else { return middleOffset; } } case 'auto': default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } }, getStartIndexForOffset: function getStartIndexForOffset(_ref5, offset) { var itemCount = _ref5.itemCount, itemSize = _ref5.itemSize; return Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize))); }, getStopIndexForStartIndex: function getStopIndexForStartIndex(_ref6, startIndex, scrollOffset) { var direction = _ref6.direction, height = _ref6.height, itemCount = _ref6.itemCount, itemSize = _ref6.itemSize, layout = _ref6.layout, width = _ref6.width; // TODO Deprecate direction "horizontal" var isHorizontal = direction === 'horizontal' || layout === 'horizontal'; var offset = startIndex * itemSize; var size = isHorizontal ? width : height; var numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize); return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive )); }, initInstanceProps: function initInstanceProps(props) {// Noop }, shouldResetStyleCacheOnItemSizeChange: true, validateProps: function validateProps(_ref7) { var itemSize = _ref7.itemSize; if (process.env.NODE_ENV !== 'production') { if (typeof itemSize !== 'number') { throw Error('An invalid "itemSize" prop has been specified. ' + 'Value should be a number. ' + ("\"" + (itemSize === null ? 'null' : typeof itemSize) + "\" was specified.")); } } } }); // Pulled from react-compat // https://github.com/developit/preact-compat/blob/7c5de00e7c85e2ffd011bf3af02899b63f699d3a/src/index.js#L349 function shallowDiffers(prev, next) { for (var attribute in prev) { if (!(attribute in next)) { return true; } } for (var _attribute in next) { if (prev[_attribute] !== next[_attribute]) { return true; } } return false; } var _excluded = ["style"], _excluded2 = ["style"]; // It knows to compare individual style props and ignore the wrapper object. // See https://reactjs.org/docs/react-api.html#reactmemo function areEqual(prevProps, nextProps) { var prevStyle = prevProps.style, prevRest = _objectWithoutPropertiesLoose(prevProps, _excluded); var nextStyle = nextProps.style, nextRest = _objectWithoutPropertiesLoose(nextProps, _excluded2); return !shallowDiffers(prevStyle, nextStyle) && !shallowDiffers(prevRest, nextRest); } // It knows to compare individual style props and ignore the wrapper object. // See https://reactjs.org/docs/react-component.html#shouldcomponentupdate function shouldComponentUpdate(nextProps, nextState) { return !areEqual(this.props, nextProps) || shallowDiffers(this.state, nextState); } export { FixedSizeGrid, FixedSizeList, VariableSizeGrid, VariableSizeList, areEqual, shouldComponentUpdate }; //# sourceMappingURL=index.esm.js.map