react-konva-grid
Version:
Canvas grid to render large set of tabular data with virtualization.
268 lines • 9.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.cellIndentifier = exports.getEstimatedTotalWidth = exports.getEstimatedTotalHeight = exports.getItemMetadata = exports.getColumnWidth = exports.getRowHeight = exports.getColumnOffset = exports.getRowOffset = exports.itemKey = exports.getBoundedCells = exports.getColumnStopIndexForStartIndex = exports.getColumnStartIndexForOffset = exports.getRowStopIndexForStartIndex = exports.getRowStartIndexForOffset = void 0;
exports.getRowStartIndexForOffset = ({ rowHeight, columnWidth, rowCount, columnCount, instanceProps, offset, }) => {
return findNearestItem({
itemType: "row",
rowHeight,
columnWidth,
rowCount,
columnCount,
instanceProps,
offset,
});
};
exports.getRowStopIndexForStartIndex = ({ startIndex, rowCount, rowHeight, columnWidth, scrollTop, containerHeight, instanceProps, }) => {
const itemMetadata = exports.getItemMetadata({
itemType: "row",
rowHeight,
columnWidth,
index: startIndex,
instanceProps,
});
const maxOffset = scrollTop + containerHeight;
let offset = itemMetadata.offset + itemMetadata.size;
let stopIndex = startIndex;
while (stopIndex < rowCount - 1 && offset < maxOffset) {
stopIndex++;
offset += exports.getItemMetadata({
itemType: "row",
rowHeight,
columnWidth,
index: stopIndex,
instanceProps,
}).size;
}
return stopIndex;
};
exports.getColumnStartIndexForOffset = ({ rowHeight, columnWidth, rowCount, columnCount, instanceProps, offset, }) => {
return findNearestItem({
itemType: "column",
rowHeight,
columnWidth,
rowCount,
columnCount,
instanceProps,
offset,
});
};
exports.getColumnStopIndexForStartIndex = ({ startIndex, rowHeight, columnWidth, instanceProps, containerWidth, scrollLeft, columnCount, }) => {
const itemMetadata = exports.getItemMetadata({
itemType: "column",
index: startIndex,
rowHeight,
columnWidth,
instanceProps,
});
const maxOffset = scrollLeft + containerWidth;
let offset = itemMetadata.offset + itemMetadata.size;
let stopIndex = startIndex;
while (stopIndex < columnCount - 1 && offset < maxOffset) {
stopIndex++;
offset += exports.getItemMetadata({
itemType: "column",
rowHeight,
columnWidth,
index: stopIndex,
instanceProps,
}).size;
}
return stopIndex;
};
exports.getBoundedCells = (area) => {
const cells = new Set();
if (!area)
return cells;
const { top, bottom, left, right } = area;
for (let i = top; i <= bottom; i++) {
for (let j = left; j <= right; j++) {
cells.add(exports.cellIndentifier(i, j));
}
}
return cells;
};
exports.itemKey = ({ rowIndex, columnIndex }) => `${rowIndex}:${columnIndex}`;
exports.getRowOffset = ({ index, rowHeight, columnWidth, instanceProps, }) => {
return exports.getItemMetadata({
itemType: "row",
index,
rowHeight,
columnWidth,
instanceProps,
}).offset;
};
exports.getColumnOffset = ({ index, rowHeight, columnWidth, instanceProps, }) => {
return exports.getItemMetadata({
itemType: "column",
index,
rowHeight,
columnWidth,
instanceProps,
}).offset;
};
exports.getRowHeight = (index, instanceProps) => {
return instanceProps.rowMetadataMap[index].size;
};
exports.getColumnWidth = (index, instanceProps) => {
return instanceProps.columnMetadataMap[index].size;
};
exports.getItemMetadata = ({ itemType, index, rowHeight, columnWidth, instanceProps, }) => {
let itemMetadataMap, itemSize, lastMeasuredIndex;
if (itemType === "column") {
itemMetadataMap = instanceProps.columnMetadataMap;
itemSize = columnWidth;
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
}
else {
itemMetadataMap = instanceProps.rowMetadataMap;
itemSize = rowHeight;
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
}
if (index > lastMeasuredIndex) {
let offset = 0;
if (lastMeasuredIndex >= 0) {
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
offset = itemMetadata.offset + itemMetadata.size;
}
for (let i = lastMeasuredIndex + 1; i <= index; i++) {
let size = itemSize(i);
itemMetadataMap[i] = {
offset,
size,
};
offset += size;
}
if (itemType === "column") {
instanceProps.lastMeasuredColumnIndex = index;
}
else {
instanceProps.lastMeasuredRowIndex = index;
}
}
return itemMetadataMap[index];
};
const findNearestItem = ({ itemType, rowHeight, columnWidth, rowCount, columnCount, instanceProps, offset, }) => {
let itemMetadataMap, lastMeasuredIndex;
if (itemType === "column") {
itemMetadataMap = instanceProps.columnMetadataMap;
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
}
else {
itemMetadataMap = instanceProps.rowMetadataMap;
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
}
const 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,
rowHeight,
columnWidth,
instanceProps,
high: lastMeasuredIndex,
low: 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,
rowHeight,
rowCount,
columnCount,
columnWidth,
instanceProps,
index: Math.max(0, lastMeasuredIndex),
offset,
});
}
};
const findNearestItemBinarySearch = ({ itemType, rowHeight, columnWidth, instanceProps, high, low, offset, }) => {
while (low <= high) {
const middle = low + Math.floor((high - low) / 2);
const currentOffset = exports.getItemMetadata({
itemType,
rowHeight,
columnWidth,
index: 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;
}
};
const findNearestItemExponentialSearch = ({ itemType, rowHeight, columnWidth, rowCount, columnCount, instanceProps, index, offset, }) => {
const itemCount = itemType === "column" ? columnCount : rowCount;
let interval = 1;
while (index < itemCount &&
exports.getItemMetadata({
itemType,
rowHeight,
columnWidth,
index,
instanceProps,
}).offset < offset) {
index += interval;
interval *= 2;
}
return findNearestItemBinarySearch({
itemType,
rowHeight,
columnWidth,
instanceProps,
high: Math.min(index, itemCount - 1),
low: Math.floor(index / 2),
offset,
});
};
exports.getEstimatedTotalHeight = (rowCount, estimatedRowHeight, instanceProps) => {
let totalSizeOfMeasuredRows = 0;
let { lastMeasuredRowIndex, rowMetadataMap } = instanceProps;
// 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) {
const itemMetadata = rowMetadataMap[lastMeasuredRowIndex];
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = rowCount - lastMeasuredRowIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedRowHeight;
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
};
exports.getEstimatedTotalWidth = (columnCount, estimatedColumnWidth, instanceProps) => {
let totalSizeOfMeasuredRows = 0;
let { lastMeasuredColumnIndex, columnMetadataMap } = instanceProps;
// 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) {
const itemMetadata = columnMetadataMap[lastMeasuredColumnIndex];
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = columnCount - lastMeasuredColumnIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedColumnWidth;
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
};
/* Create a stringified cell identifier */
exports.cellIndentifier = (rowIndex, columnIndex) => [rowIndex, columnIndex].toString();
//# sourceMappingURL=helpers.js.map