@mui/x-data-grid
Version:
The community edition of the data grid component (MUI X).
241 lines (212 loc) • 10.4 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { debounce, capitalize } from '@mui/material/utils';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridSelector } from '../../utils/useGridSelector';
import { gridDensityRowHeightSelector, gridDensityFactorSelector } from '../density/densitySelector';
import { gridFilterStateSelector } from '../filter/gridFilterSelector';
import { gridPaginationSelector } from '../pagination/gridPaginationSelector';
import { gridSortingStateSelector } from '../sorting/gridSortingSelector';
import { useGridRegisterPipeApplier } from '../../core/pipeProcessing';
import { gridPinnedRowsSelector } from './gridRowsSelector';
export var rowsMetaStateInitializer = function rowsMetaStateInitializer(state) {
return _extends({}, state, {
rowsMeta: {
currentPageTotalHeight: 0,
positions: []
}
});
};
/**
* @requires useGridPageSize (method)
* @requires useGridPage (method)
*/
export var useGridRowsMeta = function useGridRowsMeta(apiRef, props) {
var getRowHeightProp = props.getRowHeight,
getRowSpacing = props.getRowSpacing,
getEstimatedRowHeight = props.getEstimatedRowHeight;
var rowsHeightLookup = React.useRef({}); // Inspired by https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/utils/CellSizeAndPositionManager.js
var lastMeasuredRowIndex = React.useRef(-1);
var hasRowWithAutoHeight = React.useRef(false);
var rowHeightFromDensity = useGridSelector(apiRef, gridDensityRowHeightSelector);
var filterState = useGridSelector(apiRef, gridFilterStateSelector);
var paginationState = useGridSelector(apiRef, gridPaginationSelector);
var sortingState = useGridSelector(apiRef, gridSortingStateSelector);
var currentPage = useGridVisibleRows(apiRef, props);
var pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
var hydrateRowsMeta = React.useCallback(function () {
var _pinnedRows$top, _pinnedRows$bottom;
hasRowWithAutoHeight.current = false;
var densityFactor = gridDensityFactorSelector(apiRef.current.state, apiRef.current.instanceId);
var calculateRowProcessedSizes = function calculateRowProcessedSizes(row) {
if (!rowsHeightLookup.current[row.id]) {
rowsHeightLookup.current[row.id] = {
sizes: {
baseCenter: rowHeightFromDensity
},
isResized: false,
autoHeight: false,
needsFirstMeasurement: true // Assume all rows will need to be measured by default
};
}
var _rowsHeightLookup$cur = rowsHeightLookup.current[row.id],
isResized = _rowsHeightLookup$cur.isResized,
needsFirstMeasurement = _rowsHeightLookup$cur.needsFirstMeasurement,
sizes = _rowsHeightLookup$cur.sizes;
var baseRowHeight = rowHeightFromDensity;
var existingBaseRowHeight = sizes.baseCenter;
if (isResized) {
// Do not recalculate resized row height and use the value from the lookup
baseRowHeight = existingBaseRowHeight;
} else if (getRowHeightProp) {
var rowHeightFromUser = getRowHeightProp(_extends({}, row, {
densityFactor: densityFactor
}));
if (rowHeightFromUser === 'auto') {
if (needsFirstMeasurement) {
var estimatedRowHeight = getEstimatedRowHeight ? getEstimatedRowHeight(_extends({}, row, {
densityFactor: densityFactor
})) : rowHeightFromDensity; // If the row was not measured yet use the estimated row height
baseRowHeight = estimatedRowHeight != null ? estimatedRowHeight : rowHeightFromDensity;
} else {
baseRowHeight = existingBaseRowHeight;
}
hasRowWithAutoHeight.current = true;
rowsHeightLookup.current[row.id].autoHeight = true;
} else {
// Default back to base rowHeight if getRowHeight returns null or undefined.
baseRowHeight = rowHeightFromUser != null ? rowHeightFromUser : rowHeightFromDensity;
rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
rowsHeightLookup.current[row.id].autoHeight = false;
}
} else {
rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
}
var existingBaseSizes = Object.entries(sizes).reduce(function (acc, _ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
size = _ref2[1];
if (/^base[A-Z]/.test(key)) {
acc[key] = size;
}
return acc;
}, {}); // We use an object to make simple to check if a height is already added or not
var initialHeights = _extends({}, existingBaseSizes, {
baseCenter: baseRowHeight
});
if (getRowSpacing) {
var _spacing$top, _spacing$bottom;
var indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
var spacing = getRowSpacing(_extends({}, row, {
isFirstVisible: indexRelativeToCurrentPage === 0,
isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
indexRelativeToCurrentPage: indexRelativeToCurrentPage
}));
initialHeights.spacingTop = (_spacing$top = spacing.top) != null ? _spacing$top : 0;
initialHeights.spacingBottom = (_spacing$bottom = spacing.bottom) != null ? _spacing$bottom : 0;
}
var processedSizes = apiRef.current.unstable_applyPipeProcessors('rowHeight', initialHeights, row);
rowsHeightLookup.current[row.id].sizes = processedSizes;
return processedSizes;
};
var positions = [];
var currentPageTotalHeight = currentPage.rows.reduce(function (acc, row) {
positions.push(acc);
var maximumBaseSize = 0;
var otherSizes = 0;
var processedSizes = calculateRowProcessedSizes(row);
Object.entries(processedSizes).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
size = _ref4[0],
value = _ref4[1];
if (/^base[A-Z]/.test(size)) {
maximumBaseSize = value > maximumBaseSize ? value : maximumBaseSize;
} else {
otherSizes += value;
}
});
return acc + maximumBaseSize + otherSizes;
}, 0);
pinnedRows == null ? void 0 : (_pinnedRows$top = pinnedRows.top) == null ? void 0 : _pinnedRows$top.forEach(function (row) {
calculateRowProcessedSizes(row);
});
pinnedRows == null ? void 0 : (_pinnedRows$bottom = pinnedRows.bottom) == null ? void 0 : _pinnedRows$bottom.forEach(function (row) {
calculateRowProcessedSizes(row);
});
apiRef.current.setState(function (state) {
return _extends({}, state, {
rowsMeta: {
currentPageTotalHeight: currentPageTotalHeight,
positions: positions
}
});
});
if (!hasRowWithAutoHeight.current) {
// No row has height=auto, so all rows are already measured
lastMeasuredRowIndex.current = Infinity;
}
apiRef.current.forceUpdate();
}, [apiRef, currentPage.rows, rowHeightFromDensity, getRowHeightProp, getRowSpacing, getEstimatedRowHeight, pinnedRows]);
var getRowHeight = React.useCallback(function (rowId) {
var height = rowsHeightLookup.current[rowId];
return height ? height.sizes.baseCenter : rowHeightFromDensity;
}, [rowHeightFromDensity]);
var getRowInternalSizes = function getRowInternalSizes(rowId) {
var _rowsHeightLookup$cur2;
return (_rowsHeightLookup$cur2 = rowsHeightLookup.current[rowId]) == null ? void 0 : _rowsHeightLookup$cur2.sizes;
};
var setRowHeight = React.useCallback(function (id, height) {
rowsHeightLookup.current[id].sizes.baseCenter = height;
rowsHeightLookup.current[id].isResized = true;
rowsHeightLookup.current[id].needsFirstMeasurement = false;
hydrateRowsMeta();
}, [hydrateRowsMeta]);
var debouncedHydrateRowsMeta = React.useMemo(function () {
return debounce(hydrateRowsMeta);
}, [hydrateRowsMeta]);
var storeMeasuredRowHeight = React.useCallback(function (id, height, position) {
if (!rowsHeightLookup.current[id] || !rowsHeightLookup.current[id].autoHeight) {
return;
} // Only trigger hydration if the value is different, otherwise we trigger a loop
var needsHydration = rowsHeightLookup.current[id].sizes["base".concat(capitalize(position))] !== height;
rowsHeightLookup.current[id].needsFirstMeasurement = false;
rowsHeightLookup.current[id].sizes["base".concat(capitalize(position))] = height;
if (needsHydration) {
debouncedHydrateRowsMeta();
}
}, [debouncedHydrateRowsMeta]);
var rowHasAutoHeight = React.useCallback(function (id) {
var _rowsHeightLookup$cur3;
return ((_rowsHeightLookup$cur3 = rowsHeightLookup.current[id]) == null ? void 0 : _rowsHeightLookup$cur3.autoHeight) || false;
}, []);
var getLastMeasuredRowIndex = React.useCallback(function () {
return lastMeasuredRowIndex.current;
}, []);
var setLastMeasuredRowIndex = React.useCallback(function (index) {
if (hasRowWithAutoHeight.current && index > lastMeasuredRowIndex.current) {
lastMeasuredRowIndex.current = index;
}
}, []);
var resetRowHeights = React.useCallback(function () {
rowsHeightLookup.current = {};
hydrateRowsMeta();
}, [hydrateRowsMeta]); // The effect is used to build the rows meta data - currentPageTotalHeight and positions.
// Because of variable row height this is needed for the virtualization
React.useEffect(function () {
hydrateRowsMeta();
}, [rowHeightFromDensity, filterState, paginationState, sortingState, hydrateRowsMeta]);
useGridRegisterPipeApplier(apiRef, 'rowHeight', hydrateRowsMeta);
var rowsMetaApi = {
unstable_getLastMeasuredRowIndex: getLastMeasuredRowIndex,
unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex,
unstable_rowHasAutoHeight: rowHasAutoHeight,
unstable_getRowHeight: getRowHeight,
unstable_getRowInternalSizes: getRowInternalSizes,
unstable_setRowHeight: setRowHeight,
unstable_storeRowHeightMeasurement: storeMeasuredRowHeight,
resetRowHeights: resetRowHeights
};
useGridApiMethod(apiRef, rowsMetaApi, 'GridRowsMetaApi');
};