@mui/x-data-grid
Version:
The Community plan edition of the Data Grid components (MUI X).
299 lines (294 loc) • 14.5 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.dimensionsStateInitializer = void 0;
exports.useGridDimensions = useGridDimensions;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var React = _interopRequireWildcard(require("react"));
var _utils = require("@mui/utils");
var _useGridApiEventHandler = require("../../utils/useGridApiEventHandler");
var _useGridApiMethod = require("../../utils/useGridApiMethod");
var _throttle = require("../../../utils/throttle");
var _useGridLogger = require("../../utils/useGridLogger");
var _columns = require("../columns");
var _gridDimensionsSelectors = require("./gridDimensionsSelectors");
var _density = require("../density");
var _virtualization = require("../virtualization");
var _utils2 = require("../../utils");
var _useGridVisibleRows = require("../../utils/useGridVisibleRows");
var _gridRowsMetaSelector = require("../rows/gridRowsMetaSelector");
var _gridRowsUtils = require("../rows/gridRowsUtils");
var _gridColumnsUtils = require("../columns/gridColumnsUtils");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const EMPTY_SIZE = {
width: 0,
height: 0
};
const EMPTY_DIMENSIONS = {
isReady: false,
root: EMPTY_SIZE,
viewportOuterSize: EMPTY_SIZE,
viewportInnerSize: EMPTY_SIZE,
contentSize: EMPTY_SIZE,
minimumSize: EMPTY_SIZE,
hasScrollX: false,
hasScrollY: false,
scrollbarSize: 0,
headerHeight: 0,
headerFilterHeight: 0,
rowWidth: 0,
rowHeight: 0,
columnsTotalWidth: 0,
leftPinnedWidth: 0,
rightPinnedWidth: 0,
headersTotalHeight: 0,
topContainerHeight: 0,
bottomContainerHeight: 0
};
const dimensionsStateInitializer = state => {
const dimensions = EMPTY_DIMENSIONS;
return (0, _extends2.default)({}, state, {
dimensions
});
};
exports.dimensionsStateInitializer = dimensionsStateInitializer;
function useGridDimensions(apiRef, props) {
const logger = (0, _useGridLogger.useGridLogger)(apiRef, 'useResizeContainer');
const errorShown = React.useRef(false);
const rootDimensionsRef = React.useRef(EMPTY_SIZE);
const rowsMeta = (0, _utils2.useGridSelector)(apiRef, _gridRowsMetaSelector.gridRowsMetaSelector);
const pinnedColumns = (0, _utils2.useGridSelector)(apiRef, _columns.gridVisiblePinnedColumnDefinitionsSelector);
const densityFactor = (0, _utils2.useGridSelector)(apiRef, _density.gridDensityFactorSelector);
const rowHeight = Math.floor(props.rowHeight * densityFactor);
const headerHeight = Math.floor(props.columnHeaderHeight * densityFactor);
const headerFilterHeight = Math.floor((props.headerFilterHeight ?? props.columnHeaderHeight) * densityFactor);
const columnsTotalWidth = roundToDecimalPlaces((0, _columns.gridColumnsTotalWidthSelector)(apiRef), 6);
const headersTotalHeight = (0, _gridColumnsUtils.getTotalHeaderHeight)(apiRef, props);
const leftPinnedWidth = pinnedColumns.left.reduce((w, col) => w + col.computedWidth, 0);
const rightPinnedWidth = pinnedColumns.right.reduce((w, col) => w + col.computedWidth, 0);
const [savedSize, setSavedSize] = React.useState();
const debouncedSetSavedSize = React.useMemo(() => (0, _throttle.throttle)(setSavedSize, props.resizeThrottleMs), [props.resizeThrottleMs]);
const previousSize = React.useRef();
const getRootDimensions = () => apiRef.current.state.dimensions;
const setDimensions = (0, _utils.unstable_useEventCallback)(dimensions => {
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
dimensions
}));
});
const resize = React.useCallback(() => {
const element = apiRef.current.mainElementRef.current;
if (!element) {
return;
}
const computedStyle = (0, _utils.unstable_ownerWindow)(element).getComputedStyle(element);
const height = parseFloat(computedStyle.height) || 0;
const width = parseFloat(computedStyle.width) || 0;
const hasHeightChanged = height !== previousSize.current?.height;
const hasWidthChanged = width !== previousSize.current?.width;
if (!previousSize.current || hasHeightChanged || hasWidthChanged) {
const size = {
width,
height
};
apiRef.current.publishEvent('resize', size);
previousSize.current = size;
}
}, [apiRef]);
const getViewportPageSize = React.useCallback(() => {
const dimensions = (0, _gridDimensionsSelectors.gridDimensionsSelector)(apiRef.current.state);
if (!dimensions.isReady) {
return 0;
}
const currentPage = (0, _useGridVisibleRows.getVisibleRows)(apiRef, {
pagination: props.pagination,
paginationMode: props.paginationMode
});
// TODO: Use a combination of scrollTop, dimensions.viewportInnerSize.height and rowsMeta.possitions
// to find out the maximum number of rows that can fit in the visible part of the grid
if (props.getRowHeight) {
const renderContext = (0, _virtualization.gridRenderContextSelector)(apiRef);
const viewportPageSize = renderContext.lastRowIndex - renderContext.firstRowIndex;
return Math.min(viewportPageSize - 1, currentPage.rows.length);
}
const maximumPageSizeWithoutScrollBar = Math.floor(dimensions.viewportInnerSize.height / rowHeight);
return Math.min(maximumPageSizeWithoutScrollBar, currentPage.rows.length);
}, [apiRef, props.pagination, props.paginationMode, props.getRowHeight, rowHeight]);
const updateDimensions = React.useCallback(() => {
const rootElement = apiRef.current.rootElementRef.current;
const pinnedRowsHeight = (0, _gridRowsUtils.calculatePinnedRowsHeight)(apiRef);
const scrollbarSize = measureScrollbarSize(rootElement, columnsTotalWidth, props.scrollbarSize);
const topContainerHeight = headersTotalHeight + pinnedRowsHeight.top;
const bottomContainerHeight = pinnedRowsHeight.bottom;
const nonPinnedColumnsTotalWidth = columnsTotalWidth - leftPinnedWidth - rightPinnedWidth;
const contentSize = {
width: nonPinnedColumnsTotalWidth,
height: rowsMeta.currentPageTotalHeight
};
let viewportOuterSize;
let viewportInnerSize;
let hasScrollX = false;
let hasScrollY = false;
if (props.autoHeight) {
hasScrollY = false;
hasScrollX = Math.round(columnsTotalWidth) > Math.round(rootDimensionsRef.current.width);
viewportOuterSize = {
width: rootDimensionsRef.current.width,
height: topContainerHeight + bottomContainerHeight + contentSize.height
};
viewportInnerSize = {
width: Math.max(0, viewportOuterSize.width - (hasScrollY ? scrollbarSize : 0)),
height: Math.max(0, viewportOuterSize.height - (hasScrollX ? scrollbarSize : 0))
};
} else {
viewportOuterSize = {
width: rootDimensionsRef.current.width,
height: rootDimensionsRef.current.height
};
viewportInnerSize = {
width: Math.max(0, viewportOuterSize.width - leftPinnedWidth - rightPinnedWidth),
height: Math.max(0, viewportOuterSize.height - topContainerHeight - bottomContainerHeight)
};
const content = contentSize;
const container = viewportInnerSize;
const hasScrollXIfNoYScrollBar = content.width > container.width;
const hasScrollYIfNoXScrollBar = content.height > container.height;
if (hasScrollXIfNoYScrollBar || hasScrollYIfNoXScrollBar) {
hasScrollY = hasScrollYIfNoXScrollBar;
hasScrollX = content.width + (hasScrollY ? scrollbarSize : 0) > container.width;
// We recalculate the scroll y to consider the size of the x scrollbar.
if (hasScrollX) {
hasScrollY = content.height + scrollbarSize > container.height;
}
}
if (hasScrollY) {
viewportInnerSize.width -= scrollbarSize;
}
if (hasScrollX) {
viewportInnerSize.height -= scrollbarSize;
}
}
const rowWidth = Math.max(viewportOuterSize.width, columnsTotalWidth + (hasScrollY ? scrollbarSize : 0));
const minimumSize = {
width: columnsTotalWidth,
height: topContainerHeight + contentSize.height + bottomContainerHeight
};
const newDimensions = {
isReady: true,
root: rootDimensionsRef.current,
viewportOuterSize,
viewportInnerSize,
contentSize,
minimumSize,
hasScrollX,
hasScrollY,
scrollbarSize,
headerHeight,
headerFilterHeight,
rowWidth,
rowHeight,
columnsTotalWidth,
leftPinnedWidth,
rightPinnedWidth,
headersTotalHeight,
topContainerHeight,
bottomContainerHeight
};
const prevDimensions = apiRef.current.state.dimensions;
setDimensions(newDimensions);
if (newDimensions.viewportInnerSize.width !== prevDimensions.viewportInnerSize.width || newDimensions.viewportInnerSize.height !== prevDimensions.viewportInnerSize.height) {
apiRef.current.publishEvent('viewportInnerSizeChange', newDimensions.viewportInnerSize);
}
apiRef.current.updateRenderContext?.();
}, [apiRef, setDimensions, props.scrollbarSize, props.autoHeight, rowsMeta.currentPageTotalHeight, rowHeight, headerHeight, headerFilterHeight, columnsTotalWidth, headersTotalHeight, leftPinnedWidth, rightPinnedWidth]);
const apiPublic = {
resize,
getRootDimensions
};
const apiPrivate = {
updateDimensions,
getViewportPageSize
};
(0, _useGridApiMethod.useGridApiMethod)(apiRef, apiPublic, 'public');
(0, _useGridApiMethod.useGridApiMethod)(apiRef, apiPrivate, 'private');
(0, _utils.unstable_useEnhancedEffect)(() => {
if (savedSize) {
updateDimensions();
apiRef.current.publishEvent('debouncedResize', rootDimensionsRef.current);
}
}, [apiRef, savedSize, updateDimensions]);
const root = apiRef.current.rootElementRef.current;
const dimensions = apiRef.current.state.dimensions;
(0, _utils.unstable_useEnhancedEffect)(() => {
if (!root) {
return;
}
const set = (k, v) => root.style.setProperty(k, v);
set('--DataGrid-width', `${dimensions.viewportOuterSize.width}px`);
set('--DataGrid-hasScrollX', `${Number(dimensions.hasScrollX)}`);
set('--DataGrid-hasScrollY', `${Number(dimensions.hasScrollY)}`);
set('--DataGrid-scrollbarSize', `${dimensions.scrollbarSize}px`);
set('--DataGrid-rowWidth', `${dimensions.rowWidth}px`);
set('--DataGrid-columnsTotalWidth', `${dimensions.columnsTotalWidth}px`);
set('--DataGrid-leftPinnedWidth', `${dimensions.leftPinnedWidth}px`);
set('--DataGrid-rightPinnedWidth', `${dimensions.rightPinnedWidth}px`);
set('--DataGrid-headerHeight', `${dimensions.headerHeight}px`);
set('--DataGrid-headersTotalHeight', `${dimensions.headersTotalHeight}px`);
set('--DataGrid-topContainerHeight', `${dimensions.topContainerHeight}px`);
set('--DataGrid-bottomContainerHeight', `${dimensions.bottomContainerHeight}px`);
set('--height', `${dimensions.rowHeight}px`);
}, [root, dimensions]);
const isFirstSizing = React.useRef(true);
const handleResize = React.useCallback(size => {
rootDimensionsRef.current = size;
// jsdom has no layout capabilities
const isJSDOM = /jsdom/.test(window.navigator.userAgent);
if (size.height === 0 && !errorShown.current && !props.autoHeight && !isJSDOM) {
logger.error(['The parent DOM element of the data grid has an empty height.', 'Please make sure that this element has an intrinsic height.', 'The grid displays with a height of 0px.', '', 'More details: https://mui.com/r/x-data-grid-no-dimensions.'].join('\n'));
errorShown.current = true;
}
if (size.width === 0 && !errorShown.current && !isJSDOM) {
logger.error(['The parent DOM element of the data grid has an empty width.', 'Please make sure that this element has an intrinsic width.', 'The grid displays with a width of 0px.', '', 'More details: https://mui.com/r/x-data-grid-no-dimensions.'].join('\n'));
errorShown.current = true;
}
if (isFirstSizing.current) {
// We want to initialize the grid dimensions as soon as possible to avoid flickering
setSavedSize(size);
isFirstSizing.current = false;
return;
}
debouncedSetSavedSize(size);
}, [props.autoHeight, debouncedSetSavedSize, logger]);
(0, _utils.unstable_useEnhancedEffect)(updateDimensions, [updateDimensions]);
(0, _useGridApiEventHandler.useGridApiOptionHandler)(apiRef, 'sortedRowsSet', updateDimensions);
(0, _useGridApiEventHandler.useGridApiOptionHandler)(apiRef, 'paginationModelChange', updateDimensions);
(0, _useGridApiEventHandler.useGridApiOptionHandler)(apiRef, 'columnsChange', updateDimensions);
(0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'resize', handleResize);
(0, _useGridApiEventHandler.useGridApiOptionHandler)(apiRef, 'debouncedResize', props.onResize);
}
function measureScrollbarSize(rootElement, columnsTotalWidth, scrollbarSize) {
if (scrollbarSize !== undefined) {
return scrollbarSize;
}
if (rootElement === null || columnsTotalWidth === 0) {
return 0;
}
const doc = (0, _utils.unstable_ownerDocument)(rootElement);
const scrollDiv = doc.createElement('div');
scrollDiv.style.width = '99px';
scrollDiv.style.height = '99px';
scrollDiv.style.position = 'absolute';
scrollDiv.style.overflow = 'scroll';
scrollDiv.className = 'scrollDiv';
rootElement.appendChild(scrollDiv);
const size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
rootElement.removeChild(scrollDiv);
return size;
}
// Get rid of floating point imprecision errors
// https://github.com/mui/mui-x/issues/9550#issuecomment-1619020477
function roundToDecimalPlaces(value, decimals) {
return Math.round(value * 10 ** decimals) / 10 ** decimals;
}
;