UNPKG

react-flexigrid

Version:

A React table component designed to allow presenting millions of rows of data.

254 lines (212 loc) 8.93 kB
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;