UNPKG

@mui/x-data-grid

Version:

The community edition of the data grid component (MUI X).

227 lines (223 loc) 10.6 kB
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import * as React from 'react'; import { unstable_debounce as debounce, unstable_ownerDocument as ownerDocument, unstable_useEnhancedEffect as useEnhancedEffect, unstable_ownerWindow as ownerWindow } from '@mui/utils'; import { useGridApiEventHandler, useGridApiOptionHandler } from '../../utils/useGridApiEventHandler'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { gridColumnsTotalWidthSelector } from '../columns'; import { gridDensityFactorSelector } from '../density'; import { useGridSelector } from '../../utils'; import { getVisibleRows } from '../../utils/useGridVisibleRows'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { calculatePinnedRowsHeight } from '../rows/gridRowsUtils'; import { getTotalHeaderHeight } from '../columns/gridColumnsUtils'; var isTestEnvironment = process.env.NODE_ENV === 'test'; var hasScroll = function hasScroll(_ref) { var content = _ref.content, container = _ref.container, scrollBarSize = _ref.scrollBarSize; var hasScrollXIfNoYScrollBar = content.width > container.width; var hasScrollYIfNoXScrollBar = content.height > container.height; var hasScrollX = false; var hasScrollY = false; if (hasScrollXIfNoYScrollBar || hasScrollYIfNoXScrollBar) { hasScrollX = hasScrollXIfNoYScrollBar; hasScrollY = content.height + (hasScrollX ? scrollBarSize : 0) > container.height; // We recalculate the scroll x to consider the size of the y scrollbar. if (hasScrollY) { hasScrollX = content.width + scrollBarSize > container.width; } } return { hasScrollX: hasScrollX, hasScrollY: hasScrollY }; }; export function useGridDimensions(apiRef, props) { var logger = useGridLogger(apiRef, 'useResizeContainer'); var errorShown = React.useRef(false); var rootDimensionsRef = React.useRef(null); var fullDimensionsRef = React.useRef(null); var rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector); var densityFactor = useGridSelector(apiRef, gridDensityFactorSelector); var rowHeight = Math.floor(props.rowHeight * densityFactor); var totalHeaderHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight); var updateGridDimensionsRef = React.useCallback(function () { var _apiRef$current$rootE; var rootElement = (_apiRef$current$rootE = apiRef.current.rootElementRef) == null ? void 0 : _apiRef$current$rootE.current; var columnsTotalWidth = gridColumnsTotalWidthSelector(apiRef); var pinnedRowsHeight = calculatePinnedRowsHeight(apiRef); if (!rootDimensionsRef.current) { return; } var scrollBarSize; if (props.scrollbarSize != null) { scrollBarSize = props.scrollbarSize; } else if (!columnsTotalWidth || !rootElement) { scrollBarSize = 0; } else { var doc = ownerDocument(rootElement); var 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); scrollBarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth; rootElement.removeChild(scrollDiv); } var viewportOuterSize; var hasScrollX; var hasScrollY; if (props.autoHeight) { hasScrollY = false; hasScrollX = Math.round(columnsTotalWidth) > Math.round(rootDimensionsRef.current.width); viewportOuterSize = { width: rootDimensionsRef.current.width, height: rowsMeta.currentPageTotalHeight + (hasScrollX ? scrollBarSize : 0) }; } else { viewportOuterSize = { width: rootDimensionsRef.current.width, height: Math.max(rootDimensionsRef.current.height - totalHeaderHeight, 0) }; var scrollInformation = hasScroll({ content: { width: Math.round(columnsTotalWidth), height: rowsMeta.currentPageTotalHeight }, container: { width: Math.round(viewportOuterSize.width), height: viewportOuterSize.height - pinnedRowsHeight.top - pinnedRowsHeight.bottom }, scrollBarSize: scrollBarSize }); hasScrollY = scrollInformation.hasScrollY; hasScrollX = scrollInformation.hasScrollX; } var viewportInnerSize = { width: viewportOuterSize.width - (hasScrollY ? scrollBarSize : 0), height: viewportOuterSize.height - (hasScrollX ? scrollBarSize : 0) }; var newFullDimensions = { viewportOuterSize: viewportOuterSize, viewportInnerSize: viewportInnerSize, hasScrollX: hasScrollX, hasScrollY: hasScrollY, scrollBarSize: scrollBarSize }; var prevDimensions = fullDimensionsRef.current; fullDimensionsRef.current = newFullDimensions; if (newFullDimensions.viewportInnerSize.width !== (prevDimensions == null ? void 0 : prevDimensions.viewportInnerSize.width) || newFullDimensions.viewportInnerSize.height !== (prevDimensions == null ? void 0 : prevDimensions.viewportInnerSize.height)) { apiRef.current.publishEvent('viewportInnerSizeChange', newFullDimensions.viewportInnerSize); } }, [apiRef, props.scrollbarSize, props.autoHeight, rowsMeta.currentPageTotalHeight, totalHeaderHeight]); var _React$useState = React.useState(), _React$useState2 = _slicedToArray(_React$useState, 2), savedSize = _React$useState2[0], setSavedSize = _React$useState2[1]; var debouncedSetSavedSize = React.useMemo(function () { return debounce(setSavedSize, 60); }, []); var previousSize = React.useRef(); useEnhancedEffect(function () { if (savedSize) { updateGridDimensionsRef(); apiRef.current.publishEvent('debouncedResize', rootDimensionsRef.current); } }, [apiRef, savedSize, updateGridDimensionsRef]); // This is the function called by apiRef.current.resize() var resize = React.useCallback(function () { apiRef.current.computeSizeAndPublishResizeEvent(); }, [apiRef]); var getRootDimensions = React.useCallback(function () { return fullDimensionsRef.current; }, []); var getViewportPageSize = React.useCallback(function () { var dimensions = apiRef.current.getRootDimensions(); if (!dimensions) { return 0; } var currentPage = 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) { var renderContext = apiRef.current.getRenderContext(); var viewportPageSize = renderContext.lastRowIndex - renderContext.firstRowIndex; return Math.min(viewportPageSize - 1, currentPage.rows.length); } var maximumPageSizeWithoutScrollBar = Math.floor(dimensions.viewportInnerSize.height / rowHeight); return Math.min(maximumPageSizeWithoutScrollBar, currentPage.rows.length); }, [apiRef, props.pagination, props.paginationMode, props.getRowHeight, rowHeight]); var computeSizeAndPublishResizeEvent = React.useCallback(function () { var _apiRef$current$mainE, _previousSize$current, _previousSize$current2; var mainEl = (_apiRef$current$mainE = apiRef.current.mainElementRef) == null ? void 0 : _apiRef$current$mainE.current; if (!mainEl) { return; } var win = ownerWindow(mainEl); var computedStyle = win.getComputedStyle(mainEl); var height = parseFloat(computedStyle.height) || 0; var width = parseFloat(computedStyle.width) || 0; var hasHeightChanged = height !== ((_previousSize$current = previousSize.current) == null ? void 0 : _previousSize$current.height); var hasWidthChanged = width !== ((_previousSize$current2 = previousSize.current) == null ? void 0 : _previousSize$current2.width); if (!previousSize.current || hasHeightChanged || hasWidthChanged) { var size = { width: width, height: height }; apiRef.current.publishEvent('resize', size); previousSize.current = size; } }, [apiRef]); var dimensionsApi = { resize: resize, getRootDimensions: getRootDimensions }; var dimensionsPrivateApi = { getViewportPageSize: getViewportPageSize, updateGridDimensionsRef: updateGridDimensionsRef, computeSizeAndPublishResizeEvent: computeSizeAndPublishResizeEvent }; useGridApiMethod(apiRef, dimensionsApi, 'public'); useGridApiMethod(apiRef, dimensionsPrivateApi, 'private'); var isFirstSizing = React.useRef(true); var handleResize = React.useCallback(function (size) { rootDimensionsRef.current = size; // jsdom has no layout capabilities var 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 (isTestEnvironment) { // We don't need to debounce the resize for tests. setSavedSize(size); isFirstSizing.current = false; return; } 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]); useEnhancedEffect(function () { return updateGridDimensionsRef(); }, [updateGridDimensionsRef]); useGridApiOptionHandler(apiRef, 'sortedRowsSet', updateGridDimensionsRef); useGridApiOptionHandler(apiRef, 'paginationModelChange', updateGridDimensionsRef); useGridApiOptionHandler(apiRef, 'columnsChange', updateGridDimensionsRef); useGridApiEventHandler(apiRef, 'resize', handleResize); useGridApiOptionHandler(apiRef, 'debouncedResize', props.onResize); }