UNPKG

@mui/x-data-grid

Version:

The Community plan edition of the MUI X Data Grid components.

250 lines (244 loc) 10.7 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.useGridRowSpanning = exports.rowSpanningStateInitializer = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var _features = require("@mui/x-virtualizer/features"); var _gridColumnsSelector = require("../columns/gridColumnsSelector"); var _useGridVisibleRows = require("../../utils/useGridVisibleRows"); var _gridVirtualizationSelectors = require("../virtualization/gridVirtualizationSelectors"); var _gridRowSpanningUtils = require("./gridRowSpanningUtils"); var _useGridEvent = require("../../utils/useGridEvent"); var _utils = require("../../../utils/utils"); var _pagination = require("../pagination"); var _gridRowsSelector = require("./gridRowsSelector"); const EMPTY_CACHES = { spannedCells: {}, hiddenCells: {}, hiddenCellOriginMap: {} }; const EMPTY_RANGE = { firstRowIndex: 0, lastRowIndex: 0 }; const EMPTY_STATE = { caches: EMPTY_CACHES, processedRange: EMPTY_RANGE }; /** * Default number of rows to process during state initialization to avoid flickering. * Number `20` is arbitrarily chosen to be large enough to cover most of the cases without * compromising performance. */ const DEFAULT_ROWS_TO_PROCESS = 20; const computeRowSpanningState = (apiRef, colDefs, visibleRows, range, rangeToProcess, resetState) => { const virtualizer = apiRef.current.virtualizer; const previousState = resetState ? EMPTY_STATE : _features.Rowspan.selectors.state(virtualizer.store.state); const spannedCells = (0, _extends2.default)({}, previousState.caches.spannedCells); const hiddenCells = (0, _extends2.default)({}, previousState.caches.hiddenCells); const hiddenCellOriginMap = (0, _extends2.default)({}, previousState.caches.hiddenCellOriginMap); const processedRange = { firstRowIndex: Math.min(previousState.processedRange.firstRowIndex, rangeToProcess.firstRowIndex), lastRowIndex: Math.max(previousState.processedRange.lastRowIndex, rangeToProcess.lastRowIndex) }; colDefs.forEach((colDef, columnIndex) => { for (let index = rangeToProcess.firstRowIndex; index < rangeToProcess.lastRowIndex; index += 1) { const row = visibleRows[index]; if (hiddenCells[row.id]?.[columnIndex]) { continue; } const cellValue = (0, _gridRowSpanningUtils.getCellValue)(row.model, colDef, apiRef); if (cellValue == null) { continue; } let spannedRowId = row.id; let spannedRowIndex = index; let rowSpan = 0; // For first index, also scan in the previous rows to handle the reset state case e.g by sorting const backwardsHiddenCells = []; if (index === rangeToProcess.firstRowIndex) { let prevIndex = index - 1; let prevRowEntry = visibleRows[prevIndex]; while (prevIndex >= range.firstRowIndex && prevRowEntry && (0, _gridRowSpanningUtils.getCellValue)(prevRowEntry.model, colDef, apiRef) === cellValue) { const currentRow = visibleRows[prevIndex + 1]; if (hiddenCells[currentRow.id]) { hiddenCells[currentRow.id][columnIndex] = true; } else { hiddenCells[currentRow.id] = { [columnIndex]: true }; } backwardsHiddenCells.push(index); rowSpan += 1; spannedRowId = prevRowEntry.id; spannedRowIndex = prevIndex; prevIndex -= 1; prevRowEntry = visibleRows[prevIndex]; } } backwardsHiddenCells.forEach(hiddenCellIndex => { if (hiddenCellOriginMap[hiddenCellIndex]) { hiddenCellOriginMap[hiddenCellIndex][columnIndex] = spannedRowIndex; } else { hiddenCellOriginMap[hiddenCellIndex] = { [columnIndex]: spannedRowIndex }; } }); // Scan the next rows let relativeIndex = index + 1; while (relativeIndex <= range.lastRowIndex && visibleRows[relativeIndex] && (0, _gridRowSpanningUtils.getCellValue)(visibleRows[relativeIndex].model, colDef, apiRef) === cellValue) { const currentRow = visibleRows[relativeIndex]; if (hiddenCells[currentRow.id]) { hiddenCells[currentRow.id][columnIndex] = true; } else { hiddenCells[currentRow.id] = { [columnIndex]: true }; } if (hiddenCellOriginMap[relativeIndex]) { hiddenCellOriginMap[relativeIndex][columnIndex] = spannedRowIndex; } else { hiddenCellOriginMap[relativeIndex] = { [columnIndex]: spannedRowIndex }; } relativeIndex += 1; rowSpan += 1; } if (rowSpan > 0) { if (spannedCells[spannedRowId]) { spannedCells[spannedRowId][columnIndex] = rowSpan + 1; } else { spannedCells[spannedRowId] = { [columnIndex]: rowSpan + 1 }; } } } }); return { caches: { spannedCells, hiddenCells, hiddenCellOriginMap }, processedRange }; }; const getInitialRangeToProcess = (props, apiRef) => { const rowCount = (0, _gridRowsSelector.gridDataRowIdsSelector)(apiRef).length; if (props.pagination) { const pageSize = (0, _pagination.gridPageSizeSelector)(apiRef); let paginationLastRowIndex = DEFAULT_ROWS_TO_PROCESS; if (pageSize > 0) { paginationLastRowIndex = pageSize - 1; } return { firstRowIndex: 0, lastRowIndex: Math.min(paginationLastRowIndex, rowCount) }; } return { firstRowIndex: 0, lastRowIndex: Math.min(DEFAULT_ROWS_TO_PROCESS, rowCount) }; }; /** * @requires columnsStateInitializer (method) - should be initialized before * @requires rowsStateInitializer (method) - should be initialized before * @requires filterStateInitializer (method) - should be initialized before */ const rowSpanningStateInitializer = (state, props, apiRef) => { if (!props.rowSpanning) { return (0, _extends2.default)({}, state, { rowSpanning: EMPTY_STATE }); } const rowIds = state.rows.dataRowIds || []; const orderedFields = state.columns.orderedFields || []; const dataRowIdToModelLookup = state.rows.dataRowIdToModelLookup; const columnsLookup = state.columns.lookup; const isFilteringPending = Boolean(state.filter.filterModel.items.length) || Boolean(state.filter.filterModel.quickFilterValues?.length); if (!rowIds.length || !orderedFields.length || !dataRowIdToModelLookup || !columnsLookup || isFilteringPending) { return (0, _extends2.default)({}, state, { rowSpanning: EMPTY_STATE }); } const rangeToProcess = getInitialRangeToProcess(props, apiRef); const rows = rowIds.map(id => ({ id, model: dataRowIdToModelLookup[id] })); const colDefs = orderedFields.map(field => columnsLookup[field]); const rowSpanning = computeRowSpanningState(apiRef, colDefs, rows, rangeToProcess, rangeToProcess, true); return (0, _extends2.default)({}, state, { rowSpanning }); }; exports.rowSpanningStateInitializer = rowSpanningStateInitializer; const useGridRowSpanning = (apiRef, props) => { const store = apiRef.current.virtualizer.store; const updateRowSpanningState = React.useCallback((renderContext, resetState = false) => { const { range, rows: visibleRows } = (0, _useGridVisibleRows.getVisibleRows)(apiRef); if (range === null || !(0, _gridRowSpanningUtils.isRowContextInitialized)(renderContext)) { return; } const previousState = resetState ? EMPTY_STATE : _features.Rowspan.selectors.state(store.state); const rangeToProcess = (0, _gridRowSpanningUtils.getUnprocessedRange)({ firstRowIndex: renderContext.firstRowIndex, lastRowIndex: Math.min(renderContext.lastRowIndex, range.lastRowIndex - range.firstRowIndex + 1) }, previousState.processedRange); if (rangeToProcess === null) { return; } const colDefs = (0, _gridColumnsSelector.gridVisibleColumnDefinitionsSelector)(apiRef); const newState = computeRowSpanningState(apiRef, colDefs, visibleRows, range, rangeToProcess, resetState); const newSpannedCellsCount = Object.keys(newState.caches.spannedCells).length; const newHiddenCellsCount = Object.keys(newState.caches.hiddenCells).length; const previousSpannedCellsCount = Object.keys(previousState.caches.spannedCells).length; const previousHiddenCellsCount = Object.keys(previousState.caches.hiddenCells).length; const shouldUpdateState = resetState || newSpannedCellsCount !== previousSpannedCellsCount || newHiddenCellsCount !== previousHiddenCellsCount; const hasNoSpannedCells = newSpannedCellsCount === 0 && previousSpannedCellsCount === 0; if (!shouldUpdateState || hasNoSpannedCells) { return; } store.set('rowSpanning', newState); }, [apiRef, store]); // Reset events trigger a full re-computation of the row spanning state: // - The `unstable_rowSpanning` prop is updated (feature flag) // - The filtering is applied // - The sorting is applied // - The `paginationModel` is updated // - The rows are updated const resetRowSpanningState = React.useCallback(() => { const renderContext = (0, _gridVirtualizationSelectors.gridRenderContextSelector)(apiRef); if (!(0, _gridRowSpanningUtils.isRowContextInitialized)(renderContext)) { return; } updateRowSpanningState(renderContext, true); }, [apiRef, updateRowSpanningState]); (0, _useGridEvent.useGridEvent)(apiRef, 'renderedRowsIntervalChange', (0, _utils.runIf)(props.rowSpanning, updateRowSpanningState)); (0, _useGridEvent.useGridEvent)(apiRef, 'sortedRowsSet', (0, _utils.runIf)(props.rowSpanning, resetRowSpanningState)); (0, _useGridEvent.useGridEvent)(apiRef, 'paginationModelChange', (0, _utils.runIf)(props.rowSpanning, resetRowSpanningState)); (0, _useGridEvent.useGridEvent)(apiRef, 'filteredRowsSet', (0, _utils.runIf)(props.rowSpanning, resetRowSpanningState)); (0, _useGridEvent.useGridEvent)(apiRef, 'columnsChange', (0, _utils.runIf)(props.rowSpanning, resetRowSpanningState)); React.useEffect(() => { if (!props.rowSpanning) { if (store.state.rowSpanning !== EMPTY_STATE) { store.set('rowSpanning', EMPTY_STATE); } } else if (store.state.rowSpanning.caches === EMPTY_CACHES) { resetRowSpanningState(); } }, [apiRef, store, resetRowSpanningState, props.rowSpanning]); }; exports.useGridRowSpanning = useGridRowSpanning;