fastlion-amis
Version:
一种MIS页面生成工具
213 lines (212 loc) • 9.61 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* Forked from react-virtualized 💖 */
var constants_1 = require("./constants");
var SizeAndPositionManager = /** @class */ (function () {
function SizeAndPositionManager(_a) {
var itemCount = _a.itemCount, itemSizeGetter = _a.itemSizeGetter, estimatedItemSize = _a.estimatedItemSize;
this.itemSizeGetter = itemSizeGetter;
this.itemCount = itemCount;
this.estimatedItemSize = estimatedItemSize;
// Cache of size and position data for items, mapped by item index.
this.itemSizeAndPositionData = {};
// Measurements for items up to this index can be trusted; items afterward should be estimated.
this.lastMeasuredIndex = -1;
}
SizeAndPositionManager.prototype.updateConfig = function (_a) {
var itemCount = _a.itemCount, itemSizeGetter = _a.itemSizeGetter, estimatedItemSize = _a.estimatedItemSize;
if (itemCount != null) {
this.itemCount = itemCount;
}
if (estimatedItemSize != null) {
this.estimatedItemSize = estimatedItemSize;
}
if (itemSizeGetter != null) {
this.itemSizeGetter = itemSizeGetter;
}
};
SizeAndPositionManager.prototype.getLastMeasuredIndex = function () {
return this.lastMeasuredIndex;
};
/**
* This method returns the size and position for the item at the specified index.
* It just-in-time calculates (or used cached values) for items leading up to the index.
*/
SizeAndPositionManager.prototype.getSizeAndPositionForIndex = function (index) {
if (index < 0 || index >= this.itemCount) {
throw Error("Requested index " + index + " is outside of range 0.." + this.itemCount);
}
if (index > this.lastMeasuredIndex) {
var lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem();
var offset = lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size;
for (var i = this.lastMeasuredIndex + 1; i <= index; i++) {
var size = this.itemSizeGetter(i);
if (size == null || isNaN(size)) {
throw Error("Invalid size returned for index " + i + " of value " + size);
}
this.itemSizeAndPositionData[i] = {
offset: offset,
size: size
};
offset += size;
}
this.lastMeasuredIndex = index;
}
return this.itemSizeAndPositionData[index];
};
SizeAndPositionManager.prototype.getSizeAndPositionOfLastMeasuredItem = function () {
return this.lastMeasuredIndex >= 0
? this.itemSizeAndPositionData[this.lastMeasuredIndex]
: { offset: 0, size: 0 };
};
/**
* Total size of all items being measured.
* This value will be completedly estimated initially.
* As items as measured the estimate will be updated.
*/
SizeAndPositionManager.prototype.getTotalSize = function () {
var lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem();
return (lastMeasuredSizeAndPosition.offset +
lastMeasuredSizeAndPosition.size +
(this.itemCount - this.lastMeasuredIndex - 1) * this.estimatedItemSize);
};
/**
* Determines a new offset that ensures a certain item is visible, given the alignment.
*
* @param align Desired alignment within container; one of "start" (default), "center", or "end"
* @param containerSize Size (width or height) of the container viewport
* @return Offset to use to ensure the specified item is visible
*/
SizeAndPositionManager.prototype.getUpdatedOffsetForIndex = function (_a) {
var _b = _a.align, align = _b === void 0 ? constants_1.ALIGNMENT.START : _b, containerSize = _a.containerSize, currentOffset = _a.currentOffset, targetIndex = _a.targetIndex;
if (containerSize <= 0) {
return 0;
}
var datum = this.getSizeAndPositionForIndex(targetIndex);
var maxOffset = datum.offset;
var minOffset = maxOffset - containerSize + datum.size;
var idealOffset;
switch (align) {
case constants_1.ALIGNMENT.END:
idealOffset = minOffset;
break;
case constants_1.ALIGNMENT.CENTER:
idealOffset = maxOffset - (containerSize - datum.size) / 2;
break;
case constants_1.ALIGNMENT.START:
idealOffset = maxOffset;
break;
default:
idealOffset = Math.max(minOffset, Math.min(maxOffset, currentOffset));
}
var totalSize = this.getTotalSize();
return Math.max(0, Math.min(totalSize - containerSize, idealOffset));
};
SizeAndPositionManager.prototype.getVisibleRange = function (_a) {
var containerSize = _a.containerSize, offset = _a.offset, overscanCount = _a.overscanCount;
var totalSize = this.getTotalSize();
if (totalSize === 0) {
return {};
}
var maxOffset = offset + containerSize;
var start = this.findNearestItem(offset);
if (typeof start === 'undefined') {
throw Error("Invalid offset " + offset + " specified");
}
var datum = this.getSizeAndPositionForIndex(start);
offset = datum.offset + datum.size;
var stop = start;
while (offset < maxOffset && stop < this.itemCount - 1) {
stop++;
offset += this.getSizeAndPositionForIndex(stop).size;
}
if (overscanCount) {
start = Math.max(0, start - overscanCount);
stop = Math.min(stop + overscanCount, this.itemCount - 1);
}
return {
start: start,
stop: stop
};
};
/**
* Clear all cached values for items after the specified index.
* This method should be called for any item that has changed its size.
* It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionForIndex() is called.
*/
SizeAndPositionManager.prototype.resetItem = function (index) {
this.lastMeasuredIndex = Math.min(this.lastMeasuredIndex, index - 1);
};
/**
* Searches for the item (index) nearest the specified offset.
*
* If no exact match is found the next lowest item index will be returned.
* This allows partially visible items (with offsets just before/above the fold) to be visible.
*/
SizeAndPositionManager.prototype.findNearestItem = function (offset) {
if (isNaN(offset)) {
throw Error("Invalid offset " + offset + " specified");
}
// Our search algorithms find the nearest match at or below the specified offset.
// So make sure the offset is at least 0 or no match will be found.
offset = Math.max(0, offset);
var lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem();
var lastMeasuredIndex = Math.max(0, this.lastMeasuredIndex);
if (lastMeasuredSizeAndPosition.offset >= offset) {
// If we've already measured items within this range just use a binary search as it's faster.
return this.binarySearch({
high: lastMeasuredIndex,
low: 0,
offset: 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 this.exponentialSearch({
index: lastMeasuredIndex,
offset: offset
});
}
};
SizeAndPositionManager.prototype.binarySearch = function (_a) {
var low = _a.low, high = _a.high, offset = _a.offset;
var middle = 0;
var currentOffset = 0;
while (low <= high) {
middle = low + Math.floor((high - low) / 2);
currentOffset = this.getSizeAndPositionForIndex(middle).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;
}
return 0;
};
SizeAndPositionManager.prototype.exponentialSearch = function (_a) {
var index = _a.index, offset = _a.offset;
var interval = 1;
while (index < this.itemCount &&
this.getSizeAndPositionForIndex(index).offset < offset) {
index += interval;
interval *= 2;
}
return this.binarySearch({
high: Math.min(index, this.itemCount - 1),
low: Math.floor(index / 2),
offset: offset
});
};
return SizeAndPositionManager;
}());
exports.default = SizeAndPositionManager;
//# sourceMappingURL=./components/virtual-list/SizeAndPositionManager.js.map