@mui/x-data-grid
Version:
The Community plan edition of the Data Grid components (MUI X).
183 lines (182 loc) • 9.3 kB
JavaScript
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 };