UNPKG

@mui/x-data-grid

Version:

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

183 lines (182 loc) 9.3 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from 'react'; import clsx from 'clsx'; import { styled } from '@mui/system'; import useForkRef from '@mui/utils/useForkRef'; import composeClasses from '@mui/utils/composeClasses'; import { useRtl } from '@mui/system/RtlProvider'; import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from "../hooks/utils/useGridApiContext.js"; import { useGridRootProps } from "../hooks/utils/useGridRootProps.js"; import { gridColumnPositionsSelector, gridDimensionsSelector, gridVisibleColumnDefinitionsSelector, gridVisiblePinnedColumnDefinitionsSelector, useGridApiEventHandler, useGridSelector } from "../hooks/index.js"; import { PinnedColumnPosition } from "../internals/constants.js"; import { gridColumnsTotalWidthSelector } from "../hooks/features/dimensions/gridDimensionsSelectors.js"; import { getDataGridUtilityClass, gridClasses } from "../constants/gridClasses.js"; import { getPinnedCellOffset } from "../internals/utils/getPinnedCellOffset.js"; import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from "../utils/cellBorderUtils.js"; import { escapeOperandAttributeSelector } from "../utils/domUtils.js"; import { GridScrollbarFillerCell } from "./GridScrollbarFillerCell.js"; import { rtlFlipSide } from "../utils/rtlFlipSide.js"; import { attachPinnedStyle } from "../internals/utils/index.js"; import { jsx as _jsx } from "react/jsx-runtime"; const SkeletonOverlay = styled('div', { name: 'MuiDataGrid', slot: 'SkeletonLoadingOverlay', overridesResolver: (props, styles) => styles.skeletonLoadingOverlay })({ minWidth: '100%', width: 'max-content', // prevents overflow: clip; cutting off the x axis height: '100%', overflow: 'clip' // y axis is hidden while the x axis is allowed to overflow }); const useUtilityClasses = ownerState => { const { classes } = ownerState; const slots = { root: ['skeletonLoadingOverlay'] }; return composeClasses(slots, getDataGridUtilityClass, classes); }; const getColIndex = el => parseInt(el.getAttribute('data-colindex'), 10); const GridSkeletonLoadingOverlay = forwardRef(function GridSkeletonLoadingOverlay(props, forwardedRef) { const rootProps = useGridRootProps(); const { slots } = rootProps; const isRtl = useRtl(); const classes = useUtilityClasses({ classes: rootProps.classes }); const ref = React.useRef(null); const handleRef = useForkRef(ref, forwardedRef); const apiRef = useGridApiContext(); const dimensions = useGridSelector(apiRef, gridDimensionsSelector); const viewportHeight = dimensions?.viewportInnerSize.height ?? 0; const skeletonRowsCount = Math.ceil(viewportHeight / dimensions.rowHeight); const totalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); const positions = useGridSelector(apiRef, gridColumnPositionsSelector); const inViewportCount = React.useMemo(() => positions.filter(value => value <= totalWidth).length, [totalWidth, positions]); const allVisibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); const columns = React.useMemo(() => allVisibleColumns.slice(0, inViewportCount), [allVisibleColumns, inViewportCount]); const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector); const getPinnedPosition = React.useCallback(field => { if (pinnedColumns.left.findIndex(col => col.field === field) !== -1) { return PinnedColumnPosition.LEFT; } if (pinnedColumns.right.findIndex(col => col.field === field) !== -1) { return PinnedColumnPosition.RIGHT; } return undefined; }, [pinnedColumns.left, pinnedColumns.right]); const children = React.useMemo(() => { const array = []; for (let i = 0; i < skeletonRowsCount; i += 1) { const rowCells = []; for (let colIndex = 0; colIndex < columns.length; colIndex += 1) { const column = columns[colIndex]; const pinnedPosition = getPinnedPosition(column.field); const isPinnedLeft = pinnedPosition === PinnedColumnPosition.LEFT; const isPinnedRight = pinnedPosition === PinnedColumnPosition.RIGHT; const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); const sectionLength = pinnedSide ? pinnedColumns[pinnedSide].length // pinned section : columns.length - pinnedColumns.left.length - pinnedColumns.right.length; // middle section const sectionIndex = pinnedSide ? pinnedColumns[pinnedSide].findIndex(col => col.field === column.field) // pinned section : colIndex - pinnedColumns.left.length; // middle section const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; const pinnedStyle = attachPinnedStyle({}, isRtl, pinnedPosition, getPinnedCellOffset(pinnedPosition, column.computedWidth, colIndex, positions, dimensions.columnsTotalWidth, scrollbarWidth)); const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width; const showRightBorder = shouldCellShowRightBorder(pinnedPosition, sectionIndex, sectionLength, rootProps.showCellVerticalBorder, gridHasFiller); const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, sectionIndex); const isLastColumn = colIndex === columns.length - 1; const isFirstPinnedRight = isPinnedRight && sectionIndex === 0; const hasFillerBefore = isFirstPinnedRight && gridHasFiller; const hasFillerAfter = isLastColumn && !isFirstPinnedRight && gridHasFiller; const expandedWidth = dimensions.viewportOuterSize.width - dimensions.columnsTotalWidth; const emptyCellWidth = Math.max(0, expandedWidth); const emptyCell = /*#__PURE__*/_jsx(slots.skeletonCell, { width: emptyCellWidth, empty: true }, `skeleton-filler-column-${i}`); const hasScrollbarFiller = isLastColumn && scrollbarWidth !== 0; if (hasFillerBefore) { rowCells.push(emptyCell); } rowCells.push(/*#__PURE__*/_jsx(slots.skeletonCell, { field: column.field, type: column.type, align: column.align, width: "var(--width)", height: dimensions.rowHeight, "data-colindex": colIndex, className: clsx(isPinnedLeft && gridClasses['cell--pinnedLeft'], isPinnedRight && gridClasses['cell--pinnedRight'], showRightBorder && gridClasses['cell--withRightBorder'], showLeftBorder && gridClasses['cell--withLeftBorder']), style: _extends({ '--width': `${column.computedWidth}px` }, pinnedStyle) }, `skeleton-column-${i}-${column.field}`)); if (hasFillerAfter) { rowCells.push(emptyCell); } if (hasScrollbarFiller) { rowCells.push(/*#__PURE__*/_jsx(GridScrollbarFillerCell, { pinnedRight: pinnedColumns.right.length > 0 }, `skeleton-scrollbar-filler-${i}`)); } } array.push(/*#__PURE__*/_jsx("div", { className: clsx(gridClasses.row, gridClasses.rowSkeleton, i === 0 && gridClasses['row--firstVisible']), children: rowCells }, `skeleton-row-${i}`)); } return array; }, [slots, columns, pinnedColumns, skeletonRowsCount, rootProps.showCellVerticalBorder, dimensions, positions, getPinnedPosition, isRtl]); // Sync the column resize of the overlay columns with the grid const handleColumnResize = params => { const { colDef, width } = params; const cells = ref.current?.querySelectorAll(`[data-field="${escapeOperandAttributeSelector(colDef.field)}"]`); if (!cells) { throw new Error('MUI X: Expected skeleton cells to be defined with `data-field` attribute.'); } const resizedColIndex = columns.findIndex(col => col.field === colDef.field); const pinnedPosition = getPinnedPosition(colDef.field); const isPinnedLeft = pinnedPosition === PinnedColumnPosition.LEFT; const isPinnedRight = pinnedPosition === PinnedColumnPosition.RIGHT; const currentWidth = getComputedStyle(cells[0]).getPropertyValue('--width'); const delta = parseInt(currentWidth, 10) - width; if (cells) { cells.forEach(element => { element.style.setProperty('--width', `${width}px`); }); } if (isPinnedLeft) { const pinnedCells = ref.current?.querySelectorAll(`.${gridClasses['cell--pinnedLeft']}`); pinnedCells?.forEach(element => { const colIndex = getColIndex(element); if (colIndex > resizedColIndex) { element.style.left = `${parseInt(getComputedStyle(element).left, 10) - delta}px`; } }); } if (isPinnedRight) { const pinnedCells = ref.current?.querySelectorAll(`.${gridClasses['cell--pinnedRight']}`); pinnedCells?.forEach(element => { const colIndex = getColIndex(element); if (colIndex < resizedColIndex) { element.style.right = `${parseInt(getComputedStyle(element).right, 10) + delta}px`; } }); } }; useGridApiEventHandler(apiRef, 'columnResize', handleColumnResize); return /*#__PURE__*/_jsx(SkeletonOverlay, _extends({ className: classes.root }, props, { ref: handleRef, children: children })); }); export { GridSkeletonLoadingOverlay };