UNPKG

fixed-react-data-grid-custom

Version:

Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like

454 lines 24.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var react_1 = tslib_1.__importDefault(require("react")); var Grid_1 = tslib_1.__importDefault(require("./Grid")); var ToolbarContainer_1 = tslib_1.__importDefault(require("./ToolbarContainer")); var CheckboxEditor_1 = tslib_1.__importDefault(require("./common/editors/CheckboxEditor")); var formatters_1 = require("./formatters"); var rowUtils = tslib_1.__importStar(require("./RowUtils")); var ColumnUtils_1 = require("./ColumnUtils"); var KeyCodes_1 = tslib_1.__importDefault(require("./KeyCodes")); var ColumnMetrics_1 = require("./ColumnMetrics"); var masks_1 = require("./masks"); var enums_1 = require("./common/enums"); function isRowSelected(keys, indexes, isSelectedKey, rowData, rowIdx) { return rowUtils.isRowSelected(keys, indexes, isSelectedKey, rowData, rowIdx); } /** * Main API Component to render a data grid of rows and columns * * @example * * <ReactDataGrid columns={columns} rowGetter={i => rows[i]} rowsCount={3} /> */ var ReactDataGrid = /** @class */ (function (_super) { tslib_1.__extends(ReactDataGrid, _super); function ReactDataGrid(props) { var _this = _super.call(this, props) || this; _this.grid = react_1.default.createRef(); _this.base = react_1.default.createRef(); _this.selectAllCheckbox = react_1.default.createRef(); _this.eventBus = new masks_1.EventBus(); _this._keysDown = new Set(); _this.metricsUpdated = function () { var columnMetrics = _this.createColumnMetrics(); _this.setState({ columnMetrics: columnMetrics }); }; _this.handleColumnResize = function (idx, width) { var columnMetrics = ColumnMetrics_1.resizeColumn(_this.state.columnMetrics, idx, width); _this.setState({ columnMetrics: columnMetrics }); if (_this.props.onColumnResize) { _this.props.onColumnResize(idx, width); } }; _this.handleDragEnter = function (overRowIdx) { _this.eventBus.dispatch(enums_1.EventTypes.DRAG_ENTER, overRowIdx); }; _this.handleViewportKeyDown = function (e) { // Track which keys are currently down for shift clicking etc _this._keysDown.add(e.keyCode); var onGridKeyDown = _this.props.onGridKeyDown; if (onGridKeyDown) { onGridKeyDown(e); } }; _this.handleViewportKeyUp = function (e) { // Track which keys are currently down for shift clicking etc _this._keysDown.delete(e.keyCode); var onGridKeyUp = _this.props.onGridKeyUp; if (onGridKeyUp) { onGridKeyUp(e); } }; _this.handlerCellClick = function (_a) { var rowIdx = _a.rowIdx, idx = _a.idx; var _b = _this.props, onRowClick = _b.onRowClick, rowGetter = _b.rowGetter; _this.selectCell({ rowIdx: rowIdx, idx: idx }); if (onRowClick) { onRowClick(rowIdx, rowGetter(rowIdx), _this.getColumn(idx)); } }; _this.handleCellMouseDown = function (position) { _this.eventBus.dispatch(enums_1.EventTypes.SELECT_START, position); }; _this.handleCellMouseEnter = function (position) { _this.eventBus.dispatch(enums_1.EventTypes.SELECT_UPDATE, position); }; _this.handleWindowMouseUp = function () { _this.eventBus.dispatch(enums_1.EventTypes.SELECT_END); }; _this.handleCellContextMenu = function (position) { _this.selectCell(position); }; _this.handleCellDoubleClick = function (_a) { var rowIdx = _a.rowIdx, idx = _a.idx; var _b = _this.props, onRowDoubleClick = _b.onRowDoubleClick, rowGetter = _b.rowGetter; if (onRowDoubleClick) { onRowDoubleClick(rowIdx, rowGetter(rowIdx), _this.getColumn(idx)); } _this.openCellEditor(rowIdx, idx); }; _this.handleToggleFilter = function () { // setState() does not immediately mutate this.state but creates a pending state transition. // Therefore if you want to do something after the state change occurs, pass it in as a callback function. _this.setState(function (prevState) { return ({ canFilter: !prevState.canFilter }); }, function () { if (_this.state.canFilter === false && _this.props.onClearFilters) { _this.props.onClearFilters(); } }); }; _this.handleDragHandleDoubleClick = function (e) { var _a; var cellKey = _this.getColumn(e.idx).key; _this.handleGridRowsUpdated(cellKey, e.rowIdx, _this.props.rowsCount - 1, (_a = {}, _a[cellKey] = e.rowData[cellKey], _a), enums_1.UpdateActions.COLUMN_FILL); }; _this.handleGridRowsUpdated = function (cellKey, fromRow, toRow, updated, action, originRow) { var _a = _this.props, rowGetter = _a.rowGetter, rowKey = _a.rowKey, onGridRowsUpdated = _a.onGridRowsUpdated; if (!onGridRowsUpdated) { return; } var rowIds = []; var start = Math.min(fromRow, toRow); var end = Math.max(fromRow, toRow); for (var i = start; i <= end; i++) { rowIds.push(rowGetter(i)[rowKey]); } var fromRowData = rowGetter(action === enums_1.UpdateActions.COPY_PASTE ? originRow : fromRow); var fromRowId = fromRowData[rowKey]; var toRowId = rowGetter(toRow)[rowKey]; onGridRowsUpdated({ cellKey: cellKey, fromRow: fromRow, toRow: toRow, fromRowId: fromRowId, toRowId: toRowId, rowIds: rowIds, updated: updated, action: action, fromRowData: fromRowData }); }; _this.handleCommit = function (commit) { var targetRow = commit.rowIdx; _this.handleGridRowsUpdated(commit.cellKey, targetRow, targetRow, commit.updated, enums_1.UpdateActions.CELL_UPDATE); }; _this.handleSort = function (sortColumn, sortDirection) { _this.setState({ sortColumn: sortColumn, sortDirection: sortDirection }, function () { var onGridSort = _this.props.onGridSort; if (onGridSort) { onGridSort(sortColumn, sortDirection); } }); }; _this.useNewRowSelection = function () { return _this.props.rowSelection && _this.props.rowSelection.selectBy; }; // return false if not a shift select so can be handled as normal row selection _this.handleShiftSelect = function (rowIdx) { var rowSelection = _this.props.rowSelection; if (rowSelection && _this.state.lastRowIdxUiSelected > -1 && _this.isSingleKeyDown(KeyCodes_1.default.Shift)) { var _a = rowSelection.selectBy, keys = _a.keys, indexes = _a.indexes, isSelectedKey = _a.isSelectedKey; var isPreviouslySelected = isRowSelected(keys, indexes, isSelectedKey, _this.props.rowGetter(rowIdx), rowIdx); if (isPreviouslySelected) return false; var handled = false; if (rowIdx > _this.state.lastRowIdxUiSelected) { var rowsSelected = []; for (var i = _this.state.lastRowIdxUiSelected + 1; i <= rowIdx; i++) { rowsSelected.push({ rowIdx: i, row: _this.props.rowGetter(i) }); } if (typeof rowSelection.onRowsSelected === 'function') { rowSelection.onRowsSelected(rowsSelected); } handled = true; } else if (rowIdx < _this.state.lastRowIdxUiSelected) { var rowsSelected = []; for (var i = rowIdx; i <= _this.state.lastRowIdxUiSelected - 1; i++) { rowsSelected.push({ rowIdx: i, row: _this.props.rowGetter(i) }); } if (typeof rowSelection.onRowsSelected === 'function') { rowSelection.onRowsSelected(rowsSelected); } handled = true; } if (handled) { _this.setState({ lastRowIdxUiSelected: rowIdx }); } return handled; } return false; }; _this.handleNewRowSelect = function (rowIdx, rowData) { var current = _this.selectAllCheckbox.current; if (current && current.checked === true) { current.checked = false; } var rowSelection = _this.props.rowSelection; if (rowSelection) { var _a = rowSelection.selectBy, keys = _a.keys, indexes = _a.indexes, isSelectedKey = _a.isSelectedKey; var isPreviouslySelected = isRowSelected(keys, indexes, isSelectedKey, rowData, rowIdx); _this.setState({ lastRowIdxUiSelected: isPreviouslySelected ? -1 : rowIdx }); var cb = isPreviouslySelected ? rowSelection.onRowsDeselected : rowSelection.onRowsSelected; if (typeof cb === 'function') { cb([{ rowIdx: rowIdx, row: rowData }]); } } }; // columnKey not used here as this function will select the whole row, // but needed to match the function signature in the CheckboxEditor _this.handleRowSelect = function (rowIdx, columnKey, rowData, event) { event.stopPropagation(); var rowSelection = _this.props.rowSelection; if (_this.useNewRowSelection()) { if (rowSelection && rowSelection.enableShiftSelect === true) { if (!_this.handleShiftSelect(rowIdx)) { _this.handleNewRowSelect(rowIdx, rowData); } } else { _this.handleNewRowSelect(rowIdx, rowData); } } else { // Fallback to old onRowSelect handler var selectedRows = _this.props.enableRowSelect === 'single' ? [] : tslib_1.__spread(_this.state.selectedRows); var selectedRow = _this.getSelectedRow(selectedRows, rowData[_this.props.rowKey]); if (selectedRow) { selectedRow.isSelected = !selectedRow.isSelected; } else { rowData.isSelected = true; selectedRows.push(rowData); } _this.setState({ selectedRows: selectedRows }); if (_this.props.onRowSelect) { _this.props.onRowSelect(selectedRows.filter(function (r) { return r.isSelected === true; })); } } }; _this.handleCheckboxChange = function (e) { var allRowsSelected = e.currentTarget.checked; var rowSelection = _this.props.rowSelection; if (rowSelection && _this.useNewRowSelection()) { var _a = rowSelection.selectBy, keys = _a.keys, indexes = _a.indexes, isSelectedKey = _a.isSelectedKey; if (allRowsSelected && typeof rowSelection.onRowsSelected === 'function') { var selectedRows = []; for (var i = 0; i < _this.props.rowsCount; i++) { var rowData = _this.props.rowGetter(i); if (!isRowSelected(keys, indexes, isSelectedKey, rowData, i)) { selectedRows.push({ rowIdx: i, row: rowData }); } } if (selectedRows.length > 0) { rowSelection.onRowsSelected(selectedRows); } } else if (!allRowsSelected && typeof rowSelection.onRowsDeselected === 'function') { var deselectedRows = []; for (var i = 0; i < _this.props.rowsCount; i++) { var rowData = _this.props.rowGetter(i); if (isRowSelected(keys, indexes, isSelectedKey, rowData, i)) { deselectedRows.push({ rowIdx: i, row: rowData }); } } if (deselectedRows.length > 0) { rowSelection.onRowsDeselected(deselectedRows); } } } else { var selectedRows = []; for (var i = 0; i < _this.props.rowsCount; i++) { var row = tslib_1.__assign(tslib_1.__assign({}, _this.props.rowGetter(i)), { isSelected: allRowsSelected }); selectedRows.push(row); } _this.setState({ selectedRows: selectedRows }); if (typeof _this.props.onRowSelect === 'function') { _this.props.onRowSelect(selectedRows.filter(function (r) { return r.isSelected === true; })); } } }; var initialState = { columnMetrics: _this.createColumnMetrics(), selectedRows: [], canFilter: false, lastRowIdxUiSelected: -1 }; if (_this.props.sortColumn && _this.props.sortDirection) { initialState.sortColumn = _this.props.sortColumn; initialState.sortDirection = _this.props.sortDirection; } _this.state = initialState; return _this; } ReactDataGrid.prototype.componentDidMount = function () { window.addEventListener('resize', this.metricsUpdated); if (this.props.cellRangeSelection) { window.addEventListener('mouseup', this.handleWindowMouseUp); } this.metricsUpdated(); }; ReactDataGrid.prototype.componentWillUnmount = function () { window.removeEventListener('resize', this.metricsUpdated); window.removeEventListener('mouseup', this.handleWindowMouseUp); }; ReactDataGrid.prototype.componentWillReceiveProps = function (nextProps) { if (nextProps.columns && (!ColumnMetrics_1.sameColumns(this.props.columns, nextProps.columns, this.props.columnEquality) || nextProps.minWidth !== this.props.minWidth)) { var columnMetrics = this.createColumnMetrics(nextProps); this.setState({ columnMetrics: columnMetrics }); } }; ReactDataGrid.prototype.selectCell = function (_a, openEditor) { var idx = _a.idx, rowIdx = _a.rowIdx; this.eventBus.dispatch(enums_1.EventTypes.SELECT_CELL, { rowIdx: rowIdx, idx: idx }, openEditor); }; ReactDataGrid.prototype.gridWidth = function () { var current = this.grid.current; return current && current.parentElement ? current.parentElement.offsetWidth : 0; }; ReactDataGrid.prototype.getTotalWidth = function () { if (this.grid.current) { return this.gridWidth(); } return ColumnUtils_1.getSize(this.props.columns) * this.props.minColumnWidth; }; ReactDataGrid.prototype.getColumn = function (idx) { return this.state.columnMetrics.columns[idx]; }; ReactDataGrid.prototype.getSize = function () { return this.state.columnMetrics.columns.length; }; ReactDataGrid.prototype.createColumnMetrics = function (props) { if (props === void 0) { props = this.props; } var gridColumns = this.setupGridColumns(props); var metrics = { columns: gridColumns, minColumnWidth: this.props.minColumnWidth, totalWidth: this.props.minWidth || this.getTotalWidth() }; return ColumnMetrics_1.recalculate(metrics); }; ReactDataGrid.prototype.isSingleKeyDown = function (keyCode) { return this._keysDown.has(keyCode) && this._keysDown.size === 1; }; ReactDataGrid.prototype.getSelectedRow = function (rows, key) { var _this = this; return rows.find(function (r) { return r[_this.props.rowKey] === key; }); }; ReactDataGrid.prototype.getRowOffsetHeight = function () { return this.getHeaderRows().reduce(function (offsetHeight, row) { return offsetHeight += row.height; }, 0); }; ReactDataGrid.prototype.getHeaderRows = function () { var _a = this.props, headerRowHeight = _a.headerRowHeight, rowHeight = _a.rowHeight, onAddFilter = _a.onAddFilter, headerFiltersHeight = _a.headerFiltersHeight; var rows = [{ height: headerRowHeight || rowHeight, rowType: enums_1.HeaderRowType.HEADER }]; if (this.state.canFilter === true) { rows.push({ rowType: enums_1.HeaderRowType.FILTER, filterable: true, onFilterChange: onAddFilter, height: headerFiltersHeight }); } return rows; }; ReactDataGrid.prototype.getRowSelectionProps = function () { return this.props.rowSelection && this.props.rowSelection.selectBy; }; ReactDataGrid.prototype.getSelectedRows = function () { if (this.props.rowSelection) { return; } return this.state.selectedRows.filter(function (r) { return r.isSelected === true; }); }; ReactDataGrid.prototype.openCellEditor = function (rowIdx, idx) { this.selectCell({ rowIdx: rowIdx, idx: idx }, true); }; ReactDataGrid.prototype.scrollToColumn = function (colIdx) { this.eventBus.dispatch(enums_1.EventTypes.SCROLL_TO_COLUMN, colIdx); }; ReactDataGrid.prototype.setupGridColumns = function (props) { if (props === void 0) { props = this.props; } var columns = props.columns; if (this._cachedColumns === columns) { return this._cachedComputedColumns; } this._cachedColumns = columns; if (this.props.rowActionsCell || (props.enableRowSelect && !this.props.rowSelection) || (props.rowSelection && props.rowSelection.showCheckbox !== false)) { var SelectAllComponent = this.props.selectAllRenderer; var headerRenderer = props.enableRowSelect === 'single' ? undefined : react_1.default.createElement(SelectAllComponent, { onChange: this.handleCheckboxChange, ref: this.selectAllCheckbox }); var Formatter = (this.props.rowActionsCell ? this.props.rowActionsCell : CheckboxEditor_1.default); var selectColumn = { key: 'select-row', name: '', formatter: react_1.default.createElement(Formatter, { rowSelection: this.props.rowSelection }), onCellChange: this.handleRowSelect, filterable: false, headerRenderer: headerRenderer, width: 60, frozen: true, getRowMetaData: function (rowData) { return rowData; }, cellClass: this.props.rowActionsCell ? 'rdg-row-actions-cell' : '' }; this._cachedComputedColumns = Array.isArray(columns) ? tslib_1.__spread([selectColumn], columns) : columns.unshift(selectColumn); } else { this._cachedComputedColumns = columns.slice(0); } return this._cachedComputedColumns; }; ReactDataGrid.prototype.render = function () { var cellMetaData = { rowKey: this.props.rowKey, onCellClick: this.handlerCellClick, onCellContextMenu: this.handleCellContextMenu, onCellDoubleClick: this.handleCellDoubleClick, onCellExpand: this.props.onCellExpand, onRowExpandToggle: this.props.onRowExpandToggle, getCellActions: this.props.getCellActions, onDeleteSubRow: this.props.onDeleteSubRow, onAddSubRow: this.props.onAddSubRow, onDragEnter: this.handleDragEnter }; if (this.props.cellRangeSelection) { cellMetaData.onCellMouseDown = this.handleCellMouseDown; cellMetaData.onCellMouseEnter = this.handleCellMouseEnter; } var interactionMasksMetaData = { onCheckCellIsEditable: this.props.onCheckCellIsEditable, onCellCopyPaste: this.props.onCellCopyPaste, onGridRowsUpdated: this.handleGridRowsUpdated, onDragHandleDoubleClick: this.handleDragHandleDoubleClick, onCellSelected: this.props.onCellSelected, onCellDeSelected: this.props.onCellDeSelected, onCellRangeSelectionStarted: this.props.cellRangeSelection && this.props.cellRangeSelection.onStart, onCellRangeSelectionUpdated: this.props.cellRangeSelection && this.props.cellRangeSelection.onUpdate, onCellRangeSelectionCompleted: this.props.cellRangeSelection && this.props.cellRangeSelection.onComplete, onCommit: this.handleCommit }; var containerWidth = this.props.minWidth || this.gridWidth(); var gridWidth = containerWidth; // depending on the current lifecycle stage, gridWidth() may not initialize correctly // this also handles cases where it always returns undefined -- such as when inside a div with display:none // eg Bootstrap tabs and collapses if (Number.isNaN(containerWidth) || containerWidth === 0) { containerWidth = '100%'; gridWidth = '100%'; } return (react_1.default.createElement("div", { className: "react-grid-Container", style: { width: containerWidth }, ref: this.grid }, react_1.default.createElement(ToolbarContainer_1.default, { toolbar: this.props.toolbar, columns: this.props.columns, rowsCount: this.props.rowsCount, onToggleFilter: this.handleToggleFilter }), react_1.default.createElement(Grid_1.default, { ref: this.base, rowKey: this.props.rowKey, headerRows: this.getHeaderRows(), draggableHeaderCell: this.props.draggableHeaderCell, getValidFilterValues: this.props.getValidFilterValues, columnMetrics: this.state.columnMetrics, rowGetter: this.props.rowGetter, rowsCount: this.props.rowsCount, rowHeight: this.props.rowHeight, rowRenderer: this.props.rowRenderer, rowGroupRenderer: this.props.rowGroupRenderer, cellMetaData: cellMetaData, selectedRows: this.getSelectedRows(), rowSelection: this.getRowSelectionProps(), rowOffsetHeight: this.getRowOffsetHeight(), sortColumn: this.state.sortColumn, sortDirection: this.state.sortDirection, onSort: this.handleSort, minHeight: this.props.minHeight, totalWidth: gridWidth, onViewportKeydown: this.handleViewportKeyDown, onViewportKeyup: this.handleViewportKeyUp, onColumnResize: this.handleColumnResize, scrollToRowIndex: this.props.scrollToRowIndex, contextMenu: this.props.contextMenu, enableCellSelect: this.props.enableCellSelect, enableCellAutoFocus: this.props.enableCellAutoFocus, cellNavigationMode: this.props.cellNavigationMode, eventBus: this.eventBus, onScroll: this.props.onScroll, RowsContainer: this.props.RowsContainer, emptyRowsView: this.props.emptyRowsView, onHeaderDrop: this.props.onHeaderDrop, getSubRowDetails: this.props.getSubRowDetails, editorPortalTarget: this.props.editorPortalTarget, interactionMasksMetaData: interactionMasksMetaData }))); }; ReactDataGrid.displayName = 'ReactDataGrid'; ReactDataGrid.defaultProps = { enableCellSelect: false, rowHeight: 35, headerFiltersHeight: 45, enableRowSelect: false, minHeight: 350, rowKey: 'id', cellNavigationMode: enums_1.CellNavigationMode.NONE, enableCellAutoFocus: true, minColumnWidth: 80, selectAllRenderer: formatters_1.SelectAll, columnEquality: ColumnMetrics_1.sameColumn, editorPortalTarget: document.body }; return ReactDataGrid; }(react_1.default.Component)); exports.default = ReactDataGrid; //# sourceMappingURL=ReactDataGrid.js.map