react-flexigrid
Version:
A React table component designed to allow presenting millions of rows of data.
254 lines (212 loc) • 8.93 kB
JavaScript
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import PrefixIntervalTree from './struct/PrefixIntervalTree';
import { clamp } from './utils';
var BUFFER_COLUMNS = 5;
var NO_COLUMNS_SCROLL_RESULT = {
index: 0,
offset: 0,
position: 0,
contentWidth: 0
};
var FlexiGridHorizontalScrollHelper = function () {
function FlexiGridHorizontalScrollHelper(columnCount, viewportWidth, getColumnWidth) {
var _this = this;
_classCallCheck(this, FlexiGridHorizontalScrollHelper);
this.getColumnPosition = function (columnIndex) {
_this.updateColumnWidth(columnIndex);
return _this.columnOffsets.sumUntil(columnIndex);
};
this.columnCount = columnCount;
this.viewportWidth = viewportWidth;
this.getColumnWidth = getColumnWidth;
this.cachedWidths = [];
for (var i = 0; i < this.columnCount; i += 1) {
this.cachedWidths[i] = this.getColumnWidth(i);
}
this.columnOffsets = new PrefixIntervalTree(this.cachedWidths);
this.contentWidth = this.cachedWidths.reduce(function (memo, width) {
return memo + width;
}, 0);
this.position = 0;
this.updateWidthsInViewport(0, 0);
}
FlexiGridHorizontalScrollHelper.prototype.setViewportWidth = function setViewportWidth(viewportWidth) {
this.viewportWidth = viewportWidth;
};
FlexiGridHorizontalScrollHelper.prototype.setColumnWidthGetter = function setColumnWidthGetter(getColumnWidth) {
this.getColumnWidth = getColumnWidth;
};
FlexiGridHorizontalScrollHelper.prototype.getContentWidth = function getContentWidth() {
return this.contentWidth;
};
FlexiGridHorizontalScrollHelper.prototype.updateWidthsInViewport = function updateWidthsInViewport(firstColumnIndex, firstColumnOffset) {
var width = firstColumnOffset;
var index = firstColumnIndex;
while (width <= this.viewportWidth && index < this.columnCount) {
this.updateColumnWidth(index);
width += this.cachedWidths[index];
index += 1;
}
};
FlexiGridHorizontalScrollHelper.prototype.updateWidthsLeftViewport = function updateWidthsLeftViewport(firstColumnIndex) {
var index = firstColumnIndex - 1;
while (index >= 0 && index >= firstColumnIndex - BUFFER_COLUMNS) {
var delta = this.updateColumnWidth(index);
this.position += delta;
index -= 1;
}
};
FlexiGridHorizontalScrollHelper.prototype.updateColumnWidth = function updateColumnWidth(columnIndex) {
if (columnIndex < 0 || columnIndex >= this.columnCount) {
return 0;
}
var oldWidth = this.cachedWidths[columnIndex];
var newWidth = this.getColumnWidth(columnIndex);
if (newWidth !== oldWidth) {
var delta = newWidth - oldWidth;
this.columnOffsets.set(columnIndex, newWidth);
this.cachedWidths[columnIndex] = newWidth;
this.contentWidth += delta;
return delta;
}
return 0;
};
FlexiGridHorizontalScrollHelper.prototype.getColumnAtEndPosition = function getColumnAtEndPosition(columnIndex) {
this.updateColumnWidth(columnIndex);
var currentColumnIndex = columnIndex;
var width = this.cachedWidths[currentColumnIndex];
while (width < this.viewportWidth && currentColumnIndex >= 0) {
currentColumnIndex -= 1;
if (currentColumnIndex >= 0) {
this.updateColumnWidth(currentColumnIndex);
width += this.cachedWidths[currentColumnIndex];
}
}
var position = this.columnOffsets.sumTo(columnIndex) - this.viewportWidth;
if (position < 0) {
position = 0;
}
return position;
};
FlexiGridHorizontalScrollHelper.prototype.scrollTo = function scrollTo(position) {
if (this.columnCount === 0) {
return NO_COLUMNS_SCROLL_RESULT;
}
if (position <= 0) {
// If position less than or equal to 0
// first column should be fully visible on left.
this.position = 0;
this.updateWidthsInViewport(0, 0);
return {
index: 0,
offset: 0,
position: this.position,
contentWidth: this.contentWidth
};
} else if (position >= this.contentWidth - this.viewportWidth) {
// If position is equal to or greater than max scroll value, we need
// to make sure to have bottom border of last column visible.
var columnIndex = this.columnCount - 1;
position = this.getColumnAtEndPosition(columnIndex); // eslint-disable-line
}
this.position = position;
var firstColumnIndex = this.columnOffsets.greatestLowerBound(position);
firstColumnIndex = clamp(firstColumnIndex, 0, Math.max(this.columnCount - 1, 0));
var firstColumnPosition = this.columnOffsets.sumUntil(firstColumnIndex);
var firstColumnOffset = firstColumnPosition - this.position;
this.updateWidthsInViewport(firstColumnIndex, firstColumnOffset);
this.updateWidthsLeftViewport(firstColumnIndex);
return {
index: firstColumnIndex,
offset: firstColumnOffset,
position: this.position,
contentWidth: this.contentWidth
};
};
FlexiGridHorizontalScrollHelper.prototype.scrollBy = function scrollBy(delta) {
if (this.columnCount === 0) {
return NO_COLUMNS_SCROLL_RESULT;
}
var firstColumnIndex = this.columnOffsets.greatestLowerBound(this.position);
firstColumnIndex = clamp(firstColumnIndex, 0, Math.max(this.columnCount - 1, 0));
var firstColumnPosition = this.columnOffsets.sumUntil(firstColumnIndex);
var columnIndex = firstColumnIndex;
var position = this.position;
var columnWidthChange = this.updateColumnWidth(columnIndex);
if (firstColumnPosition !== 0) {
position += columnWidthChange;
}
var visibleColumnWidth = this.cachedWidths[columnIndex] - (position - firstColumnPosition);
if (delta >= 0) {
while (delta > 0 && columnIndex < this.columnCount) {
if (delta < visibleColumnWidth) {
position += delta;
delta = 0; // eslint-disable-line
} else {
delta -= visibleColumnWidth; // eslint-disable-line
position += visibleColumnWidth;
columnIndex += 1;
}
if (columnIndex < this.columnCount) {
this.updateColumnWidth(columnIndex);
visibleColumnWidth = this.cachedWidths[columnIndex];
}
}
} else if (delta < 0) {
delta = -delta; // eslint-disable-line
var invisibleColumnWidth = this.cachedWidths[columnIndex] - visibleColumnWidth;
while (delta > 0 && columnIndex >= 0) {
if (delta < invisibleColumnWidth) {
position -= delta;
delta = 0; // eslint-disable-line
} else {
position -= invisibleColumnWidth;
delta -= invisibleColumnWidth; // eslint-disable-line
columnIndex -= 1;
}
if (columnIndex >= 0) {
var change = this.updateColumnWidth(columnIndex);
invisibleColumnWidth = this.cachedWidths[columnIndex];
position += change;
}
}
}
var maxPosition = this.contentWidth - this.viewportWidth;
position = clamp(position, 0, maxPosition);
this.position = position;
firstColumnIndex = this.columnOffsets.greatestLowerBound(position);
firstColumnIndex = clamp(firstColumnIndex, 0, Math.max(this.columnCount - 1, 0));
firstColumnPosition = this.columnOffsets.sumUntil(firstColumnIndex);
var firstColumnOffset = firstColumnPosition - position;
this.updateWidthsInViewport(firstColumnIndex, firstColumnOffset);
this.updateWidthsLeftViewport(firstColumnIndex);
return {
index: firstColumnIndex,
offset: firstColumnOffset,
position: this.position,
contentWidth: this.contentWidth
};
};
FlexiGridHorizontalScrollHelper.prototype.scrollToColumn = function scrollToColumn(columnIndex) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var targetIndex = clamp(columnIndex, 0, Math.max(this.columnCount - 1, 0));
var targetOffset = clamp(offset, -this.cachedWidths[targetIndex], 0);
var position = this.columnOffsets.sumUntil(targetIndex);
return this.scrollTo(position - targetOffset);
};
FlexiGridHorizontalScrollHelper.prototype.scrollColumnIntoView = function scrollColumnIntoView(columnIndex) {
var targetIndex = clamp(columnIndex, 0, Math.max(this.columnCount - 1, 0));
this.updateColumnWidth(targetIndex);
var startPosition = this.columnOffsets.sumUntil(targetIndex);
var endPosition = startPosition + this.cachedWidths[targetIndex];
if (startPosition < this.position) {
return this.scrollTo(startPosition);
} else if (this.position + this.viewportWidth < endPosition) {
var position = this.getColumnAtEndPosition(targetIndex);
return this.scrollTo(position);
}
return this.scrollTo(this.position);
};
return FlexiGridHorizontalScrollHelper;
}();
export default FlexiGridHorizontalScrollHelper;