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,16 @@
import { GridColumnGroup } from '../../../models/gridColumnGrouping';
export type GridColumnGroupLookup = {
[groupId: string]: Omit<GridColumnGroup, 'children'>;
};
export type GridGroupingStructure = {
groupId: null | string;
columnFields: string[];
};
export interface GridColumnsGroupingState {
lookup: GridColumnGroupLookup;
headerStructure: GridGroupingStructure[][];
unwrappedGroupingModel: {
[columnField: string]: GridColumnGroup['groupId'][];
};
maxDepth: number;
}

View File

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

View File

@@ -0,0 +1,12 @@
import { GridStateCommunity } from '../../../models/gridStateCommunity';
/**
* @category ColumnGrouping
* @ignore - do not document.
*/
export declare const gridColumnGroupingSelector: (state: GridStateCommunity) => import("./gridColumnGroupsInterfaces").GridColumnsGroupingState;
export declare const gridColumnGroupsUnwrappedModelSelector: import("../../../utils/createSelector").OutputSelector<GridStateCommunity, {
[columnField: string]: string[];
}>;
export declare const gridColumnGroupsLookupSelector: import("../../../utils/createSelector").OutputSelector<GridStateCommunity, import("./gridColumnGroupsInterfaces").GridColumnGroupLookup>;
export declare const gridColumnGroupsHeaderStructureSelector: import("../../../utils/createSelector").OutputSelector<GridStateCommunity, import("./gridColumnGroupsInterfaces").GridGroupingStructure[][]>;
export declare const gridColumnGroupsHeaderMaxDepthSelector: import("../../../utils/createSelector").OutputSelector<GridStateCommunity, number>;

View File

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

View File

@@ -0,0 +1,18 @@
import { GridColumnGroupingModel, GridColumnGroup } from '../../../models/gridColumnGrouping';
import { GridColDef } from '../../../models/colDef';
import { GridGroupingStructure } from './gridColumnGroupsInterfaces';
type UnwrappedGroupingModel = {
[key: GridColDef['field']]: GridColumnGroup['groupId'][];
};
/**
* 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 declare const unwrapGroupingColumnModel: (columnGroupingModel?: GridColumnGroupingModel) => UnwrappedGroupingModel;
export declare const getColumnGroupsHeaderStructure: (orderedColumns: string[], unwrappedGroupingModel: UnwrappedGroupingModel, pinnedFields: {
right?: string[];
left?: string[];
}) => GridGroupingStructure[][];
export {};

View File

@@ -0,0 +1,86 @@
import { isLeaf } from '../../../models/gridColumnGrouping';
import { isDeepEqual } from '../../../utils/utils';
// This is the recurrence function that help writing `unwrapGroupingColumnModel()`
const recurrentUnwrapGroupingColumnModel = (columnGroupNode, parents, unwrappedGroupingModelToComplete) => {
if (isLeaf(columnGroupNode)) {
if (unwrappedGroupingModelToComplete[columnGroupNode.field] !== undefined) {
throw new Error([`MUI: columnGroupingModel contains duplicated field`, `column field ${columnGroupNode.field} occurs two times in the grouping model:`, `- ${unwrappedGroupingModelToComplete[columnGroupNode.field].join(' > ')}`, `- ${parents.join(' > ')}`].join('\n'));
}
unwrappedGroupingModelToComplete[columnGroupNode.field] = parents;
return;
}
const {
groupId,
children
} = columnGroupNode;
children.forEach(child => {
recurrentUnwrapGroupingColumnModel(child, [...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 const unwrapGroupingColumnModel = columnGroupingModel => {
if (!columnGroupingModel) {
return {};
}
const unwrappedSubTree = {};
columnGroupingModel.forEach(columnGroupNode => {
recurrentUnwrapGroupingColumnModel(columnGroupNode, [], unwrappedSubTree);
});
return unwrappedSubTree;
};
export const getColumnGroupsHeaderStructure = (orderedColumns, unwrappedGroupingModel, pinnedFields) => {
const getParents = field => {
var _unwrappedGroupingMod;
return (_unwrappedGroupingMod = unwrappedGroupingModel[field]) != null ? _unwrappedGroupingMod : [];
};
const groupingHeaderStructure = [];
const maxDepth = Math.max(...orderedColumns.map(field => getParents(field).length));
const haveSameParents = (field1, field2, depth) => isDeepEqual(getParents(field1).slice(0, depth + 1), getParents(field2).slice(0, depth + 1));
const 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;
};
for (let depth = 0; depth < maxDepth; depth += 1) {
const depthStructure = orderedColumns.reduce((structure, newField) => {
var _getParents$depth;
const groupId = (_getParents$depth = getParents(newField)[depth]) != null ? _getParents$depth : null;
if (structure.length === 0) {
return [{
columnFields: [newField],
groupId
}];
}
const lastGroup = structure[structure.length - 1];
const prevField = lastGroup.columnFields[lastGroup.columnFields.length - 1];
const 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 [...structure, {
columnFields: [newField],
groupId
}];
}
// It extends the previous group
return [...structure.slice(0, structure.length - 1), {
columnFields: [...lastGroup.columnFields, newField],
groupId
}];
}, []);
groupingHeaderStructure.push(depthStructure);
}
return groupingHeaderStructure;
};

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { GridStateInitializer } from '../../utils/useGridInitializeState';
export declare const columnGroupsStateInitializer: GridStateInitializer<Pick<DataGridProcessedProps, 'columnGroupingModel' | 'experimentalFeatures'>>;
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
*/
export declare const useGridColumnGrouping: (apiRef: React.MutableRefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, 'columnGroupingModel' | 'experimentalFeatures'>) => void;

