inferno-window-infinite-loader
Version:
This is a port of the origin library written in React, InfiniteLoader component inspired by react-virtualized but for use with react-window
268 lines (214 loc) • 8.39 kB
JavaScript
;
var inferno = require('inferno');
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
// Credit: preact-compat - https://github.com/developit/preact-compat :)
function shallowDiffers(a, b) {
var i;
for (i in a) {
if (!(i in b)) {
return true;
}
}
for (i in b) {
if (a[i] !== b[i]) {
return true;
}
}
return false;
}
var PureComponent =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(PureComponent, _Component);
function PureComponent() {
return _Component.apply(this, arguments) || this;
}
var _proto = PureComponent.prototype;
_proto.shouldComponentUpdate = function shouldComponentUpdate(props, state) {
return shallowDiffers(this.props, props) || shallowDiffers(this.state, state);
};
return PureComponent;
}(inferno.Component);
function isRangeVisible(_ref) {
var lastRenderedStartIndex = _ref.lastRenderedStartIndex,
lastRenderedStopIndex = _ref.lastRenderedStopIndex,
startIndex = _ref.startIndex,
stopIndex = _ref.stopIndex;
return !(startIndex > lastRenderedStopIndex || stopIndex < lastRenderedStartIndex);
}
function scanForUnloadedRanges(_ref) {
var isItemLoaded = _ref.isItemLoaded,
itemCount = _ref.itemCount,
minimumBatchSize = _ref.minimumBatchSize,
startIndex = _ref.startIndex,
stopIndex = _ref.stopIndex;
var unloadedRanges = [];
var rangeStartIndex = null;
var rangeStopIndex = null;
for (var _index = startIndex; _index <= stopIndex; _index++) {
var loaded = isItemLoaded(_index);
if (!loaded) {
rangeStopIndex = _index;
if (rangeStartIndex === null) {
rangeStartIndex = _index;
}
} else if (rangeStopIndex !== null) {
unloadedRanges.push([rangeStartIndex, rangeStopIndex]);
rangeStartIndex = rangeStopIndex = null;
}
} // If :rangeStopIndex is not null it means we haven't ran out of unloaded rows.
// Scan forward to try filling our :minimumBatchSize.
if (rangeStopIndex !== null) {
var potentialStopIndex = Math.min(Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1), itemCount - 1);
for (var _index2 = rangeStopIndex + 1; _index2 <= potentialStopIndex; _index2++) {
if (!isItemLoaded(_index2)) {
rangeStopIndex = _index2;
} else {
break;
}
}
unloadedRanges.push([rangeStartIndex, rangeStopIndex]);
} // Check to see if our first range ended prematurely.
// In this case we should scan backwards to try filling our :minimumBatchSize.
if (unloadedRanges.length) {
var firstRange = unloadedRanges[0];
while (firstRange[1] - firstRange[0] + 1 < minimumBatchSize && firstRange[0] > 0) {
var _index3 = firstRange[0] - 1;
if (!isItemLoaded(_index3)) {
firstRange[0] = _index3;
} else {
break;
}
}
}
return unloadedRanges;
}
var InfiniteLoader =
/*#__PURE__*/
function (_PureComponent) {
_inheritsLoose(InfiniteLoader, _PureComponent);
function InfiniteLoader() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _PureComponent.call.apply(_PureComponent, [this].concat(args)) || this;
_this._lastRenderedStartIndex = -1;
_this._lastRenderedStopIndex = -1;
_this._listRef = void 0;
_this._memoizedUnloadedRanges = [];
_this._onItemsRendered = function (_ref) {
var visibleStartIndex = _ref.visibleStartIndex,
visibleStopIndex = _ref.visibleStopIndex;
if (process.env.NODE_ENV !== 'production') {
if (!Number.isInteger(visibleStartIndex) || !Number.isInteger(visibleStopIndex)) {
console.warn('Invalid onItemsRendered signature; please refer to InfiniteLoader documentation.');
}
if (typeof _this.props.loadMoreRows === 'function') {
console.warn('InfiniteLoader "loadMoreRows" prop has been renamed to "loadMoreItems".');
}
}
_this._lastRenderedStartIndex = visibleStartIndex;
_this._lastRenderedStopIndex = visibleStopIndex;
_this._ensureRowsLoaded(visibleStartIndex, visibleStopIndex);
};
_this._setRef = function (listRef) {
_this._listRef = listRef;
};
return _this;
}
var _proto = InfiniteLoader.prototype;
_proto.resetloadMoreItemsCache = function resetloadMoreItemsCache(autoReload) {
if (autoReload === void 0) {
autoReload = false;
}
this._memoizedUnloadedRanges = [];
if (autoReload) {
this._ensureRowsLoaded(this._lastRenderedStartIndex, this._lastRenderedStopIndex);
}
};
_proto.componentDidMount = function componentDidMount() {
if (process.env.NODE_ENV !== 'production') {
if (this._listRef == null) {
console.warn('Invalid list ref; please refer to InfiniteLoader documentation.');
}
}
};
_proto.render = function render() {
var children = this.props.children;
return children({
onItemsRendered: this._onItemsRendered,
ref: this._setRef
});
};
_proto._ensureRowsLoaded = function _ensureRowsLoaded(startIndex, stopIndex) {
var _this$props = this.props,
isItemLoaded = _this$props.isItemLoaded,
itemCount = _this$props.itemCount,
_this$props$minimumBa = _this$props.minimumBatchSize,
minimumBatchSize = _this$props$minimumBa === void 0 ? 10 : _this$props$minimumBa,
_this$props$threshold = _this$props.threshold,
threshold = _this$props$threshold === void 0 ? 15 : _this$props$threshold;
var unloadedRanges = scanForUnloadedRanges({
isItemLoaded: isItemLoaded,
itemCount: itemCount,
minimumBatchSize: minimumBatchSize,
startIndex: Math.max(0, startIndex - threshold),
stopIndex: Math.min(itemCount - 1, stopIndex + threshold)
}); // Avoid calling load-rows unless range has changed.
// This shouldn't be strictly necsesary, but is maybe nice to do.
if (this._memoizedUnloadedRanges.length !== unloadedRanges.length || this._memoizedUnloadedRanges.find(function (_ref2, index) {
var startIndex = _ref2[0],
stopIndex = _ref2[1];
return unloadedRanges[index][0] !== startIndex || unloadedRanges[index][1] !== stopIndex;
})) {
this._memoizedUnloadedRanges = unloadedRanges;
this._loadUnloadedRanges(unloadedRanges);
}
};
_proto._loadUnloadedRanges = function _loadUnloadedRanges(unloadedRanges) {
var _this2 = this;
// loadMoreRows was renamed to loadMoreItems in v1.0.3; will be removed in v2.0
var loadMoreItems = this.props.loadMoreItems || this.props.loadMoreRows;
unloadedRanges.forEach(function (_ref3) {
var startIndex = _ref3[0],
stopIndex = _ref3[1];
var promise = loadMoreItems(startIndex, stopIndex);
if (promise != null) {
promise.then(function () {
// Refresh the visible rows if any of them have just been loaded.
// Otherwise they will remain in their unloaded visual state.
if (isRangeVisible({
lastRenderedStartIndex: _this2._lastRenderedStartIndex,
lastRenderedStopIndex: _this2._lastRenderedStopIndex,
startIndex: startIndex,
stopIndex: stopIndex
})) {
// Handle an unmount while promises are still in flight.
if (_this2._listRef == null) {
return;
} // Resize cached row sizes for VariableSizeList,
// otherwise just re-render the list.
if (typeof _this2._listRef.resetAfterIndex === 'function') {
_this2._listRef.resetAfterIndex(startIndex, true);
} else {
// HACK reset temporarily cached item styles to force PureComponent to re-render.
// This is pretty gross, but I'm okay with it for now.
// Don't judge me.
if (typeof _this2._listRef._getItemStyleCache === 'function') {
_this2._listRef._getItemStyleCache(-1);
}
_this2._listRef.forceUpdate();
}
}
});
}
});
};
return InfiniteLoader;
}(PureComponent);
module.exports = InfiniteLoader;