UNPKG

@blueprintjs/table

Version:

Scalable interactive table component

372 lines 16.4 kB
"use strict"; /* * Copyright 2016 Palantir Technologies, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Grid = void 0; var tslib_1 = require("tslib"); var regions_1 = require("../regions"); var Classes = tslib_1.__importStar(require("./classes")); var rect_1 = require("./rect"); var utils_1 = require("./utils"); /** * This class manages the sizes of grid cells using arrays of individual row/column sizes. */ var Grid = /** @class */ (function () { /** * This constructor accumulates the heights and widths in `O(n)`, saving * time in later calculations. * * @param bleed - The number of rows/cols that we expand beyond the * viewport (on all sides). This helps avoid displaying an empty * viewport when the user scrolls quickly. */ function Grid(rowHeights, columnWidths, bleed, ghostHeight, ghostWidth) { if (bleed === void 0) { bleed = Grid.DEFAULT_BLEED; } if (ghostHeight === void 0) { ghostHeight = Grid.DEFAULT_GHOST_HEIGHT; } if (ghostWidth === void 0) { ghostWidth = Grid.DEFAULT_GHOST_WIDTH; } var _this = this; this.getCumulativeWidthBefore = function (index) { return index === 0 ? 0 : _this.getCumulativeWidthAt(index - 1); }; this.getCumulativeWidthAt = function (index) { if (_this.numCols === 0) { return _this.ghostWidth * index; } else if (index >= _this.numCols) { return _this.cumulativeColumnWidths[_this.numCols - 1] + _this.ghostWidth * (index - _this.numCols + 1); } else { return _this.cumulativeColumnWidths[index]; } }; this.getCumulativeHeightBefore = function (index) { return index === 0 ? 0 : _this.getCumulativeHeightAt(index - 1); }; this.getCumulativeHeightAt = function (index) { if (_this.numRows === 0) { return _this.ghostHeight * index; } else if (index >= _this.numRows) { return _this.cumulativeRowHeights[_this.numRows - 1] + _this.ghostHeight * (index - _this.numRows + 1); } else { return _this.cumulativeRowHeights[index]; } }; this.columnWidths = columnWidths; this.rowHeights = rowHeights; this.cumulativeColumnWidths = utils_1.Utils.accumulate(columnWidths); this.cumulativeRowHeights = utils_1.Utils.accumulate(rowHeights); this.numCols = columnWidths.length; this.numRows = rowHeights.length; this.bleed = bleed; this.ghostHeight = ghostHeight; this.ghostWidth = ghostWidth; } /** * Returns the `Rect` bounds of a cell in scrollpane client space. * * Scrollpane client coordinate space uses the origin of the scrollpane * client (the inside part that you're moving around). * * For example, let's say you're scrolling around a block of 1000 x 1000 * cells. Regardless where you've scrolled, the first cell is always at * 0,0 in scrollpane client space. the cell to the right of it is always * at, e.g., 100,0. */ Grid.prototype.getCellRect = function (rowIndex, columnIndex) { var height = this.rowHeights[rowIndex]; var top = this.cumulativeRowHeights[rowIndex] - height; var width = this.columnWidths[columnIndex]; var left = this.cumulativeColumnWidths[columnIndex] - width; return new rect_1.Rect(left, top, width, height); }; /** * Returns the `Rect` bounds of a cell in scrollpane client space. * * If the cell is beyond the bounds of the user-defined table cells, it is * considered a "ghost" cell. If a width/height is not defined for that * row/column, we use the default width/height. */ Grid.prototype.getGhostCellRect = function (rowIndex, columnIndex) { var left = 0; var top = 0; var width = 0; var height = 0; if (rowIndex >= this.rowHeights.length) { height = this.ghostHeight; top = this.getHeight() + this.ghostHeight * (rowIndex - this.numRows); } else { height = this.rowHeights[rowIndex]; top = this.cumulativeRowHeights[rowIndex] - height; } if (columnIndex >= this.columnWidths.length) { width = this.ghostWidth; left = this.getWidth() + this.ghostWidth * (columnIndex - this.numCols); } else { width = this.columnWidths[columnIndex]; left = this.cumulativeColumnWidths[columnIndex] - width; } return new rect_1.Rect(left, top, width, height); }; /** * Returns the `Rect` with the base coordinate and height of the specified row. */ Grid.prototype.getRowRect = function (rowIndex) { var height = this.rowHeights[rowIndex]; var top = this.cumulativeRowHeights[rowIndex] - height; return new rect_1.Rect(0, top, this.getWidth(), height); }; /** * Returns the `Rect` with the base coordinate and width of the specified column. */ Grid.prototype.getColumnRect = function (columnIndex) { var width = this.columnWidths[columnIndex]; var left = this.cumulativeColumnWidths[columnIndex] - width; return new rect_1.Rect(left, 0, width, this.getHeight()); }; /** * Returns the total width of the entire grid */ Grid.prototype.getWidth = function () { return this.numCols === 0 ? 0 : this.cumulativeColumnWidths[this.numCols - 1]; }; /** * Returns the total width of the entire grid */ Grid.prototype.getHeight = function () { return this.numRows === 0 ? 0 : this.cumulativeRowHeights[this.numRows - 1]; }; /** * Returns the `Rect` bounds of entire grid */ Grid.prototype.getRect = function () { return new rect_1.Rect(0, 0, this.getWidth(), this.getHeight()); }; /** * Maps each cell that intersects with the given `Rect` argument. The * indices of iteration are extended in both directions by the integer * `bleed` class property, then are clamped between 0 and the number of * rows/columns. * * Uses a binary search for each of the 4 edges of the bounds, resulting * in a runtime of `O(log(rows) + log(cols))` plus the `O(irows * icols)` * iteration of intersecting cells. */ Grid.prototype.mapCellsInRect = function (rect, callback) { var results = []; if (rect == null) { return results; } var _a = this.getRowIndicesInRect({ rect: rect }), rowIndexStart = _a.rowIndexStart, rowIndexEnd = _a.rowIndexEnd; var _b = this.getColumnIndicesInRect(rect), columnIndexStart = _b.columnIndexStart, columnIndexEnd = _b.columnIndexEnd; for (var rowIndex = rowIndexStart; rowIndex <= rowIndexEnd; rowIndex++) { for (var columnIndex = columnIndexStart; columnIndex <= columnIndexEnd; columnIndex++) { results.push(callback(rowIndex, columnIndex)); } } return results; }; /** * Maps each row that intersects with the given `Rect` argument. * * See Grid.mapCellsInRect for more details. */ Grid.prototype.mapRowsInRect = function (rect, callback) { var results = []; if (rect == null) { return results; } var _a = this.getRowIndicesInRect({ rect: rect }), rowIndexStart = _a.rowIndexStart, rowIndexEnd = _a.rowIndexEnd; for (var rowIndex = rowIndexStart; rowIndex <= rowIndexEnd; rowIndex++) { results.push(callback(rowIndex)); } return results; }; /** * Maps each column that intersects with the given `Rect` argument. * * See Grid.mapCellsInRect for more details. */ Grid.prototype.mapColumnsInRect = function (rect, callback) { var results = []; if (rect == null) { return results; } var _a = this.getColumnIndicesInRect(rect), columnIndexStart = _a.columnIndexStart, columnIndexEnd = _a.columnIndexEnd; for (var columnIndex = columnIndexStart; columnIndex <= columnIndexEnd; columnIndex++) { results.push(callback(columnIndex)); } return results; }; /** * Returns the start and end indices of rows that intersect with the given * `Rect` argument. */ Grid.prototype.getRowIndicesInRect = function (options) { var rect = options.rect, _a = options.includeGhostCells, includeGhostCells = _a === void 0 ? false : _a, _b = options.columnHeaderHeight, columnHeaderHeight = _b === void 0 ? 0 : _b, _c = options.limit, limit = _c === void 0 ? Grid.DEFAULT_MAX_ROWS : _c; if (rect == null) { return { rowIndexEnd: 0, rowIndexStart: 0 }; } var searchEnd = includeGhostCells ? Math.max(this.numRows, Grid.DEFAULT_MAX_ROWS) : this.numRows; var _d = rect.top, top = _d === void 0 ? 0 : _d, height = rect.height; var _e = this.getIndicesInInterval(top, top + (height - columnHeaderHeight), searchEnd, !includeGhostCells, this.getCumulativeHeightAt), start = _e.start, end = _e.end; var rowIndexEnd = limit > 0 && end - start > limit ? start + limit : end; return { rowIndexEnd: rowIndexEnd, rowIndexStart: start, }; }; /** * Returns the start and end indices of columns that intersect with the * given `Rect` argument. */ Grid.prototype.getColumnIndicesInRect = function (rect, includeGhostCells, limit) { if (includeGhostCells === void 0) { includeGhostCells = false; } if (limit === void 0) { limit = Grid.DEFAULT_MAX_COLUMNS; } if (rect == null) { return { columnIndexEnd: 0, columnIndexStart: 0 }; } var searchEnd = includeGhostCells ? Math.max(this.numCols, Grid.DEFAULT_MAX_COLUMNS) : this.numCols; var _a = rect.left, left = _a === void 0 ? 0 : _a, width = rect.width; var _b = this.getIndicesInInterval(left, left + width, searchEnd, !includeGhostCells, this.getCumulativeWidthAt), start = _b.start, end = _b.end; var columnIndexEnd = limit > 0 && end - start > limit ? start + limit : end; return { columnIndexEnd: columnIndexEnd, columnIndexStart: start, }; }; Grid.prototype.isGhostIndex = function (rowIndex, columnIndex) { return rowIndex >= this.numRows || columnIndex >= this.numCols; }; Grid.prototype.isGhostColumn = function (columnIndex) { return columnIndex >= this.numCols; }; Grid.prototype.getExtremaClasses = function (rowIndex, columnIndex, rowEnd, columnEnd) { if (rowIndex === rowEnd && columnIndex === columnEnd) { return [Classes.TABLE_LAST_IN_COLUMN, Classes.TABLE_LAST_IN_ROW]; } if (rowIndex === rowEnd) { return [Classes.TABLE_LAST_IN_COLUMN]; } if (columnIndex === columnEnd) { return [Classes.TABLE_LAST_IN_ROW]; } return []; }; Grid.prototype.getRegionStyle = function (region) { var cardinality = regions_1.Regions.getRegionCardinality(region); switch (cardinality) { case regions_1.RegionCardinality.CELLS: { var _a = region.rows, rowStart = _a[0], rowEnd = _a[1]; var _b = region.cols, colStart = _b[0], colEnd = _b[1]; // if the region is outside the bounds of the table, don't display it if (this.isGhostIndex(rowStart, colStart) || this.isGhostIndex(rowEnd, colEnd)) { return { display: "none" }; } var cellRect0 = this.getCellRect(rowStart, colStart); var cellRect1 = this.getCellRect(rowEnd, colEnd); var offsetLeft = colStart === 0 ? 0 : 1; var offsetTop = rowStart === 0 ? 0 : 1; var rect = cellRect0.union(cellRect1); rect.height += offsetTop; rect.left -= offsetLeft; rect.width += offsetLeft; rect.top -= offsetTop; return tslib_1.__assign(tslib_1.__assign({}, rect.style()), { display: "block" }); } case regions_1.RegionCardinality.FULL_COLUMNS: { var _c = region.cols, colStart = _c[0], colEnd = _c[1]; // if the region is outside the bounds of the table, don't display it if (this.isGhostIndex(0, colStart) || this.isGhostIndex(0, colEnd)) { return { display: "none" }; } var cellRect0 = this.getCellRect(0, colStart); var cellRect1 = this.getCellRect(0, colEnd); var rect = cellRect0.union(cellRect1); var offsetLeft = colStart === 0 ? 0 : 1; return { bottom: 0, display: "block", left: rect.left - offsetLeft, top: 0, width: rect.width + offsetLeft, }; } case regions_1.RegionCardinality.FULL_ROWS: { var _d = region.rows, rowStart = _d[0], rowEnd = _d[1]; // if the region is outside the bounds of the table, don't display it if (this.isGhostIndex(rowStart, 0) || this.isGhostIndex(rowEnd, 0)) { return { display: "none" }; } var cellRect0 = this.getCellRect(rowStart, 0); var cellRect1 = this.getCellRect(rowEnd, 0); var rect = cellRect0.union(cellRect1); var offsetTop = rowStart === 0 ? 0 : 1; return { display: "block", height: rect.height + offsetTop, left: 0, right: 0, top: rect.top - offsetTop, }; } case regions_1.RegionCardinality.FULL_TABLE: return { bottom: 0, display: "block", left: 0, right: 0, top: 0, }; default: return { display: "none" }; } }; Grid.prototype.getIndicesInInterval = function (min, max, count, useEndBleed, lookup) { var start = utils_1.Utils.binarySearch(min, count - 1, lookup); var end = utils_1.Utils.binarySearch(max, count - 1, lookup); // correct exact pixel alignment if (start >= 0 && min === lookup(start)) { start += 1; } // apply bounded bleeds start = Math.max(0, start - this.bleed); if (useEndBleed) { end = Math.min(count - 1, end + this.bleed); } else { end = Math.min(count - 1, end); } return { end: end, start: start }; }; Grid.DEFAULT_BLEED = 3; Grid.DEFAULT_MAX_COLUMNS = 50; Grid.DEFAULT_MAX_ROWS = 200; Grid.DEFAULT_GHOST_HEIGHT = 20; Grid.DEFAULT_GHOST_WIDTH = 150; // Used on first render of the top-left and top quadrants to avoid collapsing // their heights to 0. originally defined in headers/_common.scss Grid.MIN_COLUMN_HEADER_HEIGHT = 30; // Used on first render of the top-left and left quadrants to avoid collapsing // their widths to 0. originally defined in headers/_common.scss Grid.MIN_ROW_HEADER_WIDTH = 30; return Grid; }()); exports.Grid = Grid; //# sourceMappingURL=grid.js.map