UNPKG

@mui/x-data-grid

Version:

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

445 lines (366 loc) 20.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useGridColumnHeaders = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var ReactDOM = _interopRequireWildcard(require("react-dom")); var _utils = require("@mui/material/utils"); var _styles = require("@mui/material/styles"); var _reselect = require("reselect"); var _useGridApiContext = require("../../utils/useGridApiContext"); var _useGridSelector = require("../../utils/useGridSelector"); var _gridColumnsSelector = require("../columns/gridColumnsSelector"); var _gridFocusStateSelector = require("../focus/gridFocusStateSelector"); var _densitySelector = require("../density/densitySelector"); var _gridFilterSelector = require("../filter/gridFilterSelector"); var _gridSortingSelector = require("../sorting/gridSortingSelector"); var _columnMenuSelector = require("../columnMenu/columnMenuSelector"); var _useGridRootProps = require("../../utils/useGridRootProps"); var _useGridApiEventHandler = require("../../utils/useGridApiEventHandler"); var _GridColumnHeaderItem = require("../../../components/columnHeaders/GridColumnHeaderItem"); var _gridColumnsUtils = require("../columns/gridColumnsUtils"); var _useGridVisibleRows = require("../../utils/useGridVisibleRows"); var _useGridVirtualScroller = require("../virtualization/useGridVirtualScroller"); var _GridColumnGroupHeader = require("../../../components/columnHeaders/GridColumnGroupHeader"); var _utils2 = require("../../../utils/utils"); var _jsxRuntime = require("react/jsx-runtime"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // TODO: add the possibility to switch this value if needed for customization const MERGE_EMPTY_CELLS = true; const GridColumnHeaderRow = (0, _styles.styled)('div', { name: 'MuiDataGrid', slot: 'ColumnHeaderRow', overridesResolver: (props, styles) => styles.columnHeaderRow })(() => ({ display: 'flex' })); function isUIEvent(event) { return !!event.target; } const useGridColumnHeaders = props => { const { innerRef: innerRefProp, minColumnIndex = 0 } = props; const [dragCol, setDragCol] = React.useState(''); const [resizeCol, setResizeCol] = React.useState(''); const apiRef = (0, _useGridApiContext.useGridApiContext)(); const visibleColumns = (0, _useGridSelector.useGridSelector)(apiRef, _gridColumnsSelector.gridVisibleColumnDefinitionsSelector); const columnPositions = (0, _useGridSelector.useGridSelector)(apiRef, _gridColumnsSelector.gridColumnPositionsSelector); const tabIndexState = (0, _useGridSelector.useGridSelector)(apiRef, _gridFocusStateSelector.gridTabIndexColumnHeaderSelector); const cellTabIndexState = (0, _useGridSelector.useGridSelector)(apiRef, _gridFocusStateSelector.gridTabIndexCellSelector); const columnHeaderFocus = (0, _useGridSelector.useGridSelector)(apiRef, _gridFocusStateSelector.gridFocusColumnHeaderSelector); const headerHeight = (0, _useGridSelector.useGridSelector)(apiRef, _densitySelector.gridDensityHeaderHeightSelector); const headerGroupingMaxDepth = (0, _useGridSelector.useGridSelector)(apiRef, _densitySelector.gridDensityHeaderGroupingMaxDepthSelector); const totalHeaderHeight = (0, _useGridSelector.useGridSelector)(apiRef, _densitySelector.gridDensityTotalHeaderHeightSelector); const filterColumnLookup = (0, _useGridSelector.useGridSelector)(apiRef, _gridFilterSelector.gridFilterActiveItemsLookupSelector); const sortColumnLookup = (0, _useGridSelector.useGridSelector)(apiRef, _gridSortingSelector.gridSortColumnLookupSelector); const columnMenuState = (0, _useGridSelector.useGridSelector)(apiRef, _columnMenuSelector.gridColumnMenuSelector); const rootProps = (0, _useGridRootProps.useGridRootProps)(); const innerRef = React.useRef(null); const handleInnerRef = (0, _utils.useForkRef)(innerRefProp, innerRef); const [renderContext, setRenderContext] = React.useState(null); const prevRenderContext = React.useRef(renderContext); const prevScrollLeft = React.useRef(0); const currentPage = (0, _useGridVisibleRows.useGridVisibleRows)(apiRef, rootProps); React.useEffect(() => { apiRef.current.columnHeadersContainerElementRef.current.scrollLeft = 0; }, [apiRef]); // memoize `getFirstColumnIndexToRender`, since it's called on scroll const getFirstColumnIndexToRenderRef = React.useRef((0, _reselect.defaultMemoize)(_gridColumnsUtils.getFirstColumnIndexToRender, { equalityCheck: (a, b) => ['firstColumnIndex', 'minColumnIndex', 'columnBuffer'].every(key => a[key] === b[key]) })); const updateInnerPosition = React.useCallback(nextRenderContext => { const [firstRowToRender, lastRowToRender] = (0, _useGridVirtualScroller.getRenderableIndexes)({ firstIndex: nextRenderContext.firstRowIndex, lastIndex: nextRenderContext.lastRowIndex, minFirstIndex: 0, maxLastIndex: currentPage.rows.length, buffer: rootProps.rowBuffer }); const firstColumnToRender = getFirstColumnIndexToRenderRef.current({ firstColumnIndex: nextRenderContext.firstColumnIndex, minColumnIndex, columnBuffer: rootProps.columnBuffer, firstRowToRender, lastRowToRender, apiRef, visibleRows: currentPage.rows }); const offset = firstColumnToRender > 0 ? prevScrollLeft.current - columnPositions[firstColumnToRender] : prevScrollLeft.current; innerRef.current.style.transform = `translate3d(${-offset}px, 0px, 0px)`; }, [columnPositions, minColumnIndex, rootProps.columnBuffer, apiRef, currentPage.rows, rootProps.rowBuffer]); React.useLayoutEffect(() => { if (renderContext) { updateInnerPosition(renderContext); } }, [renderContext, updateInnerPosition]); const handleScroll = React.useCallback(({ left, renderContext: nextRenderContext = null }, event) => { var _prevRenderContext$cu, _prevRenderContext$cu2; if (!innerRef.current) { return; } // Ignore vertical scroll. // Excepts the first event which sets the previous render context. if (prevScrollLeft.current === left && ((_prevRenderContext$cu = prevRenderContext.current) == null ? void 0 : _prevRenderContext$cu.firstColumnIndex) === (nextRenderContext == null ? void 0 : nextRenderContext.firstColumnIndex) && ((_prevRenderContext$cu2 = prevRenderContext.current) == null ? void 0 : _prevRenderContext$cu2.lastColumnIndex) === (nextRenderContext == null ? void 0 : nextRenderContext.lastColumnIndex)) { return; } prevScrollLeft.current = left; // We can only update the position when we guarantee that the render context has been // rendered. This is achieved using ReactDOM.flushSync or when the context doesn't change. let canUpdateInnerPosition = false; if (nextRenderContext !== prevRenderContext.current || !prevRenderContext.current) { // ReactDOM.flushSync cannot be called on `scroll` events fired inside effects if (isUIEvent(event)) { // To prevent flickering, the inner position can only be updated after the new context has // been rendered. ReactDOM.flushSync ensures that the state changes will happen before // updating the position. ReactDOM.flushSync(() => { setRenderContext(nextRenderContext); }); canUpdateInnerPosition = true; } else { setRenderContext(nextRenderContext); } prevRenderContext.current = nextRenderContext; } else { canUpdateInnerPosition = true; } // Pass directly the render context to avoid waiting for the next render if (nextRenderContext && canUpdateInnerPosition) { updateInnerPosition(nextRenderContext); } }, [updateInnerPosition]); const handleColumnResizeStart = React.useCallback(params => setResizeCol(params.field), []); const handleColumnResizeStop = React.useCallback(() => setResizeCol(''), []); const handleColumnReorderStart = React.useCallback(params => setDragCol(params.field), []); const handleColumnReorderStop = React.useCallback(() => setDragCol(''), []); (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'columnResizeStart', handleColumnResizeStart); (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'columnResizeStop', handleColumnResizeStop); (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'columnHeaderDragStart', handleColumnReorderStart); (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'columnHeaderDragEnd', handleColumnReorderStop); (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'rowsScroll', handleScroll); // Helper for computation common between getColumnHeaders and getColumnGroupHeaders const getColumnsToRender = params => { const { renderContext: nextRenderContext = renderContext, minFirstColumn = minColumnIndex, maxLastColumn = visibleColumns.length } = params || {}; if (!nextRenderContext) { return null; } const [firstRowToRender, lastRowToRender] = (0, _useGridVirtualScroller.getRenderableIndexes)({ firstIndex: nextRenderContext.firstRowIndex, lastIndex: nextRenderContext.lastRowIndex, minFirstIndex: 0, maxLastIndex: currentPage.rows.length, buffer: rootProps.rowBuffer }); const firstColumnToRender = getFirstColumnIndexToRenderRef.current({ firstColumnIndex: nextRenderContext.firstColumnIndex, minColumnIndex: minFirstColumn, columnBuffer: rootProps.columnBuffer, apiRef, firstRowToRender, lastRowToRender, visibleRows: currentPage.rows }); const lastColumnToRender = Math.min(nextRenderContext.lastColumnIndex + rootProps.columnBuffer, maxLastColumn); const renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender); return { renderedColumns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn }; }; const getColumnHeaders = (params, other = {}) => { const columnsToRender = getColumnsToRender(params); if (columnsToRender == null) { return null; } const { renderedColumns, firstColumnToRender } = columnsToRender; const columns = []; for (let i = 0; i < renderedColumns.length; i += 1) { const column = renderedColumns[i]; const columnIndex = firstColumnToRender + i; const isFirstColumn = columnIndex === 0; const hasTabbableElement = !(tabIndexState === null && cellTabIndexState === null); const tabIndex = tabIndexState !== null && tabIndexState.field === column.field || isFirstColumn && !hasTabbableElement ? 0 : -1; const hasFocus = columnHeaderFocus !== null && columnHeaderFocus.field === column.field; const open = columnMenuState.open && columnMenuState.field === column.field; columns.push( /*#__PURE__*/(0, _jsxRuntime.jsx)(_GridColumnHeaderItem.GridColumnHeaderItem, (0, _extends2.default)({}, sortColumnLookup[column.field], { columnMenuOpen: open, filterItemsCounter: filterColumnLookup[column.field] && filterColumnLookup[column.field].length, headerHeight: headerHeight, isDragging: column.field === dragCol, column: column, colIndex: columnIndex, isResizing: resizeCol === column.field, isLastColumn: columnIndex === visibleColumns.length - 1, extendRowFullWidth: !rootProps.disableExtendRowFullWidth, hasFocus: hasFocus, tabIndex: tabIndex }, other), column.field)); } return /*#__PURE__*/(0, _jsxRuntime.jsx)(GridColumnHeaderRow, { role: "row", "aria-rowindex": headerGroupingMaxDepth + 1, children: columns }); }; const getParents = (path = [], depth) => path.slice(0, depth + 1); const getColumnGroupHeaders = params => { if (headerGroupingMaxDepth === 0) { return null; } const columnsToRender = getColumnsToRender(params); if (columnsToRender == null) { return null; } const { renderedColumns, firstColumnToRender, lastColumnToRender, maxLastColumn } = columnsToRender; const columns = []; const headerToRender = []; for (let depth = 0; depth < headerGroupingMaxDepth; depth += 1) { var _visibleColumns$first, _visibleColumns$first2, _visibleColumns$first3; // Initialize the header line with a grouping item containing all the columns on the left of the virtualization which are in the same group as the first group to render const initialHeader = []; let leftOverflow = 0; let columnIndex = firstColumnToRender - 1; const firstColumnToRenderGroup = (_visibleColumns$first = visibleColumns[firstColumnToRender]) == null ? void 0 : (_visibleColumns$first2 = _visibleColumns$first.groupPath) == null ? void 0 : _visibleColumns$first2[depth]; // The array of parent is used to manage empty grouping cell // When two empty grouping cell are next to each other, we merge them if the belong to the same group. const firstColumnToRenderGroupParents = getParents((_visibleColumns$first3 = visibleColumns[firstColumnToRender]) == null ? void 0 : _visibleColumns$first3.groupPath, depth); while (firstColumnToRenderGroup !== null && columnIndex >= minColumnIndex && (_visibleColumns$colum = visibleColumns[columnIndex]) != null && _visibleColumns$colum.groupPath && (0, _utils2.isDeepEqual)(getParents((_visibleColumns$colum2 = visibleColumns[columnIndex]) == null ? void 0 : _visibleColumns$colum2.groupPath, depth), firstColumnToRenderGroupParents)) { var _visibleColumns$colum, _visibleColumns$colum2, _column$computedWidth; const column = visibleColumns[columnIndex]; leftOverflow += (_column$computedWidth = column.computedWidth) != null ? _column$computedWidth : 0; if (initialHeader.length === 0) { var _column$computedWidth2; initialHeader.push({ width: (_column$computedWidth2 = column.computedWidth) != null ? _column$computedWidth2 : 0, fields: [column.field], groupId: firstColumnToRenderGroup, groupParents: firstColumnToRenderGroupParents, colIndex: columnIndex }); } else { var _column$computedWidth3; initialHeader[0].width += (_column$computedWidth3 = column.computedWidth) != null ? _column$computedWidth3 : 0; initialHeader[0].fields.push(column.field); initialHeader[0].colIndex = columnIndex; } columnIndex -= 1; } const depthInfo = renderedColumns.reduce((aggregated, column, i) => { var _column$computedWidth7; const lastItem = aggregated[aggregated.length - 1]; if (column.groupPath && column.groupPath.length > depth) { var _column$computedWidth5; if (lastItem && lastItem.groupId === column.groupPath[depth]) { var _column$computedWidth4; // Merge with the previous columns return [...aggregated.slice(0, aggregated.length - 1), (0, _extends2.default)({}, lastItem, { width: lastItem.width + ((_column$computedWidth4 = column.computedWidth) != null ? _column$computedWidth4 : 0), fields: [...lastItem.fields, column.field] })]; } // Create a new grouping return [...aggregated, { groupId: column.groupPath[depth], groupParents: getParents(column.groupPath, depth), width: (_column$computedWidth5 = column.computedWidth) != null ? _column$computedWidth5 : 0, fields: [column.field], colIndex: firstColumnToRender + i }]; } if (MERGE_EMPTY_CELLS && lastItem && lastItem.groupId === null && (0, _utils2.isDeepEqual)(getParents(column.groupPath, depth), lastItem.groupParents)) { var _column$computedWidth6; // We merge with previous column return [...aggregated.slice(0, aggregated.length - 1), (0, _extends2.default)({}, lastItem, { width: lastItem.width + ((_column$computedWidth6 = column.computedWidth) != null ? _column$computedWidth6 : 0), fields: [...lastItem.fields, column.field] })]; } // We create new empty cell return [...aggregated, { groupId: null, groupParents: getParents(column.groupPath, depth), width: (_column$computedWidth7 = column.computedWidth) != null ? _column$computedWidth7 : 0, fields: [column.field], colIndex: firstColumnToRender + i }]; }, initialHeader); columnIndex = lastColumnToRender; const lastColumnToRenderGroup = depthInfo[depthInfo.length - 1].groupId; while (lastColumnToRenderGroup !== null && columnIndex < maxLastColumn && (_visibleColumns$colum3 = visibleColumns[columnIndex]) != null && _visibleColumns$colum3.groupPath && ((_visibleColumns$colum4 = visibleColumns[columnIndex]) == null ? void 0 : (_visibleColumns$colum5 = _visibleColumns$colum4.groupPath) == null ? void 0 : _visibleColumns$colum5[depth]) === lastColumnToRenderGroup) { var _visibleColumns$colum3, _visibleColumns$colum4, _visibleColumns$colum5, _column$computedWidth8; const column = visibleColumns[columnIndex]; depthInfo[depthInfo.length - 1].width += (_column$computedWidth8 = column.computedWidth) != null ? _column$computedWidth8 : 0; depthInfo[depthInfo.length - 1].fields.push(column.field); columnIndex += 1; } headerToRender.push({ leftOverflow, elements: [...depthInfo] }); } headerToRender.forEach((depthInfo, depthIndex) => { columns.push( /*#__PURE__*/(0, _jsxRuntime.jsx)(GridColumnHeaderRow, { style: { height: `${headerHeight}px`, transform: `translateX(-${depthInfo.leftOverflow}px)` }, role: "row", "aria-rowindex": depthIndex + 1, children: depthInfo.elements.map(({ groupId, width, fields, colIndex }, groupIndex) => { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_GridColumnGroupHeader.GridColumnGroupHeader, { groupId: groupId, width: width, fields: fields, colIndex: colIndex, depth: depthIndex, isLastColumn: colIndex === visibleColumns.length - fields.length, extendRowFullWidth: !rootProps.disableExtendRowFullWidth, maxDepth: headerToRender.length, height: headerHeight }, groupIndex); }) }, depthIndex)); }); return columns; }; const rootStyle = { minHeight: totalHeaderHeight, maxHeight: totalHeaderHeight, lineHeight: `${headerHeight}px` }; return { renderContext, getColumnHeaders, getColumnGroupHeaders, isDragging: !!dragCol, getRootProps: (other = {}) => (0, _extends2.default)({ style: rootStyle }, other), getInnerProps: () => ({ ref: handleInnerRef, role: 'rowgroup' }) }; }; exports.useGridColumnHeaders = useGridColumnHeaders;