UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

213 lines (212 loc) 9.61 kB
"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