@blueprintjs/table
Version:
Scalable interactive table component
201 lines • 9.71 kB
JavaScript
/*
* Copyright 2017 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.
*/
import { __assign, __extends } from "tslib";
import classNames from "classnames";
import * as React from "react";
import { AbstractComponent, Utils as CoreUtils } from "@blueprintjs/core";
import { emptyCellRenderer } from "./cell/cell";
import { Batcher } from "./common/batcher";
import { FocusMode } from "./common/cellTypes";
import * as Classes from "./common/classes";
import { Rect } from "./common/rect";
import { RenderMode } from "./common/renderMode";
var SHALLOW_COMPARE_DENYLIST = ["viewportRect"];
/**
* We don't want to reset the batcher when this set of keys changes. Any other
* changes should reset the batcher's internal cache.
*/
var BATCHER_RESET_PROP_KEYS_DENYLIST = [
"columnIndexEnd",
"columnIndexStart",
"rowIndexEnd",
"rowIndexStart",
];
var TableBodyCells = /** @class */ (function (_super) {
__extends(TableBodyCells, _super);
function TableBodyCells() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.batcher = new Batcher();
/**
* Set this flag to true in componentDidUpdate() when we call forceUpdate() to avoid an extra
* unnecessary update cycle.
*/
_this.didForceUpdate = false;
// Cell renderers
// ==============
_this.renderNewCell = function (rowIndex, columnIndex) {
var _a = _this.props, columnIndexEnd = _a.columnIndexEnd, grid = _a.grid, rowIndexEnd = _a.rowIndexEnd;
var extremaClasses = grid.getExtremaClasses(rowIndex, columnIndex, rowIndexEnd, columnIndexEnd);
var isGhost = grid.isGhostIndex(rowIndex, columnIndex);
return _this.renderCell(rowIndex, columnIndex, extremaClasses, isGhost);
};
_this.renderCell = function (rowIndex, columnIndex, extremaClasses, isGhost) {
var _a;
var _b = _this.props, cellRenderer = _b.cellRenderer, loading = _b.loading, grid = _b.grid;
var baseCell = isGhost ? emptyCellRenderer() : cellRenderer(rowIndex, columnIndex);
// cellRenderer still may return null
baseCell = baseCell == null ? emptyCellRenderer() : baseCell;
var className = classNames(cellClassNames(rowIndex, columnIndex), extremaClasses, (_a = {},
_a[Classes.TABLE_CELL_GHOST] = isGhost,
_a[Classes.TABLE_CELL_LEDGER_ODD] = rowIndex % 2 === 1,
_a[Classes.TABLE_CELL_LEDGER_EVEN] = rowIndex % 2 === 0,
_a), baseCell.props.className);
var key = TableBodyCells.cellReactKey(rowIndex, columnIndex);
var rect = isGhost ? grid.getGhostCellRect(rowIndex, columnIndex) : grid.getCellRect(rowIndex, columnIndex);
var cellLoading = baseCell.props.loading != null ? baseCell.props.loading : loading;
var style = __assign(__assign({}, baseCell.props.style), Rect.style(rect));
var isFocused = _this.isCellFocused(rowIndex, columnIndex);
return React.cloneElement(baseCell, {
className: className,
isFocused: isFocused,
key: key,
loading: cellLoading,
style: style,
});
};
// Other
// =====
_this.didViewportRectChange = function (nextViewportRect, currViewportRect) {
if (nextViewportRect == null && currViewportRect == null) {
return false;
}
else if (nextViewportRect == null || currViewportRect == null) {
return true;
}
else {
return !nextViewportRect.equals(currViewportRect);
}
};
return _this;
}
TableBodyCells.cellReactKey = function (rowIndex, columnIndex) {
return "cell-".concat(rowIndex, "-").concat(columnIndex);
};
TableBodyCells.prototype.componentDidMount = function () {
this.maybeInvokeOnCompleteRender();
};
TableBodyCells.prototype.shouldComponentUpdate = function (nextProps) {
return (!CoreUtils.shallowCompareKeys(nextProps, this.props, {
exclude: SHALLOW_COMPARE_DENYLIST,
}) ||
// "viewportRect" is not a plain object, so we can't just deep
// compare; we need custom logic.
this.didViewportRectChange(nextProps.viewportRect, this.props.viewportRect));
};
TableBodyCells.prototype.componentDidUpdate = function (prevProps) {
if (this.didForceUpdate) {
this.didForceUpdate = false;
return;
}
var shouldResetBatcher = !CoreUtils.shallowCompareKeys(prevProps, this.props, {
exclude: BATCHER_RESET_PROP_KEYS_DENYLIST,
});
if (shouldResetBatcher) {
this.batcher.reset();
// At this point, the batcher is reset, but it doesn't have a chance to re-run since render() is not called
// by default after this lifecycle method. This causes issues like https://github.com/palantir/blueprint/issues/5193.
// We can run forceUpdate() to re-render, but must take care to set a local flag indicating that we are doing so,
// so that this lifecycle method doesn't get re-run as well within the same forced update cycle.
this.didForceUpdate = true;
this.forceUpdate();
}
this.maybeInvokeOnCompleteRender();
};
TableBodyCells.prototype.componentWillUnmount = function () {
this.batcher.cancelOutstandingCallback();
};
TableBodyCells.prototype.render = function () {
var renderMode = this.props.renderMode;
var cells = renderMode === RenderMode.BATCH ? this.renderBatchedCells() : this.renderAllCells();
return React.createElement("div", { className: Classes.TABLE_BODY_CELLS }, cells);
};
// Render modes
// ============
TableBodyCells.prototype.renderBatchedCells = function () {
var _this = this;
var _a = this.props, columnIndexEnd = _a.columnIndexEnd, columnIndexStart = _a.columnIndexStart, rowIndexEnd = _a.rowIndexEnd, rowIndexStart = _a.rowIndexStart;
// render cells in batches
this.batcher.startNewBatch();
for (var rowIndex = rowIndexStart; rowIndex <= rowIndexEnd; rowIndex++) {
for (var columnIndex = columnIndexStart; columnIndex <= columnIndexEnd; columnIndex++) {
this.batcher.addArgsToBatch(rowIndex, columnIndex);
}
}
this.batcher.removeOldAddNew(this.renderNewCell);
if (!this.batcher.isDone()) {
this.batcher.idleCallback(function () { return _this.forceUpdate(); });
}
return this.batcher.getList();
};
TableBodyCells.prototype.renderAllCells = function () {
var _a = this.props, columnIndexEnd = _a.columnIndexEnd, columnIndexStart = _a.columnIndexStart, rowIndexEnd = _a.rowIndexEnd, rowIndexStart = _a.rowIndexStart;
var cells = [];
var cellsArgs = [];
for (var rowIndex = rowIndexStart; rowIndex <= rowIndexEnd; rowIndex++) {
for (var columnIndex = columnIndexStart; columnIndex <= columnIndexEnd; columnIndex++) {
cells.push(this.renderNewCell(rowIndex, columnIndex));
cellsArgs.push([rowIndex, columnIndex]);
}
}
// pretend we did an entire rendering pass using the batcher. that way,
// if we switch from `RenderMode.NONE` to `RenderMode.BATCH`, we don't
// have to re-paint every cell still in view.
this.batcher.setList(cellsArgs, cells);
return cells;
};
TableBodyCells.prototype.isCellFocused = function (rowIndex, columnIndex) {
var focusedRegion = this.props.focusedRegion;
switch (focusedRegion === null || focusedRegion === void 0 ? void 0 : focusedRegion.type) {
case FocusMode.CELL:
return focusedRegion.row === rowIndex && focusedRegion.col === columnIndex;
case FocusMode.ROW:
return focusedRegion.row === rowIndex;
case undefined:
return false;
}
};
// Callbacks
// =========
TableBodyCells.prototype.maybeInvokeOnCompleteRender = function () {
var _a = this.props, onCompleteRender = _a.onCompleteRender, renderMode = _a.renderMode;
if (renderMode === RenderMode.NONE || (renderMode === RenderMode.BATCH && this.batcher.isDone())) {
onCompleteRender === null || onCompleteRender === void 0 ? void 0 : onCompleteRender();
}
};
TableBodyCells.defaultProps = {
renderMode: RenderMode.BATCH,
};
return TableBodyCells;
}(AbstractComponent));
export { TableBodyCells };
/**
* Returns the array of class names that must be applied to each table
* cell so that we can locate any cell based on its coordinate.
*/
export function cellClassNames(rowIndex, columnIndex) {
return [Classes.rowCellIndexClass(rowIndex), Classes.columnCellIndexClass(columnIndex)];
}
//# sourceMappingURL=tableBodyCells.js.map