wip:milestone 0 fixes
Some checks failed
CI/CD Pipeline / unit-tests (push) Failing after 1m16s
CI/CD Pipeline / integration-tests (push) Failing after 2m32s
CI/CD Pipeline / lint (push) Successful in 5m22s
CI/CD Pipeline / e2e-tests (push) Has been skipped
CI/CD Pipeline / build (push) Has been skipped

This commit is contained in:
2026-03-15 12:35:42 +02:00
parent 6708cf28a7
commit cffdf8af86
61266 changed files with 4511646 additions and 1938 deletions

View File

@@ -0,0 +1,97 @@
import _typeof from "@babel/runtime/helpers/esm/typeof";
import * as React from 'react';
import { useGridApiOptionHandler, useGridNativeEventListener } from '../../utils';
import { gridFocusCellSelector } from '../focus/gridFocusStateSelector';
import { serializeCellValue } from '../export/serializers/csvSerializer';
function writeToClipboardPolyfill(data) {
var span = document.createElement('span');
span.style.whiteSpace = 'pre';
span.style.userSelect = 'all';
span.style.opacity = '0px';
span.textContent = data;
document.body.appendChild(span);
var range = document.createRange();
range.selectNode(span);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
document.execCommand('copy');
} finally {
document.body.removeChild(span);
}
}
function copyToClipboard(data) {
if (navigator.clipboard) {
navigator.clipboard.writeText(data).catch(function () {
writeToClipboardPolyfill(data);
});
} else {
writeToClipboardPolyfill(data);
}
}
function hasNativeSelection(element) {
var _window$getSelection;
// When getSelection is called on an <iframe> that is not displayed Firefox will return null.
if ((_window$getSelection = window.getSelection()) != null && _window$getSelection.toString()) {
return true;
}
// window.getSelection() returns an empty string in Firefox for selections inside a form element.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=85686.
// Instead, we can use element.selectionStart that is only defined on form elements.
if (element && (element.selectionEnd || 0) - (element.selectionStart || 0) > 0) {
return true;
}
return false;
}
/**
* @requires useGridCsvExport (method)
* @requires useGridSelection (method)
*/
export var useGridClipboard = function useGridClipboard(apiRef, props) {
var ignoreValueFormatterProp = props.unstable_ignoreValueFormatterDuringExport;
var ignoreValueFormatter = (_typeof(ignoreValueFormatterProp) === 'object' ? ignoreValueFormatterProp == null ? void 0 : ignoreValueFormatterProp.clipboardExport : ignoreValueFormatterProp) || false;
var clipboardCopyCellDelimiter = props.clipboardCopyCellDelimiter;
var handleCopy = React.useCallback(function (event) {
if (!((event.ctrlKey || event.metaKey) && event.key === 'c')) {
return;
}
// Do nothing if there's a native selection
if (hasNativeSelection(event.target)) {
return;
}
var textToCopy = '';
var selectedRows = apiRef.current.getSelectedRows();
if (selectedRows.size > 0) {
textToCopy = apiRef.current.getDataAsCsv({
includeHeaders: false,
delimiter: clipboardCopyCellDelimiter,
shouldAppendQuotes: false,
escapeFormulas: false
});
} else {
var focusedCell = gridFocusCellSelector(apiRef);
if (focusedCell) {
var cellParams = apiRef.current.getCellParams(focusedCell.id, focusedCell.field);
textToCopy = serializeCellValue(cellParams, {
csvOptions: {
delimiter: clipboardCopyCellDelimiter,
shouldAppendQuotes: false,
escapeFormulas: false
},
ignoreValueFormatter: ignoreValueFormatter
});
}
}
textToCopy = apiRef.current.unstable_applyPipeProcessors('clipboardCopy', textToCopy);
if (textToCopy) {
copyToClipboard(textToCopy);
apiRef.current.publishEvent('clipboardCopy', textToCopy);
}
}, [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter]);
useGridNativeEventListener(apiRef, apiRef.current.rootElementRef, 'keydown', handleCopy);
useGridApiOptionHandler(apiRef, 'clipboardCopy', props.onClipboardCopy);
};

View File

@@ -0,0 +1,24 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
/**
* @category ColumnGrouping
* @ignore - do not document.
*/
export var gridColumnGroupingSelector = function gridColumnGroupingSelector(state) {
return state.columnGrouping;
};
export var gridColumnGroupsUnwrappedModelSelector = createSelectorMemoized(gridColumnGroupingSelector, function (columnGrouping) {
var _columnGrouping$unwra;
return (_columnGrouping$unwra = columnGrouping == null ? void 0 : columnGrouping.unwrappedGroupingModel) != null ? _columnGrouping$unwra : {};
});
export var gridColumnGroupsLookupSelector = createSelectorMemoized(gridColumnGroupingSelector, function (columnGrouping) {
var _columnGrouping$looku;
return (_columnGrouping$looku = columnGrouping == null ? void 0 : columnGrouping.lookup) != null ? _columnGrouping$looku : {};
});
export var gridColumnGroupsHeaderStructureSelector = createSelectorMemoized(gridColumnGroupingSelector, function (columnGrouping) {
var _columnGrouping$heade;
return (_columnGrouping$heade = columnGrouping == null ? void 0 : columnGrouping.headerStructure) != null ? _columnGrouping$heade : [];
});
export var gridColumnGroupsHeaderMaxDepthSelector = createSelector(gridColumnGroupingSelector, function (columnGrouping) {
var _columnGrouping$maxDe;
return (_columnGrouping$maxDe = columnGrouping == null ? void 0 : columnGrouping.maxDepth) != null ? _columnGrouping$maxDe : 0;
});

View File

@@ -0,0 +1,92 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import { isLeaf } from '../../../models/gridColumnGrouping';
import { isDeepEqual } from '../../../utils/utils';
// This is the recurrence function that help writing `unwrapGroupingColumnModel()`
var recurrentUnwrapGroupingColumnModel = function recurrentUnwrapGroupingColumnModel(columnGroupNode, parents, unwrappedGroupingModelToComplete) {
if (isLeaf(columnGroupNode)) {
if (unwrappedGroupingModelToComplete[columnGroupNode.field] !== undefined) {
throw new Error(["MUI: columnGroupingModel contains duplicated field", "column field ".concat(columnGroupNode.field, " occurs two times in the grouping model:"), "- ".concat(unwrappedGroupingModelToComplete[columnGroupNode.field].join(' > ')), "- ".concat(parents.join(' > '))].join('\n'));
}
unwrappedGroupingModelToComplete[columnGroupNode.field] = parents;
return;
}
var groupId = columnGroupNode.groupId,
children = columnGroupNode.children;
children.forEach(function (child) {
recurrentUnwrapGroupingColumnModel(child, [].concat(_toConsumableArray(parents), [groupId]), unwrappedGroupingModelToComplete);
});
};
/**
* This is a function that provide for each column the array of its parents.
* Parents are ordered from the root to the leaf.
* @param columnGroupingModel The model such as provided in DataGrid props
* @returns An object `{[field]: groupIds}` where `groupIds` is the parents of the column `field`
*/
export var unwrapGroupingColumnModel = function unwrapGroupingColumnModel(columnGroupingModel) {
if (!columnGroupingModel) {
return {};
}
var unwrappedSubTree = {};
columnGroupingModel.forEach(function (columnGroupNode) {
recurrentUnwrapGroupingColumnModel(columnGroupNode, [], unwrappedSubTree);
});
return unwrappedSubTree;
};
export var getColumnGroupsHeaderStructure = function getColumnGroupsHeaderStructure(orderedColumns, unwrappedGroupingModel, pinnedFields) {
var getParents = function getParents(field) {
var _unwrappedGroupingMod;
return (_unwrappedGroupingMod = unwrappedGroupingModel[field]) != null ? _unwrappedGroupingMod : [];
};
var groupingHeaderStructure = [];
var maxDepth = Math.max.apply(Math, _toConsumableArray(orderedColumns.map(function (field) {
return getParents(field).length;
})));
var haveSameParents = function haveSameParents(field1, field2, depth) {
return isDeepEqual(getParents(field1).slice(0, depth + 1), getParents(field2).slice(0, depth + 1));
};
var haveDifferentContainers = function haveDifferentContainers(field1, field2) {
if (pinnedFields != null && pinnedFields.left && pinnedFields.left.includes(field1) && !pinnedFields.left.includes(field2)) {
return true;
}
if (pinnedFields != null && pinnedFields.right && !pinnedFields.right.includes(field1) && pinnedFields.right.includes(field2)) {
return true;
}
return false;
};
var _loop = function _loop(depth) {
var depthStructure = orderedColumns.reduce(function (structure, newField) {
var _getParents$depth;
var groupId = (_getParents$depth = getParents(newField)[depth]) != null ? _getParents$depth : null;
if (structure.length === 0) {
return [{
columnFields: [newField],
groupId: groupId
}];
}
var lastGroup = structure[structure.length - 1];
var prevField = lastGroup.columnFields[lastGroup.columnFields.length - 1];
var prevGroupId = lastGroup.groupId;
if (prevGroupId !== groupId || !haveSameParents(prevField, newField, depth) ||
// Fix for https://github.com/mui/mui-x/issues/7041
haveDifferentContainers(prevField, newField)) {
// It's a new group
return [].concat(_toConsumableArray(structure), [{
columnFields: [newField],
groupId: groupId
}]);
}
// It extends the previous group
return [].concat(_toConsumableArray(structure.slice(0, structure.length - 1)), [{
columnFields: [].concat(_toConsumableArray(lastGroup.columnFields), [newField]),
groupId: groupId
}]);
}, []);
groupingHeaderStructure.push(depthStructure);
};
for (var depth = 0; depth < maxDepth; depth += 1) {
_loop(depth);
}
return groupingHeaderStructure;
};

View File

@@ -0,0 +1,2 @@
export * from './gridColumnGroupsSelector';
export {};

View File

@@ -0,0 +1,145 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
var _excluded = ["groupId", "children"];
import * as React from 'react';
import { isLeaf } from '../../../models/gridColumnGrouping';
import { gridColumnGroupsLookupSelector, gridColumnGroupsUnwrappedModelSelector } from './gridColumnGroupsSelector';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { getColumnGroupsHeaderStructure, unwrapGroupingColumnModel } from './gridColumnGroupsUtils';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { gridColumnFieldsSelector, gridVisibleColumnFieldsSelector } from '../columns';
var createGroupLookup = function createGroupLookup(columnGroupingModel) {
var groupLookup = {};
columnGroupingModel.forEach(function (node) {
if (isLeaf(node)) {
return;
}
var groupId = node.groupId,
children = node.children,
other = _objectWithoutProperties(node, _excluded);
if (!groupId) {
throw new Error('MUI: An element of the columnGroupingModel does not have either `field` or `groupId`.');
}
if (!children) {
console.warn("MUI: group groupId=".concat(groupId, " has no children."));
}
var groupParam = _extends({}, other, {
groupId: groupId
});
var subTreeLookup = createGroupLookup(children);
if (subTreeLookup[groupId] !== undefined || groupLookup[groupId] !== undefined) {
throw new Error("MUI: The groupId ".concat(groupId, " is used multiple times in the columnGroupingModel."));
}
groupLookup = _extends({}, groupLookup, subTreeLookup, _defineProperty({}, groupId, groupParam));
});
return _extends({}, groupLookup);
};
export var columnGroupsStateInitializer = function columnGroupsStateInitializer(state, props, apiRef) {
var _props$experimentalFe, _props$columnGrouping, _props$columnGrouping2, _apiRef$current$state;
if (!((_props$experimentalFe = props.experimentalFeatures) != null && _props$experimentalFe.columnGrouping)) {
return state;
}
var columnFields = gridColumnFieldsSelector(apiRef);
var visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef);
var groupLookup = createGroupLookup((_props$columnGrouping = props.columnGroupingModel) != null ? _props$columnGrouping : []);
var unwrappedGroupingModel = unwrapGroupingColumnModel((_props$columnGrouping2 = props.columnGroupingModel) != null ? _props$columnGrouping2 : []);
var columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(columnFields, unwrappedGroupingModel, // @ts-expect-error Move this part to `Pro` package
(_apiRef$current$state = apiRef.current.state.pinnedColumns) != null ? _apiRef$current$state : {});
var maxDepth = visibleColumnFields.length === 0 ? 0 : Math.max.apply(Math, _toConsumableArray(visibleColumnFields.map(function (field) {
var _unwrappedGroupingMod, _unwrappedGroupingMod2;
return (_unwrappedGroupingMod = (_unwrappedGroupingMod2 = unwrappedGroupingModel[field]) == null ? void 0 : _unwrappedGroupingMod2.length) != null ? _unwrappedGroupingMod : 0;
})));
return _extends({}, state, {
columnGrouping: {
lookup: groupLookup,
unwrappedGroupingModel: unwrappedGroupingModel,
headerStructure: columnGroupsHeaderStructure,
maxDepth: maxDepth
}
});
};
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
*/
export var useGridColumnGrouping = function useGridColumnGrouping(apiRef, props) {
var _props$experimentalFe3;
/**
* API METHODS
*/
var getColumnGroupPath = React.useCallback(function (field) {
var _unwrappedGroupingMod3;
var unwrappedGroupingModel = gridColumnGroupsUnwrappedModelSelector(apiRef);
return (_unwrappedGroupingMod3 = unwrappedGroupingModel[field]) != null ? _unwrappedGroupingMod3 : [];
}, [apiRef]);
var getAllGroupDetails = React.useCallback(function () {
var columnGroupLookup = gridColumnGroupsLookupSelector(apiRef);
return columnGroupLookup;
}, [apiRef]);
var columnGroupingApi = {
unstable_getColumnGroupPath: getColumnGroupPath,
unstable_getAllGroupDetails: getAllGroupDetails
};
useGridApiMethod(apiRef, columnGroupingApi, 'public');
var handleColumnIndexChange = React.useCallback(function () {
var _props$columnGrouping3;
var unwrappedGroupingModel = unwrapGroupingColumnModel((_props$columnGrouping3 = props.columnGroupingModel) != null ? _props$columnGrouping3 : []);
apiRef.current.setState(function (state) {
var _state$columns$ordere, _state$columns, _state$pinnedColumns;
var orderedFields = (_state$columns$ordere = (_state$columns = state.columns) == null ? void 0 : _state$columns.orderedFields) != null ? _state$columns$ordere : [];
// @ts-expect-error Move this logic to `Pro` package
var pinnedColumns = (_state$pinnedColumns = state.pinnedColumns) != null ? _state$pinnedColumns : {};
var columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(orderedFields, unwrappedGroupingModel, pinnedColumns);
return _extends({}, state, {
columnGrouping: _extends({}, state.columnGrouping, {
headerStructure: columnGroupsHeaderStructure
})
});
});
}, [apiRef, props.columnGroupingModel]);
var updateColumnGroupingState = React.useCallback(function (columnGroupingModel) {
var _props$experimentalFe2, _apiRef$current$getPi, _apiRef$current$getPi2, _apiRef$current;
if (!((_props$experimentalFe2 = props.experimentalFeatures) != null && _props$experimentalFe2.columnGrouping)) {
return;
}
// @ts-expect-error Move this logic to `Pro` package
var pinnedColumns = (_apiRef$current$getPi = (_apiRef$current$getPi2 = (_apiRef$current = apiRef.current).getPinnedColumns) == null ? void 0 : _apiRef$current$getPi2.call(_apiRef$current)) != null ? _apiRef$current$getPi : {};
var columnFields = gridColumnFieldsSelector(apiRef);
var visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef);
var groupLookup = createGroupLookup(columnGroupingModel != null ? columnGroupingModel : []);
var unwrappedGroupingModel = unwrapGroupingColumnModel(columnGroupingModel != null ? columnGroupingModel : []);
var columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(columnFields, unwrappedGroupingModel, pinnedColumns);
var maxDepth = visibleColumnFields.length === 0 ? 0 : Math.max.apply(Math, _toConsumableArray(visibleColumnFields.map(function (field) {
var _unwrappedGroupingMod4, _unwrappedGroupingMod5;
return (_unwrappedGroupingMod4 = (_unwrappedGroupingMod5 = unwrappedGroupingModel[field]) == null ? void 0 : _unwrappedGroupingMod5.length) != null ? _unwrappedGroupingMod4 : 0;
})));
apiRef.current.setState(function (state) {
return _extends({}, state, {
columnGrouping: {
lookup: groupLookup,
unwrappedGroupingModel: unwrappedGroupingModel,
headerStructure: columnGroupsHeaderStructure,
maxDepth: maxDepth
}
});
});
}, [apiRef, (_props$experimentalFe3 = props.experimentalFeatures) == null ? void 0 : _props$experimentalFe3.columnGrouping]);
useGridApiEventHandler(apiRef, 'columnIndexChange', handleColumnIndexChange);
useGridApiEventHandler(apiRef, 'columnsChange', function () {
updateColumnGroupingState(props.columnGroupingModel);
});
useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', function () {
updateColumnGroupingState(props.columnGroupingModel);
});
/**
* EFFECTS
*/
React.useEffect(function () {
updateColumnGroupingState(props.columnGroupingModel);
}, [updateColumnGroupingState, props.columnGroupingModel]);
};

View File

@@ -0,0 +1,390 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import { styled, useTheme } from '@mui/material/styles';
import { defaultMemoize } from 'reselect';
import { useGridSelector } from '../../utils';
import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
import { useGridRootProps } from '../../utils/useGridRootProps';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { GridColumnHeaderItem } from '../../../components/columnHeaders/GridColumnHeaderItem';
import { getFirstColumnIndexToRender, getTotalHeaderHeight } from '../columns/gridColumnsUtils';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { areRenderContextsEqual, getRenderableIndexes } from '../virtualization/useGridVirtualScroller';
import { gridVirtualizationColumnEnabledSelector } from '../virtualization';
import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader';
import { jsx as _jsx } from "react/jsx-runtime";
var GridColumnHeaderRow = styled('div', {
name: 'MuiDataGrid',
slot: 'ColumnHeaderRow',
overridesResolver: function overridesResolver(props, styles) {
return styles.columnHeaderRow;
}
})(function () {
return {
display: 'flex'
};
});
function isUIEvent(event) {
return !!event.target;
}
export var useGridColumnHeaders = function useGridColumnHeaders(props) {
var innerRefProp = props.innerRef,
_props$minColumnIndex = props.minColumnIndex,
minColumnIndex = _props$minColumnIndex === void 0 ? 0 : _props$minColumnIndex,
visibleColumns = props.visibleColumns,
sortColumnLookup = props.sortColumnLookup,
filterColumnLookup = props.filterColumnLookup,
columnPositions = props.columnPositions,
columnHeaderTabIndexState = props.columnHeaderTabIndexState,
columnGroupHeaderTabIndexState = props.columnGroupHeaderTabIndexState,
columnHeaderFocus = props.columnHeaderFocus,
columnGroupHeaderFocus = props.columnGroupHeaderFocus,
densityFactor = props.densityFactor,
headerGroupingMaxDepth = props.headerGroupingMaxDepth,
columnMenuState = props.columnMenuState,
columnVisibility = props.columnVisibility,
columnGroupsHeaderStructure = props.columnGroupsHeaderStructure,
hasOtherElementInTabSequence = props.hasOtherElementInTabSequence;
var theme = useTheme();
var _React$useState = React.useState(''),
_React$useState2 = _slicedToArray(_React$useState, 2),
dragCol = _React$useState2[0],
setDragCol = _React$useState2[1];
var _React$useState3 = React.useState(''),
_React$useState4 = _slicedToArray(_React$useState3, 2),
resizeCol = _React$useState4[0],
setResizeCol = _React$useState4[1];
var apiRef = useGridPrivateApiContext();
var hasVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector);
var rootProps = useGridRootProps();
var innerRef = React.useRef(null);
var handleInnerRef = useForkRef(innerRefProp, innerRef);
var _React$useState5 = React.useState(null),
_React$useState6 = _slicedToArray(_React$useState5, 2),
renderContext = _React$useState6[0],
setRenderContextRaw = _React$useState6[1];
var prevRenderContext = React.useRef(renderContext);
var prevScrollLeft = React.useRef(0);
var currentPage = useGridVisibleRows(apiRef, rootProps);
var totalHeaderHeight = getTotalHeaderHeight(apiRef, rootProps.columnHeaderHeight);
var headerHeight = Math.floor(rootProps.columnHeaderHeight * densityFactor);
var setRenderContext = React.useCallback(function (nextRenderContext) {
if (renderContext && nextRenderContext && areRenderContextsEqual(renderContext, nextRenderContext)) {
return;
}
setRenderContextRaw(nextRenderContext);
}, [renderContext]);
React.useEffect(function () {
var _apiRef$current$colum;
if ((_apiRef$current$colum = apiRef.current.columnHeadersContainerElementRef) != null && _apiRef$current$colum.current) {
apiRef.current.columnHeadersContainerElementRef.current.scrollLeft = 0;
}
}, [apiRef]);
// memoize `getFirstColumnIndexToRender`, since it's called on scroll
var getFirstColumnIndexToRenderRef = React.useRef(defaultMemoize(getFirstColumnIndexToRender, {
equalityCheck: function equalityCheck(a, b) {
return ['firstColumnIndex', 'minColumnIndex', 'columnBuffer'].every(function (key) {
return a[key] === b[key];
});
}
}));
var updateInnerPosition = React.useCallback(function (nextRenderContext) {
var _getRenderableIndexes = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
lastIndex: nextRenderContext.lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rootProps.rowBuffer
}),
_getRenderableIndexes2 = _slicedToArray(_getRenderableIndexes, 2),
firstRowToRender = _getRenderableIndexes2[0],
lastRowToRender = _getRenderableIndexes2[1];
var firstColumnToRender = getFirstColumnIndexToRenderRef.current({
firstColumnIndex: nextRenderContext.firstColumnIndex,
minColumnIndex: minColumnIndex,
columnBuffer: rootProps.columnBuffer,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
apiRef: apiRef,
visibleRows: currentPage.rows
});
var direction = theme.direction === 'ltr' ? 1 : -1;
var offset = firstColumnToRender > 0 ? prevScrollLeft.current - direction * columnPositions[firstColumnToRender] : prevScrollLeft.current;
innerRef.current.style.transform = "translate3d(".concat(-offset, "px, 0px, 0px)");
}, [columnPositions, minColumnIndex, rootProps.columnBuffer, apiRef, currentPage.rows, rootProps.rowBuffer, theme.direction]);
React.useLayoutEffect(function () {
if (renderContext) {
updateInnerPosition(renderContext);
}
}, [renderContext, updateInnerPosition]);
var handleScroll = React.useCallback(function (_ref, event) {
var _prevRenderContext$cu, _prevRenderContext$cu2;
var left = _ref.left,
_ref$renderContext = _ref.renderContext,
nextRenderContext = _ref$renderContext === void 0 ? null : _ref$renderContext;
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.
var 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(function () {
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, setRenderContext]);
var handleColumnResizeStart = React.useCallback(function (params) {
return setResizeCol(params.field);
}, []);
var handleColumnResizeStop = React.useCallback(function () {
return setResizeCol('');
}, []);
var handleColumnReorderStart = React.useCallback(function (params) {
return setDragCol(params.field);
}, []);
var handleColumnReorderStop = React.useCallback(function () {
return setDragCol('');
}, []);
useGridApiEventHandler(apiRef, 'columnResizeStart', handleColumnResizeStart);
useGridApiEventHandler(apiRef, 'columnResizeStop', handleColumnResizeStop);
useGridApiEventHandler(apiRef, 'columnHeaderDragStart', handleColumnReorderStart);
useGridApiEventHandler(apiRef, 'columnHeaderDragEnd', handleColumnReorderStop);
useGridApiEventHandler(apiRef, 'scrollPositionChange', handleScroll);
// Helper for computation common between getColumnHeaders and getColumnGroupHeaders
var getColumnsToRender = function getColumnsToRender(params) {
var _ref2 = params || {},
_ref2$renderContext = _ref2.renderContext,
nextRenderContext = _ref2$renderContext === void 0 ? renderContext : _ref2$renderContext,
_ref2$minFirstColumn = _ref2.minFirstColumn,
minFirstColumn = _ref2$minFirstColumn === void 0 ? minColumnIndex : _ref2$minFirstColumn,
_ref2$maxLastColumn = _ref2.maxLastColumn,
maxLastColumn = _ref2$maxLastColumn === void 0 ? visibleColumns.length : _ref2$maxLastColumn;
if (!nextRenderContext) {
return null;
}
var _getRenderableIndexes3 = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
lastIndex: nextRenderContext.lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rootProps.rowBuffer
}),
_getRenderableIndexes4 = _slicedToArray(_getRenderableIndexes3, 2),
firstRowToRender = _getRenderableIndexes4[0],
lastRowToRender = _getRenderableIndexes4[1];
var firstColumnToRender = !hasVirtualization ? 0 : getFirstColumnIndexToRenderRef.current({
firstColumnIndex: nextRenderContext.firstColumnIndex,
minColumnIndex: minFirstColumn,
columnBuffer: rootProps.columnBuffer,
apiRef: apiRef,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
visibleRows: currentPage.rows
});
var lastColumnToRender = !hasVirtualization ? maxLastColumn : Math.min(nextRenderContext.lastColumnIndex + rootProps.columnBuffer, maxLastColumn);
var renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender);
return {
renderedColumns: renderedColumns,
firstColumnToRender: firstColumnToRender,
lastColumnToRender: lastColumnToRender,
minFirstColumn: minFirstColumn,
maxLastColumn: maxLastColumn
};
};
var getColumnHeaders = function getColumnHeaders(params) {
var other = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var columnsToRender = getColumnsToRender(params);
if (columnsToRender == null) {
return null;
}
var renderedColumns = columnsToRender.renderedColumns,
firstColumnToRender = columnsToRender.firstColumnToRender;
var columns = [];
for (var i = 0; i < renderedColumns.length; i += 1) {
var colDef = renderedColumns[i];
var columnIndex = firstColumnToRender + i;
var isFirstColumn = columnIndex === 0;
var tabIndex = columnHeaderTabIndexState !== null && columnHeaderTabIndexState.field === colDef.field || isFirstColumn && !hasOtherElementInTabSequence ? 0 : -1;
var hasFocus = columnHeaderFocus !== null && columnHeaderFocus.field === colDef.field;
var open = columnMenuState.open && columnMenuState.field === colDef.field;
columns.push( /*#__PURE__*/_jsx(GridColumnHeaderItem, _extends({}, sortColumnLookup[colDef.field], {
columnMenuOpen: open,
filterItemsCounter: filterColumnLookup[colDef.field] && filterColumnLookup[colDef.field].length,
headerHeight: headerHeight,
isDragging: colDef.field === dragCol,
colDef: colDef,
colIndex: columnIndex,
isResizing: resizeCol === colDef.field,
hasFocus: hasFocus,
tabIndex: tabIndex
}, other), colDef.field));
}
return /*#__PURE__*/_jsx(GridColumnHeaderRow, {
role: "row",
"aria-rowindex": headerGroupingMaxDepth + 1,
ownerState: rootProps,
children: columns
});
};
var getColumnGroupHeaders = function getColumnGroupHeaders(params) {
if (headerGroupingMaxDepth === 0) {
return null;
}
var columnsToRender = getColumnsToRender(params);
if (columnsToRender == null || columnsToRender.renderedColumns.length === 0) {
return null;
}
var firstColumnToRender = columnsToRender.firstColumnToRender,
lastColumnToRender = columnsToRender.lastColumnToRender;
var columns = [];
var headerToRender = [];
var _loop = function _loop(depth) {
var _apiRef$current$unsta, _apiRef$current$unsta2;
var rowStructure = columnGroupsHeaderStructure[depth];
var firstColumnFieldToRender = visibleColumns[firstColumnToRender].field;
var firstGroupToRender = (_apiRef$current$unsta = apiRef.current.unstable_getColumnGroupPath(firstColumnFieldToRender)[depth]) != null ? _apiRef$current$unsta : null;
var firstGroupIndex = rowStructure.findIndex(function (_ref4) {
var groupId = _ref4.groupId,
columnFields = _ref4.columnFields;
return groupId === firstGroupToRender && columnFields.includes(firstColumnFieldToRender);
});
var lastColumnFieldToRender = visibleColumns[lastColumnToRender - 1].field;
var lastGroupToRender = (_apiRef$current$unsta2 = apiRef.current.unstable_getColumnGroupPath(lastColumnFieldToRender)[depth]) != null ? _apiRef$current$unsta2 : null;
var lastGroupIndex = rowStructure.findIndex(function (_ref5) {
var groupId = _ref5.groupId,
columnFields = _ref5.columnFields;
return groupId === lastGroupToRender && columnFields.includes(lastColumnFieldToRender);
});
var visibleColumnGroupHeader = rowStructure.slice(firstGroupIndex, lastGroupIndex + 1).map(function (groupStructure) {
return _extends({}, groupStructure, {
columnFields: groupStructure.columnFields.filter(function (field) {
return columnVisibility[field] !== false;
})
});
}).filter(function (groupStructure) {
return groupStructure.columnFields.length > 0;
});
var firstVisibleColumnIndex = visibleColumnGroupHeader[0].columnFields.indexOf(firstColumnFieldToRender);
var hiddenGroupColumns = visibleColumnGroupHeader[0].columnFields.slice(0, firstVisibleColumnIndex);
var leftOverflow = hiddenGroupColumns.reduce(function (acc, field) {
var _column$computedWidth;
var column = apiRef.current.getColumn(field);
return acc + ((_column$computedWidth = column.computedWidth) != null ? _column$computedWidth : 0);
}, 0);
var columnIndex = firstColumnToRender;
var elements = visibleColumnGroupHeader.map(function (_ref6) {
var groupId = _ref6.groupId,
columnFields = _ref6.columnFields;
var hasFocus = columnGroupHeaderFocus !== null && columnGroupHeaderFocus.depth === depth && columnFields.includes(columnGroupHeaderFocus.field);
var tabIndex = columnGroupHeaderTabIndexState !== null && columnGroupHeaderTabIndexState.depth === depth && columnFields.includes(columnGroupHeaderTabIndexState.field) ? 0 : -1;
var headerInfo = {
groupId: groupId,
width: columnFields.reduce(function (acc, field) {
return acc + apiRef.current.getColumn(field).computedWidth;
}, 0),
fields: columnFields,
colIndex: columnIndex,
hasFocus: hasFocus,
tabIndex: tabIndex
};
columnIndex += columnFields.length;
return headerInfo;
});
headerToRender.push({
leftOverflow: leftOverflow,
elements: elements
});
};
for (var depth = 0; depth < headerGroupingMaxDepth; depth += 1) {
_loop(depth);
}
headerToRender.forEach(function (depthInfo, depthIndex) {
columns.push( /*#__PURE__*/_jsx(GridColumnHeaderRow, {
style: {
height: "".concat(headerHeight, "px"),
transform: "translateX(-".concat(depthInfo.leftOverflow, "px)")
},
role: "row",
"aria-rowindex": depthIndex + 1,
ownerState: rootProps,
children: depthInfo.elements.map(function (_ref3, groupIndex) {
var groupId = _ref3.groupId,
width = _ref3.width,
fields = _ref3.fields,
colIndex = _ref3.colIndex,
hasFocus = _ref3.hasFocus,
tabIndex = _ref3.tabIndex;
return /*#__PURE__*/_jsx(GridColumnGroupHeader, {
groupId: groupId,
width: width,
fields: fields,
colIndex: colIndex,
depth: depthIndex,
isLastColumn: colIndex === visibleColumns.length - fields.length,
maxDepth: headerToRender.length,
height: headerHeight,
hasFocus: hasFocus,
tabIndex: tabIndex
}, groupIndex);
})
}, depthIndex));
});
return columns;
};
var rootStyle = {
minHeight: totalHeaderHeight,
maxHeight: totalHeaderHeight,
lineHeight: "".concat(headerHeight, "px")
};
return {
renderContext: renderContext,
getColumnHeaders: getColumnHeaders,
getColumnsToRender: getColumnsToRender,
getColumnGroupHeaders: getColumnGroupHeaders,
isDragging: !!dragCol,
getRootProps: function getRootProps() {
var other = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return _extends({
style: rootStyle
}, other);
},
getInnerProps: function getInnerProps() {
return {
ref: handleInnerRef,
role: 'rowgroup'
};
},
headerHeight: headerHeight
};
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,3 @@
export var gridColumnMenuSelector = function gridColumnMenuSelector(state) {
return state.columnMenu;
};

View File

@@ -0,0 +1,2 @@
export * from './columnMenuInterfaces';
export * from './columnMenuSelector';

View File

@@ -0,0 +1,103 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridLogger, useGridApiMethod, useGridApiEventHandler } from '../../utils';
import { gridColumnMenuSelector } from './columnMenuSelector';
import { gridColumnLookupSelector, gridColumnVisibilityModelSelector, gridColumnFieldsSelector } from '../columns/gridColumnsSelector';
export var columnMenuStateInitializer = function columnMenuStateInitializer(state) {
return _extends({}, state, {
columnMenu: {
open: false
}
});
};
/**
* @requires useGridColumnResize (event)
* @requires useGridInfiniteLoader (event)
*/
export var useGridColumnMenu = function useGridColumnMenu(apiRef) {
var logger = useGridLogger(apiRef, 'useGridColumnMenu');
/**
* API METHODS
*/
var showColumnMenu = React.useCallback(function (field) {
var shouldUpdate = apiRef.current.setState(function (state) {
if (state.columnMenu.open && state.columnMenu.field === field) {
return state;
}
logger.debug('Opening Column Menu');
return _extends({}, state, {
columnMenu: {
open: true,
field: field
}
});
});
if (shouldUpdate) {
apiRef.current.hidePreferences();
apiRef.current.forceUpdate();
}
}, [apiRef, logger]);
var hideColumnMenu = React.useCallback(function () {
var columnMenuState = gridColumnMenuSelector(apiRef.current.state);
if (columnMenuState.field) {
var columnLookup = gridColumnLookupSelector(apiRef);
var columnVisibilityModel = gridColumnVisibilityModelSelector(apiRef);
var orderedFields = gridColumnFieldsSelector(apiRef);
var fieldToFocus = columnMenuState.field;
// If the column was removed from the grid, we need to find the closest visible field
if (!columnLookup[fieldToFocus]) {
fieldToFocus = orderedFields[0];
}
// If the field to focus is hidden, we need to find the closest visible field
if (columnVisibilityModel[fieldToFocus] === false) {
// contains visible column fields + the field that was just hidden
var visibleOrderedFields = orderedFields.filter(function (field) {
if (field === fieldToFocus) {
return true;
}
return columnVisibilityModel[field] !== false;
});
var fieldIndex = visibleOrderedFields.indexOf(fieldToFocus);
fieldToFocus = visibleOrderedFields[fieldIndex + 1] || visibleOrderedFields[fieldIndex - 1];
}
apiRef.current.setColumnHeaderFocus(fieldToFocus);
}
var shouldUpdate = apiRef.current.setState(function (state) {
if (!state.columnMenu.open && state.columnMenu.field === undefined) {
return state;
}
logger.debug('Hiding Column Menu');
return _extends({}, state, {
columnMenu: _extends({}, state.columnMenu, {
open: false,
field: undefined
})
});
});
if (shouldUpdate) {
apiRef.current.forceUpdate();
}
}, [apiRef, logger]);
var toggleColumnMenu = React.useCallback(function (field) {
logger.debug('Toggle Column Menu');
var columnMenu = gridColumnMenuSelector(apiRef.current.state);
if (!columnMenu.open || columnMenu.field !== field) {
showColumnMenu(field);
} else {
hideColumnMenu();
}
}, [apiRef, logger, showColumnMenu, hideColumnMenu]);
var columnMenuApi = {
showColumnMenu: showColumnMenu,
hideColumnMenu: hideColumnMenu,
toggleColumnMenu: toggleColumnMenu
};
useGridApiMethod(apiRef, columnMenuApi, 'public');
useGridApiEventHandler(apiRef, 'columnResizeStart', hideColumnMenu);
useGridApiEventHandler(apiRef, 'virtualScrollerWheel', apiRef.current.hideColumnMenu);
useGridApiEventHandler(apiRef, 'virtualScrollerTouchMove', apiRef.current.hideColumnMenu);
};

View File

@@ -0,0 +1,71 @@
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
var _excluded = ["displayOrder"];
import * as React from 'react';
import Divider from '@mui/material/Divider';
import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
var useGridColumnMenuSlots = function useGridColumnMenuSlots(props) {
var apiRef = useGridPrivateApiContext();
var defaultSlots = props.defaultSlots,
defaultSlotProps = props.defaultSlotProps,
_props$slots = props.slots,
slots = _props$slots === void 0 ? {} : _props$slots,
_props$slotProps = props.slotProps,
slotProps = _props$slotProps === void 0 ? {} : _props$slotProps,
hideMenu = props.hideMenu,
colDef = props.colDef,
_props$addDividers = props.addDividers,
addDividers = _props$addDividers === void 0 ? true : _props$addDividers;
var processedComponents = React.useMemo(function () {
return _extends({}, defaultSlots, slots);
}, [defaultSlots, slots]);
var processedSlotProps = React.useMemo(function () {
if (!slotProps || Object.keys(slotProps).length === 0) {
return defaultSlotProps;
}
var mergedProps = _extends({}, slotProps);
Object.entries(defaultSlotProps).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
currentSlotProps = _ref2[1];
mergedProps[key] = _extends({}, currentSlotProps, slotProps[key] || {});
});
return mergedProps;
}, [defaultSlotProps, slotProps]);
var defaultItems = apiRef.current.unstable_applyPipeProcessors('columnMenu', [], props.colDef);
var userItems = React.useMemo(function () {
var defaultComponentKeys = Object.keys(defaultSlots);
return Object.keys(slots).filter(function (key) {
return !defaultComponentKeys.includes(key);
});
}, [slots, defaultSlots]);
return React.useMemo(function () {
var uniqueItems = Array.from(new Set([].concat(_toConsumableArray(defaultItems), _toConsumableArray(userItems))));
var cleansedItems = uniqueItems.filter(function (key) {
return processedComponents[key] != null;
});
var sorted = cleansedItems.sort(function (a, b) {
var leftItemProps = processedSlotProps[a];
var rightItemProps = processedSlotProps[b];
var leftDisplayOrder = Number.isFinite(leftItemProps == null ? void 0 : leftItemProps.displayOrder) ? leftItemProps.displayOrder : 100;
var rightDisplayOrder = Number.isFinite(rightItemProps == null ? void 0 : rightItemProps.displayOrder) ? rightItemProps.displayOrder : 100;
return leftDisplayOrder - rightDisplayOrder;
});
return sorted.reduce(function (acc, key, index) {
var itemProps = {
colDef: colDef,
onClick: hideMenu
};
var processedComponentProps = processedSlotProps[key];
if (processedComponentProps) {
var displayOrder = processedComponentProps.displayOrder,
customProps = _objectWithoutProperties(processedComponentProps, _excluded);
itemProps = _extends({}, itemProps, customProps);
}
return addDividers && index !== sorted.length - 1 ? [].concat(_toConsumableArray(acc), [[processedComponents[key], itemProps], [Divider, {}]]) : [].concat(_toConsumableArray(acc), [[processedComponents[key], itemProps]]);
}, []);
}, [addDividers, colDef, defaultItems, hideMenu, processedComponents, processedSlotProps, userItems]);
};
export { useGridColumnMenuSlots };

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,112 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
/**
* Get the columns state
* @category Columns
*/
export var gridColumnsStateSelector = function gridColumnsStateSelector(state) {
return state.columns;
};
/**
* Get an array of column fields in the order rendered on screen.
* @category Columns
*/
export var gridColumnFieldsSelector = createSelector(gridColumnsStateSelector, function (columnsState) {
return columnsState.orderedFields;
});
/**
* Get the columns as a lookup (an object containing the field for keys and the definition for values).
* @category Columns
*/
export var gridColumnLookupSelector = createSelector(gridColumnsStateSelector, function (columnsState) {
return columnsState.lookup;
});
/**
* Get an array of column definitions in the order rendered on screen..
* @category Columns
*/
export var gridColumnDefinitionsSelector = createSelectorMemoized(gridColumnFieldsSelector, gridColumnLookupSelector, function (allFields, lookup) {
return allFields.map(function (field) {
return lookup[field];
});
});
/**
* Get the column visibility model, containing the visibility status of each column.
* If a column is not registered in the model, it is visible.
* @category Visible Columns
*/
export var gridColumnVisibilityModelSelector = createSelector(gridColumnsStateSelector, function (columnsState) {
return columnsState.columnVisibilityModel;
});
/**
* Get the visible columns as a lookup (an object containing the field for keys and the definition for values).
* @category Visible Columns
*/
export var gridVisibleColumnDefinitionsSelector = createSelectorMemoized(gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector, function (columns, columnVisibilityModel) {
return columns.filter(function (column) {
return columnVisibilityModel[column.field] !== false;
});
});
/**
* Get the field of each visible column.
* @category Visible Columns
*/
export var gridVisibleColumnFieldsSelector = createSelectorMemoized(gridVisibleColumnDefinitionsSelector, function (visibleColumns) {
return visibleColumns.map(function (column) {
return column.field;
});
});
/**
* Get the left position in pixel of each visible columns relative to the left of the first column.
* @category Visible Columns
*/
export var gridColumnPositionsSelector = createSelectorMemoized(gridVisibleColumnDefinitionsSelector, function (visibleColumns) {
var positions = [];
var currentPosition = 0;
for (var i = 0; i < visibleColumns.length; i += 1) {
positions.push(currentPosition);
currentPosition += visibleColumns[i].computedWidth;
}
return positions;
});
/**
* Get the summed width of all the visible columns.
* @category Visible Columns
*/
export var gridColumnsTotalWidthSelector = createSelector(gridVisibleColumnDefinitionsSelector, gridColumnPositionsSelector, function (visibleColumns, positions) {
var colCount = visibleColumns.length;
if (colCount === 0) {
return 0;
}
return positions[colCount - 1] + visibleColumns[colCount - 1].computedWidth;
});
/**
* Get the filterable columns as an array.
* @category Columns
*/
export var gridFilterableColumnDefinitionsSelector = createSelectorMemoized(gridColumnDefinitionsSelector, function (columns) {
return columns.filter(function (col) {
return col.filterable;
});
});
/**
* Get the filterable columns as a lookup (an object containing the field for keys and the definition for values).
* @category Columns
*/
export var gridFilterableColumnLookupSelector = createSelectorMemoized(gridColumnDefinitionsSelector, function (columns) {
return columns.reduce(function (acc, col) {
if (col.filterable) {
acc[col.field] = col;
}
return acc;
}, {});
});

View File

@@ -0,0 +1,344 @@
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import { DEFAULT_GRID_COL_TYPE_KEY, GRID_STRING_COL_DEF } from '../../../colDef';
import { gridColumnsStateSelector, gridColumnVisibilityModelSelector } from './gridColumnsSelector';
import { clamp } from '../../../utils/utils';
import { gridDensityFactorSelector } from '../density/densitySelector';
import { gridColumnGroupsHeaderMaxDepthSelector } from '../columnGrouping/gridColumnGroupsSelector';
export var COLUMNS_DIMENSION_PROPERTIES = ['maxWidth', 'minWidth', 'width', 'flex'];
/**
* Computes width for flex columns.
* Based on CSS Flexbox specification:
* https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
*/
export function computeFlexColumnsWidth(_ref) {
var initialFreeSpace = _ref.initialFreeSpace,
totalFlexUnits = _ref.totalFlexUnits,
flexColumns = _ref.flexColumns;
var uniqueFlexColumns = new Set(flexColumns.map(function (col) {
return col.field;
}));
var flexColumnsLookup = {
all: {},
frozenFields: [],
freeze: function freeze(field) {
var value = flexColumnsLookup.all[field];
if (value && value.frozen !== true) {
flexColumnsLookup.all[field].frozen = true;
flexColumnsLookup.frozenFields.push(field);
}
}
};
// Step 5 of https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
function loopOverFlexItems() {
// 5a: If all the flex items on the line are frozen, free space has been distributed.
if (flexColumnsLookup.frozenFields.length === uniqueFlexColumns.size) {
return;
}
var violationsLookup = {
min: {},
max: {}
};
var remainingFreeSpace = initialFreeSpace;
var flexUnits = totalFlexUnits;
var totalViolation = 0;
// 5b: Calculate the remaining free space
flexColumnsLookup.frozenFields.forEach(function (field) {
remainingFreeSpace -= flexColumnsLookup.all[field].computedWidth;
flexUnits -= flexColumnsLookup.all[field].flex;
});
for (var i = 0; i < flexColumns.length; i += 1) {
var column = flexColumns[i];
if (flexColumnsLookup.all[column.field] && flexColumnsLookup.all[column.field].frozen === true) {
continue;
}
// 5c: Distribute remaining free space proportional to the flex factors
var widthPerFlexUnit = remainingFreeSpace / flexUnits;
var computedWidth = widthPerFlexUnit * column.flex;
// 5d: Fix min/max violations
if (computedWidth < column.minWidth) {
totalViolation += column.minWidth - computedWidth;
computedWidth = column.minWidth;
violationsLookup.min[column.field] = true;
} else if (computedWidth > column.maxWidth) {
totalViolation += column.maxWidth - computedWidth;
computedWidth = column.maxWidth;
violationsLookup.max[column.field] = true;
}
flexColumnsLookup.all[column.field] = {
frozen: false,
computedWidth: computedWidth,
flex: column.flex
};
}
// 5e: Freeze over-flexed items
if (totalViolation < 0) {
// Freeze all the items with max violations
Object.keys(violationsLookup.max).forEach(function (field) {
flexColumnsLookup.freeze(field);
});
} else if (totalViolation > 0) {
// Freeze all the items with min violations
Object.keys(violationsLookup.min).forEach(function (field) {
flexColumnsLookup.freeze(field);
});
} else {
// Freeze all items
flexColumns.forEach(function (_ref2) {
var field = _ref2.field;
flexColumnsLookup.freeze(field);
});
}
// 5f: Return to the start of this loop
loopOverFlexItems();
}
loopOverFlexItems();
return flexColumnsLookup.all;
}
/**
* Compute the `computedWidth` (ie: the width the column should have during rendering) based on the `width` / `flex` / `minWidth` / `maxWidth` properties of `GridColDef`.
* The columns already have been merged with there `type` default values for `minWidth`, `maxWidth` and `width`, thus the `!` for those properties below.
* TODO: Unit test this function in depth and only keep basic cases for the whole grid testing.
* TODO: Improve the `GridColDef` typing to reflect the fact that `minWidth` / `maxWidth` and `width` can't be null after the merge with the `type` default values.
*/
export var hydrateColumnsWidth = function hydrateColumnsWidth(rawState, viewportInnerWidth) {
var columnsLookup = {};
var totalFlexUnits = 0;
var widthAllocatedBeforeFlex = 0;
var flexColumns = [];
// For the non-flex columns, compute their width
// For the flex columns, compute there minimum width and how much width must be allocated during the flex allocation
rawState.orderedFields.forEach(function (columnField) {
var newColumn = _extends({}, rawState.lookup[columnField]);
if (rawState.columnVisibilityModel[columnField] === false) {
newColumn.computedWidth = 0;
} else {
var computedWidth;
if (newColumn.flex && newColumn.flex > 0) {
totalFlexUnits += newColumn.flex;
computedWidth = 0;
flexColumns.push(newColumn);
} else {
computedWidth = clamp(newColumn.width || GRID_STRING_COL_DEF.width, newColumn.minWidth || GRID_STRING_COL_DEF.minWidth, newColumn.maxWidth || GRID_STRING_COL_DEF.maxWidth);
}
widthAllocatedBeforeFlex += computedWidth;
newColumn.computedWidth = computedWidth;
}
columnsLookup[columnField] = newColumn;
});
var initialFreeSpace = Math.max(viewportInnerWidth - widthAllocatedBeforeFlex, 0);
// Allocate the remaining space to the flex columns
if (totalFlexUnits > 0 && viewportInnerWidth > 0) {
var computedColumnWidths = computeFlexColumnsWidth({
initialFreeSpace: initialFreeSpace,
totalFlexUnits: totalFlexUnits,
flexColumns: flexColumns
});
Object.keys(computedColumnWidths).forEach(function (field) {
columnsLookup[field].computedWidth = computedColumnWidths[field].computedWidth;
});
}
return _extends({}, rawState, {
lookup: columnsLookup
});
};
/**
* Apply the order and the dimensions of the initial state.
* The columns not registered in `orderedFields` will be placed after the imported columns.
*/
export var applyInitialState = function applyInitialState(columnsState, initialState) {
if (!initialState) {
return columnsState;
}
var _initialState$ordered = initialState.orderedFields,
orderedFields = _initialState$ordered === void 0 ? [] : _initialState$ordered,
_initialState$dimensi = initialState.dimensions,
dimensions = _initialState$dimensi === void 0 ? {} : _initialState$dimensi;
var columnsWithUpdatedDimensions = Object.keys(dimensions);
if (columnsWithUpdatedDimensions.length === 0 && orderedFields.length === 0) {
return columnsState;
}
var orderedFieldsLookup = {};
var cleanOrderedFields = [];
for (var i = 0; i < orderedFields.length; i += 1) {
var _field = orderedFields[i];
// Ignores the fields in the initialState that matches no field on the current column state
if (columnsState.lookup[_field]) {
orderedFieldsLookup[_field] = true;
cleanOrderedFields.push(_field);
}
}
var newOrderedFields = cleanOrderedFields.length === 0 ? columnsState.orderedFields : [].concat(cleanOrderedFields, _toConsumableArray(columnsState.orderedFields.filter(function (field) {
return !orderedFieldsLookup[field];
})));
var newColumnLookup = _extends({}, columnsState.lookup);
var _loop = function _loop() {
var field = columnsWithUpdatedDimensions[_i];
var newColDef = _extends({}, newColumnLookup[field], {
hasBeenResized: true
});
Object.entries(dimensions[field]).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
key = _ref4[0],
value = _ref4[1];
newColDef[key] = value === -1 ? Infinity : value;
});
newColumnLookup[field] = newColDef;
};
for (var _i = 0; _i < columnsWithUpdatedDimensions.length; _i += 1) {
_loop();
}
var newColumnsState = _extends({}, columnsState, {
orderedFields: newOrderedFields,
lookup: newColumnLookup
});
return newColumnsState;
};
function getDefaultColTypeDef(columnTypes, type) {
var colDef = columnTypes[DEFAULT_GRID_COL_TYPE_KEY];
if (type && columnTypes[type]) {
colDef = columnTypes[type];
}
return colDef;
}
export var createColumnsState = function createColumnsState(_ref5) {
var _apiRef$current$getRo, _apiRef$current$getRo2, _apiRef$current;
var apiRef = _ref5.apiRef,
columnsToUpsert = _ref5.columnsToUpsert,
initialState = _ref5.initialState,
columnTypes = _ref5.columnTypes,
_ref5$columnVisibilit = _ref5.columnVisibilityModel,
columnVisibilityModel = _ref5$columnVisibilit === void 0 ? gridColumnVisibilityModelSelector(apiRef) : _ref5$columnVisibilit,
_ref5$keepOnlyColumns = _ref5.keepOnlyColumnsToUpsert,
keepOnlyColumnsToUpsert = _ref5$keepOnlyColumns === void 0 ? false : _ref5$keepOnlyColumns;
var isInsideStateInitializer = !apiRef.current.state.columns;
var columnsState;
if (isInsideStateInitializer) {
columnsState = {
orderedFields: [],
lookup: {},
columnVisibilityModel: columnVisibilityModel
};
} else {
var currentState = gridColumnsStateSelector(apiRef.current.state);
columnsState = {
orderedFields: keepOnlyColumnsToUpsert ? [] : _toConsumableArray(currentState.orderedFields),
lookup: _extends({}, currentState.lookup),
// Will be cleaned later if keepOnlyColumnsToUpsert=true
columnVisibilityModel: columnVisibilityModel
};
}
var columnsToKeep = {};
if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) {
columnsToKeep = Object.keys(columnsState.lookup).reduce(function (acc, key) {
return _extends({}, acc, _defineProperty({}, key, false));
}, {});
}
var columnsToUpsertLookup = {};
columnsToUpsert.forEach(function (newColumn) {
var field = newColumn.field;
columnsToUpsertLookup[field] = true;
columnsToKeep[field] = true;
var existingState = columnsState.lookup[field];
if (existingState == null) {
existingState = _extends({}, getDefaultColTypeDef(columnTypes, newColumn.type), {
field: field,
hasBeenResized: false
});
columnsState.orderedFields.push(field);
} else if (keepOnlyColumnsToUpsert) {
columnsState.orderedFields.push(field);
}
// If the column type has changed - merge the existing state with the default column type definition
if (existingState && existingState.type !== newColumn.type) {
existingState = _extends({}, getDefaultColTypeDef(columnTypes, newColumn.type), {
field: field
});
}
var hasBeenResized = existingState.hasBeenResized;
COLUMNS_DIMENSION_PROPERTIES.forEach(function (key) {
if (newColumn[key] !== undefined) {
hasBeenResized = true;
if (newColumn[key] === -1) {
newColumn[key] = Infinity;
}
}
});
columnsState.lookup[field] = _extends({}, existingState, newColumn, {
hasBeenResized: hasBeenResized
});
});
if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) {
Object.keys(columnsState.lookup).forEach(function (field) {
if (!columnsToKeep[field]) {
delete columnsState.lookup[field];
}
});
}
var columnsStateWithPreProcessing = apiRef.current.unstable_applyPipeProcessors('hydrateColumns', columnsState);
var columnsStateWithPortableColumns = applyInitialState(columnsStateWithPreProcessing, initialState);
return hydrateColumnsWidth(columnsStateWithPortableColumns, (_apiRef$current$getRo = (_apiRef$current$getRo2 = (_apiRef$current = apiRef.current).getRootDimensions) == null || (_apiRef$current$getRo2 = _apiRef$current$getRo2.call(_apiRef$current)) == null ? void 0 : _apiRef$current$getRo2.viewportInnerSize.width) != null ? _apiRef$current$getRo : 0);
};
export var mergeColumnsState = function mergeColumnsState(columnsState) {
return function (state) {
return _extends({}, state, {
columns: columnsState
});
};
};
export function getFirstNonSpannedColumnToRender(_ref6) {
var firstColumnToRender = _ref6.firstColumnToRender,
apiRef = _ref6.apiRef,
firstRowToRender = _ref6.firstRowToRender,
lastRowToRender = _ref6.lastRowToRender,
visibleRows = _ref6.visibleRows;
var firstNonSpannedColumnToRender = firstColumnToRender;
for (var i = firstRowToRender; i < lastRowToRender; i += 1) {
var row = visibleRows[i];
if (row) {
var rowId = visibleRows[i].id;
var cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, firstColumnToRender);
if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) {
firstNonSpannedColumnToRender = cellColSpanInfo.leftVisibleCellIndex;
}
}
}
return firstNonSpannedColumnToRender;
}
export function getFirstColumnIndexToRender(_ref7) {
var firstColumnIndex = _ref7.firstColumnIndex,
minColumnIndex = _ref7.minColumnIndex,
columnBuffer = _ref7.columnBuffer,
firstRowToRender = _ref7.firstRowToRender,
lastRowToRender = _ref7.lastRowToRender,
apiRef = _ref7.apiRef,
visibleRows = _ref7.visibleRows;
var initialFirstColumnToRender = Math.max(firstColumnIndex - columnBuffer, minColumnIndex);
var firstColumnToRender = getFirstNonSpannedColumnToRender({
firstColumnToRender: initialFirstColumnToRender,
apiRef: apiRef,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
visibleRows: visibleRows
});
return firstColumnToRender;
}
export function getTotalHeaderHeight(apiRef, headerHeight) {
var densityFactor = gridDensityFactorSelector(apiRef);
var maxDepth = gridColumnGroupsHeaderMaxDepthSelector(apiRef);
return Math.floor(headerHeight * densityFactor) * ((maxDepth != null ? maxDepth : 0) + 1);
}

View File

@@ -0,0 +1 @@
export { gridColumnFieldsSelector, gridColumnLookupSelector, gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector, gridVisibleColumnDefinitionsSelector, gridVisibleColumnFieldsSelector, gridColumnPositionsSelector, gridColumnsTotalWidthSelector, gridFilterableColumnDefinitionsSelector, gridFilterableColumnLookupSelector } from './gridColumnsSelector';

View File

@@ -0,0 +1,102 @@
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
*/
export var useGridColumnSpanning = function useGridColumnSpanning(apiRef) {
var lookup = React.useRef({});
var setCellColSpanInfo = React.useCallback(function (rowId, columnIndex, cellColSpanInfo) {
var sizes = lookup.current;
if (!sizes[rowId]) {
sizes[rowId] = {};
}
sizes[rowId][columnIndex] = cellColSpanInfo;
}, []);
var getCellColSpanInfo = React.useCallback(function (rowId, columnIndex) {
var _lookup$current$rowId;
return (_lookup$current$rowId = lookup.current[rowId]) == null ? void 0 : _lookup$current$rowId[columnIndex];
}, []);
// Calculate `colSpan` for the cell.
var calculateCellColSpan = React.useCallback(function (params) {
var columnIndex = params.columnIndex,
rowId = params.rowId,
minFirstColumnIndex = params.minFirstColumnIndex,
maxLastColumnIndex = params.maxLastColumnIndex,
columns = params.columns;
var columnsLength = columns.length;
var column = columns[columnIndex];
var colSpan = typeof column.colSpan === 'function' ? column.colSpan(apiRef.current.getCellParams(rowId, column.field)) : column.colSpan;
if (!colSpan || colSpan === 1) {
setCellColSpanInfo(rowId, columnIndex, {
spannedByColSpan: false,
cellProps: {
colSpan: 1,
width: column.computedWidth
}
});
return {
colSpan: 1
};
}
var width = column.computedWidth;
for (var j = 1; j < colSpan; j += 1) {
var nextColumnIndex = columnIndex + j;
// Cells should be spanned only within their column section (left-pinned, right-pinned and unpinned).
if (nextColumnIndex >= minFirstColumnIndex && nextColumnIndex < maxLastColumnIndex) {
var nextColumn = columns[nextColumnIndex];
width += nextColumn.computedWidth;
setCellColSpanInfo(rowId, columnIndex + j, {
spannedByColSpan: true,
rightVisibleCellIndex: Math.min(columnIndex + colSpan, columnsLength - 1),
leftVisibleCellIndex: columnIndex
});
}
setCellColSpanInfo(rowId, columnIndex, {
spannedByColSpan: false,
cellProps: {
colSpan: colSpan,
width: width
}
});
}
return {
colSpan: colSpan
};
}, [apiRef, setCellColSpanInfo]);
// Calculate `colSpan` for each cell in the row
var calculateColSpan = React.useCallback(function (_ref) {
var rowId = _ref.rowId,
minFirstColumn = _ref.minFirstColumn,
maxLastColumn = _ref.maxLastColumn,
columns = _ref.columns;
for (var i = minFirstColumn; i < maxLastColumn; i += 1) {
var cellProps = calculateCellColSpan({
columnIndex: i,
rowId: rowId,
minFirstColumnIndex: minFirstColumn,
maxLastColumnIndex: maxLastColumn,
columns: columns
});
if (cellProps.colSpan > 1) {
i += cellProps.colSpan - 1;
}
}
}, [calculateCellColSpan]);
var columnSpanningPublicApi = {
unstable_getCellColSpanInfo: getCellColSpanInfo
};
var columnSpanningPrivateApi = {
calculateColSpan: calculateColSpan
};
useGridApiMethod(apiRef, columnSpanningPublicApi, 'public');
useGridApiMethod(apiRef, columnSpanningPrivateApi, 'private');
var handleColumnReorderChange = React.useCallback(function () {
// `colSpan` needs to be recalculated after column reordering
lookup.current = {};
}, []);
useGridApiEventHandler(apiRef, 'columnOrderChange', handleColumnReorderChange);
};

View File

@@ -0,0 +1,320 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridColumnFieldsSelector, gridColumnDefinitionsSelector, gridColumnLookupSelector, gridColumnsStateSelector, gridColumnVisibilityModelSelector, gridVisibleColumnDefinitionsSelector, gridColumnPositionsSelector } from './gridColumnsSelector';
import { GridSignature, useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridRegisterPipeProcessor, useGridRegisterPipeApplier } from '../../core/pipeProcessing';
import { hydrateColumnsWidth, createColumnsState, mergeColumnsState, COLUMNS_DIMENSION_PROPERTIES } from './gridColumnsUtils';
import { GridPreferencePanelsValue } from '../preferencesPanel';
import { getGridDefaultColumnTypes } from '../../../colDef';
import { jsx as _jsx } from "react/jsx-runtime";
var defaultColumnTypes = getGridDefaultColumnTypes();
export var columnsStateInitializer = function columnsStateInitializer(state, props, apiRef) {
var _props$initialState, _ref, _props$columnVisibili, _props$initialState2;
var columnsState = createColumnsState({
apiRef: apiRef,
columnTypes: defaultColumnTypes,
columnsToUpsert: props.columns,
initialState: (_props$initialState = props.initialState) == null ? void 0 : _props$initialState.columns,
columnVisibilityModel: (_ref = (_props$columnVisibili = props.columnVisibilityModel) != null ? _props$columnVisibili : (_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.columns) == null ? void 0 : _props$initialState2.columnVisibilityModel) != null ? _ref : {},
keepOnlyColumnsToUpsert: true
});
return _extends({}, state, {
columns: columnsState
});
};
/**
* @requires useGridParamsApi (method)
* @requires useGridDimensions (method, event) - can be after
* TODO: Impossible priority - useGridParamsApi also needs to be after useGridColumns
*/
export function useGridColumns(apiRef, props) {
var _props$initialState4, _props$slotProps2;
var logger = useGridLogger(apiRef, 'useGridColumns');
var columnTypes = defaultColumnTypes;
var previousColumnsProp = React.useRef(props.columns);
var previousColumnTypesProp = React.useRef(columnTypes);
apiRef.current.registerControlState({
stateId: 'visibleColumns',
propModel: props.columnVisibilityModel,
propOnChange: props.onColumnVisibilityModelChange,
stateSelector: gridColumnVisibilityModelSelector,
changeEvent: 'columnVisibilityModelChange'
});
var setGridColumnsState = React.useCallback(function (columnsState) {
logger.debug('Updating columns state.');
apiRef.current.setState(mergeColumnsState(columnsState));
apiRef.current.forceUpdate();
apiRef.current.publishEvent('columnsChange', columnsState.orderedFields);
}, [logger, apiRef]);
/**
* API METHODS
*/
var getColumn = React.useCallback(function (field) {
return gridColumnLookupSelector(apiRef)[field];
}, [apiRef]);
var getAllColumns = React.useCallback(function () {
return gridColumnDefinitionsSelector(apiRef);
}, [apiRef]);
var getVisibleColumns = React.useCallback(function () {
return gridVisibleColumnDefinitionsSelector(apiRef);
}, [apiRef]);
var getColumnIndex = React.useCallback(function (field) {
var useVisibleColumns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var columns = useVisibleColumns ? gridVisibleColumnDefinitionsSelector(apiRef) : gridColumnDefinitionsSelector(apiRef);
return columns.findIndex(function (col) {
return col.field === field;
});
}, [apiRef]);
var getColumnPosition = React.useCallback(function (field) {
var index = getColumnIndex(field);
return gridColumnPositionsSelector(apiRef)[index];
}, [apiRef, getColumnIndex]);
var setColumnVisibilityModel = React.useCallback(function (model) {
var currentModel = gridColumnVisibilityModelSelector(apiRef);
if (currentModel !== model) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
columns: createColumnsState({
apiRef: apiRef,
columnTypes: columnTypes,
columnsToUpsert: [],
initialState: undefined,
columnVisibilityModel: model,
keepOnlyColumnsToUpsert: false
})
});
});
apiRef.current.forceUpdate();
}
}, [apiRef, columnTypes]);
var updateColumns = React.useCallback(function (columns) {
var columnsState = createColumnsState({
apiRef: apiRef,
columnTypes: columnTypes,
columnsToUpsert: columns,
initialState: undefined,
keepOnlyColumnsToUpsert: false
});
setGridColumnsState(columnsState);
}, [apiRef, setGridColumnsState, columnTypes]);
var setColumnVisibility = React.useCallback(function (field, isVisible) {
var _columnVisibilityMode;
var columnVisibilityModel = gridColumnVisibilityModelSelector(apiRef);
var isCurrentlyVisible = (_columnVisibilityMode = columnVisibilityModel[field]) != null ? _columnVisibilityMode : true;
if (isVisible !== isCurrentlyVisible) {
var newModel = _extends({}, columnVisibilityModel, _defineProperty({}, field, isVisible));
apiRef.current.setColumnVisibilityModel(newModel);
}
}, [apiRef]);
var getColumnIndexRelativeToVisibleColumns = React.useCallback(function (field) {
var allColumns = gridColumnFieldsSelector(apiRef);
return allColumns.findIndex(function (col) {
return col === field;
});
}, [apiRef]);
var setColumnIndex = React.useCallback(function (field, targetIndexPosition) {
var allColumns = gridColumnFieldsSelector(apiRef);
var oldIndexPosition = getColumnIndexRelativeToVisibleColumns(field);
if (oldIndexPosition === targetIndexPosition) {
return;
}
logger.debug("Moving column ".concat(field, " to index ").concat(targetIndexPosition));
var updatedColumns = _toConsumableArray(allColumns);
var fieldRemoved = updatedColumns.splice(oldIndexPosition, 1)[0];
updatedColumns.splice(targetIndexPosition, 0, fieldRemoved);
setGridColumnsState(_extends({}, gridColumnsStateSelector(apiRef.current.state), {
orderedFields: updatedColumns
}));
var params = {
column: apiRef.current.getColumn(field),
targetIndex: apiRef.current.getColumnIndexRelativeToVisibleColumns(field),
oldIndex: oldIndexPosition
};
apiRef.current.publishEvent('columnIndexChange', params);
}, [apiRef, logger, setGridColumnsState, getColumnIndexRelativeToVisibleColumns]);
var setColumnWidth = React.useCallback(function (field, width) {
var _apiRef$current$getRo, _apiRef$current$getRo2;
logger.debug("Updating column ".concat(field, " width to ").concat(width));
var columnsState = gridColumnsStateSelector(apiRef.current.state);
var column = columnsState.lookup[field];
var newColumn = _extends({}, column, {
width: width,
hasBeenResized: true
});
setGridColumnsState(hydrateColumnsWidth(_extends({}, columnsState, {
lookup: _extends({}, columnsState.lookup, _defineProperty({}, field, newColumn))
}), (_apiRef$current$getRo = (_apiRef$current$getRo2 = apiRef.current.getRootDimensions()) == null ? void 0 : _apiRef$current$getRo2.viewportInnerSize.width) != null ? _apiRef$current$getRo : 0));
apiRef.current.publishEvent('columnWidthChange', {
element: apiRef.current.getColumnHeaderElement(field),
colDef: newColumn,
width: width
});
}, [apiRef, logger, setGridColumnsState]);
var columnApi = {
getColumn: getColumn,
getAllColumns: getAllColumns,
getColumnIndex: getColumnIndex,
getColumnPosition: getColumnPosition,
getVisibleColumns: getVisibleColumns,
getColumnIndexRelativeToVisibleColumns: getColumnIndexRelativeToVisibleColumns,
updateColumns: updateColumns,
setColumnVisibilityModel: setColumnVisibilityModel,
setColumnVisibility: setColumnVisibility,
setColumnWidth: setColumnWidth
};
var columnReorderApi = {
setColumnIndex: setColumnIndex
};
useGridApiMethod(apiRef, columnApi, 'public');
useGridApiMethod(apiRef, columnReorderApi, props.signature === GridSignature.DataGrid ? 'private' : 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState$c, _props$initialState3;
var columnsStateToExport = {};
var columnVisibilityModelToExport = gridColumnVisibilityModelSelector(apiRef);
var shouldExportColumnVisibilityModel =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the model is controlled
props.columnVisibilityModel != null ||
// Always export if the model has been initialized
// TODO v6 Do a nullish check instead to export even if the initial model equals "{}"
Object.keys((_props$initialState$c = (_props$initialState3 = props.initialState) == null || (_props$initialState3 = _props$initialState3.columns) == null ? void 0 : _props$initialState3.columnVisibilityModel) != null ? _props$initialState$c : {}).length > 0 ||
// Always export if the model is not empty
Object.keys(columnVisibilityModelToExport).length > 0;
if (shouldExportColumnVisibilityModel) {
columnsStateToExport.columnVisibilityModel = columnVisibilityModelToExport;
}
columnsStateToExport.orderedFields = gridColumnFieldsSelector(apiRef);
var columns = gridColumnDefinitionsSelector(apiRef);
var dimensions = {};
columns.forEach(function (colDef) {
if (colDef.hasBeenResized) {
var colDefDimensions = {};
COLUMNS_DIMENSION_PROPERTIES.forEach(function (propertyName) {
var propertyValue = colDef[propertyName];
if (propertyValue === Infinity) {
propertyValue = -1;
}
colDefDimensions[propertyName] = propertyValue;
});
dimensions[colDef.field] = colDefDimensions;
}
});
if (Object.keys(dimensions).length > 0) {
columnsStateToExport.dimensions = dimensions;
}
return _extends({}, prevState, {
columns: columnsStateToExport
});
}, [apiRef, props.columnVisibilityModel, (_props$initialState4 = props.initialState) == null ? void 0 : _props$initialState4.columns]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto;
var columnVisibilityModelToImport = (_context$stateToResto = context.stateToRestore.columns) == null ? void 0 : _context$stateToResto.columnVisibilityModel;
var initialState = context.stateToRestore.columns;
if (columnVisibilityModelToImport == null && initialState == null) {
return params;
}
var columnsState = createColumnsState({
apiRef: apiRef,
columnTypes: columnTypes,
columnsToUpsert: [],
initialState: initialState,
columnVisibilityModel: columnVisibilityModelToImport,
keepOnlyColumnsToUpsert: false
});
apiRef.current.setState(mergeColumnsState(columnsState));
if (initialState != null) {
apiRef.current.publishEvent('columnsChange', columnsState.orderedFields);
}
return params;
}, [apiRef, columnTypes]);
var preferencePanelPreProcessing = React.useCallback(function (initialValue, value) {
if (value === GridPreferencePanelsValue.columns) {
var _props$slotProps;
var ColumnsPanel = props.slots.columnsPanel;
return /*#__PURE__*/_jsx(ColumnsPanel, _extends({}, (_props$slotProps = props.slotProps) == null ? void 0 : _props$slotProps.columnsPanel));
}
return initialValue;
}, [props.slots.columnsPanel, (_props$slotProps2 = props.slotProps) == null ? void 0 : _props$slotProps2.columnsPanel]);
var addColumnMenuItems = React.useCallback(function (columnMenuItems) {
if (props.disableColumnSelector) {
return columnMenuItems;
}
return [].concat(_toConsumableArray(columnMenuItems), ['columnMenuColumnsItem']);
}, [props.disableColumnSelector]);
useGridRegisterPipeProcessor(apiRef, 'columnMenu', addColumnMenuItems);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
useGridRegisterPipeProcessor(apiRef, 'preferencePanel', preferencePanelPreProcessing);
/**
* EVENTS
*/
var prevInnerWidth = React.useRef(null);
var handleGridSizeChange = function handleGridSizeChange(viewportInnerSize) {
if (prevInnerWidth.current !== viewportInnerSize.width) {
prevInnerWidth.current = viewportInnerSize.width;
setGridColumnsState(hydrateColumnsWidth(gridColumnsStateSelector(apiRef.current.state), viewportInnerSize.width));
}
};
useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleGridSizeChange);
/**
* APPLIERS
*/
var hydrateColumns = React.useCallback(function () {
logger.info("Columns pipe processing have changed, regenerating the columns");
var columnsState = createColumnsState({
apiRef: apiRef,
columnTypes: columnTypes,
columnsToUpsert: [],
initialState: undefined,
keepOnlyColumnsToUpsert: false
});
setGridColumnsState(columnsState);
}, [apiRef, logger, setGridColumnsState, columnTypes]);
useGridRegisterPipeApplier(apiRef, 'hydrateColumns', hydrateColumns);
/**
* EFFECTS
*/
// The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridColumns`
// As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
var isFirstRender = React.useRef(true);
React.useEffect(function () {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
logger.info("GridColumns have changed, new length ".concat(props.columns.length));
if (previousColumnsProp.current === props.columns && previousColumnTypesProp.current === columnTypes) {
return;
}
var columnsState = createColumnsState({
apiRef: apiRef,
columnTypes: columnTypes,
initialState: undefined,
// If the user provides a model, we don't want to set it in the state here because it has it's dedicated `useEffect` which calls `setColumnVisibilityModel`
columnsToUpsert: props.columns,
keepOnlyColumnsToUpsert: true
});
previousColumnsProp.current = props.columns;
previousColumnTypesProp.current = columnTypes;
setGridColumnsState(columnsState);
}, [logger, apiRef, setGridColumnsState, props.columns, columnTypes]);
React.useEffect(function () {
if (props.columnVisibilityModel !== undefined) {
apiRef.current.setColumnVisibilityModel(props.columnVisibilityModel);
}
}, [apiRef, logger, props.columnVisibilityModel]);
}

View File

@@ -0,0 +1,10 @@
import { createSelector } from '../../../utils/createSelector';
export var gridDensitySelector = function gridDensitySelector(state) {
return state.density;
};
export var gridDensityValueSelector = createSelector(gridDensitySelector, function (density) {
return density.value;
});
export var gridDensityFactorSelector = createSelector(gridDensitySelector, function (density) {
return density.factor;
});

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,2 @@
export * from './densityState';
export * from './densitySelector';

View File

@@ -0,0 +1,48 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridLogger } from '../../utils/useGridLogger';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridDensitySelector } from './densitySelector';
import { isDeepEqual } from '../../../utils/utils';
export var COMPACT_DENSITY_FACTOR = 0.7;
export var COMFORTABLE_DENSITY_FACTOR = 1.3;
var DENSITY_FACTORS = {
compact: COMPACT_DENSITY_FACTOR,
comfortable: COMFORTABLE_DENSITY_FACTOR,
standard: 1
};
export var densityStateInitializer = function densityStateInitializer(state, props) {
return _extends({}, state, {
density: {
value: props.density,
factor: DENSITY_FACTORS[props.density]
}
});
};
export var useGridDensity = function useGridDensity(apiRef, props) {
var logger = useGridLogger(apiRef, 'useDensity');
var setDensity = React.useCallback(function (newDensity) {
logger.debug("Set grid density to ".concat(newDensity));
apiRef.current.setState(function (state) {
var currentDensityState = gridDensitySelector(state);
var newDensityState = {
value: newDensity,
factor: DENSITY_FACTORS[newDensity]
};
if (isDeepEqual(currentDensityState, newDensityState)) {
return state;
}
return _extends({}, state, {
density: newDensityState
});
});
apiRef.current.forceUpdate();
}, [logger, apiRef]);
React.useEffect(function () {
apiRef.current.setDensity(props.density);
}, [apiRef, props.density]);
var densityApi = {
setDensity: setDensity
};
useGridApiMethod(apiRef, densityApi, 'public');
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,227 @@
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);
}

View File

@@ -0,0 +1,4 @@
// TODO v6: rename to gridEditingStateSelector
export var gridEditRowsStateSelector = function gridEditRowsStateSelector(state) {
return state.editRows;
};

View File

@@ -0,0 +1,459 @@
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _toPropertyKey from "@babel/runtime/helpers/esm/toPropertyKey";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
var _excluded = ["id", "field"],
_excluded2 = ["id", "field"];
import _regeneratorRuntime from "@babel/runtime/regenerator";
import * as React from 'react';
import { unstable_useEventCallback as useEventCallback, unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { useGridApiEventHandler, useGridApiOptionHandler } from '../../utils/useGridApiEventHandler';
import { GridEditModes, GridCellModes } from '../../../models/gridEditRowModel';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridEditRowsStateSelector } from './gridEditingSelectors';
import { isPrintableKey } from '../../../utils/keyboardUtils';
import { buildWarning } from '../../../utils/warning';
import { gridRowsDataRowIdToIdLookupSelector } from '../rows/gridRowsSelector';
import { deepClone } from '../../../utils/utils';
import { GridCellEditStartReasons, GridCellEditStopReasons } from '../../../models/params/gridEditCellParams';
var missingOnProcessRowUpdateErrorWarning = buildWarning(['MUI: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. `<DataGrid onProcessRowUpdateError={(error) => ...} />`.', 'For more detail, see http://mui.com/components/data-grid/editing/#server-side-persistence.'], 'error');
export var useGridCellEditing = function useGridCellEditing(apiRef, props) {
var _React$useState = React.useState({}),
_React$useState2 = _slicedToArray(_React$useState, 2),
cellModesModel = _React$useState2[0],
setCellModesModel = _React$useState2[1];
var cellModesModelRef = React.useRef(cellModesModel);
var prevCellModesModel = React.useRef({});
var processRowUpdate = props.processRowUpdate,
onProcessRowUpdateError = props.onProcessRowUpdateError,
cellModesModelProp = props.cellModesModel,
onCellModesModelChange = props.onCellModesModelChange;
var runIfEditModeIsCell = function runIfEditModeIsCell(callback) {
return function () {
if (props.editMode === GridEditModes.Cell) {
callback.apply(void 0, arguments);
}
};
};
var throwIfNotEditable = React.useCallback(function (id, field) {
var params = apiRef.current.getCellParams(id, field);
if (!apiRef.current.isCellEditable(params)) {
throw new Error("MUI: The cell with id=".concat(id, " and field=").concat(field, " is not editable."));
}
}, [apiRef]);
var throwIfNotInMode = React.useCallback(function (id, field, mode) {
if (apiRef.current.getCellMode(id, field) !== mode) {
throw new Error("MUI: The cell with id=".concat(id, " and field=").concat(field, " is not in ").concat(mode, " mode."));
}
}, [apiRef]);
var handleCellDoubleClick = React.useCallback(function (params, event) {
if (!params.isEditable) {
return;
}
if (params.cellMode === GridCellModes.Edit) {
return;
}
var newParams = _extends({}, params, {
reason: GridCellEditStartReasons.cellDoubleClick
});
apiRef.current.publishEvent('cellEditStart', newParams, event);
}, [apiRef]);
var handleCellFocusOut = React.useCallback(function (params, event) {
if (params.cellMode === GridCellModes.View) {
return;
}
if (apiRef.current.getCellMode(params.id, params.field) === GridCellModes.View) {
return;
}
var newParams = _extends({}, params, {
reason: GridCellEditStopReasons.cellFocusOut
});
apiRef.current.publishEvent('cellEditStop', newParams, event);
}, [apiRef]);
var handleCellKeyDown = React.useCallback(function (params, event) {
if (params.cellMode === GridCellModes.Edit) {
// Wait until IME is settled for Asian languages like Japanese and Chinese
// TODO: `event.which` is deprecated but this is a temporary workaround
if (event.which === 229) {
return;
}
var reason;
if (event.key === 'Escape') {
reason = GridCellEditStopReasons.escapeKeyDown;
} else if (event.key === 'Enter') {
reason = GridCellEditStopReasons.enterKeyDown;
} else if (event.key === 'Tab') {
reason = event.shiftKey ? GridCellEditStopReasons.shiftTabKeyDown : GridCellEditStopReasons.tabKeyDown;
event.preventDefault(); // Prevent going to the next element in the tab sequence
}
if (reason) {
var newParams = _extends({}, params, {
reason: reason
});
apiRef.current.publishEvent('cellEditStop', newParams, event);
}
} else if (params.isEditable) {
var _reason;
var canStartEditing = apiRef.current.unstable_applyPipeProcessors('canStartEditing', true, {
event: event,
cellParams: params,
editMode: 'cell'
});
if (!canStartEditing) {
return;
}
if (isPrintableKey(event)) {
_reason = GridCellEditStartReasons.printableKeyDown;
} else if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
_reason = GridCellEditStartReasons.pasteKeyDown;
} else if (event.key === 'Enter') {
_reason = GridCellEditStartReasons.enterKeyDown;
} else if (event.key === 'Delete' || event.key === 'Backspace') {
// Delete on Windows, Backspace on macOS
_reason = GridCellEditStartReasons.deleteKeyDown;
}
if (_reason) {
var _newParams = _extends({}, params, {
reason: _reason,
key: event.key
});
apiRef.current.publishEvent('cellEditStart', _newParams, event);
}
}
}, [apiRef]);
var handleCellEditStart = React.useCallback(function (params) {
var id = params.id,
field = params.field,
reason = params.reason;
var startCellEditModeParams = {
id: id,
field: field
};
if (reason === GridCellEditStartReasons.printableKeyDown || reason === GridCellEditStartReasons.deleteKeyDown || reason === GridCellEditStartReasons.pasteKeyDown) {
startCellEditModeParams.deleteValue = true;
}
apiRef.current.startCellEditMode(startCellEditModeParams);
}, [apiRef]);
var handleCellEditStop = React.useCallback(function (params) {
var id = params.id,
field = params.field,
reason = params.reason;
apiRef.current.runPendingEditCellValueMutation(id, field);
var cellToFocusAfter;
if (reason === GridCellEditStopReasons.enterKeyDown) {
cellToFocusAfter = 'below';
} else if (reason === GridCellEditStopReasons.tabKeyDown) {
cellToFocusAfter = 'right';
} else if (reason === GridCellEditStopReasons.shiftTabKeyDown) {
cellToFocusAfter = 'left';
}
var ignoreModifications = reason === 'escapeKeyDown';
apiRef.current.stopCellEditMode({
id: id,
field: field,
ignoreModifications: ignoreModifications,
cellToFocusAfter: cellToFocusAfter
});
}, [apiRef]);
useGridApiEventHandler(apiRef, 'cellDoubleClick', runIfEditModeIsCell(handleCellDoubleClick));
useGridApiEventHandler(apiRef, 'cellFocusOut', runIfEditModeIsCell(handleCellFocusOut));
useGridApiEventHandler(apiRef, 'cellKeyDown', runIfEditModeIsCell(handleCellKeyDown));
useGridApiEventHandler(apiRef, 'cellEditStart', runIfEditModeIsCell(handleCellEditStart));
useGridApiEventHandler(apiRef, 'cellEditStop', runIfEditModeIsCell(handleCellEditStop));
useGridApiOptionHandler(apiRef, 'cellEditStart', props.onCellEditStart);
useGridApiOptionHandler(apiRef, 'cellEditStop', props.onCellEditStop);
var getCellMode = React.useCallback(function (id, field) {
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var isEditing = editingState[id] && editingState[id][field];
return isEditing ? GridCellModes.Edit : GridCellModes.View;
}, [apiRef]);
var updateCellModesModel = useEventCallback(function (newModel) {
var isNewModelDifferentFromProp = newModel !== props.cellModesModel;
if (onCellModesModelChange && isNewModelDifferentFromProp) {
onCellModesModelChange(newModel, {});
}
if (props.cellModesModel && isNewModelDifferentFromProp) {
return; // The prop always win
}
setCellModesModel(newModel);
cellModesModelRef.current = newModel;
apiRef.current.publishEvent('cellModesModelChange', newModel);
});
var updateFieldInCellModesModel = React.useCallback(function (id, field, newProps) {
// We use the ref because it always contain the up-to-date value, different from the state
// that needs a rerender to reflect the new value
var newModel = _extends({}, cellModesModelRef.current);
if (newProps !== null) {
newModel[id] = _extends({}, newModel[id], _defineProperty({}, field, _extends({}, newProps)));
} else {
var _newModel$id = newModel[id],
fieldToRemove = _newModel$id[field],
otherFields = _objectWithoutProperties(_newModel$id, [field].map(_toPropertyKey)); // Ensure that we have a new object, not a reference
newModel[id] = otherFields;
if (Object.keys(newModel[id]).length === 0) {
delete newModel[id];
}
}
updateCellModesModel(newModel);
}, [updateCellModesModel]);
var updateOrDeleteFieldState = React.useCallback(function (id, field, newProps) {
apiRef.current.setState(function (state) {
var newEditingState = _extends({}, state.editRows);
if (newProps !== null) {
newEditingState[id] = _extends({}, newEditingState[id], _defineProperty({}, field, _extends({}, newProps)));
} else {
delete newEditingState[id][field];
if (Object.keys(newEditingState[id]).length === 0) {
delete newEditingState[id];
}
}
return _extends({}, state, {
editRows: newEditingState
});
});
apiRef.current.forceUpdate();
}, [apiRef]);
var startCellEditMode = React.useCallback(function (params) {
var id = params.id,
field = params.field,
other = _objectWithoutProperties(params, _excluded);
throwIfNotEditable(id, field);
throwIfNotInMode(id, field, GridCellModes.View);
updateFieldInCellModesModel(id, field, _extends({
mode: GridCellModes.Edit
}, other));
}, [throwIfNotEditable, throwIfNotInMode, updateFieldInCellModesModel]);
var updateStateToStartCellEditMode = useEventCallback(function (params) {
var id = params.id,
field = params.field,
deleteValue = params.deleteValue,
initialValue = params.initialValue;
var newValue = apiRef.current.getCellValue(id, field);
if (deleteValue || initialValue) {
newValue = deleteValue ? '' : initialValue;
}
var newProps = {
value: newValue,
error: false,
isProcessingProps: false
};
updateOrDeleteFieldState(id, field, newProps);
apiRef.current.setCellFocus(id, field);
});
var stopCellEditMode = React.useCallback(function (params) {
var id = params.id,
field = params.field,
other = _objectWithoutProperties(params, _excluded2);
throwIfNotInMode(id, field, GridCellModes.Edit);
updateFieldInCellModesModel(id, field, _extends({
mode: GridCellModes.View
}, other));
}, [throwIfNotInMode, updateFieldInCellModesModel]);
var updateStateToStopCellEditMode = useEventCallback( /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(params) {
var id, field, ignoreModifications, _params$cellToFocusAf, cellToFocusAfter, finishCellEditMode, editingState, _editingState$id$fiel, error, isProcessingProps, rowUpdate, handleError, row;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
id = params.id, field = params.field, ignoreModifications = params.ignoreModifications, _params$cellToFocusAf = params.cellToFocusAfter, cellToFocusAfter = _params$cellToFocusAf === void 0 ? 'none' : _params$cellToFocusAf;
throwIfNotInMode(id, field, GridCellModes.Edit);
apiRef.current.runPendingEditCellValueMutation(id, field);
finishCellEditMode = function finishCellEditMode() {
updateOrDeleteFieldState(id, field, null);
updateFieldInCellModesModel(id, field, null);
if (cellToFocusAfter !== 'none') {
apiRef.current.moveFocusToRelativeCell(id, field, cellToFocusAfter);
}
};
if (!ignoreModifications) {
_context.next = 7;
break;
}
finishCellEditMode();
return _context.abrupt("return");
case 7:
editingState = gridEditRowsStateSelector(apiRef.current.state);
_editingState$id$fiel = editingState[id][field], error = _editingState$id$fiel.error, isProcessingProps = _editingState$id$fiel.isProcessingProps;
if (!(error || isProcessingProps)) {
_context.next = 13;
break;
}
// Attempt to change cell mode to "view" was not successful
// Update previous mode to allow another attempt
prevCellModesModel.current[id][field].mode = GridCellModes.Edit;
// Revert the mode in the cellModesModel prop back to "edit"
updateFieldInCellModesModel(id, field, {
mode: GridCellModes.Edit
});
return _context.abrupt("return");
case 13:
rowUpdate = apiRef.current.getRowWithUpdatedValuesFromCellEditing(id, field);
if (processRowUpdate) {
handleError = function handleError(errorThrown) {
prevCellModesModel.current[id][field].mode = GridCellModes.Edit;
// Revert the mode in the cellModesModel prop back to "edit"
updateFieldInCellModesModel(id, field, {
mode: GridCellModes.Edit
});
if (onProcessRowUpdateError) {
onProcessRowUpdateError(errorThrown);
} else {
missingOnProcessRowUpdateErrorWarning();
}
};
try {
row = apiRef.current.getRow(id);
Promise.resolve(processRowUpdate(rowUpdate, row)).then(function (finalRowUpdate) {
apiRef.current.updateRows([finalRowUpdate]);
finishCellEditMode();
}).catch(handleError);
} catch (errorThrown) {
handleError(errorThrown);
}
} else {
apiRef.current.updateRows([rowUpdate]);
finishCellEditMode();
}
case 15:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}());
var setCellEditingEditCellValue = React.useCallback( /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(params) {
var _editingState$id;
var id, field, value, debounceMs, skipValueParser, column, row, parsedValue, editingState, newProps, hasChanged;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
id = params.id, field = params.field, value = params.value, debounceMs = params.debounceMs, skipValueParser = params.unstable_skipValueParser;
throwIfNotEditable(id, field);
throwIfNotInMode(id, field, GridCellModes.Edit);
column = apiRef.current.getColumn(field);
row = apiRef.current.getRow(id);
parsedValue = value;
if (column.valueParser && !skipValueParser) {
parsedValue = column.valueParser(value, apiRef.current.getCellParams(id, field));
}
editingState = gridEditRowsStateSelector(apiRef.current.state);
newProps = _extends({}, editingState[id][field], {
value: parsedValue,
changeReason: debounceMs ? 'debouncedSetEditCellValue' : 'setEditCellValue'
});
if (!column.preProcessEditCellProps) {
_context2.next = 16;
break;
}
hasChanged = value !== editingState[id][field].value;
newProps = _extends({}, newProps, {
isProcessingProps: true
});
updateOrDeleteFieldState(id, field, newProps);
_context2.next = 15;
return Promise.resolve(column.preProcessEditCellProps({
id: id,
row: row,
props: newProps,
hasChanged: hasChanged
}));
case 15:
newProps = _context2.sent;
case 16:
if (!(apiRef.current.getCellMode(id, field) === GridCellModes.View)) {
_context2.next = 18;
break;
}
return _context2.abrupt("return", false);
case 18:
editingState = gridEditRowsStateSelector(apiRef.current.state);
newProps = _extends({}, newProps, {
isProcessingProps: false
});
// We don't update the value with the one coming from the props pre-processing
// because when the promise resolves it may be already outdated. The only
// exception to this rule is when there's no pre-processing.
newProps.value = column.preProcessEditCellProps ? editingState[id][field].value : parsedValue;
updateOrDeleteFieldState(id, field, newProps);
editingState = gridEditRowsStateSelector(apiRef.current.state);
return _context2.abrupt("return", !((_editingState$id = editingState[id]) != null && (_editingState$id = _editingState$id[field]) != null && _editingState$id.error));
case 24:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return function (_x2) {
return _ref2.apply(this, arguments);
};
}(), [apiRef, throwIfNotEditable, throwIfNotInMode, updateOrDeleteFieldState]);
var getRowWithUpdatedValuesFromCellEditing = React.useCallback(function (id, field) {
var column = apiRef.current.getColumn(field);
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var row = apiRef.current.getRow(id);
if (!editingState[id] || !editingState[id][field]) {
return apiRef.current.getRow(id);
}
var value = editingState[id][field].value;
return column.valueSetter ? column.valueSetter({
value: value,
row: row
}) : _extends({}, row, _defineProperty({}, field, value));
}, [apiRef]);
var editingApi = {
getCellMode: getCellMode,
startCellEditMode: startCellEditMode,
stopCellEditMode: stopCellEditMode
};
var editingPrivateApi = {
setCellEditingEditCellValue: setCellEditingEditCellValue,
getRowWithUpdatedValuesFromCellEditing: getRowWithUpdatedValuesFromCellEditing
};
useGridApiMethod(apiRef, editingApi, 'public');
useGridApiMethod(apiRef, editingPrivateApi, 'private');
React.useEffect(function () {
if (cellModesModelProp) {
updateCellModesModel(cellModesModelProp);
}
}, [cellModesModelProp, updateCellModesModel]);
// Run this effect synchronously so that the keyboard event can impact the yet-to-be-rendered input.
useEnhancedEffect(function () {
var idToIdLookup = gridRowsDataRowIdToIdLookupSelector(apiRef);
// Update the ref here because updateStateToStopCellEditMode may change it later
var copyOfPrevCellModes = prevCellModesModel.current;
prevCellModesModel.current = deepClone(cellModesModel); // Do a deep-clone because the attributes might be changed later
Object.entries(cellModesModel).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
id = _ref4[0],
fields = _ref4[1];
Object.entries(fields).forEach(function (_ref5) {
var _copyOfPrevCellModes$, _idToIdLookup$id;
var _ref6 = _slicedToArray(_ref5, 2),
field = _ref6[0],
params = _ref6[1];
var prevMode = ((_copyOfPrevCellModes$ = copyOfPrevCellModes[id]) == null || (_copyOfPrevCellModes$ = _copyOfPrevCellModes$[field]) == null ? void 0 : _copyOfPrevCellModes$.mode) || GridCellModes.View;
var originalId = (_idToIdLookup$id = idToIdLookup[id]) != null ? _idToIdLookup$id : id;
if (params.mode === GridCellModes.Edit && prevMode === GridCellModes.View) {
updateStateToStartCellEditMode(_extends({
id: originalId,
field: field
}, params));
} else if (params.mode === GridCellModes.View && prevMode === GridCellModes.Edit) {
updateStateToStopCellEditMode(_extends({
id: originalId,
field: field
}, params));
}
});
});
}, [apiRef, cellModesModel, updateStateToStartCellEditMode, updateStateToStopCellEditMode]);
};

View File

@@ -0,0 +1,145 @@
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridCellEditing } from './useGridCellEditing';
import { GridCellModes, GridEditModes } from '../../../models/gridEditRowModel';
import { useGridRowEditing } from './useGridRowEditing';
import { gridEditRowsStateSelector } from './gridEditingSelectors';
import { isAutoGeneratedRow } from '../rows/gridRowsUtils';
export var editingStateInitializer = function editingStateInitializer(state) {
return _extends({}, state, {
editRows: {}
});
};
export var useGridEditing = function useGridEditing(apiRef, props) {
useGridCellEditing(apiRef, props);
useGridRowEditing(apiRef, props);
var debounceMap = React.useRef({});
var isCellEditableProp = props.isCellEditable;
var isCellEditable = React.useCallback(function (params) {
if (isAutoGeneratedRow(params.rowNode)) {
return false;
}
if (!params.colDef.editable) {
return false;
}
if (!params.colDef.renderEditCell) {
return false;
}
if (isCellEditableProp) {
return isCellEditableProp(params);
}
return true;
}, [isCellEditableProp]);
var maybeDebounce = function maybeDebounce(id, field, debounceMs, callback) {
if (!debounceMs) {
callback();
return;
}
if (!debounceMap.current[id]) {
debounceMap.current[id] = {};
}
if (debounceMap.current[id][field]) {
var _debounceMap$current$ = _slicedToArray(debounceMap.current[id][field], 1),
_timeout = _debounceMap$current$[0];
clearTimeout(_timeout);
}
// To run the callback immediately without waiting the timeout
var runImmediately = function runImmediately() {
var _debounceMap$current$2 = _slicedToArray(debounceMap.current[id][field], 1),
timeout = _debounceMap$current$2[0];
clearTimeout(timeout);
callback();
delete debounceMap.current[id][field];
};
var timeout = setTimeout(function () {
callback();
delete debounceMap.current[id][field];
}, debounceMs);
debounceMap.current[id][field] = [timeout, runImmediately];
};
React.useEffect(function () {
var debounces = debounceMap.current;
return function () {
Object.entries(debounces).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
id = _ref2[0],
fields = _ref2[1];
Object.keys(fields).forEach(function (field) {
var _debounces$id$field = _slicedToArray(debounces[id][field], 1),
timeout = _debounces$id$field[0];
clearTimeout(timeout);
delete debounces[id][field];
});
});
};
}, []);
var runPendingEditCellValueMutation = React.useCallback(function (id, field) {
if (!debounceMap.current[id]) {
return;
}
if (!field) {
Object.keys(debounceMap.current[id]).forEach(function (debouncedField) {
var _debounceMap$current$3 = _slicedToArray(debounceMap.current[id][debouncedField], 2),
runCallback = _debounceMap$current$3[1];
runCallback();
});
} else if (debounceMap.current[id][field]) {
var _debounceMap$current$4 = _slicedToArray(debounceMap.current[id][field], 2),
runCallback = _debounceMap$current$4[1];
runCallback();
}
}, []);
var setEditCellValue = React.useCallback(function (params) {
var id = params.id,
field = params.field,
debounceMs = params.debounceMs;
return new Promise(function (resolve) {
maybeDebounce(id, field, debounceMs, /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var setEditCellValueToCall, result;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
setEditCellValueToCall = props.editMode === GridEditModes.Row ? apiRef.current.setRowEditingEditCellValue : apiRef.current.setCellEditingEditCellValue; // Check if the cell is in edit mode
// By the time this callback runs the user may have cancelled the editing
if (!(apiRef.current.getCellMode(id, field) === GridCellModes.Edit)) {
_context.next = 6;
break;
}
_context.next = 4;
return setEditCellValueToCall(params);
case 4:
result = _context.sent;
resolve(result);
case 6:
case "end":
return _context.stop();
}
}, _callee);
})));
});
}, [apiRef, props.editMode]);
var getRowWithUpdatedValues = React.useCallback(function (id, field) {
return props.editMode === GridEditModes.Cell ? apiRef.current.getRowWithUpdatedValuesFromCellEditing(id, field) : apiRef.current.getRowWithUpdatedValuesFromRowEditing(id);
}, [apiRef, props.editMode]);
var getEditCellMeta = React.useCallback(function (id, field) {
var _editingState$id$fiel, _editingState$id;
var editingState = gridEditRowsStateSelector(apiRef.current.state);
return (_editingState$id$fiel = (_editingState$id = editingState[id]) == null ? void 0 : _editingState$id[field]) != null ? _editingState$id$fiel : null;
}, [apiRef]);
var editingSharedApi = {
isCellEditable: isCellEditable,
setEditCellValue: setEditCellValue,
getRowWithUpdatedValues: getRowWithUpdatedValues,
unstable_getEditCellMeta: getEditCellMeta
};
var editingSharedPrivateApi = {
runPendingEditCellValueMutation: runPendingEditCellValueMutation
};
useGridApiMethod(apiRef, editingSharedApi, 'public');
useGridApiMethod(apiRef, editingSharedPrivateApi, 'private');
};

View File

@@ -0,0 +1,570 @@
import _toPropertyKey from "@babel/runtime/helpers/esm/toPropertyKey";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
var _excluded = ["id"],
_excluded2 = ["id"];
import * as React from 'react';
import { unstable_useEventCallback as useEventCallback, unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { useGridApiEventHandler, useGridApiOptionHandler } from '../../utils/useGridApiEventHandler';
import { GridEditModes, GridRowModes } from '../../../models/gridEditRowModel';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridEditRowsStateSelector } from './gridEditingSelectors';
import { isPrintableKey } from '../../../utils/keyboardUtils';
import { gridColumnFieldsSelector, gridVisibleColumnFieldsSelector } from '../columns/gridColumnsSelector';
import { buildWarning } from '../../../utils/warning';
import { gridRowsDataRowIdToIdLookupSelector } from '../rows/gridRowsSelector';
import { deepClone } from '../../../utils/utils';
import { GridRowEditStopReasons, GridRowEditStartReasons } from '../../../models/params/gridRowParams';
import { GRID_ACTIONS_COLUMN_TYPE } from '../../../colDef';
var missingOnProcessRowUpdateErrorWarning = buildWarning(['MUI: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. `<DataGrid onProcessRowUpdateError={(error) => ...} />`.', 'For more detail, see http://mui.com/components/data-grid/editing/#server-side-persistence.'], 'error');
export var useGridRowEditing = function useGridRowEditing(apiRef, props) {
var _React$useState = React.useState({}),
_React$useState2 = _slicedToArray(_React$useState, 2),
rowModesModel = _React$useState2[0],
setRowModesModel = _React$useState2[1];
var rowModesModelRef = React.useRef(rowModesModel);
var prevRowModesModel = React.useRef({});
var focusTimeout = React.useRef(null);
var nextFocusedCell = React.useRef(null);
var processRowUpdate = props.processRowUpdate,
onProcessRowUpdateError = props.onProcessRowUpdateError,
rowModesModelProp = props.rowModesModel,
onRowModesModelChange = props.onRowModesModelChange;
var runIfEditModeIsRow = function runIfEditModeIsRow(callback) {
return function () {
if (props.editMode === GridEditModes.Row) {
callback.apply(void 0, arguments);
}
};
};
var throwIfNotEditable = React.useCallback(function (id, field) {
var params = apiRef.current.getCellParams(id, field);
if (!apiRef.current.isCellEditable(params)) {
throw new Error("MUI: The cell with id=".concat(id, " and field=").concat(field, " is not editable."));
}
}, [apiRef]);
var throwIfNotInMode = React.useCallback(function (id, mode) {
if (apiRef.current.getRowMode(id) !== mode) {
throw new Error("MUI: The row with id=".concat(id, " is not in ").concat(mode, " mode."));
}
}, [apiRef]);
var handleCellDoubleClick = React.useCallback(function (params, event) {
if (!params.isEditable) {
return;
}
if (apiRef.current.getRowMode(params.id) === GridRowModes.Edit) {
return;
}
var rowParams = apiRef.current.getRowParams(params.id);
var newParams = _extends({}, rowParams, {
field: params.field,
reason: GridRowEditStartReasons.cellDoubleClick
});
apiRef.current.publishEvent('rowEditStart', newParams, event);
}, [apiRef]);
var handleCellFocusIn = React.useCallback(function (params) {
nextFocusedCell.current = params;
}, []);
var handleCellFocusOut = React.useCallback(function (params, event) {
if (!params.isEditable) {
return;
}
if (apiRef.current.getRowMode(params.id) === GridRowModes.View) {
return;
}
// The mechanism to detect if we can stop editing a row is different from
// the cell editing. Instead of triggering it when clicking outside a cell,
// we must check if another cell in the same row was not clicked. To achieve
// that, first we keep track of all cells that gained focus. When a cell loses
// focus we check if the next cell that received focus is from a different row.
nextFocusedCell.current = null;
focusTimeout.current = setTimeout(function () {
var _nextFocusedCell$curr;
focusTimeout.current = null;
if (((_nextFocusedCell$curr = nextFocusedCell.current) == null ? void 0 : _nextFocusedCell$curr.id) !== params.id) {
// The row might have been deleted during the click
if (!apiRef.current.getRow(params.id)) {
return;
}
// The row may already changed its mode
if (apiRef.current.getRowMode(params.id) === GridRowModes.View) {
return;
}
var rowParams = apiRef.current.getRowParams(params.id);
var newParams = _extends({}, rowParams, {
field: params.field,
reason: GridRowEditStopReasons.rowFocusOut
});
apiRef.current.publishEvent('rowEditStop', newParams, event);
}
});
}, [apiRef]);
React.useEffect(function () {
return function () {
clearTimeout(focusTimeout.current);
};
}, []);
var handleCellKeyDown = React.useCallback(function (params, event) {
if (params.cellMode === GridRowModes.Edit) {
// Wait until IME is settled for Asian languages like Japanese and Chinese
// TODO: `event.which` is deprecated but this is a temporary workaround
if (event.which === 229) {
return;
}
var reason;
if (event.key === 'Escape') {
reason = GridRowEditStopReasons.escapeKeyDown;
} else if (event.key === 'Enter') {
reason = GridRowEditStopReasons.enterKeyDown;
} else if (event.key === 'Tab') {
var columnFields = gridVisibleColumnFieldsSelector(apiRef).filter(function (field) {
var column = apiRef.current.getColumn(field);
if (column.type === GRID_ACTIONS_COLUMN_TYPE) {
return true;
}
return apiRef.current.isCellEditable(apiRef.current.getCellParams(params.id, field));
});
if (event.shiftKey) {
if (params.field === columnFields[0]) {
// Exit if user pressed Shift+Tab on the first field
reason = GridRowEditStopReasons.shiftTabKeyDown;
}
} else if (params.field === columnFields[columnFields.length - 1]) {
// Exit if user pressed Tab on the last field
reason = GridRowEditStopReasons.tabKeyDown;
}
// Always prevent going to the next element in the tab sequence because the focus is
// handled manually to support edit components rendered inside Portals
event.preventDefault();
if (!reason) {
var index = columnFields.findIndex(function (field) {
return field === params.field;
});
var nextFieldToFocus = columnFields[event.shiftKey ? index - 1 : index + 1];
apiRef.current.setCellFocus(params.id, nextFieldToFocus);
}
}
if (reason) {
var newParams = _extends({}, apiRef.current.getRowParams(params.id), {
reason: reason,
field: params.field
});
apiRef.current.publishEvent('rowEditStop', newParams, event);
}
} else if (params.isEditable) {
var _reason;
var canStartEditing = apiRef.current.unstable_applyPipeProcessors('canStartEditing', true, {
event: event,
cellParams: params,
editMode: 'row'
});
if (!canStartEditing) {
return;
}
if (isPrintableKey(event)) {
_reason = GridRowEditStartReasons.printableKeyDown;
} else if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
_reason = GridRowEditStartReasons.printableKeyDown;
} else if (event.key === 'Enter') {
_reason = GridRowEditStartReasons.enterKeyDown;
} else if (event.key === 'Delete' || event.key === 'Backspace') {
// Delete on Windows, Backspace on macOS
_reason = GridRowEditStartReasons.deleteKeyDown;
}
if (_reason) {
var rowParams = apiRef.current.getRowParams(params.id);
var _newParams = _extends({}, rowParams, {
field: params.field,
reason: _reason
});
apiRef.current.publishEvent('rowEditStart', _newParams, event);
}
}
}, [apiRef]);
var handleRowEditStart = React.useCallback(function (params) {
var id = params.id,
field = params.field,
reason = params.reason;
var startRowEditModeParams = {
id: id,
fieldToFocus: field
};
if (reason === GridRowEditStartReasons.printableKeyDown || reason === GridRowEditStartReasons.deleteKeyDown) {
startRowEditModeParams.deleteValue = !!field;
}
apiRef.current.startRowEditMode(startRowEditModeParams);
}, [apiRef]);
var handleRowEditStop = React.useCallback(function (params) {
var id = params.id,
reason = params.reason,
field = params.field;
apiRef.current.runPendingEditCellValueMutation(id);
var cellToFocusAfter;
if (reason === GridRowEditStopReasons.enterKeyDown) {
cellToFocusAfter = 'below';
} else if (reason === GridRowEditStopReasons.tabKeyDown) {
cellToFocusAfter = 'right';
} else if (reason === GridRowEditStopReasons.shiftTabKeyDown) {
cellToFocusAfter = 'left';
}
var ignoreModifications = reason === 'escapeKeyDown';
apiRef.current.stopRowEditMode({
id: id,
ignoreModifications: ignoreModifications,
field: field,
cellToFocusAfter: cellToFocusAfter
});
}, [apiRef]);
useGridApiEventHandler(apiRef, 'cellDoubleClick', runIfEditModeIsRow(handleCellDoubleClick));
useGridApiEventHandler(apiRef, 'cellFocusIn', runIfEditModeIsRow(handleCellFocusIn));
useGridApiEventHandler(apiRef, 'cellFocusOut', runIfEditModeIsRow(handleCellFocusOut));
useGridApiEventHandler(apiRef, 'cellKeyDown', runIfEditModeIsRow(handleCellKeyDown));
useGridApiEventHandler(apiRef, 'rowEditStart', runIfEditModeIsRow(handleRowEditStart));
useGridApiEventHandler(apiRef, 'rowEditStop', runIfEditModeIsRow(handleRowEditStop));
useGridApiOptionHandler(apiRef, 'rowEditStart', props.onRowEditStart);
useGridApiOptionHandler(apiRef, 'rowEditStop', props.onRowEditStop);
var getRowMode = React.useCallback(function (id) {
if (props.editMode === GridEditModes.Cell) {
return GridRowModes.View;
}
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var isEditing = editingState[id] && Object.keys(editingState[id]).length > 0;
return isEditing ? GridRowModes.Edit : GridRowModes.View;
}, [apiRef, props.editMode]);
var updateRowModesModel = useEventCallback(function (newModel) {
var isNewModelDifferentFromProp = newModel !== props.rowModesModel;
if (onRowModesModelChange && isNewModelDifferentFromProp) {
onRowModesModelChange(newModel, {});
}
if (props.rowModesModel && isNewModelDifferentFromProp) {
return; // The prop always win
}
setRowModesModel(newModel);
rowModesModelRef.current = newModel;
apiRef.current.publishEvent('rowModesModelChange', newModel);
});
var updateRowInRowModesModel = React.useCallback(function (id, newProps) {
var newModel = _extends({}, rowModesModelRef.current);
if (newProps !== null) {
newModel[id] = _extends({}, newProps);
} else {
delete newModel[id];
}
updateRowModesModel(newModel);
}, [updateRowModesModel]);
var updateOrDeleteRowState = React.useCallback(function (id, newProps) {
apiRef.current.setState(function (state) {
var newEditingState = _extends({}, state.editRows);
if (newProps !== null) {
newEditingState[id] = newProps;
} else {
delete newEditingState[id];
}
return _extends({}, state, {
editRows: newEditingState
});
});
apiRef.current.forceUpdate();
}, [apiRef]);
var updateOrDeleteFieldState = React.useCallback(function (id, field, newProps) {
apiRef.current.setState(function (state) {
var newEditingState = _extends({}, state.editRows);
if (newProps !== null) {
newEditingState[id] = _extends({}, newEditingState[id], _defineProperty({}, field, _extends({}, newProps)));
} else {
delete newEditingState[id][field];
if (Object.keys(newEditingState[id]).length === 0) {
delete newEditingState[id];
}
}
return _extends({}, state, {
editRows: newEditingState
});
});
apiRef.current.forceUpdate();
}, [apiRef]);
var startRowEditMode = React.useCallback(function (params) {
var id = params.id,
other = _objectWithoutProperties(params, _excluded);
throwIfNotInMode(id, GridRowModes.View);
updateRowInRowModesModel(id, _extends({
mode: GridRowModes.Edit
}, other));
}, [throwIfNotInMode, updateRowInRowModesModel]);
var updateStateToStartRowEditMode = useEventCallback(function (params) {
var id = params.id,
fieldToFocus = params.fieldToFocus,
deleteValue = params.deleteValue,
initialValue = params.initialValue;
var columnFields = gridColumnFieldsSelector(apiRef);
var newProps = columnFields.reduce(function (acc, field) {
var cellParams = apiRef.current.getCellParams(id, field);
if (!cellParams.isEditable) {
return acc;
}
var newValue = apiRef.current.getCellValue(id, field);
if (fieldToFocus === field && (deleteValue || initialValue)) {
newValue = deleteValue ? '' : initialValue;
}
acc[field] = {
value: newValue,
error: false,
isProcessingProps: false
};
return acc;
}, {});
updateOrDeleteRowState(id, newProps);
if (fieldToFocus) {
apiRef.current.setCellFocus(id, fieldToFocus);
}
});
var stopRowEditMode = React.useCallback(function (params) {
var id = params.id,
other = _objectWithoutProperties(params, _excluded2);
throwIfNotInMode(id, GridRowModes.Edit);
updateRowInRowModesModel(id, _extends({
mode: GridRowModes.View
}, other));
}, [throwIfNotInMode, updateRowInRowModesModel]);
var updateStateToStopRowEditMode = useEventCallback(function (params) {
var id = params.id,
ignoreModifications = params.ignoreModifications,
focusedField = params.field,
_params$cellToFocusAf = params.cellToFocusAfter,
cellToFocusAfter = _params$cellToFocusAf === void 0 ? 'none' : _params$cellToFocusAf;
apiRef.current.runPendingEditCellValueMutation(id);
var finishRowEditMode = function finishRowEditMode() {
if (cellToFocusAfter !== 'none' && focusedField) {
apiRef.current.moveFocusToRelativeCell(id, focusedField, cellToFocusAfter);
}
updateOrDeleteRowState(id, null);
updateRowInRowModesModel(id, null);
};
if (ignoreModifications) {
finishRowEditMode();
return;
}
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var row = apiRef.current.getRow(id);
var isSomeFieldProcessingProps = Object.values(editingState[id]).some(function (fieldProps) {
return fieldProps.isProcessingProps;
});
if (isSomeFieldProcessingProps) {
prevRowModesModel.current[id].mode = GridRowModes.Edit;
return;
}
var hasSomeFieldWithError = Object.values(editingState[id]).some(function (fieldProps) {
return fieldProps.error;
});
if (hasSomeFieldWithError) {
prevRowModesModel.current[id].mode = GridRowModes.Edit;
// Revert the mode in the rowModesModel prop back to "edit"
updateRowInRowModesModel(id, {
mode: GridRowModes.Edit
});
return;
}
var rowUpdate = apiRef.current.getRowWithUpdatedValuesFromRowEditing(id);
if (processRowUpdate) {
var handleError = function handleError(errorThrown) {
prevRowModesModel.current[id].mode = GridRowModes.Edit;
// Revert the mode in the rowModesModel prop back to "edit"
updateRowInRowModesModel(id, {
mode: GridRowModes.Edit
});
if (onProcessRowUpdateError) {
onProcessRowUpdateError(errorThrown);
} else {
missingOnProcessRowUpdateErrorWarning();
}
};
try {
Promise.resolve(processRowUpdate(rowUpdate, row)).then(function (finalRowUpdate) {
apiRef.current.updateRows([finalRowUpdate]);
finishRowEditMode();
}).catch(handleError);
} catch (errorThrown) {
handleError(errorThrown);
}
} else {
apiRef.current.updateRows([rowUpdate]);
finishRowEditMode();
}
});
var setRowEditingEditCellValue = React.useCallback(function (params) {
var id = params.id,
field = params.field,
value = params.value,
debounceMs = params.debounceMs,
skipValueParser = params.unstable_skipValueParser;
throwIfNotEditable(id, field);
var column = apiRef.current.getColumn(field);
var row = apiRef.current.getRow(id);
var parsedValue = value;
if (column.valueParser && !skipValueParser) {
parsedValue = column.valueParser(value, apiRef.current.getCellParams(id, field));
}
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var newProps = _extends({}, editingState[id][field], {
value: parsedValue,
changeReason: debounceMs ? 'debouncedSetEditCellValue' : 'setEditCellValue'
});
if (!column.preProcessEditCellProps) {
updateOrDeleteFieldState(id, field, newProps);
}
return new Promise(function (resolve) {
var promises = [];
if (column.preProcessEditCellProps) {
var hasChanged = newProps.value !== editingState[id][field].value;
newProps = _extends({}, newProps, {
isProcessingProps: true
});
updateOrDeleteFieldState(id, field, newProps);
var _editingState$id = editingState[id],
ignoredField = _editingState$id[field],
otherFieldsProps = _objectWithoutProperties(_editingState$id, [field].map(_toPropertyKey));
var promise = Promise.resolve(column.preProcessEditCellProps({
id: id,
row: row,
props: newProps,
hasChanged: hasChanged,
otherFieldsProps: otherFieldsProps
})).then(function (processedProps) {
// Check again if the row is in edit mode because the user may have
// discarded the changes while the props were being processed.
if (apiRef.current.getRowMode(id) === GridRowModes.View) {
resolve(false);
return;
}
editingState = gridEditRowsStateSelector(apiRef.current.state);
processedProps = _extends({}, processedProps, {
isProcessingProps: false
});
// We don't reuse the value from the props pre-processing because when the
// promise resolves it may be already outdated. The only exception to this rule
// is when there's no pre-processing.
processedProps.value = column.preProcessEditCellProps ? editingState[id][field].value : parsedValue;
updateOrDeleteFieldState(id, field, processedProps);
});
promises.push(promise);
}
Object.entries(editingState[id]).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
thisField = _ref2[0],
fieldProps = _ref2[1];
if (thisField === field) {
return;
}
var fieldColumn = apiRef.current.getColumn(thisField);
if (!fieldColumn.preProcessEditCellProps) {
return;
}
fieldProps = _extends({}, fieldProps, {
isProcessingProps: true
});
updateOrDeleteFieldState(id, thisField, fieldProps);
editingState = gridEditRowsStateSelector(apiRef.current.state);
var _editingState$id2 = editingState[id],
ignoredField = _editingState$id2[thisField],
otherFieldsProps = _objectWithoutProperties(_editingState$id2, [thisField].map(_toPropertyKey));
var promise = Promise.resolve(fieldColumn.preProcessEditCellProps({
id: id,
row: row,
props: fieldProps,
hasChanged: false,
otherFieldsProps: otherFieldsProps
})).then(function (processedProps) {
// Check again if the row is in edit mode because the user may have
// discarded the changes while the props were being processed.
if (apiRef.current.getRowMode(id) === GridRowModes.View) {
resolve(false);
return;
}
processedProps = _extends({}, processedProps, {
isProcessingProps: false
});
updateOrDeleteFieldState(id, thisField, processedProps);
});
promises.push(promise);
});
Promise.all(promises).then(function () {
if (apiRef.current.getRowMode(id) === GridRowModes.Edit) {
editingState = gridEditRowsStateSelector(apiRef.current.state);
resolve(!editingState[id][field].error);
} else {
resolve(false);
}
});
});
}, [apiRef, throwIfNotEditable, updateOrDeleteFieldState]);
var getRowWithUpdatedValuesFromRowEditing = React.useCallback(function (id) {
var editingState = gridEditRowsStateSelector(apiRef.current.state);
var row = apiRef.current.getRow(id);
if (!editingState[id]) {
return apiRef.current.getRow(id);
}
var rowUpdate = _extends({}, row);
Object.entries(editingState[id]).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
field = _ref4[0],
fieldProps = _ref4[1];
var column = apiRef.current.getColumn(field);
if (column.valueSetter) {
rowUpdate = column.valueSetter({
value: fieldProps.value,
row: rowUpdate
});
} else {
rowUpdate[field] = fieldProps.value;
}
});
return rowUpdate;
}, [apiRef]);
var editingApi = {
getRowMode: getRowMode,
startRowEditMode: startRowEditMode,
stopRowEditMode: stopRowEditMode
};
var editingPrivateApi = {
setRowEditingEditCellValue: setRowEditingEditCellValue,
getRowWithUpdatedValuesFromRowEditing: getRowWithUpdatedValuesFromRowEditing
};
useGridApiMethod(apiRef, editingApi, 'public');
useGridApiMethod(apiRef, editingPrivateApi, 'private');
React.useEffect(function () {
if (rowModesModelProp) {
updateRowModesModel(rowModesModelProp);
}
}, [rowModesModelProp, updateRowModesModel]);
// Run this effect synchronously so that the keyboard event can impact the yet-to-be-rendered input.
useEnhancedEffect(function () {
var idToIdLookup = gridRowsDataRowIdToIdLookupSelector(apiRef);
// Update the ref here because updateStateToStopRowEditMode may change it later
var copyOfPrevRowModesModel = prevRowModesModel.current;
prevRowModesModel.current = deepClone(rowModesModel); // Do a deep-clone because the attributes might be changed later
Object.entries(rowModesModel).forEach(function (_ref5) {
var _copyOfPrevRowModesMo, _idToIdLookup$id;
var _ref6 = _slicedToArray(_ref5, 2),
id = _ref6[0],
params = _ref6[1];
var prevMode = ((_copyOfPrevRowModesMo = copyOfPrevRowModesModel[id]) == null ? void 0 : _copyOfPrevRowModesMo.mode) || GridRowModes.View;
var originalId = (_idToIdLookup$id = idToIdLookup[id]) != null ? _idToIdLookup$id : id;
if (params.mode === GridRowModes.Edit && prevMode === GridRowModes.View) {
updateStateToStartRowEditMode(_extends({
id: originalId
}, params));
} else if (params.mode === GridRowModes.View && prevMode === GridRowModes.Edit) {
updateStateToStopRowEditMode(_extends({
id: originalId
}, params));
}
});
}, [apiRef, rowModesModel, updateStateToStartRowEditMode, updateStateToStopRowEditMode]);
};

View File

@@ -0,0 +1,23 @@
import { useGridApiOptionHandler } from '../../utils/useGridApiEventHandler';
/**
* @requires useGridFocus (event) - can be after, async only
* @requires useGridColumns (event) - can be after, async only
*/
export function useGridEvents(apiRef, props) {
useGridApiOptionHandler(apiRef, 'columnHeaderClick', props.onColumnHeaderClick);
useGridApiOptionHandler(apiRef, 'columnHeaderDoubleClick', props.onColumnHeaderDoubleClick);
useGridApiOptionHandler(apiRef, 'columnHeaderOver', props.onColumnHeaderOver);
useGridApiOptionHandler(apiRef, 'columnHeaderOut', props.onColumnHeaderOut);
useGridApiOptionHandler(apiRef, 'columnHeaderEnter', props.onColumnHeaderEnter);
useGridApiOptionHandler(apiRef, 'columnHeaderLeave', props.onColumnHeaderLeave);
useGridApiOptionHandler(apiRef, 'cellClick', props.onCellClick);
useGridApiOptionHandler(apiRef, 'cellDoubleClick', props.onCellDoubleClick);
useGridApiOptionHandler(apiRef, 'cellKeyDown', props.onCellKeyDown);
useGridApiOptionHandler(apiRef, 'preferencePanelClose', props.onPreferencePanelClose);
useGridApiOptionHandler(apiRef, 'preferencePanelOpen', props.onPreferencePanelOpen);
useGridApiOptionHandler(apiRef, 'menuOpen', props.onMenuOpen);
useGridApiOptionHandler(apiRef, 'menuClose', props.onMenuClose);
useGridApiOptionHandler(apiRef, 'rowDoubleClick', props.onRowDoubleClick);
useGridApiOptionHandler(apiRef, 'rowClick', props.onRowClick);
useGridApiOptionHandler(apiRef, 'stateChange', props.onStateChange);
}

View File

@@ -0,0 +1,163 @@
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../../colDef';
import { buildWarning } from '../../../../utils/warning';
function sanitizeCellValue(value, csvOptions) {
if (typeof value === 'string') {
if (csvOptions.shouldAppendQuotes || csvOptions.escapeFormulas) {
var escapedValue = value.replace(/"/g, '""');
// Make sure value containing delimiter or line break won't be split into multiple cells
if ([csvOptions.delimiter, '\n', '\r', '"'].some(function (delimiter) {
return value.includes(delimiter);
})) {
return "\"".concat(escapedValue, "\"");
}
if (csvOptions.escapeFormulas) {
// See https://owasp.org/www-community/attacks/CSV_Injection
if (['=', '+', '-', '@', '\t', '\r'].includes(escapedValue[0])) {
return "'".concat(escapedValue);
}
}
return escapedValue;
}
return value;
}
return value;
}
export var serializeCellValue = function serializeCellValue(cellParams, options) {
var csvOptions = options.csvOptions,
ignoreValueFormatter = options.ignoreValueFormatter;
var value;
if (ignoreValueFormatter) {
var _cellParams$value2;
var columnType = cellParams.colDef.type;
if (columnType === 'number') {
value = String(cellParams.value);
} else if (columnType === 'date' || columnType === 'dateTime') {
var _cellParams$value;
value = (_cellParams$value = cellParams.value) == null ? void 0 : _cellParams$value.toISOString();
} else if (typeof ((_cellParams$value2 = cellParams.value) == null ? void 0 : _cellParams$value2.toString) === 'function') {
value = cellParams.value.toString();
} else {
value = cellParams.value;
}
} else {
value = cellParams.formattedValue;
}
return sanitizeCellValue(value, csvOptions);
};
var objectFormattedValueWarning = buildWarning(['MUI: When the value of a field is an object or a `renderCell` is provided, the CSV export might not display the value correctly.', 'You can provide a `valueFormatter` with a string representation to be used.']);
var CSVRow = /*#__PURE__*/function () {
function CSVRow(options) {
_classCallCheck(this, CSVRow);
this.options = void 0;
this.rowString = '';
this.isEmpty = true;
this.options = options;
}
_createClass(CSVRow, [{
key: "addValue",
value: function addValue(value) {
if (!this.isEmpty) {
this.rowString += this.options.csvOptions.delimiter;
}
if (value === null || value === undefined) {
this.rowString += '';
} else if (typeof this.options.sanitizeCellValue === 'function') {
this.rowString += this.options.sanitizeCellValue(value, this.options.csvOptions);
} else {
this.rowString += value;
}
this.isEmpty = false;
}
}, {
key: "getRowString",
value: function getRowString() {
return this.rowString;
}
}]);
return CSVRow;
}();
var serializeRow = function serializeRow(_ref) {
var id = _ref.id,
columns = _ref.columns,
getCellParams = _ref.getCellParams,
csvOptions = _ref.csvOptions,
ignoreValueFormatter = _ref.ignoreValueFormatter;
var row = new CSVRow({
csvOptions: csvOptions
});
columns.forEach(function (column) {
var cellParams = getCellParams(id, column.field);
if (process.env.NODE_ENV !== 'production') {
if (String(cellParams.formattedValue) === '[object Object]') {
objectFormattedValueWarning();
}
}
row.addValue(serializeCellValue(cellParams, {
ignoreValueFormatter: ignoreValueFormatter,
csvOptions: csvOptions
}));
});
return row.getRowString();
};
export function buildCSV(options) {
var columns = options.columns,
rowIds = options.rowIds,
csvOptions = options.csvOptions,
ignoreValueFormatter = options.ignoreValueFormatter,
apiRef = options.apiRef;
var CSVBody = rowIds.reduce(function (acc, id) {
return "".concat(acc).concat(serializeRow({
id: id,
columns: columns,
getCellParams: apiRef.current.getCellParams,
ignoreValueFormatter: ignoreValueFormatter,
csvOptions: csvOptions
}), "\r\n");
}, '').trim();
if (!csvOptions.includeHeaders) {
return CSVBody;
}
var filteredColumns = columns.filter(function (column) {
return column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field;
});
var headerRows = [];
if (csvOptions.includeColumnGroupsHeaders) {
var columnGroupLookup = apiRef.current.unstable_getAllGroupDetails();
var maxColumnGroupsDepth = 0;
var columnGroupPathsLookup = filteredColumns.reduce(function (acc, column) {
var columnGroupPath = apiRef.current.unstable_getColumnGroupPath(column.field);
acc[column.field] = columnGroupPath;
maxColumnGroupsDepth = Math.max(maxColumnGroupsDepth, columnGroupPath.length);
return acc;
}, {});
var _loop = function _loop(i) {
var headerGroupRow = new CSVRow({
csvOptions: csvOptions,
sanitizeCellValue: sanitizeCellValue
});
headerRows.push(headerGroupRow);
filteredColumns.forEach(function (column) {
var columnGroupId = (columnGroupPathsLookup[column.field] || [])[i];
var columnGroup = columnGroupLookup[columnGroupId];
headerGroupRow.addValue(columnGroup ? columnGroup.headerName || columnGroup.groupId : '');
});
};
for (var i = 0; i < maxColumnGroupsDepth; i += 1) {
_loop(i);
}
}
var mainHeaderRow = new CSVRow({
csvOptions: csvOptions,
sanitizeCellValue: sanitizeCellValue
});
filteredColumns.forEach(function (column) {
mainHeaderRow.addValue(column.headerName || column.field);
});
headerRows.push(mainHeaderRow);
var CSVHead = "".concat(headerRows.map(function (row) {
return row.getRowString();
}).join('\r\n'), "\r\n");
return "".concat(CSVHead).concat(CSVBody).trim();
}

View File

@@ -0,0 +1,79 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _typeof from "@babel/runtime/helpers/esm/typeof";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { exportAs } from '../../../utils/exportAs';
import { buildCSV } from './serializers/csvSerializer';
import { getColumnsToExport, defaultGetRowsToExport } from './utils';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { GridCsvExportMenuItem } from '../../../components/toolbar/GridToolbarExport';
import { jsx as _jsx } from "react/jsx-runtime";
/**
* @requires useGridColumns (state)
* @requires useGridFilter (state)
* @requires useGridSorting (state)
* @requires useGridSelection (state)
* @requires useGridParamsApi (method)
*/
export var useGridCsvExport = function useGridCsvExport(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridCsvExport');
var ignoreValueFormatterProp = props.unstable_ignoreValueFormatterDuringExport;
var ignoreValueFormatter = (_typeof(ignoreValueFormatterProp) === 'object' ? ignoreValueFormatterProp == null ? void 0 : ignoreValueFormatterProp.csvExport : ignoreValueFormatterProp) || false;
var getDataAsCsv = React.useCallback(function () {
var _options$getRowsToExp, _options$shouldAppend, _options$includeHeade, _options$includeColum, _options$escapeFormul;
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
logger.debug("Get data as CSV");
var exportedColumns = getColumnsToExport({
apiRef: apiRef,
options: options
});
var getRowsToExport = (_options$getRowsToExp = options.getRowsToExport) != null ? _options$getRowsToExp : defaultGetRowsToExport;
var exportedRowIds = getRowsToExport({
apiRef: apiRef
});
return buildCSV({
columns: exportedColumns,
rowIds: exportedRowIds,
csvOptions: {
delimiter: options.delimiter || ',',
shouldAppendQuotes: (_options$shouldAppend = options.shouldAppendQuotes) != null ? _options$shouldAppend : true,
includeHeaders: (_options$includeHeade = options.includeHeaders) != null ? _options$includeHeade : true,
includeColumnGroupsHeaders: (_options$includeColum = options.includeColumnGroupsHeaders) != null ? _options$includeColum : true,
escapeFormulas: (_options$escapeFormul = options.escapeFormulas) != null ? _options$escapeFormul : true
},
ignoreValueFormatter: ignoreValueFormatter,
apiRef: apiRef
});
}, [logger, apiRef, ignoreValueFormatter]);
var exportDataAsCsv = React.useCallback(function (options) {
logger.debug("Export data as CSV");
var csv = getDataAsCsv(options);
var blob = new Blob([options != null && options.utf8WithBom ? new Uint8Array([0xef, 0xbb, 0xbf]) : '', csv], {
type: 'text/csv'
});
exportAs(blob, 'csv', options == null ? void 0 : options.fileName);
}, [logger, getDataAsCsv]);
var csvExportApi = {
getDataAsCsv: getDataAsCsv,
exportDataAsCsv: exportDataAsCsv
};
useGridApiMethod(apiRef, csvExportApi, 'public');
/**
* PRE-PROCESSING
*/
var addExportMenuButtons = React.useCallback(function (initialValue, options) {
var _options$csvOptions;
if ((_options$csvOptions = options.csvOptions) != null && _options$csvOptions.disableToolbarButton) {
return initialValue;
}
return [].concat(_toConsumableArray(initialValue), [{
component: /*#__PURE__*/_jsx(GridCsvExportMenuItem, {
options: options.csvOptions
}),
componentName: 'csvExport'
}]);
}, []);
useGridRegisterPipeProcessor(apiRef, 'exportMenu', addExportMenuButtons);
};

View File

@@ -0,0 +1,326 @@
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import * as React from 'react';
import { unstable_ownerDocument as ownerDocument } from '@mui/utils';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridExpandedRowCountSelector } from '../filter/gridFilterSelector';
import { gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector } from '../columns/gridColumnsSelector';
import { gridClasses } from '../../../constants/gridClasses';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector';
import { getColumnsToExport } from './utils';
import { getDerivedPaginationModel } from '../pagination/useGridPaginationModel';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { GridPrintExportMenuItem } from '../../../components/toolbar/GridToolbarExport';
import { getTotalHeaderHeight } from '../columns/gridColumnsUtils';
import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../colDef/gridCheckboxSelectionColDef';
import { gridDataRowIdsSelector, gridRowsLookupSelector } from '../rows/gridRowsSelector';
import { jsx as _jsx } from "react/jsx-runtime";
function raf() {
return new Promise(function (resolve) {
requestAnimationFrame(function () {
resolve();
});
});
}
function buildPrintWindow(title) {
var iframeEl = document.createElement('iframe');
iframeEl.style.position = 'absolute';
iframeEl.style.width = '0px';
iframeEl.style.height = '0px';
iframeEl.title = title || document.title;
return iframeEl;
}
/**
* @requires useGridColumns (state)
* @requires useGridFilter (state)
* @requires useGridSorting (state)
* @requires useGridParamsApi (method)
*/
export var useGridPrintExport = function useGridPrintExport(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridPrintExport');
var doc = React.useRef(null);
var previousGridState = React.useRef(null);
var previousColumnVisibility = React.useRef({});
var previousRows = React.useRef([]);
React.useEffect(function () {
doc.current = ownerDocument(apiRef.current.rootElementRef.current);
}, [apiRef]);
// Returns a promise because updateColumns triggers state update and
// the new state needs to be in place before the grid can be sized correctly
var updateGridColumnsForPrint = React.useCallback(function (fields, allColumns, includeCheckboxes) {
return new Promise(function (resolve) {
var exportedColumnFields = getColumnsToExport({
apiRef: apiRef,
options: {
fields: fields,
allColumns: allColumns
}
}).map(function (column) {
return column.field;
});
var columns = gridColumnDefinitionsSelector(apiRef);
var newColumnVisibilityModel = {};
columns.forEach(function (column) {
newColumnVisibilityModel[column.field] = exportedColumnFields.includes(column.field);
});
if (includeCheckboxes) {
newColumnVisibilityModel[GRID_CHECKBOX_SELECTION_COL_DEF.field] = true;
}
apiRef.current.setColumnVisibilityModel(newColumnVisibilityModel);
resolve();
});
}, [apiRef]);
var updateGridRowsForPrint = React.useCallback(function (getRowsToExport) {
var rowsToExportIds = getRowsToExport({
apiRef: apiRef
});
var newRows = rowsToExportIds.map(function (id) {
return apiRef.current.getRow(id);
});
apiRef.current.setRows(newRows);
}, [apiRef]);
var handlePrintWindowLoad = React.useCallback(function (printWindow, options) {
var _querySelector, _querySelector2;
var normalizeOptions = _extends({
copyStyles: true,
hideToolbar: false,
hideFooter: false,
includeCheckboxes: false
}, options);
var printDoc = printWindow.contentDocument;
if (!printDoc) {
return;
}
var rowsMeta = gridRowsMetaSelector(apiRef.current.state);
var gridRootElement = apiRef.current.rootElementRef.current;
var gridClone = gridRootElement.cloneNode(true);
// Allow to overflow to not hide the border of the last row
var gridMain = gridClone.querySelector(".".concat(gridClasses.main));
gridMain.style.overflow = 'visible';
// See https://support.google.com/chrome/thread/191619088?hl=en&msgid=193009642
gridClone.style.contain = 'size';
var columnHeaders = gridClone.querySelector(".".concat(gridClasses.columnHeaders));
var columnHeadersInner = columnHeaders.querySelector(".".concat(gridClasses.columnHeadersInner));
columnHeadersInner.style.width = '100%';
var gridToolbarElementHeight = ((_querySelector = gridRootElement.querySelector(".".concat(gridClasses.toolbarContainer))) == null ? void 0 : _querySelector.offsetHeight) || 0;
var gridFooterElementHeight = ((_querySelector2 = gridRootElement.querySelector(".".concat(gridClasses.footerContainer))) == null ? void 0 : _querySelector2.offsetHeight) || 0;
if (normalizeOptions.hideToolbar) {
var _gridClone$querySelec;
(_gridClone$querySelec = gridClone.querySelector(".".concat(gridClasses.toolbarContainer))) == null || _gridClone$querySelec.remove();
gridToolbarElementHeight = 0;
}
if (normalizeOptions.hideFooter) {
var _gridClone$querySelec2;
(_gridClone$querySelec2 = gridClone.querySelector(".".concat(gridClasses.footerContainer))) == null || _gridClone$querySelec2.remove();
gridFooterElementHeight = 0;
}
// Expand container height to accommodate all rows
var computedTotalHeight = rowsMeta.currentPageTotalHeight + getTotalHeaderHeight(apiRef, props.columnHeaderHeight) + gridToolbarElementHeight + gridFooterElementHeight;
gridClone.style.height = "".concat(computedTotalHeight, "px");
// The height above does not include grid border width, so we need to exclude it
gridClone.style.boxSizing = 'content-box';
// the footer is always being placed at the bottom of the page as if all rows are exported
// so if getRowsToExport is being used to only export a subset of rows then we need to
// adjust the footer position to be correctly placed at the bottom of the grid
if (options != null && options.getRowsToExport) {
var gridFooterElement = gridClone.querySelector(".".concat(gridClasses.footerContainer));
gridFooterElement.style.position = 'absolute';
gridFooterElement.style.width = '100%';
gridFooterElement.style.top = "".concat(computedTotalHeight - gridFooterElementHeight, "px");
}
// printDoc.body.appendChild(gridClone); should be enough but a clone isolation bug in Safari
// prevents us to do it
var container = document.createElement('div');
container.appendChild(gridClone);
printDoc.body.innerHTML = container.innerHTML;
var defaultPageStyle = typeof normalizeOptions.pageStyle === 'function' ? normalizeOptions.pageStyle() : normalizeOptions.pageStyle;
if (typeof defaultPageStyle === 'string') {
// TODO custom styles should always win
var styleElement = printDoc.createElement('style');
styleElement.appendChild(printDoc.createTextNode(defaultPageStyle));
printDoc.head.appendChild(styleElement);
}
if (normalizeOptions.bodyClassName) {
var _printDoc$body$classL;
(_printDoc$body$classL = printDoc.body.classList).add.apply(_printDoc$body$classL, _toConsumableArray(normalizeOptions.bodyClassName.split(' ')));
}
var stylesheetLoadPromises = [];
if (normalizeOptions.copyStyles) {
var rootCandidate = gridRootElement.getRootNode();
var root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc.current;
var headStyleElements = root.querySelectorAll("style, link[rel='stylesheet']");
var _loop = function _loop() {
var node = headStyleElements[i];
if (node.tagName === 'STYLE') {
var newHeadStyleElements = printDoc.createElement(node.tagName);
var sheet = node.sheet;
if (sheet) {
var styleCSS = '';
// NOTE: for-of is not supported by IE
for (var j = 0; j < sheet.cssRules.length; j += 1) {
if (typeof sheet.cssRules[j].cssText === 'string') {
styleCSS += "".concat(sheet.cssRules[j].cssText, "\r\n");
}
}
newHeadStyleElements.appendChild(printDoc.createTextNode(styleCSS));
printDoc.head.appendChild(newHeadStyleElements);
}
} else if (node.getAttribute('href')) {
// If `href` tag is empty, avoid loading these links
var _newHeadStyleElements = printDoc.createElement(node.tagName);
for (var _j = 0; _j < node.attributes.length; _j += 1) {
var attr = node.attributes[_j];
if (attr) {
_newHeadStyleElements.setAttribute(attr.nodeName, attr.nodeValue || '');
}
}
stylesheetLoadPromises.push(new Promise(function (resolve) {
_newHeadStyleElements.addEventListener('load', function () {
return resolve();
});
}));
printDoc.head.appendChild(_newHeadStyleElements);
}
};
for (var i = 0; i < headStyleElements.length; i += 1) {
_loop();
}
}
// Trigger print
if (process.env.NODE_ENV !== 'test') {
// wait for remote stylesheets to load
Promise.all(stylesheetLoadPromises).then(function () {
printWindow.contentWindow.print();
});
}
}, [apiRef, doc, props.columnHeaderHeight]);
var handlePrintWindowAfterPrint = React.useCallback(function (printWindow) {
var _previousGridState$cu;
// Remove the print iframe
doc.current.body.removeChild(printWindow);
// Revert grid to previous state
apiRef.current.restoreState(previousGridState.current || {});
if (!((_previousGridState$cu = previousGridState.current) != null && (_previousGridState$cu = _previousGridState$cu.columns) != null && _previousGridState$cu.columnVisibilityModel)) {
// if the apiRef.current.exportState(); did not exported the column visibility, we update it
apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current);
}
apiRef.current.unstable_setVirtualization(true);
apiRef.current.setRows(previousRows.current);
// Clear local state
previousGridState.current = null;
previousColumnVisibility.current = {};
previousRows.current = [];
}, [apiRef]);
var exportDataAsPrint = React.useCallback( /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(options) {
var gridRowsLookup, visibleRowCount, paginationModel, printWindow;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
logger.debug("Export data as Print");
if (apiRef.current.rootElementRef.current) {
_context.next = 3;
break;
}
throw new Error('MUI: No grid root element available.');
case 3:
previousGridState.current = apiRef.current.exportState();
// It appends that the visibility model is not exported, especially if columnVisibility is not controlled
previousColumnVisibility.current = gridColumnVisibilityModelSelector(apiRef);
gridRowsLookup = gridRowsLookupSelector(apiRef);
previousRows.current = gridDataRowIdsSelector(apiRef).map(function (rowId) {
return gridRowsLookup[rowId];
});
if (props.pagination) {
visibleRowCount = gridExpandedRowCountSelector(apiRef);
paginationModel = {
page: 0,
pageSize: visibleRowCount
};
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
paginationModel: getDerivedPaginationModel(state.pagination,
// Using signature `DataGridPro` to allow more than 100 rows in the print export
'DataGridPro', paginationModel)
})
});
});
apiRef.current.forceUpdate();
}
_context.next = 10;
return updateGridColumnsForPrint(options == null ? void 0 : options.fields, options == null ? void 0 : options.allColumns, options == null ? void 0 : options.includeCheckboxes);
case 10:
if (options != null && options.getRowsToExport) {
updateGridRowsForPrint(options.getRowsToExport);
}
apiRef.current.unstable_setVirtualization(false);
_context.next = 14;
return raf();
case 14:
// wait for the state changes to take action
printWindow = buildPrintWindow(options == null ? void 0 : options.fileName);
if (process.env.NODE_ENV === 'test') {
doc.current.body.appendChild(printWindow);
// In test env, run the all pipeline without waiting for loading
handlePrintWindowLoad(printWindow, options);
handlePrintWindowAfterPrint(printWindow);
} else {
printWindow.onload = function () {
handlePrintWindowLoad(printWindow, options);
var mediaQueryList = printWindow.contentWindow.matchMedia('print');
mediaQueryList.addEventListener('change', function (mql) {
var isAfterPrint = mql.matches === false;
if (isAfterPrint) {
handlePrintWindowAfterPrint(printWindow);
}
});
};
doc.current.body.appendChild(printWindow);
}
case 16:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}(), [props, logger, apiRef, handlePrintWindowLoad, handlePrintWindowAfterPrint, updateGridColumnsForPrint, updateGridRowsForPrint]);
var printExportApi = {
exportDataAsPrint: exportDataAsPrint
};
useGridApiMethod(apiRef, printExportApi, 'public');
/**
* PRE-PROCESSING
*/
var addExportMenuButtons = React.useCallback(function (initialValue, options) {
var _options$printOptions;
if ((_options$printOptions = options.printOptions) != null && _options$printOptions.disableToolbarButton) {
return initialValue;
}
return [].concat(_toConsumableArray(initialValue), [{
component: /*#__PURE__*/_jsx(GridPrintExportMenuItem, {
options: options.printOptions
}),
componentName: 'printExport'
}]);
}, []);
useGridRegisterPipeProcessor(apiRef, 'exportMenu', addExportMenuButtons);
};

View File

@@ -0,0 +1,49 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import { gridColumnDefinitionsSelector, gridVisibleColumnDefinitionsSelector } from '../columns';
import { gridFilteredSortedRowIdsSelector } from '../filter';
import { gridPinnedRowsSelector, gridRowTreeSelector } from '../rows/gridRowsSelector';
export var getColumnsToExport = function getColumnsToExport(_ref) {
var apiRef = _ref.apiRef,
options = _ref.options;
var columns = gridColumnDefinitionsSelector(apiRef);
if (options.fields) {
return options.fields.reduce(function (currentColumns, field) {
var column = columns.find(function (col) {
return col.field === field;
});
if (column) {
currentColumns.push(column);
}
return currentColumns;
}, []);
}
var validColumns = options.allColumns ? columns : gridVisibleColumnDefinitionsSelector(apiRef);
return validColumns.filter(function (column) {
return !column.disableExport;
});
};
export var defaultGetRowsToExport = function defaultGetRowsToExport(_ref2) {
var _pinnedRows$top, _pinnedRows$bottom;
var apiRef = _ref2.apiRef;
var filteredSortedRowIds = gridFilteredSortedRowIdsSelector(apiRef);
var rowTree = gridRowTreeSelector(apiRef);
var selectedRows = apiRef.current.getSelectedRows();
var bodyRows = filteredSortedRowIds.filter(function (id) {
return rowTree[id].type !== 'footer';
});
var pinnedRows = gridPinnedRowsSelector(apiRef);
var topPinnedRowsIds = (pinnedRows == null || (_pinnedRows$top = pinnedRows.top) == null ? void 0 : _pinnedRows$top.map(function (row) {
return row.id;
})) || [];
var bottomPinnedRowsIds = (pinnedRows == null || (_pinnedRows$bottom = pinnedRows.bottom) == null ? void 0 : _pinnedRows$bottom.map(function (row) {
return row.id;
})) || [];
bodyRows.unshift.apply(bodyRows, _toConsumableArray(topPinnedRowsIds));
bodyRows.push.apply(bodyRows, _toConsumableArray(bottomPinnedRowsIds));
if (selectedRows.size > 0) {
return bodyRows.filter(function (id) {
return selectedRows.has(id);
});
}
return bodyRows;
};

View File

@@ -0,0 +1,165 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
import { gridSortedRowEntriesSelector } from '../sorting/gridSortingSelector';
import { gridColumnLookupSelector } from '../columns/gridColumnsSelector';
import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from '../rows/gridRowsSelector';
/**
* @category Filtering
*/
var gridFilterStateSelector = function gridFilterStateSelector(state) {
return state.filter;
};
/**
* Get the current filter model.
* @category Filtering
*/
export var gridFilterModelSelector = createSelector(gridFilterStateSelector, function (filterState) {
return filterState.filterModel;
});
/**
* Get the current quick filter values.
* @category Filtering
*/
export var gridQuickFilterValuesSelector = createSelector(gridFilterModelSelector, function (filterModel) {
return filterModel.quickFilterValues;
});
/**
* @category Visible rows
* @ignore - do not document.
*/
export var gridVisibleRowsLookupSelector = function gridVisibleRowsLookupSelector(state) {
return state.visibleRowsLookup;
};
/**
* @category Filtering
* @ignore - do not document.
*/
export var gridFilteredRowsLookupSelector = createSelector(gridFilterStateSelector, function (filterState) {
return filterState.filteredRowsLookup;
});
/**
* @category Filtering
* @ignore - do not document.
*/
export var gridFilteredDescendantCountLookupSelector = createSelector(gridFilterStateSelector, function (filterState) {
return filterState.filteredDescendantCountLookup;
});
/**
* Get the id and the model of the rows accessible after the filtering process.
* Does not contain the collapsed children.
* @category Filtering
*/
export var gridExpandedSortedRowEntriesSelector = createSelectorMemoized(gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, function (visibleRowsLookup, sortedRows) {
return sortedRows.filter(function (row) {
return visibleRowsLookup[row.id] !== false;
});
});
/**
* Get the id of the rows accessible after the filtering process.
* Does not contain the collapsed children.
* @category Filtering
*/
export var gridExpandedSortedRowIdsSelector = createSelectorMemoized(gridExpandedSortedRowEntriesSelector, function (visibleSortedRowEntries) {
return visibleSortedRowEntries.map(function (row) {
return row.id;
});
});
/**
* Get the id and the model of the rows accessible after the filtering process.
* Contains the collapsed children.
* @category Filtering
*/
export var gridFilteredSortedRowEntriesSelector = createSelectorMemoized(gridFilteredRowsLookupSelector, gridSortedRowEntriesSelector, function (filteredRowsLookup, sortedRows) {
return sortedRows.filter(function (row) {
return filteredRowsLookup[row.id] !== false;
});
});
/**
* Get the id of the rows accessible after the filtering process.
* Contains the collapsed children.
* @category Filtering
*/
export var gridFilteredSortedRowIdsSelector = createSelectorMemoized(gridFilteredSortedRowEntriesSelector, function (filteredSortedRowEntries) {
return filteredSortedRowEntries.map(function (row) {
return row.id;
});
});
/**
* Get the id and the model of the top level rows accessible after the filtering process.
* @category Filtering
*/
export var gridFilteredSortedTopLevelRowEntriesSelector = createSelectorMemoized(gridExpandedSortedRowEntriesSelector, gridRowTreeSelector, gridRowMaximumTreeDepthSelector, function (visibleSortedRows, rowTree, rowTreeDepth) {
if (rowTreeDepth < 2) {
return visibleSortedRows;
}
return visibleSortedRows.filter(function (row) {
var _rowTree$row$id;
return ((_rowTree$row$id = rowTree[row.id]) == null ? void 0 : _rowTree$row$id.depth) === 0;
});
});
/**
* Get the amount of rows accessible after the filtering process.
* @category Filtering
*/
export var gridExpandedRowCountSelector = createSelector(gridExpandedSortedRowEntriesSelector, function (visibleSortedRows) {
return visibleSortedRows.length;
});
/**
* Get the amount of top level rows accessible after the filtering process.
* @category Filtering
*/
export var gridFilteredTopLevelRowCountSelector = createSelector(gridFilteredSortedTopLevelRowEntriesSelector, function (visibleSortedTopLevelRows) {
return visibleSortedTopLevelRows.length;
});
/**
* @category Filtering
* @ignore - do not document.
*/
export var gridFilterActiveItemsSelector = createSelectorMemoized(gridFilterModelSelector, gridColumnLookupSelector, function (filterModel, columnLookup) {
var _filterModel$items;
return (_filterModel$items = filterModel.items) == null ? void 0 : _filterModel$items.filter(function (item) {
var _column$filterOperato, _item$value;
if (!item.field) {
return false;
}
var column = columnLookup[item.field];
if (!(column != null && column.filterOperators) || (column == null || (_column$filterOperato = column.filterOperators) == null ? void 0 : _column$filterOperato.length) === 0) {
return false;
}
var filterOperator = column.filterOperators.find(function (operator) {
return operator.value === item.operator;
});
if (!filterOperator) {
return false;
}
return !filterOperator.InputComponent || item.value != null && ((_item$value = item.value) == null ? void 0 : _item$value.toString()) !== '';
});
});
/**
* @category Filtering
* @ignore - do not document.
*/
export var gridFilterActiveItemsLookupSelector = createSelectorMemoized(gridFilterActiveItemsSelector, function (activeFilters) {
var result = activeFilters.reduce(function (res, filterItem) {
if (!res[filterItem.field]) {
res[filterItem.field] = [filterItem];
} else {
res[filterItem.field].push(filterItem);
}
return res;
}, {});
return result;
});

View File

@@ -0,0 +1,20 @@
import { GridLogicOperator } from '../../../models/gridFilterItem';
export var getDefaultGridFilterModel = function getDefaultGridFilterModel() {
return {
items: [],
logicOperator: GridLogicOperator.And,
quickFilterValues: [],
quickFilterLogicOperator: GridLogicOperator.And
};
};
/**
* @param {GridRowId} rowId The id of the row we want to filter.
* @param {(filterItem: GridFilterItem) => boolean} shouldApplyItem An optional callback to allow the filtering engine to only apply some items.
*/
/**
* Visibility status for each row.
* A row is visible if it is passing the filters AND if its parents are expanded.
* If a row is not registered in this lookup, it is visible.
*/

View File

@@ -0,0 +1,381 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import { GridLogicOperator } from '../../../models';
import { GLOBAL_API_REF, isInternalFilter } from '../../../colDef/utils';
import { getDefaultGridFilterModel } from './gridFilterState';
import { buildWarning } from '../../../utils/warning';
import { getPublicApiRef } from '../../../utils/getPublicApiRef';
import { gridColumnFieldsSelector, gridColumnLookupSelector, gridVisibleColumnFieldsSelector } from '../columns';
var hasEval;
function getHasEval() {
if (hasEval !== undefined) {
return hasEval;
}
try {
hasEval = new Function('return true')();
} catch (_) {
hasEval = false;
}
return hasEval;
}
/**
* Adds default values to the optional fields of a filter items.
* @param {GridFilterItem} item The raw filter item.
* @param {React.MutableRefObject<GridPrivateApiCommunity>} apiRef The API of the grid.
* @return {GridFilterItem} The clean filter item with an uniq ID and an always-defined operator.
* TODO: Make the typing reflect the different between GridFilterInputItem and GridFilterItem.
*/
export var cleanFilterItem = function cleanFilterItem(item, apiRef) {
var cleanItem = _extends({}, item);
if (cleanItem.id == null) {
cleanItem.id = Math.round(Math.random() * 1e5);
}
if (cleanItem.operator == null) {
// Selects a default operator
// We don't use `apiRef.current.getColumn` because it is not ready during state initialization
var column = gridColumnLookupSelector(apiRef)[cleanItem.field];
cleanItem.operator = column && column.filterOperators[0].value;
}
return cleanItem;
};
var filterModelDisableMultiColumnsFilteringWarning = buildWarning(['MUI: The `filterModel` can only contain a single item when the `disableMultipleColumnsFiltering` prop is set to `true`.', 'If you are using the community version of the `DataGrid`, this prop is always `true`.'], 'error');
var filterModelMissingItemIdWarning = buildWarning('MUI: The `id` field is required on `filterModel.items` when you use multiple filters.', 'error');
var filterModelMissingItemOperatorWarning = buildWarning('MUI: The `operator` field is required on `filterModel.items`, one or more of your filtering item has no `operator` provided.', 'error');
export var sanitizeFilterModel = function sanitizeFilterModel(model, disableMultipleColumnsFiltering, apiRef) {
var hasSeveralItems = model.items.length > 1;
var items;
if (hasSeveralItems && disableMultipleColumnsFiltering) {
filterModelDisableMultiColumnsFilteringWarning();
items = [model.items[0]];
} else {
items = model.items;
}
var hasItemsWithoutIds = hasSeveralItems && items.some(function (item) {
return item.id == null;
});
var hasItemWithoutOperator = items.some(function (item) {
return item.operator == null;
});
if (hasItemsWithoutIds) {
filterModelMissingItemIdWarning();
}
if (hasItemWithoutOperator) {
filterModelMissingItemOperatorWarning();
}
if (hasItemWithoutOperator || hasItemsWithoutIds) {
return _extends({}, model, {
items: items.map(function (item) {
return cleanFilterItem(item, apiRef);
})
});
}
if (model.items !== items) {
return _extends({}, model, {
items: items
});
}
return model;
};
export var mergeStateWithFilterModel = function mergeStateWithFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef) {
return function (filteringState) {
return _extends({}, filteringState, {
filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef)
});
};
};
export var removeDiacritics = function removeDiacritics(value) {
if (typeof value === 'string') {
return value.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
return value;
};
var getFilterCallbackFromItem = function getFilterCallbackFromItem(filterItem, apiRef) {
if (!filterItem.field || !filterItem.operator) {
return null;
}
var column = apiRef.current.getColumn(filterItem.field);
if (!column) {
return null;
}
var parsedValue;
if (column.valueParser) {
var _filterItem$value;
var parser = column.valueParser;
parsedValue = Array.isArray(filterItem.value) ? (_filterItem$value = filterItem.value) == null ? void 0 : _filterItem$value.map(function (x) {
return parser(x);
}) : parser(filterItem.value);
} else {
parsedValue = filterItem.value;
}
var ignoreDiacritics = apiRef.current.rootProps.ignoreDiacritics;
if (ignoreDiacritics) {
parsedValue = removeDiacritics(parsedValue);
}
var newFilterItem = _extends({}, filterItem, {
value: parsedValue
});
var filterOperators = column.filterOperators;
if (!(filterOperators != null && filterOperators.length)) {
throw new Error("MUI: No filter operators found for column '".concat(column.field, "'."));
}
var filterOperator = filterOperators.find(function (operator) {
return operator.value === newFilterItem.operator;
});
if (!filterOperator) {
throw new Error("MUI: No filter operator found for column '".concat(column.field, "' and operator value '").concat(newFilterItem.operator, "'."));
}
var hasUserFunctionLegacy = !isInternalFilter(filterOperator.getApplyFilterFn);
var hasUserFunctionV7 = !isInternalFilter(filterOperator.getApplyFilterFnV7);
var publicApiRef = getPublicApiRef(apiRef);
if (filterOperator.getApplyFilterFnV7 && !(hasUserFunctionLegacy && !hasUserFunctionV7)) {
var _applyFilterOnRow = filterOperator.getApplyFilterFnV7(newFilterItem, column);
if (typeof _applyFilterOnRow !== 'function') {
return null;
}
return {
v7: true,
item: newFilterItem,
fn: function fn(row) {
var value = apiRef.current.getRowValue(row, column);
if (ignoreDiacritics) {
value = removeDiacritics(value);
}
return _applyFilterOnRow(value, row, column, publicApiRef);
}
};
}
var applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column);
if (typeof applyFilterOnRow !== 'function') {
return null;
}
return {
v7: false,
item: newFilterItem,
fn: function fn(rowId) {
var params = apiRef.current.getCellParams(rowId, newFilterItem.field);
GLOBAL_API_REF.current = publicApiRef;
if (ignoreDiacritics) {
params.value = removeDiacritics(params.value);
}
var result = applyFilterOnRow(params);
GLOBAL_API_REF.current = null;
return result;
}
};
};
var filterItemsApplierId = 1;
/**
* Generates a method to easily check if a row is matching the current filter model.
* @param {GridFilterModel} filterModel The model with which we want to filter the rows.
* @param {React.MutableRefObject<GridPrivateApiCommunity>} apiRef The API of the grid.
* @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters.
*/
var buildAggregatedFilterItemsApplier = function buildAggregatedFilterItemsApplier(filterModel, apiRef, disableEval) {
var items = filterModel.items;
var appliers = items.map(function (item) {
return getFilterCallbackFromItem(item, apiRef);
}).filter(function (callback) {
return !!callback;
});
if (appliers.length === 0) {
return null;
}
if (disableEval || !getHasEval()) {
// This is the original logic, which is used if `eval()` is not supported (aka prevented by CSP).
return function (row, shouldApplyFilter) {
var resultPerItemId = {};
for (var i = 0; i < appliers.length; i += 1) {
var applier = appliers[i];
if (!shouldApplyFilter || shouldApplyFilter(applier.item.field)) {
resultPerItemId[applier.item.id] = applier.v7 ? applier.fn(row) : applier.fn(apiRef.current.getRowId(row));
}
}
return resultPerItemId;
};
}
// We generate a new function with `new Function()` to avoid expensive patterns for JS engines
// such as a dynamic object assignment, e.g. `{ [dynamicKey]: value }`.
var filterItemCore = new Function('getRowId', 'appliers', 'row', 'shouldApplyFilter', "\"use strict\";\n".concat(appliers.map(function (applier, i) {
return "const shouldApply".concat(i, " = !shouldApplyFilter || shouldApplyFilter(").concat(JSON.stringify(applier.item.field), ");");
}).join('\n'), "\n\nconst result$$ = {\n").concat(appliers.map(function (applier, i) {
return " ".concat(JSON.stringify(String(applier.item.id)), ": !shouldApply").concat(i, "\n ? false\n : ").concat(applier.v7 ? "appliers[".concat(i, "].fn(row)") : "appliers[".concat(i, "].fn(getRowId(row))"), ",");
}).join('\n'), "\n};\n\nreturn result$$;").replaceAll('$$', String(filterItemsApplierId)));
filterItemsApplierId += 1;
// Assign to the arrow function a name to help debugging
var filterItem = function filterItem(row, shouldApplyItem) {
return filterItemCore(apiRef.current.getRowId, appliers, row, shouldApplyItem);
};
return filterItem;
};
/**
* Generates a method to easily check if a row is matching the current quick filter.
* @param {any[]} filterModel The model with which we want to filter the rows.
* @param {React.MutableRefObject<GridPrivateApiCommunity>} apiRef The API of the grid.
* @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters.
*/
var buildAggregatedQuickFilterApplier = function buildAggregatedQuickFilterApplier(filterModel, apiRef) {
var _filterModel$quickFil, _filterModel$quickFil2, _filterModel$quickFil3;
var quickFilterValues = (_filterModel$quickFil = (_filterModel$quickFil2 = filterModel.quickFilterValues) == null ? void 0 : _filterModel$quickFil2.filter(Boolean)) != null ? _filterModel$quickFil : [];
if (quickFilterValues.length === 0) {
return null;
}
var quickFilterExcludeHiddenColumns = (_filterModel$quickFil3 = filterModel.quickFilterExcludeHiddenColumns) != null ? _filterModel$quickFil3 : false;
var columnFields = quickFilterExcludeHiddenColumns ? gridVisibleColumnFieldsSelector(apiRef) : gridColumnFieldsSelector(apiRef);
var appliersPerField = [];
var ignoreDiacritics = apiRef.current.rootProps.ignoreDiacritics;
var publicApiRef = getPublicApiRef(apiRef);
columnFields.forEach(function (field) {
var column = apiRef.current.getColumn(field);
var getApplyQuickFilterFn = column == null ? void 0 : column.getApplyQuickFilterFn;
var getApplyQuickFilterFnV7 = column == null ? void 0 : column.getApplyQuickFilterFnV7;
var hasUserFunctionLegacy = !isInternalFilter(getApplyQuickFilterFn);
var hasUserFunctionV7 = !isInternalFilter(getApplyQuickFilterFnV7);
if (getApplyQuickFilterFnV7 && !(hasUserFunctionLegacy && !hasUserFunctionV7)) {
appliersPerField.push({
column: column,
appliers: quickFilterValues.map(function (quickFilterValue) {
var value = ignoreDiacritics ? removeDiacritics(quickFilterValue) : quickFilterValue;
return {
v7: true,
fn: getApplyQuickFilterFnV7(value, column, publicApiRef)
};
})
});
} else if (getApplyQuickFilterFn) {
appliersPerField.push({
column: column,
appliers: quickFilterValues.map(function (quickFilterValue) {
var value = ignoreDiacritics ? removeDiacritics(quickFilterValue) : quickFilterValue;
return {
v7: false,
fn: getApplyQuickFilterFn(value, column, publicApiRef)
};
})
});
}
});
return function isRowMatchingQuickFilter(row, shouldApplyFilter) {
var result = {};
var usedCellParams = {};
/* eslint-disable no-restricted-syntax, no-labels */
outer: for (var v = 0; v < quickFilterValues.length; v += 1) {
var filterValue = quickFilterValues[v];
for (var i = 0; i < appliersPerField.length; i += 1) {
var _appliersPerField$i = appliersPerField[i],
column = _appliersPerField$i.column,
appliers = _appliersPerField$i.appliers;
var _field = column.field;
if (shouldApplyFilter && !shouldApplyFilter(_field)) {
continue;
}
var applier = appliers[v];
var value = apiRef.current.getRowValue(row, column);
if (applier.fn === null) {
continue;
}
if (applier.v7) {
if (ignoreDiacritics) {
value = removeDiacritics(value);
}
var isMatching = applier.fn(value, row, column, publicApiRef);
if (isMatching) {
result[filterValue] = true;
continue outer;
}
} else {
var _usedCellParams$_fiel;
var cellParams = (_usedCellParams$_fiel = usedCellParams[_field]) != null ? _usedCellParams$_fiel : apiRef.current.getCellParams(apiRef.current.getRowId(row), _field);
if (ignoreDiacritics) {
cellParams.value = removeDiacritics(cellParams.value);
}
usedCellParams[_field] = cellParams;
var _isMatching = applier.fn(cellParams);
if (_isMatching) {
result[filterValue] = true;
continue outer;
}
}
}
result[filterValue] = false;
}
/* eslint-enable no-restricted-syntax, no-labels */
return result;
};
};
export var buildAggregatedFilterApplier = function buildAggregatedFilterApplier(filterModel, apiRef, disableEval) {
var isRowMatchingFilterItems = buildAggregatedFilterItemsApplier(filterModel, apiRef, disableEval);
var isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(filterModel, apiRef);
return function isRowMatchingFilters(row, shouldApplyFilter, result) {
var _isRowMatchingFilterI, _isRowMatchingQuickFi;
result.passingFilterItems = (_isRowMatchingFilterI = isRowMatchingFilterItems == null ? void 0 : isRowMatchingFilterItems(row, shouldApplyFilter)) != null ? _isRowMatchingFilterI : null;
result.passingQuickFilterValues = (_isRowMatchingQuickFi = isRowMatchingQuickFilter == null ? void 0 : isRowMatchingQuickFilter(row, shouldApplyFilter)) != null ? _isRowMatchingQuickFi : null;
};
};
var isNotNull = function isNotNull(result) {
return result != null;
};
var filterModelItems = function filterModelItems(cache, apiRef, items) {
if (!cache.cleanedFilterItems) {
cache.cleanedFilterItems = items.filter(function (item) {
return getFilterCallbackFromItem(item, apiRef) !== null;
});
}
return cache.cleanedFilterItems;
};
export var passFilterLogic = function passFilterLogic(allFilterItemResults, allQuickFilterResults, filterModel, apiRef, cache) {
var cleanedFilterItems = filterModelItems(cache, apiRef, filterModel.items);
var cleanedFilterItemResults = allFilterItemResults.filter(isNotNull);
var cleanedQuickFilterResults = allQuickFilterResults.filter(isNotNull);
// get result for filter items model
if (cleanedFilterItemResults.length > 0) {
var _filterModel$logicOpe;
// Return true if the item pass with one of the rows
var filterItemPredicate = function filterItemPredicate(item) {
return cleanedFilterItemResults.some(function (filterItemResult) {
return filterItemResult[item.id];
});
};
var logicOperator = (_filterModel$logicOpe = filterModel.logicOperator) != null ? _filterModel$logicOpe : getDefaultGridFilterModel().logicOperator;
if (logicOperator === GridLogicOperator.And) {
var passesAllFilters = cleanedFilterItems.every(filterItemPredicate);
if (!passesAllFilters) {
return false;
}
} else {
var passesSomeFilters = cleanedFilterItems.some(filterItemPredicate);
if (!passesSomeFilters) {
return false;
}
}
}
// get result for quick filter model
if (cleanedQuickFilterResults.length > 0 && filterModel.quickFilterValues != null) {
var _filterModel$quickFil4;
// Return true if the item pass with one of the rows
var quickFilterValuePredicate = function quickFilterValuePredicate(value) {
return cleanedQuickFilterResults.some(function (quickFilterValueResult) {
return quickFilterValueResult[value];
});
};
var quickFilterLogicOperator = (_filterModel$quickFil4 = filterModel.quickFilterLogicOperator) != null ? _filterModel$quickFil4 : getDefaultGridFilterModel().quickFilterLogicOperator;
if (quickFilterLogicOperator === GridLogicOperator.And) {
var passesAllQuickFilterValues = filterModel.quickFilterValues.every(quickFilterValuePredicate);
if (!passesAllQuickFilterValues) {
return false;
}
} else {
var passesSomeQuickFilterValues = filterModel.quickFilterValues.some(quickFilterValuePredicate);
if (!passesSomeQuickFilterValues) {
return false;
}
}
}
return true;
};

View File

@@ -0,0 +1,2 @@
export { getDefaultGridFilterModel } from './gridFilterState';
export * from './gridFilterSelector';

View File

@@ -0,0 +1,378 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { defaultMemoize } from 'reselect';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { useLazyRef } from '../../utils/useLazyRef';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridFilterableColumnLookupSelector } from '../columns/gridColumnsSelector';
import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue';
import { getDefaultGridFilterModel } from './gridFilterState';
import { gridFilterModelSelector } from './gridFilterSelector';
import { useFirstRender } from '../../utils/useFirstRender';
import { gridRowsLookupSelector } from '../rows';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { GRID_DEFAULT_STRATEGY, useGridRegisterStrategyProcessor } from '../../core/strategyProcessing';
import { buildAggregatedFilterApplier, sanitizeFilterModel, mergeStateWithFilterModel, cleanFilterItem, passFilterLogic } from './gridFilterUtils';
import { isDeepEqual } from '../../../utils/utils';
import { jsx as _jsx } from "react/jsx-runtime";
export var filterStateInitializer = function filterStateInitializer(state, props, apiRef) {
var _ref, _props$filterModel, _props$initialState;
var filterModel = (_ref = (_props$filterModel = props.filterModel) != null ? _props$filterModel : (_props$initialState = props.initialState) == null || (_props$initialState = _props$initialState.filter) == null ? void 0 : _props$initialState.filterModel) != null ? _ref : getDefaultGridFilterModel();
return _extends({}, state, {
filter: {
filterModel: sanitizeFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef),
filteredRowsLookup: {},
filteredDescendantCountLookup: {}
},
visibleRowsLookup: {}
});
};
var getVisibleRowsLookup = function getVisibleRowsLookup(params) {
// For flat tree, the `visibleRowsLookup` and the `filteredRowsLookup` are equals since no row is collapsed.
return params.filteredRowsLookup;
};
function getVisibleRowsLookupState(apiRef, state) {
return apiRef.current.applyStrategyProcessor('visibleRowsLookupCreation', {
tree: state.rows.tree,
filteredRowsLookup: state.filter.filteredRowsLookup
});
}
function createMemoizedValues() {
return defaultMemoize(Object.values);
}
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
* @requires useGridRows (event)
*/
export var useGridFilter = function useGridFilter(apiRef, props) {
var _props$initialState3, _props$slotProps2;
var logger = useGridLogger(apiRef, 'useGridFilter');
apiRef.current.registerControlState({
stateId: 'filter',
propModel: props.filterModel,
propOnChange: props.onFilterModelChange,
stateSelector: gridFilterModelSelector,
changeEvent: 'filterModelChange'
});
var updateFilteredRows = React.useCallback(function () {
apiRef.current.setState(function (state) {
var filterModel = gridFilterModelSelector(state, apiRef.current.instanceId);
var isRowMatchingFilters = props.filterMode === 'client' ? buildAggregatedFilterApplier(filterModel, apiRef, props.disableEval) : null;
var filteringResult = apiRef.current.applyStrategyProcessor('filtering', {
isRowMatchingFilters: isRowMatchingFilters,
filterModel: filterModel != null ? filterModel : getDefaultGridFilterModel()
});
var newState = _extends({}, state, {
filter: _extends({}, state.filter, filteringResult)
});
var visibleRowsLookupState = getVisibleRowsLookupState(apiRef, newState);
return _extends({}, newState, {
visibleRowsLookup: visibleRowsLookupState
});
});
apiRef.current.publishEvent('filteredRowsSet');
}, [apiRef, props.filterMode, props.disableEval]);
var addColumnMenuItem = React.useCallback(function (columnMenuItems, colDef) {
if (colDef == null || colDef.filterable === false || props.disableColumnFilter) {
return columnMenuItems;
}
return [].concat(_toConsumableArray(columnMenuItems), ['columnMenuFilterItem']);
}, [props.disableColumnFilter]);
/**
* API METHODS
*/
var applyFilters = React.useCallback(function () {
updateFilteredRows();
apiRef.current.forceUpdate();
}, [apiRef, updateFilteredRows]);
var upsertFilterItem = React.useCallback(function (item) {
var filterModel = gridFilterModelSelector(apiRef);
var items = _toConsumableArray(filterModel.items);
var itemIndex = items.findIndex(function (filterItem) {
return filterItem.id === item.id;
});
if (itemIndex === -1) {
items.push(item);
} else {
items[itemIndex] = item;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'upsertFilterItem');
}, [apiRef]);
var upsertFilterItems = React.useCallback(function (items) {
var filterModel = gridFilterModelSelector(apiRef);
var existingItems = _toConsumableArray(filterModel.items);
items.forEach(function (item) {
var itemIndex = items.findIndex(function (filterItem) {
return filterItem.id === item.id;
});
if (itemIndex === -1) {
existingItems.push(item);
} else {
existingItems[itemIndex] = item;
}
});
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'upsertFilterItems');
}, [apiRef]);
var deleteFilterItem = React.useCallback(function (itemToDelete) {
var filterModel = gridFilterModelSelector(apiRef);
var items = filterModel.items.filter(function (item) {
return item.id !== itemToDelete.id;
});
if (items.length === filterModel.items.length) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'deleteFilterItem');
}, [apiRef]);
var showFilterPanel = React.useCallback(function (targetColumnField, panelId, labelId) {
logger.debug('Displaying filter panel');
if (targetColumnField) {
var filterModel = gridFilterModelSelector(apiRef);
var filterItemsWithValue = filterModel.items.filter(function (item) {
var _column$filterOperato;
if (item.value !== undefined) {
// Some filters like `isAnyOf` support array as `item.value`.
// If array is empty, we want to remove it from the filter model.
if (Array.isArray(item.value) && item.value.length === 0) {
return false;
}
return true;
}
var column = apiRef.current.getColumn(item.field);
var filterOperator = (_column$filterOperato = column.filterOperators) == null ? void 0 : _column$filterOperato.find(function (operator) {
return operator.value === item.operator;
});
var requiresFilterValue = typeof (filterOperator == null ? void 0 : filterOperator.requiresFilterValue) === 'undefined' ? true : filterOperator == null ? void 0 : filterOperator.requiresFilterValue;
// Operators like `isEmpty` don't have and don't require `item.value`.
// So we don't want to remove them from the filter model if `item.value === undefined`.
// See https://github.com/mui/mui-x/issues/5402
if (requiresFilterValue) {
return false;
}
return true;
});
var newFilterItems;
var filterItemOnTarget = filterItemsWithValue.find(function (item) {
return item.field === targetColumnField;
});
var targetColumn = apiRef.current.getColumn(targetColumnField);
if (filterItemOnTarget) {
newFilterItems = filterItemsWithValue;
} else if (props.disableMultipleColumnsFiltering) {
newFilterItems = [cleanFilterItem({
field: targetColumnField,
operator: targetColumn.filterOperators[0].value
}, apiRef)];
} else {
newFilterItems = [].concat(_toConsumableArray(filterItemsWithValue), [cleanFilterItem({
field: targetColumnField,
operator: targetColumn.filterOperators[0].value
}, apiRef)]);
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: newFilterItems
}));
}
apiRef.current.showPreferences(GridPreferencePanelsValue.filters, panelId, labelId);
}, [apiRef, logger, props.disableMultipleColumnsFiltering]);
var hideFilterPanel = React.useCallback(function () {
logger.debug('Hiding filter panel');
apiRef.current.hidePreferences();
}, [apiRef, logger]);
var setFilterLogicOperator = React.useCallback(function (logicOperator) {
var filterModel = gridFilterModelSelector(apiRef);
if (filterModel.logicOperator === logicOperator) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
logicOperator: logicOperator
}), 'changeLogicOperator');
}, [apiRef]);
var setQuickFilterValues = React.useCallback(function (values) {
var filterModel = gridFilterModelSelector(apiRef);
if (isDeepEqual(filterModel.quickFilterValues, values)) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
quickFilterValues: _toConsumableArray(values)
}));
}, [apiRef]);
var setFilterModel = React.useCallback(function (model, reason) {
var currentModel = gridFilterModelSelector(apiRef);
if (currentModel !== model) {
logger.debug('Setting filter model');
apiRef.current.updateControlState('filter', mergeStateWithFilterModel(model, props.disableMultipleColumnsFiltering, apiRef), reason);
apiRef.current.unstable_applyFilters();
}
}, [apiRef, logger, props.disableMultipleColumnsFiltering]);
var filterApi = {
setFilterLogicOperator: setFilterLogicOperator,
unstable_applyFilters: applyFilters,
deleteFilterItem: deleteFilterItem,
upsertFilterItem: upsertFilterItem,
upsertFilterItems: upsertFilterItems,
setFilterModel: setFilterModel,
showFilterPanel: showFilterPanel,
hideFilterPanel: hideFilterPanel,
setQuickFilterValues: setQuickFilterValues,
ignoreDiacritics: props.ignoreDiacritics
};
useGridApiMethod(apiRef, filterApi, 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState2;
var filterModelToExport = gridFilterModelSelector(apiRef);
var shouldExportFilterModel =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the model is controlled
props.filterModel != null ||
// Always export if the model has been initialized
((_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.filter) == null ? void 0 : _props$initialState2.filterModel) != null ||
// Export if the model is not equal to the default value
!isDeepEqual(filterModelToExport, getDefaultGridFilterModel());
if (!shouldExportFilterModel) {
return prevState;
}
return _extends({}, prevState, {
filter: {
filterModel: filterModelToExport
}
});
}, [apiRef, props.filterModel, (_props$initialState3 = props.initialState) == null || (_props$initialState3 = _props$initialState3.filter) == null ? void 0 : _props$initialState3.filterModel]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto;
var filterModel = (_context$stateToResto = context.stateToRestore.filter) == null ? void 0 : _context$stateToResto.filterModel;
if (filterModel == null) {
return params;
}
apiRef.current.updateControlState('filter', mergeStateWithFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef), 'restoreState');
return _extends({}, params, {
callbacks: [].concat(_toConsumableArray(params.callbacks), [apiRef.current.unstable_applyFilters])
});
}, [apiRef, props.disableMultipleColumnsFiltering]);
var preferencePanelPreProcessing = React.useCallback(function (initialValue, value) {
if (value === GridPreferencePanelsValue.filters) {
var _props$slotProps;
var FilterPanel = props.slots.filterPanel;
return /*#__PURE__*/_jsx(FilterPanel, _extends({}, (_props$slotProps = props.slotProps) == null ? void 0 : _props$slotProps.filterPanel));
}
return initialValue;
}, [props.slots.filterPanel, (_props$slotProps2 = props.slotProps) == null ? void 0 : _props$slotProps2.filterPanel]);
var getRowId = props.getRowId;
var getRowsRef = useLazyRef(createMemoizedValues);
var flatFilteringMethod = React.useCallback(function (params) {
if (props.filterMode !== 'client' || !params.isRowMatchingFilters) {
return {
filteredRowsLookup: {},
filteredDescendantCountLookup: {}
};
}
var dataRowIdToModelLookup = gridRowsLookupSelector(apiRef);
var filteredRowsLookup = {};
var isRowMatchingFilters = params.isRowMatchingFilters;
var filterCache = {};
var result = {
passingFilterItems: null,
passingQuickFilterValues: null
};
var rows = getRowsRef.current(apiRef.current.state.rows.dataRowIdToModelLookup);
for (var i = 0; i < rows.length; i += 1) {
var row = rows[i];
var id = getRowId ? getRowId(row) : row.id;
isRowMatchingFilters(row, undefined, result);
var isRowPassing = passFilterLogic([result.passingFilterItems], [result.passingQuickFilterValues], params.filterModel, apiRef, filterCache);
filteredRowsLookup[id] = isRowPassing;
}
var footerId = 'auto-generated-group-footer-root';
var footer = dataRowIdToModelLookup[footerId];
if (footer) {
filteredRowsLookup[footerId] = true;
}
return {
filteredRowsLookup: filteredRowsLookup,
filteredDescendantCountLookup: {}
};
}, [apiRef, props.filterMode, getRowId, getRowsRef]);
useGridRegisterPipeProcessor(apiRef, 'columnMenu', addColumnMenuItem);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
useGridRegisterPipeProcessor(apiRef, 'preferencePanel', preferencePanelPreProcessing);
useGridRegisterStrategyProcessor(apiRef, GRID_DEFAULT_STRATEGY, 'filtering', flatFilteringMethod);
useGridRegisterStrategyProcessor(apiRef, GRID_DEFAULT_STRATEGY, 'visibleRowsLookupCreation', getVisibleRowsLookup);
/**
* EVENTS
*/
var handleColumnsChange = React.useCallback(function () {
logger.debug('onColUpdated - GridColumns changed, applying filters');
var filterModel = gridFilterModelSelector(apiRef);
var filterableColumnsLookup = gridFilterableColumnLookupSelector(apiRef);
var newFilterItems = filterModel.items.filter(function (item) {
return item.field && filterableColumnsLookup[item.field];
});
if (newFilterItems.length < filterModel.items.length) {
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: newFilterItems
}));
}
}, [apiRef, logger]);
var handleStrategyProcessorChange = React.useCallback(function (methodName) {
if (methodName === 'filtering') {
apiRef.current.unstable_applyFilters();
}
}, [apiRef]);
var updateVisibleRowsLookupState = React.useCallback(function () {
apiRef.current.setState(function (state) {
return _extends({}, state, {
visibleRowsLookup: getVisibleRowsLookupState(apiRef, state)
});
});
apiRef.current.forceUpdate();
}, [apiRef]);
// Do not call `apiRef.current.forceUpdate` to avoid re-render before updating the sorted rows.
// Otherwise, the state is not consistent during the render
useGridApiEventHandler(apiRef, 'rowsSet', updateFilteredRows);
useGridApiEventHandler(apiRef, 'columnsChange', handleColumnsChange);
useGridApiEventHandler(apiRef, 'activeStrategyProcessorChange', handleStrategyProcessorChange);
useGridApiEventHandler(apiRef, 'rowExpansionChange', updateVisibleRowsLookupState);
useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', function () {
var filterModel = gridFilterModelSelector(apiRef);
if (filterModel.quickFilterValues && filterModel.quickFilterExcludeHiddenColumns) {
// re-apply filters because the quick filter results may have changed
apiRef.current.unstable_applyFilters();
}
});
/**
* 1ST RENDER
*/
useFirstRender(function () {
apiRef.current.unstable_applyFilters();
});
/**
* EFFECTS
*/
useEnhancedEffect(function () {
if (props.filterModel !== undefined) {
apiRef.current.setFilterModel(props.filterModel);
}
}, [apiRef, logger, props.filterModel]);
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,39 @@
import { createSelector } from '../../../utils/createSelector';
export var gridFocusStateSelector = function gridFocusStateSelector(state) {
return state.focus;
};
export var gridFocusCellSelector = createSelector(gridFocusStateSelector, function (focusState) {
return focusState.cell;
});
export var gridFocusColumnHeaderSelector = createSelector(gridFocusStateSelector, function (focusState) {
return focusState.columnHeader;
});
// eslint-disable-next-line @typescript-eslint/naming-convention
export var unstable_gridFocusColumnHeaderFilterSelector = createSelector(gridFocusStateSelector, function (focusState) {
return focusState.columnHeaderFilter;
});
// eslint-disable-next-line @typescript-eslint/naming-convention
export var unstable_gridFocusColumnGroupHeaderSelector = createSelector(gridFocusStateSelector, function (focusState) {
return focusState.columnGroupHeader;
});
export var gridTabIndexStateSelector = function gridTabIndexStateSelector(state) {
return state.tabIndex;
};
export var gridTabIndexCellSelector = createSelector(gridTabIndexStateSelector, function (state) {
return state.cell;
});
export var gridTabIndexColumnHeaderSelector = createSelector(gridTabIndexStateSelector, function (state) {
return state.columnHeader;
});
// eslint-disable-next-line @typescript-eslint/naming-convention
export var unstable_gridTabIndexColumnHeaderFilterSelector = createSelector(gridTabIndexStateSelector, function (state) {
return state.columnHeaderFilter;
});
// eslint-disable-next-line @typescript-eslint/naming-convention
export var unstable_gridTabIndexColumnGroupHeaderSelector = createSelector(gridTabIndexStateSelector, function (state) {
return state.columnGroupHeader;
});

View File

@@ -0,0 +1,2 @@
export * from './gridFocusStateSelector';
export * from './gridFocusState';

View File

@@ -0,0 +1,408 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_ownerDocument as ownerDocument, unstable_useEventCallback as useEventcallback } from '@mui/utils';
import { gridClasses } from '../../../constants/gridClasses';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { isNavigationKey } from '../../../utils/keyboardUtils';
import { gridFocusCellSelector, unstable_gridFocusColumnGroupHeaderSelector } from './gridFocusStateSelector';
import { gridVisibleColumnDefinitionsSelector } from '../columns/gridColumnsSelector';
import { getVisibleRows } from '../../utils/useGridVisibleRows';
import { clamp } from '../../../utils/utils';
import { gridPinnedRowsSelector } from '../rows/gridRowsSelector';
export var focusStateInitializer = function focusStateInitializer(state) {
return _extends({}, state, {
focus: {
cell: null,
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
},
tabIndex: {
cell: null,
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
}
});
};
/**
* @requires useGridParamsApi (method)
* @requires useGridRows (method)
* @requires useGridEditing (event)
*/
export var useGridFocus = function useGridFocus(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridFocus');
var lastClickedCell = React.useRef(null);
var publishCellFocusOut = React.useCallback(function (cell, event) {
if (cell) {
// The row might have been deleted
if (apiRef.current.getRow(cell.id)) {
apiRef.current.publishEvent('cellFocusOut', apiRef.current.getCellParams(cell.id, cell.field), event);
}
}
}, [apiRef]);
var setCellFocus = React.useCallback(function (id, field) {
var focusedCell = gridFocusCellSelector(apiRef);
if ((focusedCell == null ? void 0 : focusedCell.id) === id && (focusedCell == null ? void 0 : focusedCell.field) === field) {
return;
}
apiRef.current.setState(function (state) {
logger.debug("Focusing on cell with id=".concat(id, " and field=").concat(field));
return _extends({}, state, {
tabIndex: {
cell: {
id: id,
field: field
},
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
},
focus: {
cell: {
id: id,
field: field
},
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
}
});
});
apiRef.current.forceUpdate();
// The row might have been deleted
if (!apiRef.current.getRow(id)) {
return;
}
if (focusedCell) {
// There's a focused cell but another cell was clicked
// Publishes an event to notify that the focus was lost
publishCellFocusOut(focusedCell, {});
}
apiRef.current.publishEvent('cellFocusIn', apiRef.current.getCellParams(id, field));
}, [apiRef, logger, publishCellFocusOut]);
var setColumnHeaderFocus = React.useCallback(function (field) {
var event = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var cell = gridFocusCellSelector(apiRef);
publishCellFocusOut(cell, event);
apiRef.current.setState(function (state) {
logger.debug("Focusing on column header with colIndex=".concat(field));
return _extends({}, state, {
tabIndex: {
columnHeader: {
field: field
},
columnHeaderFilter: null,
cell: null,
columnGroupHeader: null
},
focus: {
columnHeader: {
field: field
},
columnHeaderFilter: null,
cell: null,
columnGroupHeader: null
}
});
});
apiRef.current.forceUpdate();
}, [apiRef, logger, publishCellFocusOut]);
var setColumnHeaderFilterFocus = React.useCallback(function (field) {
var event = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var cell = gridFocusCellSelector(apiRef);
publishCellFocusOut(cell, event);
apiRef.current.setState(function (state) {
logger.debug("Focusing on column header filter with colIndex=".concat(field));
return _extends({}, state, {
tabIndex: {
columnHeader: null,
columnHeaderFilter: {
field: field
},
cell: null,
columnGroupHeader: null
},
focus: {
columnHeader: null,
columnHeaderFilter: {
field: field
},
cell: null,
columnGroupHeader: null
}
});
});
apiRef.current.forceUpdate();
}, [apiRef, logger, publishCellFocusOut]);
var setColumnGroupHeaderFocus = React.useCallback(function (field, depth) {
var event = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var cell = gridFocusCellSelector(apiRef);
if (cell) {
apiRef.current.publishEvent('cellFocusOut', apiRef.current.getCellParams(cell.id, cell.field), event);
}
apiRef.current.setState(function (state) {
return _extends({}, state, {
tabIndex: {
columnGroupHeader: {
field: field,
depth: depth
},
columnHeader: null,
columnHeaderFilter: null,
cell: null
},
focus: {
columnGroupHeader: {
field: field,
depth: depth
},
columnHeader: null,
columnHeaderFilter: null,
cell: null
}
});
});
apiRef.current.forceUpdate();
}, [apiRef]);
var getColumnGroupHeaderFocus = React.useCallback(function () {
return unstable_gridFocusColumnGroupHeaderSelector(apiRef);
}, [apiRef]);
var moveFocusToRelativeCell = React.useCallback(function (id, field, direction) {
var columnIndexToFocus = apiRef.current.getColumnIndex(field);
var visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
var currentPage = getVisibleRows(apiRef, {
pagination: props.pagination,
paginationMode: props.paginationMode
});
var pinnedRows = gridPinnedRowsSelector(apiRef);
// Include pinned rows as well
var currentPageRows = [].concat(pinnedRows.top || [], currentPage.rows, pinnedRows.bottom || []);
var rowIndexToFocus = currentPageRows.findIndex(function (row) {
return row.id === id;
});
if (direction === 'right') {
columnIndexToFocus += 1;
} else if (direction === 'left') {
columnIndexToFocus -= 1;
} else {
rowIndexToFocus += 1;
}
if (columnIndexToFocus >= visibleColumns.length) {
// Go to next row if we are after the last column
rowIndexToFocus += 1;
if (rowIndexToFocus < currentPageRows.length) {
// Go to first column of the next row if there's one more row
columnIndexToFocus = 0;
}
} else if (columnIndexToFocus < 0) {
// Go to previous row if we are before the first column
rowIndexToFocus -= 1;
if (rowIndexToFocus >= 0) {
// Go to last column of the previous if there's one more row
columnIndexToFocus = visibleColumns.length - 1;
}
}
rowIndexToFocus = clamp(rowIndexToFocus, 0, currentPageRows.length - 1);
var rowToFocus = currentPageRows[rowIndexToFocus];
if (!rowToFocus) {
return;
}
var colSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowToFocus.id, columnIndexToFocus);
if (colSpanInfo && colSpanInfo.spannedByColSpan) {
if (direction === 'left' || direction === 'below') {
columnIndexToFocus = colSpanInfo.leftVisibleCellIndex;
} else if (direction === 'right') {
columnIndexToFocus = colSpanInfo.rightVisibleCellIndex;
}
}
columnIndexToFocus = clamp(columnIndexToFocus, 0, visibleColumns.length - 1);
var columnToFocus = visibleColumns[columnIndexToFocus];
apiRef.current.setCellFocus(rowToFocus.id, columnToFocus.field);
}, [apiRef, props.pagination, props.paginationMode]);
var handleCellDoubleClick = React.useCallback(function (_ref) {
var id = _ref.id,
field = _ref.field;
apiRef.current.setCellFocus(id, field);
}, [apiRef]);
var handleCellKeyDown = React.useCallback(function (params, event) {
// GRID_CELL_NAVIGATION_KEY_DOWN handles the focus on Enter, Tab and navigation keys
if (event.key === 'Enter' || event.key === 'Tab' || event.key === 'Shift' || isNavigationKey(event.key)) {
return;
}
apiRef.current.setCellFocus(params.id, params.field);
}, [apiRef]);
var handleColumnHeaderFocus = React.useCallback(function (_ref2, event) {
var field = _ref2.field;
if (event.target !== event.currentTarget) {
return;
}
apiRef.current.setColumnHeaderFocus(field, event);
}, [apiRef]);
var handleColumnGroupHeaderFocus = React.useCallback(function (_ref3, event) {
var fields = _ref3.fields,
depth = _ref3.depth;
if (event.target !== event.currentTarget) {
return;
}
var focusedColumnGroup = unstable_gridFocusColumnGroupHeaderSelector(apiRef);
if (focusedColumnGroup !== null && focusedColumnGroup.depth === depth && fields.includes(focusedColumnGroup.field)) {
// This group cell has already been focused
return;
}
apiRef.current.setColumnGroupHeaderFocus(fields[0], depth, event);
}, [apiRef]);
var handleBlur = React.useCallback(function (_, event) {
var _event$relatedTarget;
if ((_event$relatedTarget = event.relatedTarget) != null && _event$relatedTarget.className.includes(gridClasses.columnHeader)) {
return;
}
logger.debug("Clearing focus");
apiRef.current.setState(function (state) {
return _extends({}, state, {
focus: {
cell: null,
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
}
});
});
}, [logger, apiRef]);
var handleCellMouseDown = React.useCallback(function (params) {
lastClickedCell.current = params;
}, []);
var handleDocumentClick = React.useCallback(function (event) {
var cellParams = lastClickedCell.current;
lastClickedCell.current = null;
var focusedCell = gridFocusCellSelector(apiRef);
var canUpdateFocus = apiRef.current.unstable_applyPipeProcessors('canUpdateFocus', true, {
event: event,
cell: cellParams
});
if (!canUpdateFocus) {
return;
}
if (!focusedCell) {
if (cellParams) {
apiRef.current.setCellFocus(cellParams.id, cellParams.field);
}
return;
}
if ((cellParams == null ? void 0 : cellParams.id) === focusedCell.id && (cellParams == null ? void 0 : cellParams.field) === focusedCell.field) {
return;
}
var cellElement = apiRef.current.getCellElement(focusedCell.id, focusedCell.field);
if (cellElement != null && cellElement.contains(event.target)) {
return;
}
if (cellParams) {
apiRef.current.setCellFocus(cellParams.id, cellParams.field);
} else {
apiRef.current.setState(function (state) {
return _extends({}, state, {
focus: {
cell: null,
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
}
});
});
apiRef.current.forceUpdate();
// There's a focused cell but another element (not a cell) was clicked
// Publishes an event to notify that the focus was lost
publishCellFocusOut(focusedCell, event);
}
}, [apiRef, publishCellFocusOut]);
var handleCellModeChange = React.useCallback(function (params) {
if (params.cellMode === 'view') {
return;
}
var cell = gridFocusCellSelector(apiRef);
if ((cell == null ? void 0 : cell.id) !== params.id || (cell == null ? void 0 : cell.field) !== params.field) {
apiRef.current.setCellFocus(params.id, params.field);
}
}, [apiRef]);
var handleRowSet = React.useCallback(function () {
var cell = gridFocusCellSelector(apiRef);
// If the focused cell is in a row which does not exist anymore, then remove the focus
if (cell && !apiRef.current.getRow(cell.id)) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
focus: {
cell: null,
columnHeader: null,
columnHeaderFilter: null,
columnGroupHeader: null
}
});
});
}
}, [apiRef]);
var handlePaginationModelChange = useEventcallback(function () {
var currentFocusedCell = gridFocusCellSelector(apiRef);
if (!currentFocusedCell) {
return;
}
var currentPage = getVisibleRows(apiRef, {
pagination: props.pagination,
paginationMode: props.paginationMode
});
var rowIsInCurrentPage = currentPage.rows.find(function (row) {
return row.id === currentFocusedCell.id;
});
if (rowIsInCurrentPage) {
return;
}
var visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
apiRef.current.setState(function (state) {
return _extends({}, state, {
tabIndex: {
cell: {
id: currentPage.rows[0].id,
field: visibleColumns[0].field
},
columnGroupHeader: null,
columnHeader: null,
columnHeaderFilter: null
}
});
});
});
var focusApi = {
setCellFocus: setCellFocus,
setColumnHeaderFocus: setColumnHeaderFocus,
setColumnHeaderFilterFocus: setColumnHeaderFilterFocus
};
var focusPrivateApi = {
moveFocusToRelativeCell: moveFocusToRelativeCell,
setColumnGroupHeaderFocus: setColumnGroupHeaderFocus,
getColumnGroupHeaderFocus: getColumnGroupHeaderFocus
};
useGridApiMethod(apiRef, focusApi, 'public');
useGridApiMethod(apiRef, focusPrivateApi, 'private');
React.useEffect(function () {
var doc = ownerDocument(apiRef.current.rootElementRef.current);
doc.addEventListener('mouseup', handleDocumentClick);
return function () {
doc.removeEventListener('mouseup', handleDocumentClick);
};
}, [apiRef, handleDocumentClick]);
useGridApiEventHandler(apiRef, 'columnHeaderBlur', handleBlur);
useGridApiEventHandler(apiRef, 'cellDoubleClick', handleCellDoubleClick);
useGridApiEventHandler(apiRef, 'cellMouseDown', handleCellMouseDown);
useGridApiEventHandler(apiRef, 'cellKeyDown', handleCellKeyDown);
useGridApiEventHandler(apiRef, 'cellModeChange', handleCellModeChange);
useGridApiEventHandler(apiRef, 'columnHeaderFocus', handleColumnHeaderFocus);
useGridApiEventHandler(apiRef, 'columnGroupHeaderFocus', handleColumnGroupHeaderFocus);
useGridApiEventHandler(apiRef, 'rowsSet', handleRowSet);
useGridApiEventHandler(apiRef, 'paginationModelChange', handlePaginationModelChange);
};

View File

@@ -0,0 +1,11 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { createSelector } from '../../../utils/createSelector';
export var unstable_gridHeaderFilteringStateSelector = function unstable_gridHeaderFilteringStateSelector(state) {
return state.headerFiltering;
};
export var unstable_gridHeaderFilteringEditFieldSelector = createSelector(unstable_gridHeaderFilteringStateSelector, function (headerFilteringState) {
return headerFilteringState.editing;
});
export var unstable_gridHeaderFilteringMenuSelector = createSelector(unstable_gridHeaderFilteringStateSelector, function (headerFilteringState) {
return headerFilteringState.menuOpen;
});

View File

@@ -0,0 +1 @@
export * from './gridHeaderFilteringSelectors';

View File

@@ -0,0 +1,93 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils';
import { gridColumnLookupSelector, gridColumnVisibilityModelSelector, gridColumnFieldsSelector } from '../columns/gridColumnsSelector';
export var headerFilteringStateInitializer = function headerFilteringStateInitializer(state) {
return _extends({}, state, {
headerFiltering: {
editing: null,
menuOpen: null
}
});
};
export var useGridHeaderFiltering = function useGridHeaderFiltering(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridHeaderFiltering');
var setHeaderFilterState = React.useCallback(function (headerFilterState) {
apiRef.current.setState(function (state) {
var _headerFilterState$ed, _headerFilterState$me;
// Safety check to avoid MIT users from using it
// This hook should ultimately be moved to the Pro package
if (props.signature === 'DataGrid') {
return state;
}
return _extends({}, state, {
headerFiltering: {
editing: (_headerFilterState$ed = headerFilterState.editing) != null ? _headerFilterState$ed : null,
menuOpen: (_headerFilterState$me = headerFilterState.menuOpen) != null ? _headerFilterState$me : null
}
});
});
apiRef.current.forceUpdate();
}, [apiRef, props.signature]);
var startHeaderFilterEditMode = React.useCallback(function (field) {
logger.debug("Starting edit mode on header filter for field: ".concat(field));
apiRef.current.setHeaderFilterState({
editing: field
});
}, [apiRef, logger]);
var stopHeaderFilterEditMode = React.useCallback(function () {
logger.debug("Stopping edit mode on header filter");
apiRef.current.setHeaderFilterState({
editing: null
});
}, [apiRef, logger]);
var showHeaderFilterMenu = React.useCallback(function (field) {
logger.debug("Opening header filter menu for field: ".concat(field));
apiRef.current.setHeaderFilterState({
menuOpen: field
});
}, [apiRef, logger]);
var hideHeaderFilterMenu = React.useCallback(function () {
logger.debug("Hiding header filter menu for active field");
var fieldToFocus = apiRef.current.state.headerFiltering.menuOpen;
if (fieldToFocus) {
var columnLookup = gridColumnLookupSelector(apiRef);
var columnVisibilityModel = gridColumnVisibilityModelSelector(apiRef);
var orderedFields = gridColumnFieldsSelector(apiRef);
// If the column was removed from the grid, we need to find the closest visible field
if (!columnLookup[fieldToFocus]) {
fieldToFocus = orderedFields[0];
}
// If the field to focus is hidden, we need to find the closest visible field
if (columnVisibilityModel[fieldToFocus] === false) {
// contains visible column fields + the field that was just hidden
var visibleOrderedFields = orderedFields.filter(function (field) {
if (field === fieldToFocus) {
return true;
}
return columnVisibilityModel[field] !== false;
});
var fieldIndex = visibleOrderedFields.indexOf(fieldToFocus);
fieldToFocus = visibleOrderedFields[fieldIndex + 1] || visibleOrderedFields[fieldIndex - 1];
}
apiRef.current.setHeaderFilterState({
menuOpen: null
});
apiRef.current.setColumnHeaderFilterFocus(fieldToFocus);
}
}, [apiRef, logger]);
var headerFilterPrivateApi = {
setHeaderFilterState: setHeaderFilterState
};
var headerFilterApi = {
startHeaderFilterEditMode: startHeaderFilterEditMode,
stopHeaderFilterEditMode: stopHeaderFilterEditMode,
showHeaderFilterMenu: showHeaderFilterMenu,
hideHeaderFilterMenu: hideHeaderFilterMenu
};
useGridApiMethod(apiRef, headerFilterApi, 'public');
useGridApiMethod(apiRef, headerFilterPrivateApi, 'private');
};

View File

@@ -0,0 +1,16 @@
// Only export the variable and types that should be publicly exposed and re-exported from `@mui/x-data-grid-pro`
export * from './columnMenu';
export * from './columns';
export * from './columnGrouping';
export * from './density';
export * from './filter';
export * from './focus';
export * from './pagination';
export * from './preferencesPanel';
export * from './rows';
export * from './rowSelection';
export * from './sorting';
export * from './dimensions';
export * from './statePersistence';
export * from './headerFiltering';
export * from './virtualization';

View File

@@ -0,0 +1,576 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import * as React from 'react';
import { useTheme } from '@mui/material/styles';
import { gridVisibleColumnDefinitionsSelector } from '../columns/gridColumnsSelector';
import { useGridLogger } from '../../utils/useGridLogger';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { gridExpandedSortedRowEntriesSelector } from '../filter/gridFilterSelector';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../colDef/gridCheckboxSelectionColDef';
import { gridClasses } from '../../../constants/gridClasses';
import { GridCellModes } from '../../../models/gridEditRowModel';
import { isNavigationKey } from '../../../utils/keyboardUtils';
import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from '../../../constants/gridDetailPanelToggleField';
import { gridPinnedRowsSelector } from '../rows/gridRowsSelector';
import { unstable_gridFocusColumnGroupHeaderSelector } from '../focus';
import { gridColumnGroupsHeaderMaxDepthSelector } from '../columnGrouping/gridColumnGroupsSelector';
import { unstable_gridHeaderFilteringEditFieldSelector, unstable_gridHeaderFilteringMenuSelector } from '../headerFiltering/gridHeaderFilteringSelectors';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { isEventTargetInPortal } from '../../../utils/domUtils';
function enrichPageRowsWithPinnedRows(apiRef, rows) {
var pinnedRows = gridPinnedRowsSelector(apiRef) || {};
return [].concat(_toConsumableArray(pinnedRows.top || []), _toConsumableArray(rows), _toConsumableArray(pinnedRows.bottom || []));
}
var getLeftColumnIndex = function getLeftColumnIndex(_ref) {
var currentColIndex = _ref.currentColIndex,
firstColIndex = _ref.firstColIndex,
lastColIndex = _ref.lastColIndex,
direction = _ref.direction;
if (direction === 'rtl') {
if (currentColIndex < lastColIndex) {
return currentColIndex + 1;
}
} else if (direction === 'ltr') {
if (currentColIndex > firstColIndex) {
return currentColIndex - 1;
}
}
return null;
};
var getRightColumnIndex = function getRightColumnIndex(_ref2) {
var currentColIndex = _ref2.currentColIndex,
firstColIndex = _ref2.firstColIndex,
lastColIndex = _ref2.lastColIndex,
direction = _ref2.direction;
if (direction === 'rtl') {
if (currentColIndex > firstColIndex) {
return currentColIndex - 1;
}
} else if (direction === 'ltr') {
if (currentColIndex < lastColIndex) {
return currentColIndex + 1;
}
}
return null;
};
/**
* @requires useGridSorting (method) - can be after
* @requires useGridFilter (state) - can be after
* @requires useGridColumns (state, method) - can be after
* @requires useGridDimensions (method) - can be after
* @requires useGridFocus (method) - can be after
* @requires useGridScroll (method) - can be after
* @requires useGridColumnSpanning (method) - can be after
*/
export var useGridKeyboardNavigation = function useGridKeyboardNavigation(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridKeyboardNavigation');
var initialCurrentPageRows = useGridVisibleRows(apiRef, props).rows;
var theme = useTheme();
var currentPageRows = React.useMemo(function () {
return enrichPageRowsWithPinnedRows(apiRef, initialCurrentPageRows);
}, [apiRef, initialCurrentPageRows]);
var headerFilteringEnabled =
// @ts-expect-error // TODO move relevant code to the `DataGridPro`
props.signature !== 'DataGrid' && props.unstable_headerFilters;
/**
* @param {number} colIndex Index of the column to focus
* @param {number} rowIndex index of the row to focus
* @param {string} closestColumnToUse Which closest column cell to use when the cell is spanned by `colSpan`.
* TODO replace with apiRef.current.moveFocusToRelativeCell()
*/
var goToCell = React.useCallback(function (colIndex, rowId) {
var closestColumnToUse = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'left';
var visibleSortedRows = gridExpandedSortedRowEntriesSelector(apiRef);
var nextCellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, colIndex);
if (nextCellColSpanInfo && nextCellColSpanInfo.spannedByColSpan) {
if (closestColumnToUse === 'left') {
colIndex = nextCellColSpanInfo.leftVisibleCellIndex;
} else if (closestColumnToUse === 'right') {
colIndex = nextCellColSpanInfo.rightVisibleCellIndex;
}
}
// `scrollToIndexes` requires a rowIndex relative to all visible rows.
// Those rows do not include pinned rows, but pinned rows do not need scroll anyway.
var rowIndexRelativeToAllRows = visibleSortedRows.findIndex(function (row) {
return row.id === rowId;
});
logger.debug("Navigating to cell row ".concat(rowIndexRelativeToAllRows, ", col ").concat(colIndex));
apiRef.current.scrollToIndexes({
colIndex: colIndex,
rowIndex: rowIndexRelativeToAllRows
});
var field = apiRef.current.getVisibleColumns()[colIndex].field;
apiRef.current.setCellFocus(rowId, field);
}, [apiRef, logger]);
var goToHeader = React.useCallback(function (colIndex, event) {
logger.debug("Navigating to header col ".concat(colIndex));
apiRef.current.scrollToIndexes({
colIndex: colIndex
});
var field = apiRef.current.getVisibleColumns()[colIndex].field;
apiRef.current.setColumnHeaderFocus(field, event);
}, [apiRef, logger]);
var goToHeaderFilter = React.useCallback(function (colIndex, event) {
logger.debug("Navigating to header filter col ".concat(colIndex));
apiRef.current.scrollToIndexes({
colIndex: colIndex
});
var field = apiRef.current.getVisibleColumns()[colIndex].field;
apiRef.current.setColumnHeaderFilterFocus(field, event);
}, [apiRef, logger]);
var goToGroupHeader = React.useCallback(function (colIndex, depth, event) {
logger.debug("Navigating to header col ".concat(colIndex));
apiRef.current.scrollToIndexes({
colIndex: colIndex
});
var field = apiRef.current.getVisibleColumns()[colIndex].field;
apiRef.current.setColumnGroupHeaderFocus(field, depth, event);
}, [apiRef, logger]);
var getRowIdFromIndex = React.useCallback(function (rowIndex) {
var _currentPageRows$rowI;
return (_currentPageRows$rowI = currentPageRows[rowIndex]) == null ? void 0 : _currentPageRows$rowI.id;
}, [currentPageRows]);
var handleColumnHeaderKeyDown = React.useCallback(function (params, event) {
var headerTitleNode = event.currentTarget.querySelector(".".concat(gridClasses.columnHeaderTitleContainerContent));
var isFromInsideContent = !!headerTitleNode && headerTitleNode.contains(event.target);
if (isFromInsideContent && params.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field) {
// When focus is on a nested input, keyboard events have no effect to avoid conflicts with native events.
// There is one exception for the checkBoxHeader
return;
}
var dimensions = apiRef.current.getRootDimensions();
if (!dimensions) {
return;
}
var viewportPageSize = apiRef.current.getViewportPageSize();
var colIndexBefore = params.field ? apiRef.current.getColumnIndex(params.field) : 0;
var firstRowIndexInPage = currentPageRows.length > 0 ? 0 : null;
var lastRowIndexInPage = currentPageRows.length - 1;
var firstColIndex = 0;
var lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
var columnGroupMaxDepth = gridColumnGroupsHeaderMaxDepthSelector(apiRef);
var shouldPreventDefault = true;
switch (event.key) {
case 'ArrowDown':
{
if (firstRowIndexInPage !== null) {
if (headerFilteringEnabled) {
goToHeaderFilter(colIndexBefore, event);
} else {
goToCell(colIndexBefore, getRowIdFromIndex(firstRowIndexInPage));
}
}
break;
}
case 'ArrowRight':
{
var rightColIndex = getRightColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: theme.direction
});
if (rightColIndex !== null) {
goToHeader(rightColIndex, event);
}
break;
}
case 'ArrowLeft':
{
var leftColIndex = getLeftColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: theme.direction
});
if (leftColIndex !== null) {
goToHeader(leftColIndex, event);
}
break;
}
case 'ArrowUp':
{
if (columnGroupMaxDepth > 0) {
goToGroupHeader(colIndexBefore, columnGroupMaxDepth - 1, event);
}
break;
}
case 'PageDown':
{
if (firstRowIndexInPage !== null && lastRowIndexInPage !== null) {
goToCell(colIndexBefore, getRowIdFromIndex(Math.min(firstRowIndexInPage + viewportPageSize, lastRowIndexInPage)));
}
break;
}
case 'Home':
{
goToHeader(firstColIndex, event);
break;
}
case 'End':
{
goToHeader(lastColIndex, event);
break;
}
case 'Enter':
{
if (event.ctrlKey || event.metaKey) {
apiRef.current.toggleColumnMenu(params.field);
}
break;
}
case ' ':
{
// prevent Space event from scrolling
break;
}
default:
{
shouldPreventDefault = false;
}
}
if (shouldPreventDefault) {
event.preventDefault();
}
}, [apiRef, currentPageRows.length, headerFilteringEnabled, goToHeaderFilter, goToCell, getRowIdFromIndex, theme.direction, goToHeader, goToGroupHeader]);
var handleHeaderFilterKeyDown = React.useCallback(function (params, event) {
var dimensions = apiRef.current.getRootDimensions();
if (!dimensions) {
return;
}
var isEditing = unstable_gridHeaderFilteringEditFieldSelector(apiRef) === params.field;
var isHeaderMenuOpen = unstable_gridHeaderFilteringMenuSelector(apiRef) === params.field;
if (isEditing || isHeaderMenuOpen || !isNavigationKey(event.key)) {
return;
}
var viewportPageSize = apiRef.current.getViewportPageSize();
var colIndexBefore = params.field ? apiRef.current.getColumnIndex(params.field) : 0;
var firstRowIndexInPage = 0;
var lastRowIndexInPage = currentPageRows.length - 1;
var firstColIndex = 0;
var lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
var shouldPreventDefault = true;
switch (event.key) {
case 'ArrowDown':
{
var rowId = getRowIdFromIndex(firstRowIndexInPage);
if (firstRowIndexInPage !== null && rowId != null) {
goToCell(colIndexBefore, rowId);
}
break;
}
case 'ArrowRight':
{
var rightColIndex = getRightColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: theme.direction
});
if (rightColIndex !== null) {
goToHeaderFilter(rightColIndex, event);
}
break;
}
case 'ArrowLeft':
{
var leftColIndex = getLeftColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: theme.direction
});
if (leftColIndex !== null) {
goToHeaderFilter(leftColIndex, event);
} else {
apiRef.current.setColumnHeaderFilterFocus(params.field, event);
}
break;
}
case 'ArrowUp':
{
goToHeader(colIndexBefore, event);
break;
}
case 'PageDown':
{
if (firstRowIndexInPage !== null && lastRowIndexInPage !== null) {
goToCell(colIndexBefore, getRowIdFromIndex(Math.min(firstRowIndexInPage + viewportPageSize, lastRowIndexInPage)));
}
break;
}
case 'Home':
{
goToHeaderFilter(firstColIndex, event);
break;
}
case 'End':
{
goToHeaderFilter(lastColIndex, event);
break;
}
case ' ':
{
// prevent Space event from scrolling
break;
}
default:
{
shouldPreventDefault = false;
}
}
if (shouldPreventDefault) {
event.preventDefault();
}
}, [apiRef, currentPageRows.length, goToHeaderFilter, theme.direction, goToHeader, goToCell, getRowIdFromIndex]);
var handleColumnGroupHeaderKeyDown = React.useCallback(function (params, event) {
var dimensions = apiRef.current.getRootDimensions();
if (!dimensions) {
return;
}
var focusedColumnGroup = unstable_gridFocusColumnGroupHeaderSelector(apiRef);
if (focusedColumnGroup === null) {
return;
}
var currentField = focusedColumnGroup.field,
currentDepth = focusedColumnGroup.depth;
var fields = params.fields,
depth = params.depth,
maxDepth = params.maxDepth;
var viewportPageSize = apiRef.current.getViewportPageSize();
var currentColIndex = apiRef.current.getColumnIndex(currentField);
var colIndexBefore = currentField ? apiRef.current.getColumnIndex(currentField) : 0;
var firstRowIndexInPage = 0;
var lastRowIndexInPage = currentPageRows.length - 1;
var firstColIndex = 0;
var lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
var shouldPreventDefault = true;
switch (event.key) {
case 'ArrowDown':
{
if (depth === maxDepth - 1) {
goToHeader(currentColIndex, event);
} else {
goToGroupHeader(currentColIndex, currentDepth + 1, event);
}
break;
}
case 'ArrowUp':
{
if (depth > 0) {
goToGroupHeader(currentColIndex, currentDepth - 1, event);
}
break;
}
case 'ArrowRight':
{
var remainingRightColumns = fields.length - fields.indexOf(currentField) - 1;
if (currentColIndex + remainingRightColumns + 1 <= lastColIndex) {
goToGroupHeader(currentColIndex + remainingRightColumns + 1, currentDepth, event);
}
break;
}
case 'ArrowLeft':
{
var remainingLeftColumns = fields.indexOf(currentField);
if (currentColIndex - remainingLeftColumns - 1 >= firstColIndex) {
goToGroupHeader(currentColIndex - remainingLeftColumns - 1, currentDepth, event);
}
break;
}
case 'PageDown':
{
if (firstRowIndexInPage !== null && lastRowIndexInPage !== null) {
goToCell(colIndexBefore, getRowIdFromIndex(Math.min(firstRowIndexInPage + viewportPageSize, lastRowIndexInPage)));
}
break;
}
case 'Home':
{
goToGroupHeader(firstColIndex, currentDepth, event);
break;
}
case 'End':
{
goToGroupHeader(lastColIndex, currentDepth, event);
break;
}
case ' ':
{
// prevent Space event from scrolling
break;
}
default:
{
shouldPreventDefault = false;
}
}
if (shouldPreventDefault) {
event.preventDefault();
}
}, [apiRef, currentPageRows.length, goToHeader, goToGroupHeader, goToCell, getRowIdFromIndex]);
var handleCellKeyDown = React.useCallback(function (params, event) {
// Ignore portal
if (isEventTargetInPortal(event)) {
return;
}
// Get the most recent params because the cell mode may have changed by another listener
var cellParams = apiRef.current.getCellParams(params.id, params.field);
if (cellParams.cellMode === GridCellModes.Edit || !isNavigationKey(event.key)) {
return;
}
var canUpdateFocus = apiRef.current.unstable_applyPipeProcessors('canUpdateFocus', true, {
event: event,
cell: cellParams
});
if (!canUpdateFocus) {
return;
}
var dimensions = apiRef.current.getRootDimensions();
if (currentPageRows.length === 0 || !dimensions) {
return;
}
var direction = theme.direction;
var viewportPageSize = apiRef.current.getViewportPageSize();
var colIndexBefore = params.field ? apiRef.current.getColumnIndex(params.field) : 0;
var rowIndexBefore = currentPageRows.findIndex(function (row) {
return row.id === params.id;
});
var firstRowIndexInPage = 0;
var lastRowIndexInPage = currentPageRows.length - 1;
var firstColIndex = 0;
var lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
var shouldPreventDefault = true;
switch (event.key) {
case 'ArrowDown':
{
// "Enter" is only triggered by the row / cell editing feature
if (rowIndexBefore < lastRowIndexInPage) {
goToCell(colIndexBefore, getRowIdFromIndex(rowIndexBefore + 1));
}
break;
}
case 'ArrowUp':
{
if (rowIndexBefore > firstRowIndexInPage) {
goToCell(colIndexBefore, getRowIdFromIndex(rowIndexBefore - 1));
} else if (headerFilteringEnabled) {
goToHeaderFilter(colIndexBefore, event);
} else {
goToHeader(colIndexBefore, event);
}
break;
}
case 'ArrowRight':
{
var rightColIndex = getRightColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: direction
});
if (rightColIndex !== null) {
goToCell(rightColIndex, getRowIdFromIndex(rowIndexBefore), direction === 'rtl' ? 'left' : 'right');
}
break;
}
case 'ArrowLeft':
{
var leftColIndex = getLeftColumnIndex({
currentColIndex: colIndexBefore,
firstColIndex: firstColIndex,
lastColIndex: lastColIndex,
direction: direction
});
if (leftColIndex !== null) {
goToCell(leftColIndex, getRowIdFromIndex(rowIndexBefore), direction === 'rtl' ? 'right' : 'left');
}
break;
}
case 'Tab':
{
// "Tab" is only triggered by the row / cell editing feature
if (event.shiftKey && colIndexBefore > firstColIndex) {
goToCell(colIndexBefore - 1, getRowIdFromIndex(rowIndexBefore), 'left');
} else if (!event.shiftKey && colIndexBefore < lastColIndex) {
goToCell(colIndexBefore + 1, getRowIdFromIndex(rowIndexBefore), 'right');
}
break;
}
case ' ':
{
var field = params.field;
if (field === GRID_DETAIL_PANEL_TOGGLE_FIELD) {
break;
}
var colDef = params.colDef;
if (colDef && colDef.type === 'treeDataGroup') {
break;
}
if (!event.shiftKey && rowIndexBefore < lastRowIndexInPage) {
goToCell(colIndexBefore, getRowIdFromIndex(Math.min(rowIndexBefore + viewportPageSize, lastRowIndexInPage)));
}
break;
}
case 'PageDown':
{
if (rowIndexBefore < lastRowIndexInPage) {
goToCell(colIndexBefore, getRowIdFromIndex(Math.min(rowIndexBefore + viewportPageSize, lastRowIndexInPage)));
}
break;
}
case 'PageUp':
{
// Go to the first row before going to header
var nextRowIndex = Math.max(rowIndexBefore - viewportPageSize, firstRowIndexInPage);
if (nextRowIndex !== rowIndexBefore && nextRowIndex >= firstRowIndexInPage) {
goToCell(colIndexBefore, getRowIdFromIndex(nextRowIndex));
} else {
goToHeader(colIndexBefore, event);
}
break;
}
case 'Home':
{
if (event.ctrlKey || event.metaKey || event.shiftKey) {
goToCell(firstColIndex, getRowIdFromIndex(firstRowIndexInPage));
} else {
goToCell(firstColIndex, getRowIdFromIndex(rowIndexBefore));
}
break;
}
case 'End':
{
if (event.ctrlKey || event.metaKey || event.shiftKey) {
goToCell(lastColIndex, getRowIdFromIndex(lastRowIndexInPage));
} else {
goToCell(lastColIndex, getRowIdFromIndex(rowIndexBefore));
}
break;
}
default:
{
shouldPreventDefault = false;
}
}
if (shouldPreventDefault) {
event.preventDefault();
}
}, [apiRef, currentPageRows, theme.direction, goToCell, getRowIdFromIndex, headerFilteringEnabled, goToHeaderFilter, goToHeader]);
var checkIfCanStartEditing = React.useCallback(function (initialValue, _ref3) {
var event = _ref3.event;
if (event.key === ' ') {
// Space scrolls to the last row
return false;
}
return initialValue;
}, []);
useGridRegisterPipeProcessor(apiRef, 'canStartEditing', checkIfCanStartEditing);
useGridApiEventHandler(apiRef, 'columnHeaderKeyDown', handleColumnHeaderKeyDown);
useGridApiEventHandler(apiRef, 'headerFilterKeyDown', handleHeaderFilterKeyDown);
useGridApiEventHandler(apiRef, 'columnGroupHeaderKeyDown', handleColumnGroupHeaderKeyDown);
useGridApiEventHandler(apiRef, 'cellKeyDown', handleCellKeyDown);
};

View File

@@ -0,0 +1,123 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
import { gridExpandedSortedRowEntriesSelector, gridExpandedSortedRowIdsSelector, gridFilteredSortedTopLevelRowEntriesSelector } from '../filter/gridFilterSelector';
import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from '../rows/gridRowsSelector';
import { getPageCount } from './gridPaginationUtils';
/**
* @category Pagination
* @ignore - do not document.
*/
export var gridPaginationSelector = function gridPaginationSelector(state) {
return state.pagination;
};
/**
* Get the pagination model
* @category Pagination
*/
export var gridPaginationModelSelector = createSelector(gridPaginationSelector, function (pagination) {
return pagination.paginationModel;
});
/**
* Get the row count
* @category Pagination
*/
export var gridPaginationRowCountSelector = createSelector(gridPaginationSelector, function (pagination) {
return pagination.rowCount;
});
/**
* Get the index of the page to render if the pagination is enabled
* @category Pagination
*/
export var gridPageSelector = createSelector(gridPaginationModelSelector, function (paginationModel) {
return paginationModel.page;
});
/**
* Get the maximum amount of rows to display on a single page if the pagination is enabled
* @category Pagination
*/
export var gridPageSizeSelector = createSelector(gridPaginationModelSelector, function (paginationModel) {
return paginationModel.pageSize;
});
/**
* Get the amount of pages needed to display all the rows if the pagination is enabled
* @category Pagination
*/
export var gridPageCountSelector = createSelector(gridPageSizeSelector, gridPaginationRowCountSelector, function (pageSize, rowCount) {
return getPageCount(rowCount, pageSize);
});
/**
* Get the index of the first and the last row to include in the current page if the pagination is enabled.
* @category Pagination
*/
export var gridPaginationRowRangeSelector = createSelectorMemoized(gridPaginationModelSelector, gridRowTreeSelector, gridRowMaximumTreeDepthSelector, gridExpandedSortedRowEntriesSelector, gridFilteredSortedTopLevelRowEntriesSelector, function (paginationModel, rowTree, rowTreeDepth, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) {
var visibleTopLevelRowCount = visibleSortedTopLevelRowEntries.length;
var topLevelFirstRowIndex = Math.min(paginationModel.pageSize * paginationModel.page, visibleTopLevelRowCount - 1);
var topLevelLastRowIndex = Math.min(topLevelFirstRowIndex + paginationModel.pageSize - 1, visibleTopLevelRowCount - 1);
// The range contains no element
if (topLevelFirstRowIndex === -1 || topLevelLastRowIndex === -1) {
return null;
}
// The tree is flat, there is no need to look for children
if (rowTreeDepth < 2) {
return {
firstRowIndex: topLevelFirstRowIndex,
lastRowIndex: topLevelLastRowIndex
};
}
var topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex];
var topLevelRowsInCurrentPageCount = topLevelLastRowIndex - topLevelFirstRowIndex + 1;
var firstRowIndex = visibleSortedRowEntries.findIndex(function (row) {
return row.id === topLevelFirstRow.id;
});
var lastRowIndex = firstRowIndex;
var topLevelRowAdded = 0;
while (lastRowIndex < visibleSortedRowEntries.length && topLevelRowAdded <= topLevelRowsInCurrentPageCount) {
var _rowTree$row$id;
var row = visibleSortedRowEntries[lastRowIndex];
var depth = (_rowTree$row$id = rowTree[row.id]) == null ? void 0 : _rowTree$row$id.depth;
if (depth === undefined) {
lastRowIndex += 1;
} else {
if (topLevelRowAdded < topLevelRowsInCurrentPageCount || depth > 0) {
lastRowIndex += 1;
}
if (depth === 0) {
topLevelRowAdded += 1;
}
}
}
return {
firstRowIndex: firstRowIndex,
lastRowIndex: lastRowIndex - 1
};
});
/**
* Get the id and the model of each row to include in the current page if the pagination is enabled.
* @category Pagination
*/
export var gridPaginatedVisibleSortedGridRowEntriesSelector = createSelectorMemoized(gridExpandedSortedRowEntriesSelector, gridPaginationRowRangeSelector, function (visibleSortedRowEntries, paginationRange) {
if (!paginationRange) {
return [];
}
return visibleSortedRowEntries.slice(paginationRange.firstRowIndex, paginationRange.lastRowIndex + 1);
});
/**
* Get the id of each row to include in the current page if the pagination is enabled.
* @category Pagination
*/
export var gridPaginatedVisibleSortedGridRowIdsSelector = createSelectorMemoized(gridExpandedSortedRowIdsSelector, gridPaginationRowRangeSelector, function (visibleSortedRowIds, paginationRange) {
if (!paginationRange) {
return [];
}
return visibleSortedRowIds.slice(paginationRange.firstRowIndex, paginationRange.lastRowIndex + 1);
});

View File

@@ -0,0 +1,31 @@
import { buildWarning } from '../../../utils/warning';
import { GridSignature } from '../../utils';
var MAX_PAGE_SIZE = 100;
export var defaultPageSize = function defaultPageSize(autoPageSize) {
return autoPageSize ? 0 : 100;
};
export var getPageCount = function getPageCount(rowCount, pageSize) {
if (pageSize > 0 && rowCount > 0) {
return Math.ceil(rowCount / pageSize);
}
return 0;
};
export var noRowCountInServerMode = buildWarning(["MUI: the 'rowCount' prop is undefined while using paginationMode='server'", 'For more detail, see http://mui.com/components/data-grid/pagination/#basic-implementation'], 'error');
export var getDefaultGridPaginationModel = function getDefaultGridPaginationModel(autoPageSize) {
return {
page: 0,
pageSize: autoPageSize ? 0 : 100
};
};
export var getValidPage = function getValidPage(page) {
var pageCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
if (pageCount === 0) {
return page;
}
return Math.max(Math.min(page, pageCount - 1), 0);
};
export var throwIfPageSizeExceedsTheLimit = function throwIfPageSizeExceedsTheLimit(pageSize, signatureProp) {
if (signatureProp === GridSignature.DataGrid && pageSize > MAX_PAGE_SIZE) {
throw new Error(['MUI: `pageSize` cannot exceed 100 in the MIT version of the DataGrid.', 'You need to upgrade to DataGridPro or DataGridPremium component to unlock this feature.'].join('\n'));
}
};

View File

@@ -0,0 +1,2 @@
export * from './gridPaginationSelector';
export {};

View File

@@ -0,0 +1,25 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import { throwIfPageSizeExceedsTheLimit, getDefaultGridPaginationModel } from './gridPaginationUtils';
import { useGridPaginationModel } from './useGridPaginationModel';
import { useGridRowCount } from './useGridRowCount';
export var paginationStateInitializer = function paginationStateInitializer(state, props) {
var _props$paginationMode, _props$initialState, _ref, _props$rowCount, _props$initialState2;
var paginationModel = _extends({}, getDefaultGridPaginationModel(props.autoPageSize), (_props$paginationMode = props.paginationModel) != null ? _props$paginationMode : (_props$initialState = props.initialState) == null || (_props$initialState = _props$initialState.pagination) == null ? void 0 : _props$initialState.paginationModel);
throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, props.signature);
var rowCount = (_ref = (_props$rowCount = props.rowCount) != null ? _props$rowCount : (_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.pagination) == null ? void 0 : _props$initialState2.rowCount) != null ? _ref : 0;
return _extends({}, state, {
pagination: {
paginationModel: paginationModel,
rowCount: rowCount
}
});
};
/**
* @requires useGridFilter (state)
* @requires useGridDimensions (event) - can be after
*/
export var useGridPagination = function useGridPagination(apiRef, props) {
useGridPaginationModel(apiRef, props);
useGridRowCount(apiRef, props);
};

View File

@@ -0,0 +1,182 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { gridDensityFactorSelector } from '../density';
import { calculatePinnedRowsHeight } from '../rows/gridRowsUtils';
import { useGridLogger, useGridSelector, useGridApiMethod, useGridApiEventHandler } from '../../utils';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { gridPageCountSelector, gridPaginationModelSelector } from './gridPaginationSelector';
import { getPageCount, defaultPageSize, throwIfPageSizeExceedsTheLimit, getDefaultGridPaginationModel, getValidPage } from './gridPaginationUtils';
export var getDerivedPaginationModel = function getDerivedPaginationModel(paginationState, signature, paginationModelProp) {
var _paginationModelProp$;
var paginationModel = paginationState.paginationModel;
var rowCount = paginationState.rowCount;
var pageSize = (_paginationModelProp$ = paginationModelProp == null ? void 0 : paginationModelProp.pageSize) != null ? _paginationModelProp$ : paginationModel.pageSize;
var pageCount = getPageCount(rowCount, pageSize);
if (paginationModelProp && ((paginationModelProp == null ? void 0 : paginationModelProp.page) !== paginationModel.page || (paginationModelProp == null ? void 0 : paginationModelProp.pageSize) !== paginationModel.pageSize)) {
paginationModel = paginationModelProp;
}
var validPage = getValidPage(paginationModel.page, pageCount);
if (validPage !== paginationModel.page) {
paginationModel = _extends({}, paginationModel, {
page: validPage
});
}
throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, signature);
return paginationModel;
};
/**
* @requires useGridFilter (state)
* @requires useGridDimensions (event) - can be after
*/
export var useGridPaginationModel = function useGridPaginationModel(apiRef, props) {
var _props$initialState2;
var logger = useGridLogger(apiRef, 'useGridPaginationModel');
var densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
var rowHeight = Math.floor(props.rowHeight * densityFactor);
apiRef.current.registerControlState({
stateId: 'paginationModel',
propModel: props.paginationModel,
propOnChange: props.onPaginationModelChange,
stateSelector: gridPaginationModelSelector,
changeEvent: 'paginationModelChange'
});
/**
* API METHODS
*/
var setPage = React.useCallback(function (page) {
var currentModel = gridPaginationModelSelector(apiRef);
if (page === currentModel.page) {
return;
}
logger.debug("Setting page to ".concat(page));
apiRef.current.setPaginationModel({
page: page,
pageSize: currentModel.pageSize
});
}, [apiRef, logger]);
var setPageSize = React.useCallback(function (pageSize) {
var currentModel = gridPaginationModelSelector(apiRef);
if (pageSize === currentModel.pageSize) {
return;
}
logger.debug("Setting page size to ".concat(pageSize));
apiRef.current.setPaginationModel({
pageSize: pageSize,
page: currentModel.page
});
}, [apiRef, logger]);
var setPaginationModel = React.useCallback(function (paginationModel) {
var currentModel = gridPaginationModelSelector(apiRef);
if (paginationModel === currentModel) {
return;
}
logger.debug("Setting 'paginationModel' to", paginationModel);
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
paginationModel: getDerivedPaginationModel(state.pagination, props.signature, paginationModel)
})
});
});
}, [apiRef, logger, props.signature]);
var paginationModelApi = {
setPage: setPage,
setPageSize: setPageSize,
setPaginationModel: setPaginationModel
};
useGridApiMethod(apiRef, paginationModelApi, 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState;
var paginationModel = gridPaginationModelSelector(apiRef);
var shouldExportPaginationModel =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the `paginationModel` is controlled
props.paginationModel != null ||
// Always export if the `paginationModel` has been initialized
((_props$initialState = props.initialState) == null || (_props$initialState = _props$initialState.pagination) == null ? void 0 : _props$initialState.paginationModel) != null ||
// Export if `page` or `pageSize` is not equal to the default value
paginationModel.page !== 0 && paginationModel.pageSize !== defaultPageSize(props.autoPageSize);
if (!shouldExportPaginationModel) {
return prevState;
}
return _extends({}, prevState, {
pagination: _extends({}, prevState.pagination, {
paginationModel: paginationModel
})
});
}, [apiRef, props.paginationModel, (_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.pagination) == null ? void 0 : _props$initialState2.paginationModel, props.autoPageSize]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto, _context$stateToResto2;
var paginationModel = (_context$stateToResto = context.stateToRestore.pagination) != null && _context$stateToResto.paginationModel ? _extends({}, getDefaultGridPaginationModel(props.autoPageSize), (_context$stateToResto2 = context.stateToRestore.pagination) == null ? void 0 : _context$stateToResto2.paginationModel) : gridPaginationModelSelector(apiRef);
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
paginationModel: getDerivedPaginationModel(state.pagination, props.signature, paginationModel)
})
});
});
return params;
}, [apiRef, props.autoPageSize, props.signature]);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
/**
* EVENTS
*/
var handlePaginationModelChange = function handlePaginationModelChange() {
var _apiRef$current$virtu;
var paginationModel = gridPaginationModelSelector(apiRef);
if ((_apiRef$current$virtu = apiRef.current.virtualScrollerRef) != null && _apiRef$current$virtu.current) {
apiRef.current.scrollToIndexes({
rowIndex: paginationModel.page * paginationModel.pageSize
});
}
};
var handleUpdateAutoPageSize = React.useCallback(function () {
if (!props.autoPageSize) {
return;
}
var dimensions = apiRef.current.getRootDimensions() || {
viewportInnerSize: {
height: 0
}
};
var pinnedRowsHeight = calculatePinnedRowsHeight(apiRef);
var maximumPageSizeWithoutScrollBar = Math.floor((dimensions.viewportInnerSize.height - pinnedRowsHeight.top - pinnedRowsHeight.bottom) / rowHeight);
apiRef.current.setPageSize(maximumPageSizeWithoutScrollBar);
}, [apiRef, props.autoPageSize, rowHeight]);
var handleRowCountChange = React.useCallback(function (newRowCount) {
if (newRowCount == null) {
return;
}
var paginationModel = gridPaginationModelSelector(apiRef);
var pageCount = gridPageCountSelector(apiRef);
if (paginationModel.page > pageCount - 1) {
apiRef.current.setPage(Math.max(0, pageCount - 1));
}
}, [apiRef]);
useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleUpdateAutoPageSize);
useGridApiEventHandler(apiRef, 'paginationModelChange', handlePaginationModelChange);
useGridApiEventHandler(apiRef, 'rowCountChange', handleRowCountChange);
/**
* EFFECTS
*/
React.useEffect(function () {
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
paginationModel: getDerivedPaginationModel(state.pagination, props.signature, props.paginationModel)
})
});
});
}, [apiRef, props.paginationModel, props.paginationMode, props.signature]);
React.useEffect(handleUpdateAutoPageSize, [handleUpdateAutoPageSize]);
};

View File

@@ -0,0 +1,101 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { gridFilteredTopLevelRowCountSelector } from '../filter';
import { useGridLogger, useGridSelector, useGridApiMethod } from '../../utils';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { gridPaginationRowCountSelector } from './gridPaginationSelector';
import { noRowCountInServerMode } from './gridPaginationUtils';
/**
* @requires useGridFilter (state)
* @requires useGridDimensions (event) - can be after
*/
export var useGridRowCount = function useGridRowCount(apiRef, props) {
var _props$initialState2;
var logger = useGridLogger(apiRef, 'useGridRowCount');
var visibleTopLevelRowCount = useGridSelector(apiRef, gridFilteredTopLevelRowCountSelector);
var rowCount = useGridSelector(apiRef, gridPaginationRowCountSelector);
apiRef.current.registerControlState({
stateId: 'paginationRowCount',
propModel: props.rowCount,
propOnChange: props.onRowCountChange,
stateSelector: gridPaginationRowCountSelector,
changeEvent: 'rowCountChange'
});
/**
* API METHODS
*/
var setRowCount = React.useCallback(function (newRowCount) {
if (rowCount === newRowCount) {
return;
}
logger.debug("Setting 'rowCount' to", newRowCount);
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
rowCount: newRowCount
})
});
});
}, [apiRef, logger, rowCount]);
var paginationRowCountApi = {
setRowCount: setRowCount
};
useGridApiMethod(apiRef, paginationRowCountApi, 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState;
var exportedRowCount = gridPaginationRowCountSelector(apiRef);
var shouldExportRowCount =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the `rowCount` is controlled
props.rowCount != null ||
// Always export if the `rowCount` has been initialized
((_props$initialState = props.initialState) == null || (_props$initialState = _props$initialState.pagination) == null ? void 0 : _props$initialState.rowCount) != null;
if (!shouldExportRowCount) {
return prevState;
}
return _extends({}, prevState, {
pagination: _extends({}, prevState.pagination, {
rowCount: exportedRowCount
})
});
}, [apiRef, props.rowCount, (_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.pagination) == null ? void 0 : _props$initialState2.rowCount]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto;
var restoredRowCount = (_context$stateToResto = context.stateToRestore.pagination) != null && _context$stateToResto.rowCount ? context.stateToRestore.pagination.rowCount : gridPaginationRowCountSelector(apiRef);
apiRef.current.setState(function (state) {
return _extends({}, state, {
pagination: _extends({}, state.pagination, {
rowCount: restoredRowCount
})
});
});
return params;
}, [apiRef]);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
/**
* EFFECTS
*/
React.useEffect(function () {
if (process.env.NODE_ENV !== 'production') {
if (props.paginationMode === 'server' && props.rowCount == null) {
noRowCountInServerMode();
}
}
}, [props.rowCount, props.paginationMode]);
React.useEffect(function () {
if (props.paginationMode === 'client') {
apiRef.current.setRowCount(visibleTopLevelRowCount);
} else if (props.rowCount != null) {
apiRef.current.setRowCount(props.rowCount);
}
}, [apiRef, visibleTopLevelRowCount, props.paginationMode, props.rowCount]);
};

View File

@@ -0,0 +1,3 @@
export var gridPreferencePanelStateSelector = function gridPreferencePanelStateSelector(state) {
return state.preferencePanel;
};

View File

@@ -0,0 +1,6 @@
var GridPreferencePanelsValue = /*#__PURE__*/function (GridPreferencePanelsValue) {
GridPreferencePanelsValue["filters"] = "filters";
GridPreferencePanelsValue["columns"] = "columns";
return GridPreferencePanelsValue;
}(GridPreferencePanelsValue || {});
export { GridPreferencePanelsValue };

View File

@@ -0,0 +1,3 @@
export * from './gridPreferencePanelSelector';
export * from './gridPreferencePanelState';
export * from './gridPreferencePanelsValue';

View File

@@ -0,0 +1,125 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { gridPreferencePanelStateSelector } from './gridPreferencePanelSelector';
export var preferencePanelStateInitializer = function preferencePanelStateInitializer(state, props) {
var _props$initialState$p, _props$initialState;
return _extends({}, state, {
preferencePanel: (_props$initialState$p = (_props$initialState = props.initialState) == null ? void 0 : _props$initialState.preferencePanel) != null ? _props$initialState$p : {
open: false
}
});
};
/**
* TODO: Add a single `setPreferencePanel` method to avoid multiple `setState`
*/
export var useGridPreferencesPanel = function useGridPreferencesPanel(apiRef, props) {
var _props$initialState3;
var logger = useGridLogger(apiRef, 'useGridPreferencesPanel');
var hideTimeout = React.useRef();
var immediateTimeout = React.useRef();
/**
* API METHODS
*/
var hidePreferences = React.useCallback(function () {
logger.debug('Hiding Preferences Panel');
var preferencePanelState = gridPreferencePanelStateSelector(apiRef.current.state);
if (preferencePanelState.openedPanelValue) {
apiRef.current.publishEvent('preferencePanelClose', {
openedPanelValue: preferencePanelState.openedPanelValue
});
}
apiRef.current.setState(function (state) {
return _extends({}, state, {
preferencePanel: {
open: false
}
});
});
apiRef.current.forceUpdate();
}, [apiRef, logger]);
// This is to prevent the preferences from closing when you open a select box or another panel,
// The issue is in MUI core V4 => Fixed in V5
var doNotHidePanel = React.useCallback(function () {
immediateTimeout.current = setTimeout(function () {
return clearTimeout(hideTimeout.current);
}, 0);
}, []);
// This is a hack for the issue with Core V4, by delaying hiding the panel on the clickAwayListener,
// we can cancel the action if the trigger element still need the panel...
var hidePreferencesDelayed = React.useCallback(function () {
hideTimeout.current = setTimeout(hidePreferences, 100);
}, [hidePreferences]);
var showPreferences = React.useCallback(function (newValue, panelId, labelId) {
logger.debug('Opening Preferences Panel');
doNotHidePanel();
apiRef.current.setState(function (state) {
return _extends({}, state, {
preferencePanel: _extends({}, state.preferencePanel, {
open: true,
openedPanelValue: newValue,
panelId: panelId,
labelId: labelId
})
});
});
apiRef.current.publishEvent('preferencePanelOpen', {
openedPanelValue: newValue
});
apiRef.current.forceUpdate();
}, [logger, doNotHidePanel, apiRef]);
useGridApiMethod(apiRef, {
showPreferences: showPreferences,
hidePreferences: hidePreferencesDelayed
}, 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState2;
var preferencePanelToExport = gridPreferencePanelStateSelector(apiRef.current.state);
var shouldExportPreferencePanel =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the panel was initialized
((_props$initialState2 = props.initialState) == null ? void 0 : _props$initialState2.preferencePanel) != null ||
// Always export if the panel is opened
preferencePanelToExport.open;
if (!shouldExportPreferencePanel) {
return prevState;
}
return _extends({}, prevState, {
preferencePanel: preferencePanelToExport
});
}, [apiRef, (_props$initialState3 = props.initialState) == null ? void 0 : _props$initialState3.preferencePanel]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var preferencePanel = context.stateToRestore.preferencePanel;
if (preferencePanel != null) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
preferencePanel: preferencePanel
});
});
}
return params;
}, [apiRef]);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
/**
* EFFECTS
*/
React.useEffect(function () {
return function () {
clearTimeout(hideTimeout.current);
clearTimeout(immediateTimeout.current);
};
}, []);
};

View File

@@ -0,0 +1,19 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
import { gridRowsLookupSelector } from '../rows/gridRowsSelector';
export var gridRowSelectionStateSelector = function gridRowSelectionStateSelector(state) {
return state.rowSelection;
};
export var selectedGridRowsCountSelector = createSelector(gridRowSelectionStateSelector, function (selection) {
return selection.length;
});
export var selectedGridRowsSelector = createSelectorMemoized(gridRowSelectionStateSelector, gridRowsLookupSelector, function (selectedRows, rowsLookup) {
return new Map(selectedRows.map(function (id) {
return [id, rowsLookup[id]];
}));
});
export var selectedIdsLookupSelector = createSelectorMemoized(gridRowSelectionStateSelector, function (selection) {
return selection.reduce(function (lookup, rowId) {
lookup[rowId] = rowId;
return lookup;
}, {});
});

View File

@@ -0,0 +1 @@
export * from './gridRowSelectionSelector';

View File

@@ -0,0 +1,420 @@
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { GridSignature, useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridRowsLookupSelector } from '../rows/gridRowsSelector';
import { gridRowSelectionStateSelector, selectedGridRowsSelector, selectedIdsLookupSelector } from './gridRowSelectionSelector';
import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination';
import { gridFocusCellSelector } from '../focus/gridFocusStateSelector';
import { gridExpandedSortedRowIdsSelector, gridFilterModelSelector } from '../filter/gridFilterSelector';
import { GRID_CHECKBOX_SELECTION_COL_DEF, GRID_ACTIONS_COLUMN_TYPE } from '../../../colDef';
import { GridCellModes } from '../../../models/gridEditRowModel';
import { isKeyboardEvent, isNavigationKey } from '../../../utils/keyboardUtils';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from '../../../constants/gridDetailPanelToggleField';
import { gridClasses } from '../../../constants/gridClasses';
import { isEventTargetInPortal } from '../../../utils/domUtils';
var getSelectionModelPropValue = function getSelectionModelPropValue(selectionModelProp, prevSelectionModel) {
if (selectionModelProp == null) {
return selectionModelProp;
}
if (Array.isArray(selectionModelProp)) {
return selectionModelProp;
}
if (prevSelectionModel && prevSelectionModel[0] === selectionModelProp) {
return prevSelectionModel;
}
return [selectionModelProp];
};
export var rowSelectionStateInitializer = function rowSelectionStateInitializer(state, props) {
var _getSelectionModelPro;
return _extends({}, state, {
rowSelection: props.rowSelection ? (_getSelectionModelPro = getSelectionModelPropValue(props.rowSelectionModel)) != null ? _getSelectionModelPro : [] : []
});
};
/**
* @requires useGridRows (state, method) - can be after
* @requires useGridParamsApi (method) - can be after
* @requires useGridFocus (state) - can be after
* @requires useGridKeyboardNavigation (`cellKeyDown` event must first be consumed by it)
*/
export var useGridRowSelection = function useGridRowSelection(apiRef, props) {
var logger = useGridLogger(apiRef, 'useGridSelection');
var runIfRowSelectionIsEnabled = function runIfRowSelectionIsEnabled(callback) {
return function () {
if (props.rowSelection) {
callback.apply(void 0, arguments);
}
};
};
var propRowSelectionModel = React.useMemo(function () {
return getSelectionModelPropValue(props.rowSelectionModel, gridRowSelectionStateSelector(apiRef.current.state));
}, [apiRef, props.rowSelectionModel]);
var lastRowToggled = React.useRef(null);
apiRef.current.registerControlState({
stateId: 'rowSelection',
propModel: propRowSelectionModel,
propOnChange: props.onRowSelectionModelChange,
stateSelector: gridRowSelectionStateSelector,
changeEvent: 'rowSelectionChange'
});
var checkboxSelection = props.checkboxSelection,
disableMultipleRowSelection = props.disableMultipleRowSelection,
disableRowSelectionOnClick = props.disableRowSelectionOnClick,
propIsRowSelectable = props.isRowSelectable;
var canHaveMultipleSelection = !disableMultipleRowSelection || checkboxSelection;
var visibleRows = useGridVisibleRows(apiRef, props);
var expandMouseRowRangeSelection = React.useCallback(function (id) {
var _lastRowToggled$curre;
var endId = id;
var startId = (_lastRowToggled$curre = lastRowToggled.current) != null ? _lastRowToggled$curre : id;
var isSelected = apiRef.current.isRowSelected(id);
if (isSelected) {
var visibleRowIds = gridExpandedSortedRowIdsSelector(apiRef);
var startIndex = visibleRowIds.findIndex(function (rowId) {
return rowId === startId;
});
var endIndex = visibleRowIds.findIndex(function (rowId) {
return rowId === endId;
});
if (startIndex === endIndex) {
return;
}
if (startIndex > endIndex) {
endId = visibleRowIds[endIndex + 1];
} else {
endId = visibleRowIds[endIndex - 1];
}
}
lastRowToggled.current = id;
apiRef.current.selectRowRange({
startId: startId,
endId: endId
}, !isSelected);
}, [apiRef]);
/**
* API METHODS
*/
var setRowSelectionModel = React.useCallback(function (model) {
if (props.signature === GridSignature.DataGrid && !props.checkboxSelection && Array.isArray(model) && model.length > 1) {
throw new Error(['MUI: `rowSelectionModel` can only contain 1 item in DataGrid.', 'You need to upgrade to DataGridPro or DataGridPremium component to unlock multiple selection.'].join('\n'));
}
var currentModel = gridRowSelectionStateSelector(apiRef.current.state);
if (currentModel !== model) {
logger.debug("Setting selection model");
apiRef.current.setState(function (state) {
return _extends({}, state, {
rowSelection: props.rowSelection ? model : []
});
});
apiRef.current.forceUpdate();
}
}, [apiRef, logger, props.rowSelection, props.signature, props.checkboxSelection]);
var isRowSelected = React.useCallback(function (id) {
return gridRowSelectionStateSelector(apiRef.current.state).includes(id);
}, [apiRef]);
var isRowSelectable = React.useCallback(function (id) {
if (propIsRowSelectable && !propIsRowSelectable(apiRef.current.getRowParams(id))) {
return false;
}
var rowNode = apiRef.current.getRowNode(id);
if ((rowNode == null ? void 0 : rowNode.type) === 'footer' || (rowNode == null ? void 0 : rowNode.type) === 'pinnedRow') {
return false;
}
return true;
}, [apiRef, propIsRowSelectable]);
var getSelectedRows = React.useCallback(function () {
return selectedGridRowsSelector(apiRef);
}, [apiRef]);
var selectRow = React.useCallback(function (id) {
var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (!apiRef.current.isRowSelectable(id)) {
return;
}
lastRowToggled.current = id;
if (resetSelection) {
logger.debug("Setting selection for row ".concat(id));
apiRef.current.setRowSelectionModel(isSelected ? [id] : []);
} else {
logger.debug("Toggling selection for row ".concat(id));
var selection = gridRowSelectionStateSelector(apiRef.current.state);
var newSelection = selection.filter(function (el) {
return el !== id;
});
if (isSelected) {
newSelection.push(id);
}
var isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
if (isSelectionValid) {
apiRef.current.setRowSelectionModel(newSelection);
}
}
}, [apiRef, logger, canHaveMultipleSelection]);
var selectRows = React.useCallback(function (ids) {
var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
logger.debug("Setting selection for several rows");
var selectableIds = ids.filter(function (id) {
return apiRef.current.isRowSelectable(id);
});
var newSelection;
if (resetSelection) {
newSelection = isSelected ? selectableIds : [];
} else {
// We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
var selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
selectableIds.forEach(function (id) {
if (isSelected) {
selectionLookup[id] = id;
} else {
delete selectionLookup[id];
}
});
newSelection = Object.values(selectionLookup);
}
var isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
if (isSelectionValid) {
apiRef.current.setRowSelectionModel(newSelection);
}
}, [apiRef, logger, canHaveMultipleSelection]);
var selectRowRange = React.useCallback(function (_ref) {
var startId = _ref.startId,
endId = _ref.endId;
var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (!apiRef.current.getRow(startId) || !apiRef.current.getRow(endId)) {
return;
}
logger.debug("Expanding selection from row ".concat(startId, " to row ").concat(endId));
// Using rows from all pages allow to select a range across several pages
var allPagesRowIds = gridExpandedSortedRowIdsSelector(apiRef);
var startIndex = allPagesRowIds.indexOf(startId);
var endIndex = allPagesRowIds.indexOf(endId);
var _ref2 = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex],
_ref3 = _slicedToArray(_ref2, 2),
start = _ref3[0],
end = _ref3[1];
var rowsBetweenStartAndEnd = allPagesRowIds.slice(start, end + 1);
apiRef.current.selectRows(rowsBetweenStartAndEnd, isSelected, resetSelection);
}, [apiRef, logger]);
var selectionPublicApi = {
selectRow: selectRow,
setRowSelectionModel: setRowSelectionModel,
getSelectedRows: getSelectedRows,
isRowSelected: isRowSelected,
isRowSelectable: isRowSelectable
};
var selectionPrivateApi = {
selectRows: selectRows,
selectRowRange: selectRowRange
};
useGridApiMethod(apiRef, selectionPublicApi, 'public');
useGridApiMethod(apiRef, selectionPrivateApi, props.signature === GridSignature.DataGrid ? 'private' : 'public');
/**
* EVENTS
*/
var removeOutdatedSelection = React.useCallback(function () {
if (props.keepNonExistentRowsSelected) {
return;
}
var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
var rowsLookup = gridRowsLookupSelector(apiRef);
// We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
var selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
var hasChanged = false;
currentSelection.forEach(function (id) {
if (!rowsLookup[id]) {
delete selectionLookup[id];
hasChanged = true;
}
});
if (hasChanged) {
apiRef.current.setRowSelectionModel(Object.values(selectionLookup));
}
}, [apiRef, props.keepNonExistentRowsSelected]);
var handleSingleRowSelection = React.useCallback(function (id, event) {
var hasCtrlKey = event.metaKey || event.ctrlKey;
// multiple selection is only allowed if:
// - it is a checkboxSelection
// - it is a keyboard selection
// - Ctrl is pressed
var isMultipleSelectionDisabled = !checkboxSelection && !hasCtrlKey && !isKeyboardEvent(event);
var resetSelection = !canHaveMultipleSelection || isMultipleSelectionDisabled;
var isSelected = apiRef.current.isRowSelected(id);
if (resetSelection) {
apiRef.current.selectRow(id, !isMultipleSelectionDisabled ? !isSelected : true, true);
} else {
apiRef.current.selectRow(id, !isSelected, false);
}
}, [apiRef, canHaveMultipleSelection, checkboxSelection]);
var handleRowClick = React.useCallback(function (params, event) {
var _closest;
if (disableRowSelectionOnClick) {
return;
}
var field = (_closest = event.target.closest(".".concat(gridClasses.cell))) == null ? void 0 : _closest.getAttribute('data-field');
if (field === GRID_CHECKBOX_SELECTION_COL_DEF.field) {
// click on checkbox should not trigger row selection
return;
}
if (field === GRID_DETAIL_PANEL_TOGGLE_FIELD) {
// click to open the detail panel should not select the row
return;
}
if (field) {
var column = apiRef.current.getColumn(field);
if ((column == null ? void 0 : column.type) === GRID_ACTIONS_COLUMN_TYPE) {
return;
}
}
var rowNode = apiRef.current.getRowNode(params.id);
if (rowNode.type === 'pinnedRow') {
return;
}
if (event.shiftKey && (canHaveMultipleSelection || checkboxSelection)) {
expandMouseRowRangeSelection(params.id);
} else {
handleSingleRowSelection(params.id, event);
}
}, [disableRowSelectionOnClick, canHaveMultipleSelection, checkboxSelection, apiRef, expandMouseRowRangeSelection, handleSingleRowSelection]);
var preventSelectionOnShift = React.useCallback(function (params, event) {
if (canHaveMultipleSelection && event.shiftKey) {
var _window$getSelection;
(_window$getSelection = window.getSelection()) == null || _window$getSelection.removeAllRanges();
}
}, [canHaveMultipleSelection]);
var handleRowSelectionCheckboxChange = React.useCallback(function (params, event) {
if (event.nativeEvent.shiftKey) {
expandMouseRowRangeSelection(params.id);
} else {
apiRef.current.selectRow(params.id, params.value);
}
}, [apiRef, expandMouseRowRangeSelection]);
var handleHeaderSelectionCheckboxChange = React.useCallback(function (params) {
var shouldLimitSelectionToCurrentPage = props.checkboxSelectionVisibleOnly && props.pagination;
var rowsToBeSelected = shouldLimitSelectionToCurrentPage ? gridPaginatedVisibleSortedGridRowIdsSelector(apiRef) : gridExpandedSortedRowIdsSelector(apiRef);
var filterModel = gridFilterModelSelector(apiRef);
apiRef.current.selectRows(rowsToBeSelected, params.value, (filterModel == null ? void 0 : filterModel.items.length) > 0);
}, [apiRef, props.checkboxSelectionVisibleOnly, props.pagination]);
var handleCellKeyDown = React.useCallback(function (params, event) {
// Get the most recent cell mode because it may have been changed by another listener
if (apiRef.current.getCellMode(params.id, params.field) === GridCellModes.Edit) {
return;
}
// Ignore portal
// Do not apply shortcuts if the focus is not on the cell root component
if (isEventTargetInPortal(event)) {
return;
}
if (isNavigationKey(event.key) && event.shiftKey) {
// The cell that has focus after the keyboard navigation
var focusCell = gridFocusCellSelector(apiRef);
if (focusCell && focusCell.id !== params.id) {
event.preventDefault();
var isNextRowSelected = apiRef.current.isRowSelected(focusCell.id);
if (!canHaveMultipleSelection) {
apiRef.current.selectRow(focusCell.id, !isNextRowSelected, true);
return;
}
var newRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(focusCell.id);
var previousRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(params.id);
var start;
var end;
if (newRowIndex > previousRowIndex) {
if (isNextRowSelected) {
// We are navigating to the bottom of the page and adding selected rows
start = previousRowIndex;
end = newRowIndex - 1;
} else {
// We are navigating to the bottom of the page and removing selected rows
start = previousRowIndex;
end = newRowIndex;
}
} else {
// eslint-disable-next-line no-lonely-if
if (isNextRowSelected) {
// We are navigating to the top of the page and removing selected rows
start = newRowIndex + 1;
end = previousRowIndex;
} else {
// We are navigating to the top of the page and adding selected rows
start = newRowIndex;
end = previousRowIndex;
}
}
var rowsBetweenStartAndEnd = visibleRows.rows.slice(start, end + 1).map(function (row) {
return row.id;
});
apiRef.current.selectRows(rowsBetweenStartAndEnd, !isNextRowSelected);
return;
}
}
if (event.key === ' ' && event.shiftKey) {
event.preventDefault();
handleSingleRowSelection(params.id, event);
return;
}
if (event.key === 'a' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
selectRows(apiRef.current.getAllRowIds(), true);
}
}, [apiRef, handleSingleRowSelection, selectRows, visibleRows.rows, canHaveMultipleSelection]);
useGridApiEventHandler(apiRef, 'sortedRowsSet', runIfRowSelectionIsEnabled(removeOutdatedSelection));
useGridApiEventHandler(apiRef, 'rowClick', runIfRowSelectionIsEnabled(handleRowClick));
useGridApiEventHandler(apiRef, 'rowSelectionCheckboxChange', runIfRowSelectionIsEnabled(handleRowSelectionCheckboxChange));
useGridApiEventHandler(apiRef, 'headerSelectionCheckboxChange', handleHeaderSelectionCheckboxChange);
useGridApiEventHandler(apiRef, 'cellMouseDown', runIfRowSelectionIsEnabled(preventSelectionOnShift));
useGridApiEventHandler(apiRef, 'cellKeyDown', runIfRowSelectionIsEnabled(handleCellKeyDown));
/**
* EFFECTS
*/
React.useEffect(function () {
if (propRowSelectionModel !== undefined) {
apiRef.current.setRowSelectionModel(propRowSelectionModel);
}
}, [apiRef, propRowSelectionModel, props.rowSelection]);
React.useEffect(function () {
if (!props.rowSelection) {
apiRef.current.setRowSelectionModel([]);
}
}, [apiRef, props.rowSelection]);
var isStateControlled = propRowSelectionModel != null;
React.useEffect(function () {
if (isStateControlled || !props.rowSelection) {
return;
}
// props.isRowSelectable changed
var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
if (isRowSelectable) {
var newSelection = currentSelection.filter(function (id) {
return isRowSelectable(id);
});
if (newSelection.length < currentSelection.length) {
apiRef.current.setRowSelectionModel(newSelection);
}
}
}, [apiRef, isRowSelectable, isStateControlled, props.rowSelection]);
React.useEffect(function () {
if (!props.rowSelection || isStateControlled) {
return;
}
var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
if (!canHaveMultipleSelection && currentSelection.length > 1) {
// See https://github.com/mui/mui-x/issues/8455
apiRef.current.setRowSelectionModel([]);
}
}, [apiRef, canHaveMultipleSelection, checkboxSelection, isStateControlled, props.rowSelection]);
};

View File

@@ -0,0 +1,45 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_composeClasses as composeClasses } from '@mui/utils';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { getDataGridUtilityClass } from '../../../constants';
import { GRID_CHECKBOX_SELECTION_COL_DEF, GRID_CHECKBOX_SELECTION_FIELD } from '../../../colDef';
var useUtilityClasses = function useUtilityClasses(ownerState) {
var classes = ownerState.classes;
return React.useMemo(function () {
var slots = {
cellCheckbox: ['cellCheckbox'],
columnHeaderCheckbox: ['columnHeaderCheckbox']
};
return composeClasses(slots, getDataGridUtilityClass, classes);
}, [classes]);
};
export var useGridRowSelectionPreProcessors = function useGridRowSelectionPreProcessors(apiRef, props) {
var ownerState = {
classes: props.classes
};
var classes = useUtilityClasses(ownerState);
var updateSelectionColumn = React.useCallback(function (columnsState) {
var selectionColumn = _extends({}, GRID_CHECKBOX_SELECTION_COL_DEF, {
cellClassName: classes.cellCheckbox,
headerClassName: classes.columnHeaderCheckbox,
headerName: apiRef.current.getLocaleText('checkboxSelectionHeaderName')
});
var shouldHaveSelectionColumn = props.checkboxSelection;
var haveSelectionColumn = columnsState.lookup[GRID_CHECKBOX_SELECTION_FIELD] != null;
if (shouldHaveSelectionColumn && !haveSelectionColumn) {
columnsState.lookup[GRID_CHECKBOX_SELECTION_FIELD] = selectionColumn;
columnsState.orderedFields = [GRID_CHECKBOX_SELECTION_FIELD].concat(_toConsumableArray(columnsState.orderedFields));
} else if (!shouldHaveSelectionColumn && haveSelectionColumn) {
delete columnsState.lookup[GRID_CHECKBOX_SELECTION_FIELD];
columnsState.orderedFields = columnsState.orderedFields.filter(function (field) {
return field !== GRID_CHECKBOX_SELECTION_FIELD;
});
} else if (shouldHaveSelectionColumn && haveSelectionColumn) {
columnsState.lookup[GRID_CHECKBOX_SELECTION_FIELD] = _extends({}, selectionColumn, columnsState.lookup[GRID_CHECKBOX_SELECTION_FIELD]);
}
return columnsState;
}, [apiRef, classes, props.checkboxSelection]);
useGridRegisterPipeProcessor(apiRef, 'hydrateColumns', updateSelectionColumn);
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,3 @@
export var gridRowsMetaSelector = function gridRowsMetaSelector(state) {
return state.rowsMeta;
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,90 @@
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
var gridRowsStateSelector = function gridRowsStateSelector(state) {
return state.rows;
};
export var gridRowCountSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.totalRowCount;
});
export var gridRowsLoadingSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.loading;
});
export var gridTopLevelRowCountSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.totalTopLevelRowCount;
});
// TODO rows v6: Rename
export var gridRowsLookupSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.dataRowIdToModelLookup;
});
export var gridRowsDataRowIdToIdLookupSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.dataRowIdToIdLookup;
});
export var gridRowTreeSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.tree;
});
export var gridRowGroupingNameSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.groupingName;
});
export var gridRowTreeDepthsSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.treeDepths;
});
export var gridRowMaximumTreeDepthSelector = createSelectorMemoized(gridRowsStateSelector, function (rows) {
var entries = Object.entries(rows.treeDepths);
if (entries.length === 0) {
return 1;
}
return entries.filter(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
nodeCount = _ref2[1];
return nodeCount > 0;
}).map(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 1),
depth = _ref4[0];
return Number(depth);
}).sort(function (a, b) {
return b - a;
})[0] + 1;
});
export var gridDataRowIdsSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows.dataRowIds;
});
/**
* @ignore - do not document.
*/
export var gridAdditionalRowGroupsSelector = createSelector(gridRowsStateSelector, function (rows) {
return rows == null ? void 0 : rows.additionalRowGroups;
});
/**
* @ignore - do not document.
*/
export var gridPinnedRowsSelector = createSelectorMemoized(gridAdditionalRowGroupsSelector, function (additionalRowGroups) {
var _rawPinnedRows$bottom, _rawPinnedRows$top;
var rawPinnedRows = additionalRowGroups == null ? void 0 : additionalRowGroups.pinnedRows;
return {
bottom: rawPinnedRows == null || (_rawPinnedRows$bottom = rawPinnedRows.bottom) == null ? void 0 : _rawPinnedRows$bottom.map(function (rowEntry) {
var _rowEntry$model;
return {
id: rowEntry.id,
model: (_rowEntry$model = rowEntry.model) != null ? _rowEntry$model : {}
};
}),
top: rawPinnedRows == null || (_rawPinnedRows$top = rawPinnedRows.top) == null ? void 0 : _rawPinnedRows$top.map(function (rowEntry) {
var _rowEntry$model2;
return {
id: rowEntry.id,
model: (_rowEntry$model2 = rowEntry.model) != null ? _rowEntry$model2 : {}
};
})
};
});
/**
* @ignore - do not document.
*/
export var gridPinnedRowsCountSelector = createSelector(gridPinnedRowsSelector, function (pinnedRows) {
var _pinnedRows$top, _pinnedRows$bottom;
return ((pinnedRows == null || (_pinnedRows$top = pinnedRows.top) == null ? void 0 : _pinnedRows$top.length) || 0) + ((pinnedRows == null || (_pinnedRows$bottom = pinnedRows.bottom) == null ? void 0 : _pinnedRows$bottom.length) || 0);
});

View File

@@ -0,0 +1,294 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import { gridPinnedRowsSelector } from './gridRowsSelector';
import { gridDensityFactorSelector } from '../density/densitySelector';
export var GRID_ROOT_GROUP_ID = "auto-generated-group-node-root";
export var GRID_ID_AUTOGENERATED = Symbol('mui.id_autogenerated');
export var buildRootGroup = function buildRootGroup() {
return {
type: 'group',
id: GRID_ROOT_GROUP_ID,
depth: -1,
groupingField: null,
groupingKey: null,
isAutoGenerated: true,
children: [],
childrenFromPath: {},
childrenExpanded: true,
parent: null
};
};
/**
* A helper function to check if the id provided is valid.
* @param {GridRowId} id Id as [[GridRowId]].
* @param {GridRowModel | Partial<GridRowModel>} row Row as [[GridRowModel]].
* @param {string} detailErrorMessage A custom error message to display for invalid IDs
*/
export function checkGridRowIdIsValid(id, row) {
var detailErrorMessage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'A row was provided without id in the rows prop:';
if (id == null) {
throw new Error(['MUI: The data grid component requires all rows to have a unique `id` property.', 'Alternatively, you can use the `getRowId` prop to specify a custom id for each row.', detailErrorMessage, JSON.stringify(row)].join('\n'));
}
}
export var getRowIdFromRowModel = function getRowIdFromRowModel(rowModel, getRowId, detailErrorMessage) {
var id = getRowId ? getRowId(rowModel) : rowModel.id;
checkGridRowIdIsValid(id, rowModel, detailErrorMessage);
return id;
};
export var createRowsInternalCache = function createRowsInternalCache(_ref) {
var rows = _ref.rows,
getRowId = _ref.getRowId,
loading = _ref.loading,
rowCount = _ref.rowCount;
var updates = {
type: 'full',
rows: []
};
var dataRowIdToModelLookup = {};
var dataRowIdToIdLookup = {};
for (var i = 0; i < rows.length; i += 1) {
var model = rows[i];
var _id = getRowIdFromRowModel(model, getRowId);
dataRowIdToModelLookup[_id] = model;
dataRowIdToIdLookup[_id] = _id;
updates.rows.push(_id);
}
return {
rowsBeforePartialUpdates: rows,
loadingPropBeforePartialUpdates: loading,
rowCountPropBeforePartialUpdates: rowCount,
updates: updates,
dataRowIdToIdLookup: dataRowIdToIdLookup,
dataRowIdToModelLookup: dataRowIdToModelLookup
};
};
export var getTopLevelRowCount = function getTopLevelRowCount(_ref2) {
var tree = _ref2.tree,
_ref2$rowCountProp = _ref2.rowCountProp,
rowCountProp = _ref2$rowCountProp === void 0 ? 0 : _ref2$rowCountProp;
var rootGroupNode = tree[GRID_ROOT_GROUP_ID];
return Math.max(rowCountProp, rootGroupNode.children.length + (rootGroupNode.footerId == null ? 0 : 1));
};
export var getRowsStateFromCache = function getRowsStateFromCache(_ref3) {
var apiRef = _ref3.apiRef,
_ref3$rowCountProp = _ref3.rowCountProp,
rowCountProp = _ref3$rowCountProp === void 0 ? 0 : _ref3$rowCountProp,
loadingProp = _ref3.loadingProp,
previousTree = _ref3.previousTree,
previousTreeDepths = _ref3.previousTreeDepths;
var cache = apiRef.current.caches.rows;
// 1. Apply the "rowTreeCreation" family processing.
var _apiRef$current$apply = apiRef.current.applyStrategyProcessor('rowTreeCreation', {
previousTree: previousTree,
previousTreeDepths: previousTreeDepths,
updates: cache.updates,
dataRowIdToIdLookup: cache.dataRowIdToIdLookup,
dataRowIdToModelLookup: cache.dataRowIdToModelLookup
}),
unProcessedTree = _apiRef$current$apply.tree,
unProcessedTreeDepths = _apiRef$current$apply.treeDepths,
unProcessedDataRowIds = _apiRef$current$apply.dataRowIds,
groupingName = _apiRef$current$apply.groupingName;
// 2. Apply the "hydrateRows" pipe-processing.
var groupingParamsWithHydrateRows = apiRef.current.unstable_applyPipeProcessors('hydrateRows', {
tree: unProcessedTree,
treeDepths: unProcessedTreeDepths,
dataRowIdToIdLookup: cache.dataRowIdToIdLookup,
dataRowIds: unProcessedDataRowIds,
dataRowIdToModelLookup: cache.dataRowIdToModelLookup
});
// 3. Reset the cache updates
apiRef.current.caches.rows.updates = {
type: 'partial',
actions: {
insert: [],
modify: [],
remove: []
},
idToActionLookup: {}
};
return _extends({}, groupingParamsWithHydrateRows, {
totalRowCount: Math.max(rowCountProp, groupingParamsWithHydrateRows.dataRowIds.length),
totalTopLevelRowCount: getTopLevelRowCount({
tree: groupingParamsWithHydrateRows.tree,
rowCountProp: rowCountProp
}),
groupingName: groupingName,
loading: loadingProp
});
};
export var isAutoGeneratedRow = function isAutoGeneratedRow(rowNode) {
return rowNode.type === 'skeletonRow' || rowNode.type === 'footer' || rowNode.type === 'group' && rowNode.isAutoGenerated || rowNode.type === 'pinnedRow' && rowNode.isAutoGenerated;
};
export var getTreeNodeDescendants = function getTreeNodeDescendants(tree, parentId, skipAutoGeneratedRows) {
var node = tree[parentId];
if (node.type !== 'group') {
return [];
}
var validDescendants = [];
for (var i = 0; i < node.children.length; i += 1) {
var child = node.children[i];
if (!skipAutoGeneratedRows || !isAutoGeneratedRow(tree[child])) {
validDescendants.push(child);
}
var childDescendants = getTreeNodeDescendants(tree, child, skipAutoGeneratedRows);
for (var j = 0; j < childDescendants.length; j += 1) {
validDescendants.push(childDescendants[j]);
}
}
if (!skipAutoGeneratedRows && node.footerId != null) {
validDescendants.push(node.footerId);
}
return validDescendants;
};
export var updateCacheWithNewRows = function updateCacheWithNewRows(_ref4) {
var _previousCache$update, _previousCache$update2, _previousCache$update3;
var previousCache = _ref4.previousCache,
getRowId = _ref4.getRowId,
updates = _ref4.updates;
if (previousCache.updates.type === 'full') {
throw new Error('MUI: Unable to prepare a partial update if a full update is not applied yet');
}
// Remove duplicate updates.
// A server can batch updates, and send several updates for the same row in one fn call.
var uniqueUpdates = new Map();
updates.forEach(function (update) {
var id = getRowIdFromRowModel(update, getRowId, 'A row was provided without id when calling updateRows():');
if (uniqueUpdates.has(id)) {
uniqueUpdates.set(id, _extends({}, uniqueUpdates.get(id), update));
} else {
uniqueUpdates.set(id, update);
}
});
var partialUpdates = {
type: 'partial',
actions: {
insert: _toConsumableArray((_previousCache$update = previousCache.updates.actions.insert) != null ? _previousCache$update : []),
modify: _toConsumableArray((_previousCache$update2 = previousCache.updates.actions.modify) != null ? _previousCache$update2 : []),
remove: _toConsumableArray((_previousCache$update3 = previousCache.updates.actions.remove) != null ? _previousCache$update3 : [])
},
idToActionLookup: _extends({}, previousCache.updates.idToActionLookup)
};
var dataRowIdToModelLookup = _extends({}, previousCache.dataRowIdToModelLookup);
var dataRowIdToIdLookup = _extends({}, previousCache.dataRowIdToIdLookup);
var alreadyAppliedActionsToRemove = {
insert: {},
modify: {},
remove: {}
};
// Depending on the action already applied to the data row,
// We might want drop the already-applied-update.
// For instance:
// - if you delete then insert, then you don't want to apply the deletion in the tree.
// - if you insert, then modify, then you just want to apply the insertion in the tree.
uniqueUpdates.forEach(function (partialRow, id) {
var actionAlreadyAppliedToRow = partialUpdates.idToActionLookup[id];
// Action === "delete"
// eslint-disable-next-line no-underscore-dangle
if (partialRow._action === 'delete') {
// If the data row has been removed since the last state update,
// Then do nothing.
if (actionAlreadyAppliedToRow === 'remove' || !dataRowIdToModelLookup[id]) {
return;
}
// If the data row has been inserted / modified since the last state update,
// Then drop this "insert" / "modify" update.
if (actionAlreadyAppliedToRow != null) {
alreadyAppliedActionsToRemove[actionAlreadyAppliedToRow][id] = true;
}
// Remove the data row from the lookups and add it to the "delete" update.
partialUpdates.actions.remove.push(id);
delete dataRowIdToModelLookup[id];
delete dataRowIdToIdLookup[id];
return;
}
var oldRow = dataRowIdToModelLookup[id];
// Action === "modify"
if (oldRow) {
// If the data row has been removed since the last state update,
// Then drop this "remove" update and add it to the "modify" update instead.
if (actionAlreadyAppliedToRow === 'remove') {
alreadyAppliedActionsToRemove.remove[id] = true;
partialUpdates.actions.modify.push(id);
}
// If the date has not been inserted / modified since the last state update,
// Then add it to the "modify" update (if it has been inserted it should just remain "inserted").
else if (actionAlreadyAppliedToRow == null) {
partialUpdates.actions.modify.push(id);
}
// Update the data row lookups.
dataRowIdToModelLookup[id] = _extends({}, oldRow, partialRow);
return;
}
// Action === "insert"
// If the data row has been removed since the last state update,
// Then drop the "remove" update and add it to the "insert" update instead.
if (actionAlreadyAppliedToRow === 'remove') {
alreadyAppliedActionsToRemove.remove[id] = true;
partialUpdates.actions.insert.push(id);
}
// If the data row has not been inserted since the last state update,
// Then add it to the "insert" update.
// `actionAlreadyAppliedToRow` can't be equal to "modify", otherwise we would have an `oldRow` above.
else if (actionAlreadyAppliedToRow == null) {
partialUpdates.actions.insert.push(id);
}
// Update the data row lookups.
dataRowIdToModelLookup[id] = partialRow;
dataRowIdToIdLookup[id] = id;
});
var actionTypeWithActionsToRemove = Object.keys(alreadyAppliedActionsToRemove);
var _loop = function _loop() {
var actionType = actionTypeWithActionsToRemove[i];
var idsToRemove = alreadyAppliedActionsToRemove[actionType];
if (Object.keys(idsToRemove).length > 0) {
partialUpdates.actions[actionType] = partialUpdates.actions[actionType].filter(function (id) {
return !idsToRemove[id];
});
}
};
for (var i = 0; i < actionTypeWithActionsToRemove.length; i += 1) {
_loop();
}
return {
dataRowIdToModelLookup: dataRowIdToModelLookup,
dataRowIdToIdLookup: dataRowIdToIdLookup,
updates: partialUpdates,
rowsBeforePartialUpdates: previousCache.rowsBeforePartialUpdates,
loadingPropBeforePartialUpdates: previousCache.loadingPropBeforePartialUpdates,
rowCountPropBeforePartialUpdates: previousCache.rowCountPropBeforePartialUpdates
};
};
export function calculatePinnedRowsHeight(apiRef) {
var _pinnedRows$top, _pinnedRows$bottom;
var pinnedRows = gridPinnedRowsSelector(apiRef);
var topPinnedRowsHeight = (pinnedRows == null || (_pinnedRows$top = pinnedRows.top) == null ? void 0 : _pinnedRows$top.reduce(function (acc, value) {
acc += apiRef.current.unstable_getRowHeight(value.id);
return acc;
}, 0)) || 0;
var bottomPinnedRowsHeight = (pinnedRows == null || (_pinnedRows$bottom = pinnedRows.bottom) == null ? void 0 : _pinnedRows$bottom.reduce(function (acc, value) {
acc += apiRef.current.unstable_getRowHeight(value.id);
return acc;
}, 0)) || 0;
return {
top: topPinnedRowsHeight,
bottom: bottomPinnedRowsHeight
};
}
export function getMinimalContentHeight(apiRef, rowHeight) {
var densityFactor = gridDensityFactorSelector(apiRef);
return "var(--DataGrid-overlayHeight, ".concat(2 * Math.floor(rowHeight * densityFactor), "px)");
}

View File

@@ -0,0 +1,4 @@
export * from './gridRowsMetaSelector';
export * from './gridRowsMetaState';
export { gridRowCountSelector, gridRowsLoadingSelector, gridTopLevelRowCountSelector, gridRowsLookupSelector, gridRowsDataRowIdToIdLookupSelector, gridRowTreeSelector, gridRowGroupingNameSelector, gridRowTreeDepthsSelector, gridRowMaximumTreeDepthSelector, gridDataRowIdsSelector } from './gridRowsSelector';
export { GRID_ROOT_GROUP_ID, checkGridRowIdIsValid } from './gridRowsUtils';

View File

@@ -0,0 +1,175 @@
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/esm/inherits";
import _wrapNativeSuper from "@babel/runtime/helpers/esm/wrapNativeSuper";
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
import * as React from 'react';
import { getGridCellElement, getGridColumnHeaderElement, getGridRowElement } from '../../../utils/domUtils';
import { GRID_ID_AUTOGENERATED } from './gridRowsUtils';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFocusStateSelector';
export var MissingRowIdError = /*#__PURE__*/function (_Error) {
_inherits(MissingRowIdError, _Error);
function MissingRowIdError() {
_classCallCheck(this, MissingRowIdError);
return _callSuper(this, MissingRowIdError, arguments);
}
return _createClass(MissingRowIdError);
}( /*#__PURE__*/_wrapNativeSuper(Error));
/**
* @requires useGridColumns (method)
* @requires useGridRows (method)
* @requires useGridFocus (state)
* @requires useGridEditing (method)
* TODO: Impossible priority - useGridEditing also needs to be after useGridParamsApi
* TODO: Impossible priority - useGridFocus also needs to be after useGridParamsApi
*/
export function useGridParamsApi(apiRef, props) {
var getRowId = props.getRowId;
var getColumnHeaderParams = React.useCallback(function (field) {
return {
field: field,
colDef: apiRef.current.getColumn(field)
};
}, [apiRef]);
var getRowParams = React.useCallback(function (id) {
var row = apiRef.current.getRow(id);
if (!row) {
throw new MissingRowIdError("No row with id #".concat(id, " found"));
}
var params = {
id: id,
columns: apiRef.current.getAllColumns(),
row: row
};
return params;
}, [apiRef]);
var getBaseCellParams = React.useCallback(function (id, field) {
var row = apiRef.current.getRow(id);
var rowNode = apiRef.current.getRowNode(id);
if (!row || !rowNode) {
throw new MissingRowIdError("No row with id #".concat(id, " found"));
}
var cellFocus = gridFocusCellSelector(apiRef);
var cellTabIndex = gridTabIndexCellSelector(apiRef);
var params = {
id: id,
field: field,
row: row,
rowNode: rowNode,
value: row[field],
colDef: apiRef.current.getColumn(field),
cellMode: apiRef.current.getCellMode(id, field),
api: apiRef.current,
hasFocus: cellFocus !== null && cellFocus.field === field && cellFocus.id === id,
tabIndex: cellTabIndex && cellTabIndex.field === field && cellTabIndex.id === id ? 0 : -1
};
return params;
}, [apiRef]);
var getCellParams = React.useCallback(function (id, field) {
var colDef = apiRef.current.getColumn(field);
var value = apiRef.current.getCellValue(id, field);
var row = apiRef.current.getRow(id);
var rowNode = apiRef.current.getRowNode(id);
if (!row || !rowNode) {
throw new MissingRowIdError("No row with id #".concat(id, " found"));
}
var cellFocus = gridFocusCellSelector(apiRef);
var cellTabIndex = gridTabIndexCellSelector(apiRef);
var params = {
id: id,
field: field,
row: row,
rowNode: rowNode,
colDef: colDef,
cellMode: apiRef.current.getCellMode(id, field),
hasFocus: cellFocus !== null && cellFocus.field === field && cellFocus.id === id,
tabIndex: cellTabIndex && cellTabIndex.field === field && cellTabIndex.id === id ? 0 : -1,
value: value,
formattedValue: value,
isEditable: false
};
if (colDef && colDef.valueFormatter) {
params.formattedValue = colDef.valueFormatter({
id: id,
field: params.field,
value: params.value,
api: apiRef.current
});
}
params.isEditable = colDef && apiRef.current.isCellEditable(params);
return params;
}, [apiRef]);
var getCellValue = React.useCallback(function (id, field) {
var colDef = apiRef.current.getColumn(field);
if (!colDef || !colDef.valueGetter) {
var rowModel = apiRef.current.getRow(id);
if (!rowModel) {
throw new MissingRowIdError("No row with id #".concat(id, " found"));
}
return rowModel[field];
}
return colDef.valueGetter(getBaseCellParams(id, field));
}, [apiRef, getBaseCellParams]);
var getRowValue = React.useCallback(function (row, colDef) {
var _getRowId;
var id = GRID_ID_AUTOGENERATED in row ? row[GRID_ID_AUTOGENERATED] : (_getRowId = getRowId == null ? void 0 : getRowId(row)) != null ? _getRowId : row.id;
var field = colDef.field;
if (!colDef || !colDef.valueGetter) {
return row[field];
}
return colDef.valueGetter(getBaseCellParams(id, field));
}, [getBaseCellParams, getRowId]);
var getRowFormattedValue = React.useCallback(function (row, colDef) {
var _ref;
var value = getRowValue(row, colDef);
if (!colDef || !colDef.valueFormatter) {
return value;
}
var id = (_ref = getRowId ? getRowId(row) : row.id) != null ? _ref : row[GRID_ID_AUTOGENERATED];
var field = colDef.field;
return colDef.valueFormatter({
id: id,
field: field,
value: value,
api: apiRef.current
});
}, [apiRef, getRowId, getRowValue]);
var getColumnHeaderElement = React.useCallback(function (field) {
if (!apiRef.current.rootElementRef.current) {
return null;
}
return getGridColumnHeaderElement(apiRef.current.rootElementRef.current, field);
}, [apiRef]);
var getRowElement = React.useCallback(function (id) {
if (!apiRef.current.rootElementRef.current) {
return null;
}
return getGridRowElement(apiRef.current.rootElementRef.current, id);
}, [apiRef]);
var getCellElement = React.useCallback(function (id, field) {
if (!apiRef.current.rootElementRef.current) {
return null;
}
return getGridCellElement(apiRef.current.rootElementRef.current, {
id: id,
field: field
});
}, [apiRef]);
var paramsApi = {
getCellValue: getCellValue,
getCellParams: getCellParams,
getCellElement: getCellElement,
getRowValue: getRowValue,
getRowFormattedValue: getRowFormattedValue,
getRowParams: getRowParams,
getRowElement: getRowElement,
getColumnHeaderParams: getColumnHeaderParams,
getColumnHeaderElement: getColumnHeaderElement
};
useGridApiMethod(apiRef, paramsApi, 'public');
}

View File

@@ -0,0 +1,469 @@
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridRowCountSelector, gridRowsLookupSelector, gridRowTreeSelector, gridRowGroupingNameSelector, gridRowTreeDepthsSelector, gridDataRowIdsSelector, gridRowsDataRowIdToIdLookupSelector, gridRowMaximumTreeDepthSelector } from './gridRowsSelector';
import { useTimeout } from '../../utils/useTimeout';
import { GridSignature, useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector';
import { gridFilteredRowsLookupSelector } from '../filter/gridFilterSelector';
import { getTreeNodeDescendants, createRowsInternalCache, getRowsStateFromCache, isAutoGeneratedRow, GRID_ROOT_GROUP_ID, GRID_ID_AUTOGENERATED, updateCacheWithNewRows, getTopLevelRowCount, getRowIdFromRowModel } from './gridRowsUtils';
import { useGridRegisterPipeApplier } from '../../core/pipeProcessing';
export var rowsStateInitializer = function rowsStateInitializer(state, props, apiRef) {
apiRef.current.caches.rows = createRowsInternalCache({
rows: props.rows,
getRowId: props.getRowId,
loading: props.loading,
rowCount: props.rowCount
});
return _extends({}, state, {
rows: getRowsStateFromCache({
apiRef: apiRef,
rowCountProp: props.rowCount,
loadingProp: props.loading,
previousTree: null,
previousTreeDepths: null
})
});
};
export var useGridRows = function useGridRows(apiRef, props) {
if (process.env.NODE_ENV !== 'production') {
try {
// Freeze the `rows` prop so developers have a fast failure if they try to use Array.prototype.push().
Object.freeze(props.rows);
} catch (error) {
// Sometimes, it's impossible to freeze, so we give up on it.
}
}
var logger = useGridLogger(apiRef, 'useGridRows');
var currentPage = useGridVisibleRows(apiRef, props);
var lastUpdateMs = React.useRef(Date.now());
var timeout = useTimeout();
var getRow = React.useCallback(function (id) {
var model = gridRowsLookupSelector(apiRef)[id];
if (model) {
return model;
}
var node = apiRef.current.getRowNode(id);
if (node && isAutoGeneratedRow(node)) {
return _defineProperty({}, GRID_ID_AUTOGENERATED, id);
}
return null;
}, [apiRef]);
var getRowIdProp = props.getRowId;
var getRowId = React.useCallback(function (row) {
if (GRID_ID_AUTOGENERATED in row) {
return row[GRID_ID_AUTOGENERATED];
}
if (getRowIdProp) {
return getRowIdProp(row);
}
return row.id;
}, [getRowIdProp]);
var lookup = React.useMemo(function () {
return currentPage.rows.reduce(function (acc, _ref2, index) {
var id = _ref2.id;
acc[id] = index;
return acc;
}, {});
}, [currentPage.rows]);
var throttledRowsChange = React.useCallback(function (_ref3) {
var cache = _ref3.cache,
throttle = _ref3.throttle;
var run = function run() {
lastUpdateMs.current = Date.now();
apiRef.current.setState(function (state) {
return _extends({}, state, {
rows: getRowsStateFromCache({
apiRef: apiRef,
rowCountProp: props.rowCount,
loadingProp: props.loading,
previousTree: gridRowTreeSelector(apiRef),
previousTreeDepths: gridRowTreeDepthsSelector(apiRef)
})
});
});
apiRef.current.publishEvent('rowsSet');
apiRef.current.forceUpdate();
};
timeout.clear();
apiRef.current.caches.rows = cache;
if (!throttle) {
run();
return;
}
var throttleRemainingTimeMs = props.throttleRowsMs - (Date.now() - lastUpdateMs.current);
if (throttleRemainingTimeMs > 0) {
timeout.start(throttleRemainingTimeMs, run);
return;
}
run();
}, [props.throttleRowsMs, props.rowCount, props.loading, apiRef, timeout]);
/**
* API METHODS
*/
var setRows = React.useCallback(function (rows) {
logger.debug("Updating all rows, new length ".concat(rows.length));
var cache = createRowsInternalCache({
rows: rows,
getRowId: props.getRowId,
loading: props.loading,
rowCount: props.rowCount
});
var prevCache = apiRef.current.caches.rows;
cache.rowsBeforePartialUpdates = prevCache.rowsBeforePartialUpdates;
throttledRowsChange({
cache: cache,
throttle: true
});
}, [logger, props.getRowId, props.loading, props.rowCount, throttledRowsChange, apiRef]);
var updateRows = React.useCallback(function (updates) {
if (props.signature === GridSignature.DataGrid && updates.length > 1) {
throw new Error(["MUI: You can't update several rows at once in `apiRef.current.updateRows` on the DataGrid.", 'You need to upgrade to DataGridPro or DataGridPremium component to unlock this feature.'].join('\n'));
}
var nonPinnedRowsUpdates = [];
updates.forEach(function (update) {
var id = getRowIdFromRowModel(update, props.getRowId, 'A row was provided without id when calling updateRows():');
var rowNode = apiRef.current.getRowNode(id);
if ((rowNode == null ? void 0 : rowNode.type) === 'pinnedRow') {
// @ts-ignore because otherwise `release:build` doesn't work
var pinnedRowsCache = apiRef.current.caches.pinnedRows;
var prevModel = pinnedRowsCache.idLookup[id];
if (prevModel) {
pinnedRowsCache.idLookup[id] = _extends({}, prevModel, update);
}
} else {
nonPinnedRowsUpdates.push(update);
}
});
var cache = updateCacheWithNewRows({
updates: nonPinnedRowsUpdates,
getRowId: props.getRowId,
previousCache: apiRef.current.caches.rows
});
throttledRowsChange({
cache: cache,
throttle: true
});
}, [props.signature, props.getRowId, throttledRowsChange, apiRef]);
var getRowModels = React.useCallback(function () {
var dataRows = gridDataRowIdsSelector(apiRef);
var idRowsLookup = gridRowsLookupSelector(apiRef);
return new Map(dataRows.map(function (id) {
var _idRowsLookup$id;
return [id, (_idRowsLookup$id = idRowsLookup[id]) != null ? _idRowsLookup$id : {}];
}));
}, [apiRef]);
var getRowsCount = React.useCallback(function () {
return gridRowCountSelector(apiRef);
}, [apiRef]);
var getAllRowIds = React.useCallback(function () {
return gridDataRowIdsSelector(apiRef);
}, [apiRef]);
var getRowIndexRelativeToVisibleRows = React.useCallback(function (id) {
return lookup[id];
}, [lookup]);
var setRowChildrenExpansion = React.useCallback(function (id, isExpanded) {
var currentNode = apiRef.current.getRowNode(id);
if (!currentNode) {
throw new Error("MUI: No row with id #".concat(id, " found"));
}
if (currentNode.type !== 'group') {
throw new Error('MUI: Only group nodes can be expanded or collapsed');
}
var newNode = _extends({}, currentNode, {
childrenExpanded: isExpanded
});
apiRef.current.setState(function (state) {
return _extends({}, state, {
rows: _extends({}, state.rows, {
tree: _extends({}, state.rows.tree, _defineProperty({}, id, newNode))
})
});
});
apiRef.current.forceUpdate();
apiRef.current.publishEvent('rowExpansionChange', newNode);
}, [apiRef]);
var getRowNode = React.useCallback(function (id) {
var _ref4;
return (_ref4 = gridRowTreeSelector(apiRef)[id]) != null ? _ref4 : null;
}, [apiRef]);
var getRowGroupChildren = React.useCallback(function (_ref5) {
var _ref5$skipAutoGenerat = _ref5.skipAutoGeneratedRows,
skipAutoGeneratedRows = _ref5$skipAutoGenerat === void 0 ? true : _ref5$skipAutoGenerat,
groupId = _ref5.groupId,
applySorting = _ref5.applySorting,
applyFiltering = _ref5.applyFiltering;
var tree = gridRowTreeSelector(apiRef);
var children;
if (applySorting) {
var groupNode = tree[groupId];
if (!groupNode) {
return [];
}
var sortedRowIds = gridSortedRowIdsSelector(apiRef);
children = [];
var startIndex = sortedRowIds.findIndex(function (id) {
return id === groupId;
}) + 1;
for (var index = startIndex; index < sortedRowIds.length && tree[sortedRowIds[index]].depth > groupNode.depth; index += 1) {
var id = sortedRowIds[index];
if (!skipAutoGeneratedRows || !isAutoGeneratedRow(tree[id])) {
children.push(id);
}
}
} else {
children = getTreeNodeDescendants(tree, groupId, skipAutoGeneratedRows);
}
if (applyFiltering) {
var filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
children = children.filter(function (childId) {
return filteredRowsLookup[childId] !== false;
});
}
return children;
}, [apiRef]);
var setRowIndex = React.useCallback(function (rowId, targetIndex) {
var node = apiRef.current.getRowNode(rowId);
if (!node) {
throw new Error("MUI: No row with id #".concat(rowId, " found"));
}
if (node.parent !== GRID_ROOT_GROUP_ID) {
throw new Error("MUI: The row reordering do not support reordering of grouped rows yet");
}
if (node.type !== 'leaf') {
throw new Error("MUI: The row reordering do not support reordering of footer or grouping rows");
}
apiRef.current.setState(function (state) {
var group = gridRowTreeSelector(state, apiRef.current.instanceId)[GRID_ROOT_GROUP_ID];
var allRows = group.children;
var oldIndex = allRows.findIndex(function (row) {
return row === rowId;
});
if (oldIndex === -1 || oldIndex === targetIndex) {
return state;
}
logger.debug("Moving row ".concat(rowId, " to index ").concat(targetIndex));
var updatedRows = _toConsumableArray(allRows);
updatedRows.splice(targetIndex, 0, updatedRows.splice(oldIndex, 1)[0]);
return _extends({}, state, {
rows: _extends({}, state.rows, {
tree: _extends({}, state.rows.tree, _defineProperty({}, GRID_ROOT_GROUP_ID, _extends({}, group, {
children: updatedRows
})))
})
});
});
apiRef.current.publishEvent('rowsSet');
}, [apiRef, logger]);
var replaceRows = React.useCallback(function (firstRowToRender, newRows) {
if (props.signature === GridSignature.DataGrid && newRows.length > 1) {
throw new Error(["MUI: You can't replace rows using `apiRef.current.unstable_replaceRows` on the DataGrid.", 'You need to upgrade to DataGridPro or DataGridPremium component to unlock this feature.'].join('\n'));
}
if (newRows.length === 0) {
return;
}
var treeDepth = gridRowMaximumTreeDepthSelector(apiRef);
if (treeDepth > 1) {
throw new Error('`apiRef.current.unstable_replaceRows` is not compatible with tree data and row grouping');
}
var tree = _extends({}, gridRowTreeSelector(apiRef));
var dataRowIdToModelLookup = _extends({}, gridRowsLookupSelector(apiRef));
var dataRowIdToIdLookup = _extends({}, gridRowsDataRowIdToIdLookupSelector(apiRef));
var rootGroup = tree[GRID_ROOT_GROUP_ID];
var rootGroupChildren = _toConsumableArray(rootGroup.children);
var seenIds = new Set();
for (var i = 0; i < newRows.length; i += 1) {
var rowModel = newRows[i];
var rowId = getRowIdFromRowModel(rowModel, props.getRowId, 'A row was provided without id when calling replaceRows().');
var _rootGroupChildren$sp = rootGroupChildren.splice(firstRowToRender + i, 1, rowId),
_rootGroupChildren$sp2 = _slicedToArray(_rootGroupChildren$sp, 1),
removedRowId = _rootGroupChildren$sp2[0];
if (!seenIds.has(removedRowId)) {
delete dataRowIdToModelLookup[removedRowId];
delete dataRowIdToIdLookup[removedRowId];
delete tree[removedRowId];
}
var rowTreeNodeConfig = {
id: rowId,
depth: 0,
parent: GRID_ROOT_GROUP_ID,
type: 'leaf',
groupingKey: null
};
dataRowIdToModelLookup[rowId] = rowModel;
dataRowIdToIdLookup[rowId] = rowId;
tree[rowId] = rowTreeNodeConfig;
seenIds.add(rowId);
}
tree[GRID_ROOT_GROUP_ID] = _extends({}, rootGroup, {
children: rootGroupChildren
});
// Removes potential remaining skeleton rows from the dataRowIds.
var dataRowIds = rootGroupChildren.filter(function (childId) {
return tree[childId].type === 'leaf';
});
apiRef.current.caches.rows.dataRowIdToModelLookup = dataRowIdToModelLookup;
apiRef.current.caches.rows.dataRowIdToIdLookup = dataRowIdToIdLookup;
apiRef.current.setState(function (state) {
return _extends({}, state, {
rows: _extends({}, state.rows, {
dataRowIdToModelLookup: dataRowIdToModelLookup,
dataRowIdToIdLookup: dataRowIdToIdLookup,
dataRowIds: dataRowIds,
tree: tree
})
});
});
apiRef.current.publishEvent('rowsSet');
}, [apiRef, props.signature, props.getRowId]);
var rowApi = {
getRow: getRow,
getRowId: getRowId,
getRowModels: getRowModels,
getRowsCount: getRowsCount,
getAllRowIds: getAllRowIds,
setRows: setRows,
updateRows: updateRows,
getRowNode: getRowNode,
getRowIndexRelativeToVisibleRows: getRowIndexRelativeToVisibleRows,
unstable_replaceRows: replaceRows
};
var rowProApi = {
setRowIndex: setRowIndex,
setRowChildrenExpansion: setRowChildrenExpansion,
getRowGroupChildren: getRowGroupChildren
};
/**
* EVENTS
*/
var groupRows = React.useCallback(function () {
logger.info("Row grouping pre-processing have changed, regenerating the row tree");
var cache;
if (apiRef.current.caches.rows.rowsBeforePartialUpdates === props.rows) {
// The `props.rows` did not change since the last row grouping
// We can use the current rows cache which contains the partial updates done recently.
cache = _extends({}, apiRef.current.caches.rows, {
updates: {
type: 'full',
rows: gridDataRowIdsSelector(apiRef)
}
});
} else {
// The `props.rows` has changed since the last row grouping
// We must use the new `props.rows` on the new grouping
// This occurs because this event is triggered before the `useEffect` on the rows when both the grouping pre-processing and the rows changes on the same render
cache = createRowsInternalCache({
rows: props.rows,
getRowId: props.getRowId,
loading: props.loading,
rowCount: props.rowCount
});
}
throttledRowsChange({
cache: cache,
throttle: false
});
}, [logger, apiRef, props.rows, props.getRowId, props.loading, props.rowCount, throttledRowsChange]);
var handleStrategyProcessorChange = React.useCallback(function (methodName) {
if (methodName === 'rowTreeCreation') {
groupRows();
}
}, [groupRows]);
var handleStrategyActivityChange = React.useCallback(function () {
// `rowTreeCreation` is the only processor ran when `strategyAvailabilityChange` is fired.
// All the other processors listen to `rowsSet` which will be published by the `groupRows` method below.
if (apiRef.current.getActiveStrategy('rowTree') !== gridRowGroupingNameSelector(apiRef)) {
groupRows();
}
}, [apiRef, groupRows]);
useGridApiEventHandler(apiRef, 'activeStrategyProcessorChange', handleStrategyProcessorChange);
useGridApiEventHandler(apiRef, 'strategyAvailabilityChange', handleStrategyActivityChange);
/**
* APPLIERS
*/
var applyHydrateRowsProcessor = React.useCallback(function () {
apiRef.current.setState(function (state) {
var response = apiRef.current.unstable_applyPipeProcessors('hydrateRows', {
tree: gridRowTreeSelector(state, apiRef.current.instanceId),
treeDepths: gridRowTreeDepthsSelector(state, apiRef.current.instanceId),
dataRowIds: gridDataRowIdsSelector(state, apiRef.current.instanceId),
dataRowIdToModelLookup: gridRowsLookupSelector(state, apiRef.current.instanceId),
dataRowIdToIdLookup: gridRowsDataRowIdToIdLookupSelector(state, apiRef.current.instanceId)
});
return _extends({}, state, {
rows: _extends({}, state.rows, response, {
totalTopLevelRowCount: getTopLevelRowCount({
tree: response.tree,
rowCountProp: props.rowCount
})
})
});
});
apiRef.current.publishEvent('rowsSet');
apiRef.current.forceUpdate();
}, [apiRef, props.rowCount]);
useGridRegisterPipeApplier(apiRef, 'hydrateRows', applyHydrateRowsProcessor);
useGridApiMethod(apiRef, rowApi, 'public');
useGridApiMethod(apiRef, rowProApi, props.signature === GridSignature.DataGrid ? 'private' : 'public');
// The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridRows`
// As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
var isFirstRender = React.useRef(true);
React.useEffect(function () {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
var areNewRowsAlreadyInState = apiRef.current.caches.rows.rowsBeforePartialUpdates === props.rows;
var isNewLoadingAlreadyInState = apiRef.current.caches.rows.loadingPropBeforePartialUpdates === props.loading;
var isNewRowCountAlreadyInState = apiRef.current.caches.rows.rowCountPropBeforePartialUpdates === props.rowCount;
// The new rows have already been applied (most likely in the `'rowGroupsPreProcessingChange'` listener)
if (areNewRowsAlreadyInState) {
// If the loading prop has changed, we need to update its value in the state because it won't be done by `throttledRowsChange`
if (!isNewLoadingAlreadyInState) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
rows: _extends({}, state.rows, {
loading: props.loading
})
});
});
apiRef.current.caches.rows.loadingPropBeforePartialUpdates = props.loading;
apiRef.current.forceUpdate();
}
if (!isNewRowCountAlreadyInState) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
rows: _extends({}, state.rows, {
totalRowCount: Math.max(props.rowCount || 0, state.rows.totalRowCount),
totalTopLevelRowCount: Math.max(props.rowCount || 0, state.rows.totalTopLevelRowCount)
})
});
});
apiRef.current.caches.rows.rowCountPropBeforePartialUpdates = props.rowCount;
apiRef.current.forceUpdate();
}
return;
}
logger.debug("Updating all rows, new length ".concat(props.rows.length));
throttledRowsChange({
cache: createRowsInternalCache({
rows: props.rows,
getRowId: props.getRowId,
loading: props.loading,
rowCount: props.rowCount
}),
throttle: false
});
}, [props.rows, props.rowCount, props.getRowId, props.loading, logger, throttledRowsChange, apiRef]);
};

View File

@@ -0,0 +1,233 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_debounce as debounce, unstable_capitalize as capitalize } from '@mui/utils';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridSelector } from '../../utils/useGridSelector';
import { gridDensityFactorSelector } from '../density/densitySelector';
import { gridFilterModelSelector } from '../filter/gridFilterSelector';
import { gridPaginationSelector } from '../pagination/gridPaginationSelector';
import { gridSortModelSelector } from '../sorting/gridSortingSelector';
import { useGridRegisterPipeApplier } from '../../core/pipeProcessing';
import { gridPinnedRowsSelector } from './gridRowsSelector';
import { DATA_GRID_PROPS_DEFAULT_VALUES } from '../../../DataGrid/useDataGridProps';
export var rowsMetaStateInitializer = function rowsMetaStateInitializer(state) {
return _extends({}, state, {
rowsMeta: {
currentPageTotalHeight: 0,
positions: []
}
});
};
var warnedOnceInvalidRowHeight = false;
var getValidRowHeight = function getValidRowHeight(rowHeightProp, defaultRowHeight, warningMessage) {
if (typeof rowHeightProp === 'number' && rowHeightProp > 0) {
return rowHeightProp;
}
if (process.env.NODE_ENV !== 'production' && !warnedOnceInvalidRowHeight && typeof rowHeightProp !== 'undefined' && rowHeightProp !== null) {
console.warn(warningMessage);
warnedOnceInvalidRowHeight = true;
}
return defaultRowHeight;
};
var rowHeightWarning = ["MUI: The `rowHeight` prop should be a number greater than 0.", "The default value will be used instead."].join('\n');
var getRowHeightWarning = ["MUI: The `getRowHeight` prop should return a number greater than 0 or 'auto'.", "The default value will be used instead."].join('\n');
/**
* @requires useGridPageSize (method)
* @requires useGridPage (method)
*/
export var useGridRowsMeta = function useGridRowsMeta(apiRef, props) {
var getRowHeightProp = props.getRowHeight,
getRowSpacing = props.getRowSpacing,
getEstimatedRowHeight = props.getEstimatedRowHeight;
var rowsHeightLookup = React.useRef(Object.create(null));
// Inspired by https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/utils/CellSizeAndPositionManager.js
var lastMeasuredRowIndex = React.useRef(-1);
var hasRowWithAutoHeight = React.useRef(false);
var densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
var filterModel = useGridSelector(apiRef, gridFilterModelSelector);
var paginationState = useGridSelector(apiRef, gridPaginationSelector);
var sortModel = useGridSelector(apiRef, gridSortModelSelector);
var currentPage = useGridVisibleRows(apiRef, props);
var pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
var validRowHeight = getValidRowHeight(props.rowHeight, DATA_GRID_PROPS_DEFAULT_VALUES.rowHeight, rowHeightWarning);
var rowHeight = Math.floor(validRowHeight * densityFactor);
var hydrateRowsMeta = React.useCallback(function () {
var _pinnedRows$top, _pinnedRows$bottom;
hasRowWithAutoHeight.current = false;
var calculateRowProcessedSizes = function calculateRowProcessedSizes(row) {
if (!rowsHeightLookup.current[row.id]) {
rowsHeightLookup.current[row.id] = {
sizes: {
baseCenter: rowHeight
},
isResized: false,
autoHeight: false,
needsFirstMeasurement: true // Assume all rows will need to be measured by default
};
}
var _rowsHeightLookup$cur = rowsHeightLookup.current[row.id],
isResized = _rowsHeightLookup$cur.isResized,
needsFirstMeasurement = _rowsHeightLookup$cur.needsFirstMeasurement,
sizes = _rowsHeightLookup$cur.sizes;
var baseRowHeight = typeof rowHeight === 'number' && rowHeight > 0 ? rowHeight : 52;
var existingBaseRowHeight = sizes.baseCenter;
if (isResized) {
// Do not recalculate resized row height and use the value from the lookup
baseRowHeight = existingBaseRowHeight;
} else if (getRowHeightProp) {
var rowHeightFromUser = getRowHeightProp(_extends({}, row, {
densityFactor: densityFactor
}));
if (rowHeightFromUser === 'auto') {
if (needsFirstMeasurement) {
var estimatedRowHeight = getEstimatedRowHeight ? getEstimatedRowHeight(_extends({}, row, {
densityFactor: densityFactor
})) : rowHeight;
// If the row was not measured yet use the estimated row height
baseRowHeight = estimatedRowHeight != null ? estimatedRowHeight : rowHeight;
} else {
baseRowHeight = existingBaseRowHeight;
}
hasRowWithAutoHeight.current = true;
rowsHeightLookup.current[row.id].autoHeight = true;
} else {
// Default back to base rowHeight if getRowHeight returns invalid value.
baseRowHeight = getValidRowHeight(rowHeightFromUser, rowHeight, getRowHeightWarning);
rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
rowsHeightLookup.current[row.id].autoHeight = false;
}
} else {
rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
}
var initialHeights = {};
/* eslint-disable-next-line no-restricted-syntax */
for (var _key in sizes) {
if (/^base[A-Z]/.test(_key)) {
initialHeights[_key] = sizes[_key];
}
}
initialHeights.baseCenter = baseRowHeight;
if (getRowSpacing) {
var _spacing$top, _spacing$bottom;
var indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
var spacing = getRowSpacing(_extends({}, row, {
isFirstVisible: indexRelativeToCurrentPage === 0,
isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
indexRelativeToCurrentPage: indexRelativeToCurrentPage
}));
initialHeights.spacingTop = (_spacing$top = spacing.top) != null ? _spacing$top : 0;
initialHeights.spacingBottom = (_spacing$bottom = spacing.bottom) != null ? _spacing$bottom : 0;
}
var processedSizes = apiRef.current.unstable_applyPipeProcessors('rowHeight', initialHeights, row);
rowsHeightLookup.current[row.id].sizes = processedSizes;
return processedSizes;
};
var positions = [];
var currentPageTotalHeight = currentPage.rows.reduce(function (acc, row) {
positions.push(acc);
var maximumBaseSize = 0;
var otherSizes = 0;
var processedSizes = calculateRowProcessedSizes(row);
/* eslint-disable-next-line no-restricted-syntax, guard-for-in */
for (var _key2 in processedSizes) {
var value = processedSizes[_key2];
if (/^base[A-Z]/.test(_key2)) {
maximumBaseSize = value > maximumBaseSize ? value : maximumBaseSize;
} else {
otherSizes += value;
}
}
return acc + maximumBaseSize + otherSizes;
}, 0);
pinnedRows == null || (_pinnedRows$top = pinnedRows.top) == null || _pinnedRows$top.forEach(function (row) {
calculateRowProcessedSizes(row);
});
pinnedRows == null || (_pinnedRows$bottom = pinnedRows.bottom) == null || _pinnedRows$bottom.forEach(function (row) {
calculateRowProcessedSizes(row);
});
apiRef.current.setState(function (state) {
return _extends({}, state, {
rowsMeta: {
currentPageTotalHeight: currentPageTotalHeight,
positions: positions
}
});
});
if (!hasRowWithAutoHeight.current) {
// No row has height=auto, so all rows are already measured
lastMeasuredRowIndex.current = Infinity;
}
apiRef.current.forceUpdate();
}, [apiRef, currentPage.rows, rowHeight, getRowHeightProp, getRowSpacing, getEstimatedRowHeight, pinnedRows, densityFactor]);
var getRowHeight = React.useCallback(function (rowId) {
var height = rowsHeightLookup.current[rowId];
return height ? height.sizes.baseCenter : rowHeight;
}, [rowHeight]);
var getRowInternalSizes = function getRowInternalSizes(rowId) {
var _rowsHeightLookup$cur2;
return (_rowsHeightLookup$cur2 = rowsHeightLookup.current[rowId]) == null ? void 0 : _rowsHeightLookup$cur2.sizes;
};
var setRowHeight = React.useCallback(function (id, height) {
rowsHeightLookup.current[id].sizes.baseCenter = height;
rowsHeightLookup.current[id].isResized = true;
rowsHeightLookup.current[id].needsFirstMeasurement = false;
hydrateRowsMeta();
}, [hydrateRowsMeta]);
var debouncedHydrateRowsMeta = React.useMemo(function () {
return debounce(hydrateRowsMeta, props.rowPositionsDebounceMs);
}, [hydrateRowsMeta, props.rowPositionsDebounceMs]);
var storeMeasuredRowHeight = React.useCallback(function (id, height, position) {
if (!rowsHeightLookup.current[id] || !rowsHeightLookup.current[id].autoHeight) {
return;
}
// Only trigger hydration if the value is different, otherwise we trigger a loop
var needsHydration = rowsHeightLookup.current[id].sizes["base".concat(capitalize(position))] !== height;
rowsHeightLookup.current[id].needsFirstMeasurement = false;
rowsHeightLookup.current[id].sizes["base".concat(capitalize(position))] = height;
if (needsHydration) {
debouncedHydrateRowsMeta();
}
}, [debouncedHydrateRowsMeta]);
var rowHasAutoHeight = React.useCallback(function (id) {
var _rowsHeightLookup$cur3;
return ((_rowsHeightLookup$cur3 = rowsHeightLookup.current[id]) == null ? void 0 : _rowsHeightLookup$cur3.autoHeight) || false;
}, []);
var getLastMeasuredRowIndex = React.useCallback(function () {
return lastMeasuredRowIndex.current;
}, []);
var setLastMeasuredRowIndex = React.useCallback(function (index) {
if (hasRowWithAutoHeight.current && index > lastMeasuredRowIndex.current) {
lastMeasuredRowIndex.current = index;
}
}, []);
var resetRowHeights = React.useCallback(function () {
rowsHeightLookup.current = {};
hydrateRowsMeta();
}, [hydrateRowsMeta]);
// The effect is used to build the rows meta data - currentPageTotalHeight and positions.
// Because of variable row height this is needed for the virtualization
React.useEffect(function () {
hydrateRowsMeta();
}, [rowHeight, filterModel, paginationState, sortModel, hydrateRowsMeta]);
useGridRegisterPipeApplier(apiRef, 'rowHeight', hydrateRowsMeta);
var rowsMetaApi = {
unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex,
unstable_getRowHeight: getRowHeight,
unstable_getRowInternalSizes: getRowInternalSizes,
unstable_setRowHeight: setRowHeight,
unstable_storeRowHeightMeasurement: storeMeasuredRowHeight,
resetRowHeights: resetRowHeights
};
var rowsMetaPrivateApi = {
getLastMeasuredRowIndex: getLastMeasuredRowIndex,
rowHasAutoHeight: rowHasAutoHeight
};
useGridApiMethod(apiRef, rowsMetaApi, 'public');
useGridApiMethod(apiRef, rowsMetaPrivateApi, 'private');
};

View File

@@ -0,0 +1,82 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import { GRID_DEFAULT_STRATEGY, useGridRegisterStrategyProcessor } from '../../core/strategyProcessing';
import { buildRootGroup, GRID_ROOT_GROUP_ID } from './gridRowsUtils';
var createFlatRowTree = function createFlatRowTree(rows) {
var tree = _defineProperty({}, GRID_ROOT_GROUP_ID, _extends({}, buildRootGroup(), {
children: rows
}));
for (var i = 0; i < rows.length; i += 1) {
var rowId = rows[i];
tree[rowId] = {
id: rowId,
depth: 0,
parent: GRID_ROOT_GROUP_ID,
type: 'leaf',
groupingKey: null
};
}
return {
groupingName: GRID_DEFAULT_STRATEGY,
tree: tree,
treeDepths: {
0: rows.length
},
dataRowIds: rows
};
};
var updateFlatRowTree = function updateFlatRowTree(_ref) {
var previousTree = _ref.previousTree,
actions = _ref.actions;
var tree = _extends({}, previousTree);
var idsToRemoveFromRootGroup = {};
for (var i = 0; i < actions.remove.length; i += 1) {
var idToDelete = actions.remove[i];
idsToRemoveFromRootGroup[idToDelete] = true;
delete tree[idToDelete];
}
for (var _i = 0; _i < actions.insert.length; _i += 1) {
var idToInsert = actions.insert[_i];
tree[idToInsert] = {
id: idToInsert,
depth: 0,
parent: GRID_ROOT_GROUP_ID,
type: 'leaf',
groupingKey: null
};
}
// TODO rows v6: Support row unpinning
var rootGroup = tree[GRID_ROOT_GROUP_ID];
var rootGroupChildren = [].concat(_toConsumableArray(rootGroup.children), _toConsumableArray(actions.insert));
if (Object.values(idsToRemoveFromRootGroup).length) {
rootGroupChildren = rootGroupChildren.filter(function (id) {
return !idsToRemoveFromRootGroup[id];
});
}
tree[GRID_ROOT_GROUP_ID] = _extends({}, rootGroup, {
children: rootGroupChildren
});
return {
groupingName: GRID_DEFAULT_STRATEGY,
tree: tree,
treeDepths: {
0: rootGroupChildren.length
},
dataRowIds: rootGroupChildren
};
};
var flatRowTreeCreationMethod = function flatRowTreeCreationMethod(params) {
if (params.updates.type === 'full') {
return createFlatRowTree(params.updates.rows);
}
return updateFlatRowTree({
previousTree: params.previousTree,
actions: params.updates.actions
});
};
export var useGridRowsPreProcessors = function useGridRowsPreProcessors(apiRef) {
useGridRegisterStrategyProcessor(apiRef, GRID_DEFAULT_STRATEGY, 'rowTreeCreation', flatRowTreeCreationMethod);
};

View File

@@ -0,0 +1,135 @@
import _typeof from "@babel/runtime/helpers/esm/typeof";
import * as React from 'react';
import { useTheme } from '@mui/material/styles';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridColumnPositionsSelector, gridVisibleColumnDefinitionsSelector } from '../columns/gridColumnsSelector';
import { useGridSelector } from '../../utils/useGridSelector';
import { gridPageSelector, gridPageSizeSelector } from '../pagination/gridPaginationSelector';
import { gridRowCountSelector } from '../rows/gridRowsSelector';
import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { gridExpandedSortedRowEntriesSelector } from '../filter/gridFilterSelector';
import { gridClasses } from '../../../constants/gridClasses';
// Logic copied from https://www.w3.org/TR/wai-aria-practices/examples/listbox/js/listbox.js
// Similar to https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
function scrollIntoView(dimensions) {
var clientHeight = dimensions.clientHeight,
scrollTop = dimensions.scrollTop,
offsetHeight = dimensions.offsetHeight,
offsetTop = dimensions.offsetTop;
var elementBottom = offsetTop + offsetHeight;
// Always scroll to top when cell is higher than viewport to avoid scroll jump
// See https://github.com/mui/mui-x/issues/4513 and https://github.com/mui/mui-x/issues/4514
if (offsetHeight > clientHeight) {
return offsetTop;
}
if (elementBottom - clientHeight > scrollTop) {
return elementBottom - clientHeight;
}
if (offsetTop < scrollTop) {
return offsetTop;
}
return undefined;
}
/**
* @requires useGridPagination (state) - can be after, async only
* @requires useGridColumns (state) - can be after, async only
* @requires useGridRows (state) - can be after, async only
* @requires useGridRowsMeta (state) - can be after, async only
* @requires useGridFilter (state)
* @requires useGridColumnSpanning (method)
*/
export var useGridScroll = function useGridScroll(apiRef, props) {
var theme = useTheme();
var logger = useGridLogger(apiRef, 'useGridScroll');
var colRef = apiRef.current.columnHeadersElementRef;
var virtualScrollerRef = apiRef.current.virtualScrollerRef;
var visibleSortedRows = useGridSelector(apiRef, gridExpandedSortedRowEntriesSelector);
var scrollToIndexes = React.useCallback(function (params) {
var totalRowCount = gridRowCountSelector(apiRef);
var visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
var scrollToHeader = params.rowIndex == null;
if (!scrollToHeader && totalRowCount === 0 || visibleColumns.length === 0) {
return false;
}
logger.debug("Scrolling to cell at row ".concat(params.rowIndex, ", col: ").concat(params.colIndex, " "));
var scrollCoordinates = {};
if (params.colIndex != null) {
var columnPositions = gridColumnPositionsSelector(apiRef);
var cellWidth;
if (typeof params.rowIndex !== 'undefined') {
var _visibleSortedRows$pa;
var rowId = (_visibleSortedRows$pa = visibleSortedRows[params.rowIndex]) == null ? void 0 : _visibleSortedRows$pa.id;
var cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, params.colIndex);
if (cellColSpanInfo && !cellColSpanInfo.spannedByColSpan) {
cellWidth = cellColSpanInfo.cellProps.width;
}
}
if (typeof cellWidth === 'undefined') {
cellWidth = visibleColumns[params.colIndex].computedWidth;
}
// When using RTL, `scrollLeft` becomes negative, so we must ensure that we only compare values.
scrollCoordinates.left = scrollIntoView({
clientHeight: virtualScrollerRef.current.clientWidth,
scrollTop: Math.abs(virtualScrollerRef.current.scrollLeft),
offsetHeight: cellWidth,
offsetTop: columnPositions[params.colIndex]
});
}
if (params.rowIndex != null) {
var _querySelector, _querySelector2;
var rowsMeta = gridRowsMetaSelector(apiRef.current.state);
var page = gridPageSelector(apiRef);
var pageSize = gridPageSizeSelector(apiRef);
var elementIndex = !props.pagination ? params.rowIndex : params.rowIndex - page * pageSize;
var targetOffsetHeight = rowsMeta.positions[elementIndex + 1] ? rowsMeta.positions[elementIndex + 1] - rowsMeta.positions[elementIndex] : rowsMeta.currentPageTotalHeight - rowsMeta.positions[elementIndex];
var topPinnedRowsHeight = ((_querySelector = virtualScrollerRef.current.querySelector(".".concat(gridClasses['pinnedRows--top']))) == null ? void 0 : _querySelector.clientHeight) || 0;
var bottomPinnedRowsHeight = ((_querySelector2 = virtualScrollerRef.current.querySelector(".".concat(gridClasses['pinnedRows--bottom']))) == null ? void 0 : _querySelector2.clientHeight) || 0;
scrollCoordinates.top = scrollIntoView({
clientHeight: virtualScrollerRef.current.clientHeight - topPinnedRowsHeight - bottomPinnedRowsHeight,
scrollTop: virtualScrollerRef.current.scrollTop,
offsetHeight: targetOffsetHeight,
offsetTop: rowsMeta.positions[elementIndex]
});
}
scrollCoordinates = apiRef.current.unstable_applyPipeProcessors('scrollToIndexes', scrollCoordinates, params);
if (_typeof(scrollCoordinates.left) !== undefined || _typeof(scrollCoordinates.top) !== undefined) {
apiRef.current.scroll(scrollCoordinates);
return true;
}
return false;
}, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows]);
var scroll = React.useCallback(function (params) {
if (virtualScrollerRef.current && params.left != null && colRef.current) {
var direction = theme.direction === 'rtl' ? -1 : 1;
colRef.current.scrollLeft = params.left;
virtualScrollerRef.current.scrollLeft = direction * params.left;
logger.debug("Scrolling left: ".concat(params.left));
}
if (virtualScrollerRef.current && params.top != null) {
virtualScrollerRef.current.scrollTop = params.top;
logger.debug("Scrolling top: ".concat(params.top));
}
logger.debug("Scrolling, updating container, and viewport");
}, [virtualScrollerRef, theme.direction, colRef, logger]);
var getScrollPosition = React.useCallback(function () {
if (!(virtualScrollerRef != null && virtualScrollerRef.current)) {
return {
top: 0,
left: 0
};
}
return {
top: virtualScrollerRef.current.scrollTop,
left: virtualScrollerRef.current.scrollLeft
};
}, [virtualScrollerRef]);
var scrollApi = {
scroll: scroll,
scrollToIndexes: scrollToIndexes,
getScrollPosition: getScrollPosition
};
useGridApiMethod(apiRef, scrollApi, 'public');
};

View File

@@ -0,0 +1,56 @@
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
import { gridRowsLookupSelector } from '../rows/gridRowsSelector';
/**
* @category Sorting
* @ignore - do not document.
*/
var gridSortingStateSelector = function gridSortingStateSelector(state) {
return state.sorting;
};
/**
* Get the id of the rows after the sorting process.
* @category Sorting
*/
export var gridSortedRowIdsSelector = createSelector(gridSortingStateSelector, function (sortingState) {
return sortingState.sortedRows;
});
/**
* Get the id and the model of the rows after the sorting process.
* @category Sorting
*/
export var gridSortedRowEntriesSelector = createSelectorMemoized(gridSortedRowIdsSelector, gridRowsLookupSelector,
// TODO rows v6: Is this the best approach ?
function (sortedIds, idRowsLookup) {
return sortedIds.map(function (id) {
var _idRowsLookup$id;
return {
id: id,
model: (_idRowsLookup$id = idRowsLookup[id]) != null ? _idRowsLookup$id : {}
};
});
});
/**
* Get the current sorting model.
* @category Sorting
*/
export var gridSortModelSelector = createSelector(gridSortingStateSelector, function (sorting) {
return sorting.sortModel;
});
/**
* @category Sorting
* @ignore - do not document.
*/
export var gridSortColumnLookupSelector = createSelectorMemoized(gridSortModelSelector, function (sortModel) {
var result = sortModel.reduce(function (res, sortItem, index) {
res[sortItem.field] = {
sortDirection: sortItem.sort,
sortIndex: sortModel.length > 1 ? index + 1 : undefined
};
return res;
}, {});
return result;
});

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,152 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import { buildWarning } from '../../../utils/warning';
var sortModelDisableMultiColumnsSortingWarning = buildWarning(['MUI: The `sortModel` can only contain a single item when the `disableMultipleColumnsSorting` prop is set to `true`.', 'If you are using the community version of the `DataGrid`, this prop is always `true`.'], 'error');
export var sanitizeSortModel = function sanitizeSortModel(model, disableMultipleColumnsSorting) {
if (disableMultipleColumnsSorting && model.length > 1) {
sortModelDisableMultiColumnsSortingWarning();
return [model[0]];
}
return model;
};
export var mergeStateWithSortModel = function mergeStateWithSortModel(sortModel, disableMultipleColumnsSorting) {
return function (state) {
return _extends({}, state, {
sorting: _extends({}, state.sorting, {
sortModel: sanitizeSortModel(sortModel, disableMultipleColumnsSorting)
})
});
};
};
var isDesc = function isDesc(direction) {
return direction === 'desc';
};
/**
* Transform an item of the sorting model into a method comparing two rows.
* @param {GridSortItem} sortItem The sort item we want to apply.
* @param {React.MutableRefObject<GridApiCommunity>} apiRef The API of the grid.
* @returns {GridParsedSortItem | null} The parsed sort item. Returns `null` is the sort item is not valid.
*/
var parseSortItem = function parseSortItem(sortItem, apiRef) {
var column = apiRef.current.getColumn(sortItem.field);
if (!column) {
return null;
}
var comparator = isDesc(sortItem.sort) ? function () {
return -1 * column.sortComparator.apply(column, arguments);
} : column.sortComparator;
var getSortCellParams = function getSortCellParams(id) {
return {
id: id,
field: column.field,
rowNode: apiRef.current.getRowNode(id),
value: apiRef.current.getCellValue(id, column.field),
api: apiRef.current
};
};
return {
getSortCellParams: getSortCellParams,
comparator: comparator
};
};
/**
* Compare two rows according to a list of valid sort items.
* The `row1Params` and `row2Params` must have the same length as `parsedSortItems`,
* and each of their index must contain the `GridSortCellParams` of the sort item with the same index.
* @param {GridParsedSortItem[]} parsedSortItems All the sort items with which we want to compare the rows.
* @param {GridRowAggregatedSortingParams} row1 The node and params of the 1st row for each sort item.
* @param {GridRowAggregatedSortingParams} row2 The node and params of the 2nd row for each sort item.
*/
var compareRows = function compareRows(parsedSortItems, row1, row2) {
return parsedSortItems.reduce(function (res, item, index) {
if (res !== 0) {
// return the results of the first comparator which distinguish the two rows
return res;
}
var sortCellParams1 = row1.params[index];
var sortCellParams2 = row2.params[index];
res = item.comparator(sortCellParams1.value, sortCellParams2.value, sortCellParams1, sortCellParams2);
return res;
}, 0);
};
/**
* Generates a method to easily sort a list of rows according to the current sort model.
* @param {GridSortModel} sortModel The model with which we want to sort the rows.
* @param {React.MutableRefObject<GridApiCommunity>} apiRef The API of the grid.
* @returns {GridSortingModelApplier | null} A method that generates a list of sorted row ids from a list of rows according to the current sort model. If `null`, we consider that the rows should remain in the order there were provided.
*/
export var buildAggregatedSortingApplier = function buildAggregatedSortingApplier(sortModel, apiRef) {
var comparatorList = sortModel.map(function (item) {
return parseSortItem(item, apiRef);
}).filter(function (comparator) {
return !!comparator;
});
if (comparatorList.length === 0) {
return null;
}
return function (rowList) {
return rowList.map(function (node) {
return {
node: node,
params: comparatorList.map(function (el) {
return el.getSortCellParams(node.id);
})
};
}).sort(function (a, b) {
return compareRows(comparatorList, a, b);
}).map(function (row) {
return row.node.id;
});
};
};
export var getNextGridSortDirection = function getNextGridSortDirection(sortingOrder, current) {
var currentIdx = sortingOrder.indexOf(current);
if (!current || currentIdx === -1 || currentIdx + 1 === sortingOrder.length) {
return sortingOrder[0];
}
return sortingOrder[currentIdx + 1];
};
var gridNillComparator = function gridNillComparator(v1, v2) {
if (v1 == null && v2 != null) {
return -1;
}
if (v2 == null && v1 != null) {
return 1;
}
if (v1 == null && v2 == null) {
return 0;
}
return null;
};
var collator = new Intl.Collator();
export var gridStringOrNumberComparator = function gridStringOrNumberComparator(value1, value2) {
var nillResult = gridNillComparator(value1, value2);
if (nillResult !== null) {
return nillResult;
}
if (typeof value1 === 'string') {
return collator.compare(value1.toString(), value2.toString());
}
return value1 - value2;
};
export var gridNumberComparator = function gridNumberComparator(value1, value2) {
var nillResult = gridNillComparator(value1, value2);
if (nillResult !== null) {
return nillResult;
}
return Number(value1) - Number(value2);
};
export var gridDateComparator = function gridDateComparator(value1, value2) {
var nillResult = gridNillComparator(value1, value2);
if (nillResult !== null) {
return nillResult;
}
if (value1 > value2) {
return 1;
}
if (value1 < value2) {
return -1;
}
return 0;
};

View File

@@ -0,0 +1,2 @@
export * from './gridSortingSelector';
export { gridDateComparator, gridNumberComparator, gridStringOrNumberComparator } from './gridSortingUtils';

View File

@@ -0,0 +1,270 @@
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { isEnterKey } from '../../../utils/keyboardUtils';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridColumnLookupSelector } from '../columns/gridColumnsSelector';
import { gridSortedRowEntriesSelector, gridSortedRowIdsSelector, gridSortModelSelector } from './gridSortingSelector';
import { GRID_ROOT_GROUP_ID, gridRowTreeSelector } from '../rows';
import { useFirstRender } from '../../utils/useFirstRender';
import { useGridRegisterStrategyProcessor, GRID_DEFAULT_STRATEGY } from '../../core/strategyProcessing';
import { buildAggregatedSortingApplier, mergeStateWithSortModel, getNextGridSortDirection, sanitizeSortModel } from './gridSortingUtils';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { getTreeNodeDescendants } from '../rows/gridRowsUtils';
export var sortingStateInitializer = function sortingStateInitializer(state, props) {
var _ref, _props$sortModel, _props$initialState;
var sortModel = (_ref = (_props$sortModel = props.sortModel) != null ? _props$sortModel : (_props$initialState = props.initialState) == null || (_props$initialState = _props$initialState.sorting) == null ? void 0 : _props$initialState.sortModel) != null ? _ref : [];
return _extends({}, state, {
sorting: {
sortModel: sanitizeSortModel(sortModel, props.disableMultipleColumnsSorting),
sortedRows: []
}
});
};
/**
* @requires useGridRows (event)
* @requires useGridColumns (event)
*/
export var useGridSorting = function useGridSorting(apiRef, props) {
var _props$initialState3;
var logger = useGridLogger(apiRef, 'useGridSorting');
apiRef.current.registerControlState({
stateId: 'sortModel',
propModel: props.sortModel,
propOnChange: props.onSortModelChange,
stateSelector: gridSortModelSelector,
changeEvent: 'sortModelChange'
});
var upsertSortModel = React.useCallback(function (field, sortItem) {
var sortModel = gridSortModelSelector(apiRef);
var existingIdx = sortModel.findIndex(function (c) {
return c.field === field;
});
var newSortModel = _toConsumableArray(sortModel);
if (existingIdx > -1) {
if (!sortItem) {
newSortModel.splice(existingIdx, 1);
} else {
newSortModel.splice(existingIdx, 1, sortItem);
}
} else {
newSortModel = [].concat(_toConsumableArray(sortModel), [sortItem]);
}
return newSortModel;
}, [apiRef]);
var createSortItem = React.useCallback(function (col, directionOverride) {
var _col$sortingOrder2;
var sortModel = gridSortModelSelector(apiRef);
var existing = sortModel.find(function (c) {
return c.field === col.field;
});
if (existing) {
var _col$sortingOrder;
var nextSort = directionOverride === undefined ? getNextGridSortDirection((_col$sortingOrder = col.sortingOrder) != null ? _col$sortingOrder : props.sortingOrder, existing.sort) : directionOverride;
return nextSort == null ? undefined : _extends({}, existing, {
sort: nextSort
});
}
return {
field: col.field,
sort: directionOverride === undefined ? getNextGridSortDirection((_col$sortingOrder2 = col.sortingOrder) != null ? _col$sortingOrder2 : props.sortingOrder) : directionOverride
};
}, [apiRef, props.sortingOrder]);
var addColumnMenuItem = React.useCallback(function (columnMenuItems, colDef) {
if (colDef == null || colDef.sortable === false) {
return columnMenuItems;
}
var sortingOrder = colDef.sortingOrder || props.sortingOrder;
if (sortingOrder.some(function (item) {
return !!item;
})) {
return [].concat(_toConsumableArray(columnMenuItems), ['columnMenuSortItem']);
}
return columnMenuItems;
}, [props.sortingOrder]);
/**
* API METHODS
*/
var applySorting = React.useCallback(function () {
apiRef.current.setState(function (state) {
if (props.sortingMode === 'server') {
logger.debug('Skipping sorting rows as sortingMode = server');
return _extends({}, state, {
sorting: _extends({}, state.sorting, {
sortedRows: getTreeNodeDescendants(gridRowTreeSelector(apiRef), GRID_ROOT_GROUP_ID, false)
})
});
}
var sortModel = gridSortModelSelector(state, apiRef.current.instanceId);
var sortRowList = buildAggregatedSortingApplier(sortModel, apiRef);
var sortedRows = apiRef.current.applyStrategyProcessor('sorting', {
sortRowList: sortRowList
});
return _extends({}, state, {
sorting: _extends({}, state.sorting, {
sortedRows: sortedRows
})
});
});
apiRef.current.publishEvent('sortedRowsSet');
apiRef.current.forceUpdate();
}, [apiRef, logger, props.sortingMode]);
var setSortModel = React.useCallback(function (model) {
var currentModel = gridSortModelSelector(apiRef);
if (currentModel !== model) {
logger.debug("Setting sort model");
apiRef.current.setState(mergeStateWithSortModel(model, props.disableMultipleColumnsSorting));
apiRef.current.forceUpdate();
apiRef.current.applySorting();
}
}, [apiRef, logger, props.disableMultipleColumnsSorting]);
var sortColumn = React.useCallback(function (column, direction, allowMultipleSorting) {
if (!column.sortable) {
return;
}
var sortItem = createSortItem(column, direction);
var sortModel;
if (!allowMultipleSorting || props.disableMultipleColumnsSorting) {
sortModel = !sortItem ? [] : [sortItem];
} else {
sortModel = upsertSortModel(column.field, sortItem);
}
apiRef.current.setSortModel(sortModel);
}, [apiRef, upsertSortModel, createSortItem, props.disableMultipleColumnsSorting]);
var getSortModel = React.useCallback(function () {
return gridSortModelSelector(apiRef);
}, [apiRef]);
var getSortedRows = React.useCallback(function () {
var sortedRows = gridSortedRowEntriesSelector(apiRef);
return sortedRows.map(function (row) {
return row.model;
});
}, [apiRef]);
var getSortedRowIds = React.useCallback(function () {
return gridSortedRowIdsSelector(apiRef);
}, [apiRef]);
var getRowIdFromRowIndex = React.useCallback(function (index) {
return apiRef.current.getSortedRowIds()[index];
}, [apiRef]);
var sortApi = {
getSortModel: getSortModel,
getSortedRows: getSortedRows,
getSortedRowIds: getSortedRowIds,
getRowIdFromRowIndex: getRowIdFromRowIndex,
setSortModel: setSortModel,
sortColumn: sortColumn,
applySorting: applySorting
};
useGridApiMethod(apiRef, sortApi, 'public');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState2;
var sortModelToExport = gridSortModelSelector(apiRef);
var shouldExportSortModel =
// Always export if the `exportOnlyDirtyModels` property is not activated
!context.exportOnlyDirtyModels ||
// Always export if the model is controlled
props.sortModel != null ||
// Always export if the model has been initialized
((_props$initialState2 = props.initialState) == null || (_props$initialState2 = _props$initialState2.sorting) == null ? void 0 : _props$initialState2.sortModel) != null ||
// Export if the model is not empty
sortModelToExport.length > 0;
if (!shouldExportSortModel) {
return prevState;
}
return _extends({}, prevState, {
sorting: {
sortModel: sortModelToExport
}
});
}, [apiRef, props.sortModel, (_props$initialState3 = props.initialState) == null || (_props$initialState3 = _props$initialState3.sorting) == null ? void 0 : _props$initialState3.sortModel]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto;
var sortModel = (_context$stateToResto = context.stateToRestore.sorting) == null ? void 0 : _context$stateToResto.sortModel;
if (sortModel == null) {
return params;
}
apiRef.current.setState(mergeStateWithSortModel(sortModel, props.disableMultipleColumnsSorting));
return _extends({}, params, {
callbacks: [].concat(_toConsumableArray(params.callbacks), [apiRef.current.applySorting])
});
}, [apiRef, props.disableMultipleColumnsSorting]);
var flatSortingMethod = React.useCallback(function (params) {
var rowTree = gridRowTreeSelector(apiRef);
var rootGroupNode = rowTree[GRID_ROOT_GROUP_ID];
var sortedChildren = params.sortRowList ? params.sortRowList(rootGroupNode.children.map(function (childId) {
return rowTree[childId];
})) : _toConsumableArray(rootGroupNode.children);
if (rootGroupNode.footerId != null) {
sortedChildren.push(rootGroupNode.footerId);
}
return sortedChildren;
}, [apiRef]);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
useGridRegisterStrategyProcessor(apiRef, GRID_DEFAULT_STRATEGY, 'sorting', flatSortingMethod);
/**
* EVENTS
*/
var handleColumnHeaderClick = React.useCallback(function (_ref2, event) {
var colDef = _ref2.colDef;
var allowMultipleSorting = event.shiftKey || event.metaKey || event.ctrlKey;
sortColumn(colDef, undefined, allowMultipleSorting);
}, [sortColumn]);
var handleColumnHeaderKeyDown = React.useCallback(function (_ref3, event) {
var colDef = _ref3.colDef;
// Ctrl + Enter opens the column menu
if (isEnterKey(event.key) && !event.ctrlKey && !event.metaKey) {
sortColumn(colDef, undefined, event.shiftKey);
}
}, [sortColumn]);
var handleColumnsChange = React.useCallback(function () {
// When the columns change we check that the sorted columns are still part of the dataset
var sortModel = gridSortModelSelector(apiRef);
var latestColumns = gridColumnLookupSelector(apiRef);
if (sortModel.length > 0) {
var newModel = sortModel.filter(function (sortItem) {
return latestColumns[sortItem.field];
});
if (newModel.length < sortModel.length) {
apiRef.current.setSortModel(newModel);
}
}
}, [apiRef]);
var handleStrategyProcessorChange = React.useCallback(function (methodName) {
if (methodName === 'sorting') {
apiRef.current.applySorting();
}
}, [apiRef]);
useGridRegisterPipeProcessor(apiRef, 'columnMenu', addColumnMenuItem);
useGridApiEventHandler(apiRef, 'columnHeaderClick', handleColumnHeaderClick);
useGridApiEventHandler(apiRef, 'columnHeaderKeyDown', handleColumnHeaderKeyDown);
useGridApiEventHandler(apiRef, 'rowsSet', apiRef.current.applySorting);
useGridApiEventHandler(apiRef, 'columnsChange', handleColumnsChange);
useGridApiEventHandler(apiRef, 'activeStrategyProcessorChange', handleStrategyProcessorChange);
/**
* 1ST RENDER
*/
useFirstRender(function () {
apiRef.current.applySorting();
});
/**
* EFFECTS
*/
useEnhancedEffect(function () {
if (props.sortModel !== undefined) {
apiRef.current.setSortModel(props.sortModel);
}
}, [apiRef, props.sortModel]);
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,25 @@
import * as React from 'react';
import { useGridApiMethod } from '../../utils';
export var useGridStatePersistence = function useGridStatePersistence(apiRef) {
var exportState = React.useCallback(function () {
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var stateToExport = apiRef.current.unstable_applyPipeProcessors('exportState', {}, params);
return stateToExport;
}, [apiRef]);
var restoreState = React.useCallback(function (stateToRestore) {
var response = apiRef.current.unstable_applyPipeProcessors('restoreState', {
callbacks: []
}, {
stateToRestore: stateToRestore
});
response.callbacks.forEach(function (callback) {
callback();
});
apiRef.current.forceUpdate();
}, [apiRef]);
var statePersistenceApi = {
exportState: exportState,
restoreState: restoreState
};
useGridApiMethod(apiRef, statePersistenceApi, 'public');
};

View File

@@ -0,0 +1,24 @@
import { createSelector } from '../../../utils/createSelector';
/**
* Get the columns state
* @category Virtualization
*/
export var gridVirtualizationSelector = function gridVirtualizationSelector(state) {
return state.virtualization;
};
/**
* Get the enabled state for virtualization
* @category Virtualization
*/
export var gridVirtualizationEnabledSelector = createSelector(gridVirtualizationSelector, function (state) {
return state.enabled;
});
/**
* Get the enabled state for virtualization
* @category Virtualization
*/
export var gridVirtualizationColumnEnabledSelector = createSelector(gridVirtualizationSelector, function (state) {
return state.enabledForColumns;
});

View File

@@ -0,0 +1,2 @@
export * from './useGridVirtualization';
export * from './gridVirtualizationSelectors';

View File

@@ -0,0 +1,636 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
var _excluded = ["style"],
_excluded2 = ["style"];
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useEventCallback as useEventCallback } from '@mui/utils';
import { useTheme } from '@mui/material/styles';
import { defaultMemoize } from 'reselect';
import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
import { useGridRootProps } from '../../utils/useGridRootProps';
import { useGridSelector } from '../../utils/useGridSelector';
import { gridVisibleColumnDefinitionsSelector, gridColumnsTotalWidthSelector, gridColumnPositionsSelector } from '../columns/gridColumnsSelector';
import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFocusStateSelector';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { clamp } from '../../../utils/utils';
import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector';
import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector';
import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils';
import { getMinimalContentHeight } from '../rows/gridRowsUtils';
import { gridVirtualizationEnabledSelector, gridVirtualizationColumnEnabledSelector } from './gridVirtualizationSelectors';
// Uses binary search to avoid looping through all possible positions
import { jsx as _jsx } from "react/jsx-runtime";
export function binarySearch(offset, positions) {
var sliceStart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var sliceEnd = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : positions.length;
if (positions.length <= 0) {
return -1;
}
if (sliceStart >= sliceEnd) {
return sliceStart;
}
var pivot = sliceStart + Math.floor((sliceEnd - sliceStart) / 2);
var itemOffset = positions[pivot];
return offset <= itemOffset ? binarySearch(offset, positions, sliceStart, pivot) : binarySearch(offset, positions, pivot + 1, sliceEnd);
}
function exponentialSearch(offset, positions, index) {
var interval = 1;
while (index < positions.length && Math.abs(positions[index]) < offset) {
index += interval;
interval *= 2;
}
return binarySearch(offset, positions, Math.floor(index / 2), Math.min(index, positions.length));
}
export var getRenderableIndexes = function getRenderableIndexes(_ref) {
var firstIndex = _ref.firstIndex,
lastIndex = _ref.lastIndex,
buffer = _ref.buffer,
minFirstIndex = _ref.minFirstIndex,
maxLastIndex = _ref.maxLastIndex;
return [clamp(firstIndex - buffer, minFirstIndex, maxLastIndex), clamp(lastIndex + buffer, minFirstIndex, maxLastIndex)];
};
export var areRenderContextsEqual = function areRenderContextsEqual(context1, context2) {
if (context1 === context2) {
return true;
}
return context1.firstRowIndex === context2.firstRowIndex && context1.lastRowIndex === context2.lastRowIndex && context1.firstColumnIndex === context2.firstColumnIndex && context1.lastColumnIndex === context2.lastColumnIndex;
};
// The `maxSize` is 3 so that reselect caches the `renderedColumns` values for the pinned left,
// unpinned, and pinned right sections.
var MEMOIZE_OPTIONS = {
maxSize: 3
};
export var useGridVirtualScroller = function useGridVirtualScroller(props) {
var apiRef = useGridPrivateApiContext();
var rootProps = useGridRootProps();
var visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector);
var enabled = useGridSelector(apiRef, gridVirtualizationEnabledSelector);
var enabledForColumns = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector);
var ref = props.ref,
onRenderZonePositioning = props.onRenderZonePositioning,
_props$renderZoneMinC = props.renderZoneMinColumnIndex,
renderZoneMinColumnIndex = _props$renderZoneMinC === void 0 ? 0 : _props$renderZoneMinC,
_props$renderZoneMaxC = props.renderZoneMaxColumnIndex,
renderZoneMaxColumnIndex = _props$renderZoneMaxC === void 0 ? visibleColumns.length : _props$renderZoneMaxC,
getRowProps = props.getRowProps;
var theme = useTheme();
var columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector);
var columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
var cellFocus = useGridSelector(apiRef, gridFocusCellSelector);
var cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector);
var rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector);
var selectedRowsLookup = useGridSelector(apiRef, selectedIdsLookupSelector);
var currentPage = useGridVisibleRows(apiRef, rootProps);
var renderZoneRef = React.useRef(null);
var rootRef = React.useRef(null);
var handleRef = useForkRef(ref, rootRef);
var _React$useState = React.useState(null),
_React$useState2 = _slicedToArray(_React$useState, 2),
renderContext = _React$useState2[0],
setRenderContextState = _React$useState2[1];
var prevRenderContext = React.useRef(renderContext);
var scrollPosition = React.useRef({
top: 0,
left: 0
});
var _React$useState3 = React.useState({
width: null,
height: null
}),
_React$useState4 = _slicedToArray(_React$useState3, 2),
containerDimensions = _React$useState4[0],
setContainerDimensions = _React$useState4[1];
var prevTotalWidth = React.useRef(columnsTotalWidth);
// Each visible row (not to be confused with a filter result) is composed of a central row element
// and up to two additional row elements for pinned columns (left and right).
// When hovering any of these elements, the :hover styles are applied only to the row element that
// was actually hovered, not its additional siblings. To make it look like a contiguous row,
// we add/remove the .Mui-hovered class to all of the row elements inside one visible row.
var _React$useState5 = React.useState(null),
_React$useState6 = _slicedToArray(_React$useState5, 2),
hoveredRowId = _React$useState6[0],
setHoveredRowId = _React$useState6[1];
var rowStyleCache = React.useRef(Object.create(null));
var prevGetRowProps = React.useRef();
var prevRootRowStyle = React.useRef();
var getRenderedColumnsRef = React.useRef(defaultMemoize(function (columns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, indexOfColumnWithFocusedCell) {
// If the selected column is not within the current range of columns being displayed,
// we need to render it at either the left or right of the columns,
// depending on whether it is above or below the range.
var focusedCellColumnIndexNotInRange;
var renderedColumns = columns.slice(firstColumnToRender, lastColumnToRender);
if (indexOfColumnWithFocusedCell > -1) {
// check if it is not on the left pinned column.
if (firstColumnToRender > indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell >= minFirstColumn) {
focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
}
// check if it is not on the right pinned column.
else if (lastColumnToRender < indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell < maxLastColumn) {
focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
}
}
return {
focusedCellColumnIndexNotInRange: focusedCellColumnIndexNotInRange,
renderedColumns: renderedColumns
};
}, MEMOIZE_OPTIONS));
var indexOfColumnWithFocusedCell = React.useMemo(function () {
if (cellFocus !== null) {
return visibleColumns.findIndex(function (column) {
return column.field === cellFocus.field;
});
}
return -1;
}, [cellFocus, visibleColumns]);
var computeRenderContext = React.useCallback(function () {
if (!enabled) {
return {
firstRowIndex: 0,
lastRowIndex: currentPage.rows.length,
firstColumnIndex: 0,
lastColumnIndex: visibleColumns.length
};
}
var _ref2 = scrollPosition.current,
top = _ref2.top,
left = _ref2.left;
// Clamp the value because the search may return an index out of bounds.
// In the last index, this is not needed because Array.slice doesn't include it.
var firstRowIndex = Math.min(getNearestIndexToRender(apiRef, currentPage, rowsMeta, top), rowsMeta.positions.length - 1);
var lastRowIndex = rootProps.autoHeight ? firstRowIndex + currentPage.rows.length : getNearestIndexToRender(apiRef, currentPage, rowsMeta, top + containerDimensions.height);
var firstColumnIndex = 0;
var lastColumnIndex = columnPositions.length;
if (enabledForColumns) {
var hasRowWithAutoHeight = false;
var _getRenderableIndexes = getRenderableIndexes({
firstIndex: firstRowIndex,
lastIndex: lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rootProps.rowBuffer
}),
_getRenderableIndexes2 = _slicedToArray(_getRenderableIndexes, 2),
firstRowToRender = _getRenderableIndexes2[0],
lastRowToRender = _getRenderableIndexes2[1];
for (var i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) {
var row = currentPage.rows[i];
hasRowWithAutoHeight = apiRef.current.rowHasAutoHeight(row.id);
}
if (!hasRowWithAutoHeight) {
firstColumnIndex = binarySearch(Math.abs(left), columnPositions);
lastColumnIndex = binarySearch(Math.abs(left) + containerDimensions.width, columnPositions);
}
}
return {
firstRowIndex: firstRowIndex,
lastRowIndex: lastRowIndex,
firstColumnIndex: firstColumnIndex,
lastColumnIndex: lastColumnIndex
};
}, [enabled, enabledForColumns, rowsMeta, rootProps.autoHeight, rootProps.rowBuffer, currentPage, columnPositions, visibleColumns.length, apiRef, containerDimensions]);
useEnhancedEffect(function () {
if (enabled) {
// TODO a scroll reset should not be necessary
rootRef.current.scrollLeft = 0;
rootRef.current.scrollTop = 0;
} else {
renderZoneRef.current.style.transform = "translate3d(0px, 0px, 0px)";
}
}, [enabled]);
useEnhancedEffect(function () {
setContainerDimensions({
width: rootRef.current.clientWidth,
height: rootRef.current.clientHeight
});
}, [rowsMeta.currentPageTotalHeight]);
var handleResize = React.useCallback(function () {
if (rootRef.current) {
setContainerDimensions({
width: rootRef.current.clientWidth,
height: rootRef.current.clientHeight
});
}
}, []);
useGridApiEventHandler(apiRef, 'debouncedResize', handleResize);
var updateRenderZonePosition = React.useCallback(function (nextRenderContext) {
var _getRenderableIndexes3 = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
lastIndex: nextRenderContext.lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rootProps.rowBuffer
}),
_getRenderableIndexes4 = _slicedToArray(_getRenderableIndexes3, 2),
firstRowToRender = _getRenderableIndexes4[0],
lastRowToRender = _getRenderableIndexes4[1];
var _getRenderableIndexes5 = getRenderableIndexes({
firstIndex: nextRenderContext.firstColumnIndex,
lastIndex: nextRenderContext.lastColumnIndex,
minFirstIndex: renderZoneMinColumnIndex,
maxLastIndex: renderZoneMaxColumnIndex,
buffer: rootProps.columnBuffer
}),
_getRenderableIndexes6 = _slicedToArray(_getRenderableIndexes5, 1),
initialFirstColumnToRender = _getRenderableIndexes6[0];
var firstColumnToRender = getFirstNonSpannedColumnToRender({
firstColumnToRender: initialFirstColumnToRender,
apiRef: apiRef,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
visibleRows: currentPage.rows
});
var direction = theme.direction === 'ltr' ? 1 : -1;
var top = gridRowsMetaSelector(apiRef.current.state).positions[firstRowToRender];
var left = direction * gridColumnPositionsSelector(apiRef)[firstColumnToRender]; // Call directly the selector because it might be outdated when this method is called
renderZoneRef.current.style.transform = "translate3d(".concat(left, "px, ").concat(top, "px, 0px)");
if (typeof onRenderZonePositioning === 'function') {
onRenderZonePositioning({
top: top,
left: left
});
}
}, [apiRef, currentPage.rows, onRenderZonePositioning, renderZoneMinColumnIndex, renderZoneMaxColumnIndex, rootProps.columnBuffer, rootProps.rowBuffer, theme.direction]);
var getRenderContext = React.useCallback(function () {
return prevRenderContext.current;
}, []);
var setRenderContext = React.useCallback(function (nextRenderContext) {
if (prevRenderContext.current && areRenderContextsEqual(nextRenderContext, prevRenderContext.current)) {
updateRenderZonePosition(nextRenderContext);
return;
}
setRenderContextState(nextRenderContext);
updateRenderZonePosition(nextRenderContext);
var _getRenderableIndexes7 = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
lastIndex: nextRenderContext.lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rootProps.rowBuffer
}),
_getRenderableIndexes8 = _slicedToArray(_getRenderableIndexes7, 2),
firstRowToRender = _getRenderableIndexes8[0],
lastRowToRender = _getRenderableIndexes8[1];
apiRef.current.publishEvent('renderedRowsIntervalChange', {
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender
});
prevRenderContext.current = nextRenderContext;
}, [apiRef, setRenderContextState, prevRenderContext, currentPage.rows.length, rootProps.rowBuffer, updateRenderZonePosition]);
useEnhancedEffect(function () {
if (containerDimensions.width == null) {
return;
}
var initialRenderContext = computeRenderContext();
setRenderContext(initialRenderContext);
var _ref3 = scrollPosition.current,
top = _ref3.top,
left = _ref3.left;
var params = {
top: top,
left: left,
renderContext: initialRenderContext
};
apiRef.current.publishEvent('scrollPositionChange', params);
}, [apiRef, computeRenderContext, containerDimensions.width, setRenderContext]);
var handleScroll = useEventCallback(function (event) {
var _event$currentTarget = event.currentTarget,
scrollTop = _event$currentTarget.scrollTop,
scrollLeft = _event$currentTarget.scrollLeft;
scrollPosition.current.top = scrollTop;
scrollPosition.current.left = scrollLeft;
// On iOS and macOS, negative offsets are possible when swiping past the start
if (!prevRenderContext.current || scrollTop < 0) {
return;
}
if (theme.direction === 'ltr') {
if (scrollLeft < 0) {
return;
}
}
if (theme.direction === 'rtl') {
if (scrollLeft > 0) {
return;
}
}
// When virtualization is disabled, the context never changes during scroll
var nextRenderContext = enabled ? computeRenderContext() : prevRenderContext.current;
var topRowsScrolledSincePreviousRender = Math.abs(nextRenderContext.firstRowIndex - prevRenderContext.current.firstRowIndex);
var bottomRowsScrolledSincePreviousRender = Math.abs(nextRenderContext.lastRowIndex - prevRenderContext.current.lastRowIndex);
var topColumnsScrolledSincePreviousRender = Math.abs(nextRenderContext.firstColumnIndex - prevRenderContext.current.firstColumnIndex);
var bottomColumnsScrolledSincePreviousRender = Math.abs(nextRenderContext.lastColumnIndex - prevRenderContext.current.lastColumnIndex);
var shouldSetState = topRowsScrolledSincePreviousRender >= rootProps.rowThreshold || bottomRowsScrolledSincePreviousRender >= rootProps.rowThreshold || topColumnsScrolledSincePreviousRender >= rootProps.columnThreshold || bottomColumnsScrolledSincePreviousRender >= rootProps.columnThreshold || prevTotalWidth.current !== columnsTotalWidth;
apiRef.current.publishEvent('scrollPositionChange', {
top: scrollTop,
left: scrollLeft,
renderContext: shouldSetState ? nextRenderContext : prevRenderContext.current
}, event);
if (shouldSetState) {
// Prevents batching render context changes
ReactDOM.flushSync(function () {
setRenderContext(nextRenderContext);
});
prevTotalWidth.current = columnsTotalWidth;
}
});
var handleWheel = useEventCallback(function (event) {
apiRef.current.publishEvent('virtualScrollerWheel', {}, event);
});
var handleTouchMove = useEventCallback(function (event) {
apiRef.current.publishEvent('virtualScrollerTouchMove', {}, event);
});
var indexOfRowWithFocusedCell = React.useMemo(function () {
if (cellFocus !== null) {
return currentPage.rows.findIndex(function (row) {
return row.id === cellFocus.id;
});
}
return -1;
}, [cellFocus, currentPage.rows]);
useGridApiEventHandler(apiRef, 'rowMouseOver', function (params, event) {
var _params$id;
if (event.currentTarget.contains(event.relatedTarget)) {
return;
}
setHoveredRowId((_params$id = params.id) != null ? _params$id : null);
});
useGridApiEventHandler(apiRef, 'rowMouseOut', function (params, event) {
if (event.currentTarget.contains(event.relatedTarget)) {
return;
}
setHoveredRowId(null);
});
var getRows = function getRows() {
var _rootProps$slotProps;
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
renderContext: renderContext
};
var onRowRender = params.onRowRender,
nextRenderContext = params.renderContext,
_params$minFirstColum = params.minFirstColumn,
minFirstColumn = _params$minFirstColum === void 0 ? renderZoneMinColumnIndex : _params$minFirstColum,
_params$maxLastColumn = params.maxLastColumn,
maxLastColumn = _params$maxLastColumn === void 0 ? renderZoneMaxColumnIndex : _params$maxLastColumn,
_params$availableSpac = params.availableSpace,
availableSpace = _params$availableSpac === void 0 ? containerDimensions.width : _params$availableSpac,
_params$rowIndexOffse = params.rowIndexOffset,
rowIndexOffset = _params$rowIndexOffse === void 0 ? 0 : _params$rowIndexOffse,
_params$position = params.position,
position = _params$position === void 0 ? 'center' : _params$position;
if (!nextRenderContext || availableSpace == null) {
return null;
}
var rowBuffer = enabled ? rootProps.rowBuffer : 0;
var columnBuffer = enabled ? rootProps.columnBuffer : 0;
var _getRenderableIndexes9 = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
lastIndex: nextRenderContext.lastRowIndex,
minFirstIndex: 0,
maxLastIndex: currentPage.rows.length,
buffer: rowBuffer
}),
_getRenderableIndexes10 = _slicedToArray(_getRenderableIndexes9, 2),
firstRowToRender = _getRenderableIndexes10[0],
lastRowToRender = _getRenderableIndexes10[1];
var renderedRows = [];
if (params.rows) {
params.rows.forEach(function (row) {
renderedRows.push(row);
apiRef.current.calculateColSpan({
rowId: row.id,
minFirstColumn: minFirstColumn,
maxLastColumn: maxLastColumn,
columns: visibleColumns
});
});
} else {
if (!currentPage.range) {
return null;
}
for (var i = firstRowToRender; i < lastRowToRender; i += 1) {
var row = currentPage.rows[i];
renderedRows.push(row);
apiRef.current.calculateColSpan({
rowId: row.id,
minFirstColumn: minFirstColumn,
maxLastColumn: maxLastColumn,
columns: visibleColumns
});
}
}
// If the selected row is not within the current range of rows being displayed,
// we need to render it at either the top or bottom of the rows,
// depending on whether it is above or below the range.
var isRowWithFocusedCellNotInRange = false;
if (indexOfRowWithFocusedCell > -1) {
var rowWithFocusedCell = currentPage.rows[indexOfRowWithFocusedCell];
if (firstRowToRender > indexOfRowWithFocusedCell || lastRowToRender < indexOfRowWithFocusedCell) {
isRowWithFocusedCellNotInRange = true;
if (indexOfRowWithFocusedCell > firstRowToRender) {
renderedRows.push(rowWithFocusedCell);
} else {
renderedRows.unshift(rowWithFocusedCell);
}
apiRef.current.calculateColSpan({
rowId: rowWithFocusedCell.id,
minFirstColumn: minFirstColumn,
maxLastColumn: maxLastColumn,
columns: visibleColumns
});
}
}
var _getRenderableIndexes11 = getRenderableIndexes({
firstIndex: nextRenderContext.firstColumnIndex,
lastIndex: nextRenderContext.lastColumnIndex,
minFirstIndex: minFirstColumn,
maxLastIndex: maxLastColumn,
buffer: columnBuffer
}),
_getRenderableIndexes12 = _slicedToArray(_getRenderableIndexes11, 2),
initialFirstColumnToRender = _getRenderableIndexes12[0],
lastColumnToRender = _getRenderableIndexes12[1];
var firstColumnToRender = getFirstNonSpannedColumnToRender({
firstColumnToRender: initialFirstColumnToRender,
apiRef: apiRef,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
visibleRows: currentPage.rows
});
var isColumnWihFocusedCellNotInRange = false;
if (firstColumnToRender > indexOfColumnWithFocusedCell || lastColumnToRender < indexOfColumnWithFocusedCell) {
isColumnWihFocusedCellNotInRange = true;
}
var _getRenderedColumnsRe = getRenderedColumnsRef.current(visibleColumns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, isColumnWihFocusedCellNotInRange ? indexOfColumnWithFocusedCell : -1),
focusedCellColumnIndexNotInRange = _getRenderedColumnsRe.focusedCellColumnIndexNotInRange,
renderedColumns = _getRenderedColumnsRe.renderedColumns;
var _ref4 = ((_rootProps$slotProps = rootProps.slotProps) == null ? void 0 : _rootProps$slotProps.row) || {},
rootRowStyle = _ref4.style,
rootRowProps = _objectWithoutProperties(_ref4, _excluded);
var invalidatesCachedRowStyle = prevGetRowProps.current !== getRowProps || prevRootRowStyle.current !== rootRowStyle;
if (invalidatesCachedRowStyle) {
rowStyleCache.current = Object.create(null);
}
var rows = [];
var isRowWithFocusedCellRendered = false;
for (var _i = 0; _i < renderedRows.length; _i += 1) {
var _currentPage$range;
var _renderedRows$_i = renderedRows[_i],
_id = _renderedRows$_i.id,
_model = _renderedRows$_i.model;
var isRowNotVisible = isRowWithFocusedCellNotInRange && cellFocus.id === _id;
var lastVisibleRowIndex = isRowWithFocusedCellNotInRange ? firstRowToRender + _i === currentPage.rows.length : firstRowToRender + _i === currentPage.rows.length - 1;
var baseRowHeight = !apiRef.current.rowHasAutoHeight(_id) ? apiRef.current.unstable_getRowHeight(_id) : 'auto';
var isSelected = void 0;
if (selectedRowsLookup[_id] == null) {
isSelected = false;
} else {
isSelected = apiRef.current.isRowSelectable(_id);
}
if (onRowRender) {
onRowRender(_id);
}
var focusedCell = cellFocus !== null && cellFocus.id === _id ? cellFocus.field : null;
var columnWithFocusedCellNotInRange = focusedCellColumnIndexNotInRange !== undefined && visibleColumns[focusedCellColumnIndexNotInRange];
var renderedColumnsWithFocusedCell = columnWithFocusedCellNotInRange && focusedCell ? [columnWithFocusedCellNotInRange].concat(_toConsumableArray(renderedColumns)) : renderedColumns;
var tabbableCell = null;
if (cellTabIndex !== null && cellTabIndex.id === _id) {
var cellParams = apiRef.current.getCellParams(_id, cellTabIndex.field);
tabbableCell = cellParams.cellMode === 'view' ? cellTabIndex.field : null;
}
var _ref5 = typeof getRowProps === 'function' && getRowProps(_id, _model) || {},
rowStyle = _ref5.style,
rowProps = _objectWithoutProperties(_ref5, _excluded2);
if (!rowStyleCache.current[_id]) {
var style = _extends({}, rowStyle, rootRowStyle);
rowStyleCache.current[_id] = style;
}
var index = rowIndexOffset + ((currentPage == null || (_currentPage$range = currentPage.range) == null ? void 0 : _currentPage$range.firstRowIndex) || 0) + firstRowToRender + _i;
if (isRowWithFocusedCellNotInRange && (cellFocus == null ? void 0 : cellFocus.id) === _id) {
index = indexOfRowWithFocusedCell;
isRowWithFocusedCellRendered = true;
} else if (isRowWithFocusedCellRendered) {
index -= 1;
}
rows.push( /*#__PURE__*/_jsx(rootProps.slots.row, _extends({
row: _model,
rowId: _id,
focusedCellColumnIndexNotInRange: focusedCellColumnIndexNotInRange,
isNotVisible: isRowNotVisible,
rowHeight: baseRowHeight,
focusedCell: focusedCell,
tabbableCell: tabbableCell,
renderedColumns: renderedColumnsWithFocusedCell,
visibleColumns: visibleColumns,
firstColumnToRender: firstColumnToRender,
lastColumnToRender: lastColumnToRender,
selected: isSelected,
index: index,
containerWidth: availableSpace,
isLastVisible: lastVisibleRowIndex,
position: position
}, rowProps, rootRowProps, {
hovered: hoveredRowId === _id,
style: rowStyleCache.current[_id]
}), _id));
}
prevGetRowProps.current = getRowProps;
prevRootRowStyle.current = rootRowStyle;
return rows;
};
var needsHorizontalScrollbar = containerDimensions.width && columnsTotalWidth >= containerDimensions.width;
var contentSize = React.useMemo(function () {
// In cases where the columns exceed the available width,
// the horizontal scrollbar should be shown even when there're no rows.
// Keeping 1px as minimum height ensures that the scrollbar will visible if necessary.
var height = Math.max(rowsMeta.currentPageTotalHeight, 1);
var shouldExtendContent = false;
if (rootRef != null && rootRef.current && height <= (rootRef == null ? void 0 : rootRef.current.clientHeight)) {
shouldExtendContent = true;
}
var size = {
width: needsHorizontalScrollbar ? columnsTotalWidth : 'auto',
height: height,
minHeight: shouldExtendContent ? '100%' : 'auto'
};
if (rootProps.autoHeight && currentPage.rows.length === 0) {
size.height = getMinimalContentHeight(apiRef, rootProps.rowHeight); // Give room to show the overlay when there no rows.
}
return size;
}, [apiRef, rootRef, columnsTotalWidth, rowsMeta.currentPageTotalHeight, needsHorizontalScrollbar, rootProps.autoHeight, rootProps.rowHeight, currentPage.rows.length]);
React.useEffect(function () {
apiRef.current.publishEvent('virtualScrollerContentSizeChange');
}, [apiRef, contentSize]);
var rootStyle = React.useMemo(function () {
var style = {};
if (!needsHorizontalScrollbar) {
style.overflowX = 'hidden';
}
if (rootProps.autoHeight) {
style.overflowY = 'hidden';
}
return style;
}, [needsHorizontalScrollbar, rootProps.autoHeight]);
apiRef.current.register('private', {
getRenderContext: getRenderContext
});
return {
renderContext: renderContext,
updateRenderZonePosition: updateRenderZonePosition,
getRows: getRows,
getRootProps: function getRootProps() {
var inputProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return _extends({
ref: handleRef,
onScroll: handleScroll,
onWheel: handleWheel,
onTouchMove: handleTouchMove
}, inputProps, {
style: inputProps.style ? _extends({}, inputProps.style, rootStyle) : rootStyle,
role: 'presentation'
});
},
getContentProps: function getContentProps() {
var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
style = _ref6.style;
return {
style: style ? _extends({}, style, contentSize) : contentSize,
role: 'presentation'
};
},
getRenderZoneProps: function getRenderZoneProps() {
return {
ref: renderZoneRef,
role: 'rowgroup'
};
}
};
};
function getNearestIndexToRender(apiRef, currentPage, rowsMeta, offset) {
var _currentPage$range2, _currentPage$range3;
var lastMeasuredIndexRelativeToAllRows = apiRef.current.getLastMeasuredRowIndex();
var allRowsMeasured = lastMeasuredIndexRelativeToAllRows === Infinity;
if ((_currentPage$range2 = currentPage.range) != null && _currentPage$range2.lastRowIndex && !allRowsMeasured) {
// Check if all rows in this page are already measured
allRowsMeasured = lastMeasuredIndexRelativeToAllRows >= currentPage.range.lastRowIndex;
}
var lastMeasuredIndexRelativeToCurrentPage = clamp(lastMeasuredIndexRelativeToAllRows - (((_currentPage$range3 = currentPage.range) == null ? void 0 : _currentPage$range3.firstRowIndex) || 0), 0, rowsMeta.positions.length);
if (allRowsMeasured || rowsMeta.positions[lastMeasuredIndexRelativeToCurrentPage] >= offset) {
// If all rows were measured (when no row has "auto" as height) or all rows before the offset
// were measured, then use a binary search because it's faster.
return binarySearch(offset, rowsMeta.positions);
}
// Otherwise, use an exponential search.
// If rows have "auto" as height, their positions will be based on estimated heights.
// In this case, we can skip several steps until we find a position higher than the offset.
// Inspired by https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/utils/CellSizeAndPositionManager.js
return exponentialSearch(offset, rowsMeta.positions, lastMeasuredIndexRelativeToCurrentPage);
}

View File

@@ -0,0 +1,51 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
export var virtualizationStateInitializer = function virtualizationStateInitializer(state, props) {
var virtualization = {
enabled: !props.disableVirtualization,
enabledForColumns: true
};
return _extends({}, state, {
virtualization: virtualization
});
};
export function useGridVirtualization(apiRef, props) {
/*
* API METHODS
*/
var setVirtualization = function setVirtualization(enabled) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
virtualization: _extends({}, state.virtualization, {
enabled: enabled
})
});
});
};
var setColumnVirtualization = function setColumnVirtualization(enabled) {
apiRef.current.setState(function (state) {
return _extends({}, state, {
virtualization: _extends({}, state.virtualization, {
enabledForColumns: enabled
})
});
});
};
var api = {
unstable_setVirtualization: setVirtualization,
unstable_setColumnVirtualization: setColumnVirtualization
};
useGridApiMethod(apiRef, api, 'public');
/*
* EFFECTS
*/
/* eslint-disable react-hooks/exhaustive-deps */
React.useEffect(function () {
setVirtualization(!props.disableVirtualization);
}, [props.disableVirtualization]);
/* eslint-enable react-hooks/exhaustive-deps */
}