UNPKG

@mui/x-data-grid

Version:

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

227 lines (223 loc) 9.49 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from 'react'; import useLazyRef from '@mui/utils/useLazyRef'; import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils'; import { ResizeObserver } from "../../../utils/ResizeObserver.js"; import { useGridVisibleRows } from "../../utils/useGridVisibleRows.js"; import { eslintUseValue } from "../../../utils/utils.js"; import { useGridApiMethod } from "../../utils/useGridApiMethod.js"; import { useGridSelector } from "../../utils/useGridSelector.js"; import { gridDensityFactorSelector } from "../density/densitySelector.js"; import { gridPaginationSelector } from "../pagination/gridPaginationSelector.js"; import { useGridRegisterPipeApplier } from "../../core/pipeProcessing/index.js"; import { gridPinnedRowsSelector, gridRowCountSelector } from "./gridRowsSelector.js"; import { gridDimensionsSelector, gridRowHeightSelector } from "../dimensions/gridDimensionsSelectors.js"; import { getValidRowHeight, getRowHeightWarning } from "./gridRowsUtils.js"; import { gridFocusedVirtualCellSelector } from "../virtualization/gridFocusedVirtualCellSelector.js"; /* eslint-disable no-underscore-dangle */ export const rowsMetaStateInitializer = (state, props, apiRef) => { apiRef.current.caches.rowsMeta = { heights: new Map() }; const baseRowHeight = gridRowHeightSelector(apiRef.current.state); const dataRowCount = gridRowCountSelector(apiRef); const pagination = gridPaginationSelector(apiRef.current.state); const rowCount = Math.min(pagination.enabled ? pagination.paginationModel.pageSize : dataRowCount, dataRowCount); return _extends({}, state, { rowsMeta: { currentPageTotalHeight: rowCount * baseRowHeight, positions: Array.from({ length: rowCount }, (_, i) => i * baseRowHeight), pinnedTopRowsTotalHeight: 0, pinnedBottomRowsTotalHeight: 0 } }); }; /** * @requires useGridPageSize (method) * @requires useGridPage (method) */ export const useGridRowsMeta = (apiRef, props) => { const { getRowHeight: getRowHeightProp, getRowSpacing, getEstimatedRowHeight } = props; const heightCache = apiRef.current.caches.rowsMeta.heights; const lastMeasuredRowIndex = React.useRef(-1); const hasRowWithAutoHeight = React.useRef(false); const isHeightMetaValid = React.useRef(false); const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector); const currentPage = useGridVisibleRows(apiRef, props); const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector); const rowHeight = useGridSelector(apiRef, gridRowHeightSelector); const getRowHeightEntry = rowId => { let entry = heightCache.get(rowId); if (entry === undefined) { entry = { content: rowHeight, spacingTop: 0, spacingBottom: 0, detail: 0, autoHeight: false, needsFirstMeasurement: true }; heightCache.set(rowId, entry); } return entry; }; const processHeightEntry = React.useCallback(row => { // HACK: rowHeight trails behind the most up-to-date value just enough to // mess the initial rowsMeta hydration :/ const baseRowHeight = gridDimensionsSelector(apiRef.current.state).rowHeight; eslintUseValue(rowHeight); const entry = apiRef.current.getRowHeightEntry(row.id); if (!getRowHeightProp) { entry.content = baseRowHeight; entry.needsFirstMeasurement = false; } else { const rowHeightFromUser = getRowHeightProp(_extends({}, row, { densityFactor })); if (rowHeightFromUser === 'auto') { if (entry.needsFirstMeasurement) { const estimatedRowHeight = getEstimatedRowHeight ? getEstimatedRowHeight(_extends({}, row, { densityFactor })) : baseRowHeight; // If the row was not measured yet use the estimated row height entry.content = estimatedRowHeight ?? baseRowHeight; } hasRowWithAutoHeight.current = true; entry.autoHeight = true; } else { // Default back to base rowHeight if getRowHeight returns invalid value. entry.content = getValidRowHeight(rowHeightFromUser, baseRowHeight, getRowHeightWarning); entry.needsFirstMeasurement = false; entry.autoHeight = false; } } if (getRowSpacing) { const indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id); const spacing = getRowSpacing(_extends({}, row, { isFirstVisible: indexRelativeToCurrentPage === 0, isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1, indexRelativeToCurrentPage })); entry.spacingTop = spacing.top ?? 0; entry.spacingBottom = spacing.bottom ?? 0; } else { entry.spacingTop = 0; entry.spacingBottom = 0; } apiRef.current.unstable_applyPipeProcessors('rowHeight', entry, row); return entry; }, [apiRef, currentPage.rows, getRowHeightProp, getEstimatedRowHeight, rowHeight, getRowSpacing, densityFactor]); const hydrateRowsMeta = React.useCallback(() => { hasRowWithAutoHeight.current = false; const pinnedTopRowsTotalHeight = pinnedRows.top.reduce((acc, row) => { const entry = processHeightEntry(row); return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail; }, 0); const pinnedBottomRowsTotalHeight = pinnedRows.bottom.reduce((acc, row) => { const entry = processHeightEntry(row); return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail; }, 0); const positions = []; const currentPageTotalHeight = currentPage.rows.reduce((acc, row) => { positions.push(acc); const entry = processHeightEntry(row); const total = entry.content + entry.spacingTop + entry.spacingBottom + entry.detail; return acc + total; }, 0); if (!hasRowWithAutoHeight.current) { // No row has height=auto, so all rows are already measured lastMeasuredRowIndex.current = Infinity; } const didHeightsChange = pinnedTopRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedTopRowsTotalHeight || pinnedBottomRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedBottomRowsTotalHeight || currentPageTotalHeight !== apiRef.current.state.rowsMeta.currentPageTotalHeight; const rowsMeta = { currentPageTotalHeight, positions, pinnedTopRowsTotalHeight, pinnedBottomRowsTotalHeight }; apiRef.current.setState(state => { return _extends({}, state, { rowsMeta }); }); if (didHeightsChange) { apiRef.current.updateDimensions(); } isHeightMetaValid.current = true; }, [apiRef, pinnedRows, currentPage.rows, processHeightEntry]); const getRowHeight = rowId => { return heightCache.get(rowId)?.content ?? rowHeight; }; const storeRowHeightMeasurement = (id, height) => { const entry = apiRef.current.getRowHeightEntry(id); const didChange = entry.content !== height; entry.needsFirstMeasurement = false; entry.content = height; isHeightMetaValid.current && (isHeightMetaValid.current = !didChange); }; const rowHasAutoHeight = id => { return heightCache.get(id)?.autoHeight ?? false; }; const getLastMeasuredRowIndex = () => { return lastMeasuredRowIndex.current; }; const setLastMeasuredRowIndex = index => { if (hasRowWithAutoHeight.current && index > lastMeasuredRowIndex.current) { lastMeasuredRowIndex.current = index; } }; const resetRowHeights = () => { heightCache.clear(); hydrateRowsMeta(); }; const resizeObserver = useLazyRef(() => new ResizeObserver(entries => { for (let i = 0; i < entries.length; i += 1) { const entry = entries[i]; const height = entry.borderBoxSize && entry.borderBoxSize.length > 0 ? entry.borderBoxSize[0].blockSize : entry.contentRect.height; const rowId = entry.target.__mui_id; const focusedVirtualRowId = gridFocusedVirtualCellSelector(apiRef)?.id; if (focusedVirtualRowId === rowId && height === 0) { // Focused virtual row has 0 height. // We don't want to store it to avoid scroll jumping. // https://github.com/mui/mui-x/issues/14726 return; } apiRef.current.unstable_storeRowHeightMeasurement(rowId, height); } if (!isHeightMetaValid.current) { apiRef.current.requestPipeProcessorsApplication('rowHeight'); } })).current; const observeRowHeight = (element, rowId) => { element.__mui_id = rowId; resizeObserver.observe(element); return () => resizeObserver.unobserve(element); }; useGridRegisterPipeApplier(apiRef, 'rowHeight', 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 useEnhancedEffect(() => { hydrateRowsMeta(); }, [hydrateRowsMeta]); const rowsMetaApi = { unstable_getRowHeight: getRowHeight, unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex, unstable_storeRowHeightMeasurement: storeRowHeightMeasurement, resetRowHeights }; const rowsMetaPrivateApi = { hydrateRowsMeta, observeRowHeight, rowHasAutoHeight, getRowHeightEntry, getLastMeasuredRowIndex }; useGridApiMethod(apiRef, rowsMetaApi, 'public'); useGridApiMethod(apiRef, rowsMetaPrivateApi, 'private'); };