View File

@@ -0,0 +1,147 @@
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
const _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';
const createGroupLookup = columnGroupingModel => {
let groupLookup = {};
columnGroupingModel.forEach(node => {
if (isLeaf(node)) {
return;
}
const {
groupId,
children
} = node,
other = _objectWithoutPropertiesLoose(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=${groupId} has no children.`);
}
const groupParam = _extends({}, other, {
groupId
});
const subTreeLookup = createGroupLookup(children);
if (subTreeLookup[groupId] !== undefined || groupLookup[groupId] !== undefined) {
throw new Error(`MUI: The groupId ${groupId} is used multiple times in the columnGroupingModel.`);
}
groupLookup = _extends({}, groupLookup, subTreeLookup, {
[groupId]: groupParam
});
});
return _extends({}, groupLookup);
};
export const 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;
}
const columnFields = gridColumnFieldsSelector(apiRef);
const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef);
const groupLookup = createGroupLookup((_props$columnGrouping = props.columnGroupingModel) != null ? _props$columnGrouping : []);
const unwrappedGroupingModel = unwrapGroupingColumnModel((_props$columnGrouping2 = props.columnGroupingModel) != null ? _props$columnGrouping2 : []);
const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(columnFields, unwrappedGroupingModel, // @ts-expect-error Move this part to `Pro` package
(_apiRef$current$state = apiRef.current.state.pinnedColumns) != null ? _apiRef$current$state : {});
const maxDepth = visibleColumnFields.length === 0 ? 0 : Math.max(...visibleColumnFields.map(field => {
var _unwrappedGroupingMod, _unwrappedGroupingMod2;
return (_unwrappedGroupingMod = (_unwrappedGroupingMod2 = unwrappedGroupingModel[field]) == null ? void 0 : _unwrappedGroupingMod2.length) != null ? _unwrappedGroupingMod : 0;
}));
return _extends({}, state, {
columnGrouping: {
lookup: groupLookup,
unwrappedGroupingModel,
headerStructure: columnGroupsHeaderStructure,
maxDepth
}
});
};
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
*/
export const useGridColumnGrouping = (apiRef, props) => {
var _props$experimentalFe3;
/**
* API METHODS
*/
const getColumnGroupPath = React.useCallback(field => {
var _unwrappedGroupingMod3;
const unwrappedGroupingModel = gridColumnGroupsUnwrappedModelSelector(apiRef);
return (_unwrappedGroupingMod3 = unwrappedGroupingModel[field]) != null ? _unwrappedGroupingMod3 : [];
}, [apiRef]);
const getAllGroupDetails = React.useCallback(() => {
const columnGroupLookup = gridColumnGroupsLookupSelector(apiRef);
return columnGroupLookup;
}, [apiRef]);
const columnGroupingApi = {
unstable_getColumnGroupPath: getColumnGroupPath,
unstable_getAllGroupDetails: getAllGroupDetails
};
useGridApiMethod(apiRef, columnGroupingApi, 'public');
const handleColumnIndexChange = React.useCallback(() => {
var _props$columnGrouping3;
const unwrappedGroupingModel = unwrapGroupingColumnModel((_props$columnGrouping3 = props.columnGroupingModel) != null ? _props$columnGrouping3 : []);
apiRef.current.setState(state => {
var _state$columns$ordere, _state$columns, _state$pinnedColumns;
const 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
const pinnedColumns = (_state$pinnedColumns = state.pinnedColumns) != null ? _state$pinnedColumns : {};
const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(orderedFields, unwrappedGroupingModel, pinnedColumns);
return _extends({}, state, {
columnGrouping: _extends({}, state.columnGrouping, {
headerStructure: columnGroupsHeaderStructure
})
});
});
}, [apiRef, props.columnGroupingModel]);
const updateColumnGroupingState = React.useCallback(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
const 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 : {};
const columnFields = gridColumnFieldsSelector(apiRef);
const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef);
const groupLookup = createGroupLookup(columnGroupingModel != null ? columnGroupingModel : []);
const unwrappedGroupingModel = unwrapGroupingColumnModel(columnGroupingModel != null ? columnGroupingModel : []);
const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure(columnFields, unwrappedGroupingModel, pinnedColumns);
const maxDepth = visibleColumnFields.length === 0 ? 0 : Math.max(...visibleColumnFields.map(field => {
var _unwrappedGroupingMod4, _unwrappedGroupingMod5;
return (_unwrappedGroupingMod4 = (_unwrappedGroupingMod5 = unwrappedGroupingModel[field]) == null ? void 0 : _unwrappedGroupingMod5.length) != null ? _unwrappedGroupingMod4 : 0;
}));
apiRef.current.setState(state => {
return _extends({}, state, {
columnGrouping: {
lookup: groupLookup,
unwrappedGroupingModel,
headerStructure: columnGroupsHeaderStructure,
maxDepth
}
});
});
}, [apiRef, (_props$experimentalFe3 = props.experimentalFeatures) == null ? void 0 : _props$experimentalFe3.columnGrouping]);
useGridApiEventHandler(apiRef, 'columnIndexChange', handleColumnIndexChange);
useGridApiEventHandler(apiRef, 'columnsChange', () => {
updateColumnGroupingState(props.columnGroupingModel);
});
useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', () => {
updateColumnGroupingState(props.columnGroupingModel);
});
/**
* EFFECTS
*/
React.useEffect(() => {
updateColumnGroupingState(props.columnGroupingModel);
}, [updateColumnGroupingState, props.columnGroupingModel]);
};