diff --git a/docs/pages/x/api/tree-view/rich-tree-view.json b/docs/pages/x/api/tree-view/rich-tree-view.json
index c80f7ce68570a..bc08974346195 100644
--- a/docs/pages/x/api/tree-view/rich-tree-view.json
+++ b/docs/pages/x/api/tree-view/rich-tree-view.json
@@ -16,6 +16,9 @@
"disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" },
"disableSelection": { "type": { "name": "bool" }, "default": "false" },
"expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } },
+ "experimentalFeatures": {
+ "type": { "name": "shape", "description": "{ indentationAtItemLevel?: bool }" }
+ },
"getItemId": {
"type": { "name": "func" },
"default": "(item) => item.id",
diff --git a/docs/pages/x/api/tree-view/simple-tree-view.json b/docs/pages/x/api/tree-view/simple-tree-view.json
index f346fcd7a7004..6003d38a33a93 100644
--- a/docs/pages/x/api/tree-view/simple-tree-view.json
+++ b/docs/pages/x/api/tree-view/simple-tree-view.json
@@ -17,6 +17,9 @@
"disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" },
"disableSelection": { "type": { "name": "bool" }, "default": "false" },
"expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } },
+ "experimentalFeatures": {
+ "type": { "name": "shape", "description": "{ indentationAtItemLevel?: bool }" }
+ },
"id": { "type": { "name": "string" } },
"multiSelect": { "type": { "name": "bool" }, "default": "false" },
"onExpandedItemsChange": {
diff --git a/docs/pages/x/api/tree-view/tree-view.json b/docs/pages/x/api/tree-view/tree-view.json
index 4fd0587a6b119..45a574640afff 100644
--- a/docs/pages/x/api/tree-view/tree-view.json
+++ b/docs/pages/x/api/tree-view/tree-view.json
@@ -17,6 +17,9 @@
"disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" },
"disableSelection": { "type": { "name": "bool" }, "default": "false" },
"expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } },
+ "experimentalFeatures": {
+ "type": { "name": "shape", "description": "{ indentationAtItemLevel?: bool }" }
+ },
"id": { "type": { "name": "string" } },
"multiSelect": { "type": { "name": "bool" }, "default": "false" },
"onExpandedItemsChange": {
diff --git a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json
index 1dc23fbbce097..17c5fb3d6eb9d 100644
--- a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json
+++ b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json
@@ -21,6 +21,9 @@
"expandedItems": {
"description": "Expanded item ids. Used when the item's expansion is controlled."
},
+ "experimentalFeatures": {
+ "description": "Unstable features, breaking changes might be introduced. For each feature, if the flag is not explicitly set to true
, the feature will be fully disabled and any property / method call will not have any effect."
+ },
"getItemId": {
"description": "Used to determine the id of a given item.",
"typeDescriptions": { "item": "The item to check.", "string": "The id of the item." }
diff --git a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json
index 637ca728dab07..2523d471b4e72 100644
--- a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json
+++ b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json
@@ -22,6 +22,9 @@
"expandedItems": {
"description": "Expanded item ids. Used when the item's expansion is controlled."
},
+ "experimentalFeatures": {
+ "description": "Unstable features, breaking changes might be introduced. For each feature, if the flag is not explicitly set to true
, the feature will be fully disabled and any property / method call will not have any effect."
+ },
"id": {
"description": "This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id."
},
diff --git a/docs/translations/api-docs/tree-view/tree-view/tree-view.json b/docs/translations/api-docs/tree-view/tree-view/tree-view.json
index 907ed9df8c7c3..2e1fed84729f8 100644
--- a/docs/translations/api-docs/tree-view/tree-view/tree-view.json
+++ b/docs/translations/api-docs/tree-view/tree-view/tree-view.json
@@ -22,6 +22,9 @@
"expandedItems": {
"description": "Expanded item ids. Used when the item's expansion is controlled."
},
+ "experimentalFeatures": {
+ "description": "Unstable features, breaking changes might be introduced. For each feature, if the flag is not explicitly set to true
, the feature will be fully disabled and any property / method call will not have any effect."
+ },
"id": {
"description": "This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id."
},
diff --git a/packages/x-tree-view-pro/src/RichTreeViewPro/RichTreeViewPro.types.ts b/packages/x-tree-view-pro/src/RichTreeViewPro/RichTreeViewPro.types.ts
index 5921da69be73f..7603b6fde12d8 100644
--- a/packages/x-tree-view-pro/src/RichTreeViewPro/RichTreeViewPro.types.ts
+++ b/packages/x-tree-view-pro/src/RichTreeViewPro/RichTreeViewPro.types.ts
@@ -5,7 +5,7 @@ import { SlotComponentProps } from '@mui/base/utils';
import { TreeItem, TreeItemProps } from '@mui/x-tree-view/TreeItem';
import { TreeItem2Props } from '@mui/x-tree-view/TreeItem2';
import { TreeViewItemId } from '@mui/x-tree-view/models';
-import { TreeViewPublicAPI } from '@mui/x-tree-view/internals';
+import { TreeViewPublicAPI, TreeViewExperimentalFeatures } from '@mui/x-tree-view/internals';
import { RichTreeViewProClasses } from './richTreeViewProClasses';
import {
DefaultTreeViewProPluginParameters,
@@ -71,4 +71,10 @@ export interface RichTreeViewProProps;
}
diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx
index 624d106a3a0d8..7a3ea33fbf288 100644
--- a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx
+++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx
@@ -194,6 +194,14 @@ RichTreeView.propTypes = {
* Used when the item's expansion is controlled.
*/
expandedItems: PropTypes.arrayOf(PropTypes.string),
+ /**
+ * Unstable features, breaking changes might be introduced.
+ * For each feature, if the flag is not explicitly set to `true`,
+ * the feature will be fully disabled and any property / method call will not have any effect.
+ */
+ experimentalFeatures: PropTypes.shape({
+ indentationAtItemLevel: PropTypes.bool,
+ }),
/**
* Used to determine the id of a given item.
*
diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts
index a2ffd144c2f8d..974a665b69a46 100644
--- a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts
+++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts
@@ -12,7 +12,11 @@ import {
import { TreeItemProps } from '../TreeItem';
import { TreeItem2Props } from '../TreeItem2';
import { TreeViewItemId } from '../models';
-import { SlotComponentPropsFromProps, TreeViewPublicAPI } from '../internals/models';
+import {
+ SlotComponentPropsFromProps,
+ TreeViewExperimentalFeatures,
+ TreeViewPublicAPI,
+} from '../internals/models';
interface RichTreeViewItemSlotOwnerState {
itemId: TreeViewItemId;
@@ -75,4 +79,10 @@ export interface RichTreeViewProps;
}
diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx
index 4db161facf850..d25cb79422364 100644
--- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx
+++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx
@@ -161,6 +161,14 @@ SimpleTreeView.propTypes = {
* Used when the item's expansion is controlled.
*/
expandedItems: PropTypes.arrayOf(PropTypes.string),
+ /**
+ * Unstable features, breaking changes might be introduced.
+ * For each feature, if the flag is not explicitly set to `true`,
+ * the feature will be fully disabled and any property / method call will not have any effect.
+ */
+ experimentalFeatures: PropTypes.shape({
+ indentationAtItemLevel: PropTypes.bool,
+ }),
/**
* This prop is used to help implement the accessibility logic.
* If you don't provide this prop. It falls back to a randomly generated id.
diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.types.ts b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.types.ts
index b5be79437a156..b7e0ddcbbcdb2 100644
--- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.types.ts
+++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.types.ts
@@ -9,7 +9,7 @@ import {
SimpleTreeViewPluginSlots,
SimpleTreeViewPlugins,
} from './SimpleTreeView.plugins';
-import { TreeViewPublicAPI } from '../internals/models';
+import { TreeViewExperimentalFeatures, TreeViewPublicAPI } from '../internals/models';
export interface SimpleTreeViewSlots extends SimpleTreeViewPluginSlots {
/**
@@ -55,4 +55,10 @@ export interface SimpleTreeViewProps
* The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
*/
apiRef?: SimpleTreeViewApiRef;
+ /**
+ * Unstable features, breaking changes might be introduced.
+ * For each feature, if the flag is not explicitly set to `true`,
+ * the feature will be fully disabled and any property / method call will not have any effect.
+ */
+ experimentalFeatures?: TreeViewExperimentalFeatures;
}
diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx
index 74da492eaff36..a70b2e39d3f8e 100644
--- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx
+++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx
@@ -30,6 +30,7 @@ const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue
wrapItem: ({ children }) => children,
wrapRoot: ({ children }) => children,
disabledItemsFocusable: false,
+ indentationAtItemLevel: false,
icons: {
slots: {},
slotProps: {},
diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx
index 8201c9940460d..fdf9820fd0d67 100644
--- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx
+++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx
@@ -4,6 +4,7 @@ import clsx from 'clsx';
import Collapse from '@mui/material/Collapse';
import { resolveComponentProps, useSlotProps } from '@mui/base/utils';
import useForkRef from '@mui/utils/useForkRef';
+import { shouldForwardProp } from '@mui/system/createStyled';
import { alpha, styled, useThemeProps } from '@mui/material/styles';
import { TransitionProps } from '@mui/material/transitions';
import unsupportedProp from '@mui/utils/unsupportedProp';
@@ -16,6 +17,7 @@ import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewCon
import { DefaultTreeViewPlugins } from '../internals/plugins';
import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons';
import { TreeItem2Provider } from '../TreeItem2Provider';
+import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
const useUtilityClasses = (ownerState: TreeItemOwnerState) => {
const { classes } = ownerState;
@@ -61,6 +63,7 @@ const StyledTreeItemContent = styled(TreeItemContent, {
},
];
},
+ shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel',
})<{ ownerState: TreeItemOwnerState }>(({ theme }) => ({
padding: theme.spacing(0.5, 1),
borderRadius: theme.shape.borderRadius,
@@ -132,16 +135,31 @@ const StyledTreeItemContent = styled(TreeItemContent, {
[`& .${treeItemClasses.checkbox}`]: {
padding: 0,
},
+ variants: [
+ {
+ props: { indentationAtItemLevel: true },
+ style: {
+ paddingLeft: `calc(${theme.spacing(1)} + 12px * var(--TreeView-itemDepth))`,
+ },
+ },
+ ],
}));
const TreeItemGroup = styled(Collapse, {
name: 'MuiTreeItem',
slot: 'GroupTransition',
overridesResolver: (props, styles) => styles.groupTransition,
+ shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel',
})({
margin: 0,
padding: 0,
paddingLeft: 12,
+ variants: [
+ {
+ props: { indentationAtItemLevel: true },
+ style: { paddingLeft: 0 },
+ },
+ ],
});
/**
@@ -163,8 +181,10 @@ export const TreeItem = React.forwardRef(function TreeItem(
runItemPlugins,
selection: { multiSelect },
disabledItemsFocusable,
+ indentationAtItemLevel,
instance,
} = useTreeViewContext();
+ const depthContext = React.useContext(TreeViewItemDepthContext);
const props = useThemeProps({ props: inProps, name: 'MuiTreeItem' });
@@ -216,6 +236,7 @@ export const TreeItem = React.forwardRef(function TreeItem(
focused,
selected,
disabled,
+ indentationAtItemLevel,
};
const classes = useUtilityClasses(ownerState);
@@ -230,6 +251,7 @@ export const TreeItem = React.forwardRef(function TreeItem(
in: expanded,
component: 'ul',
role: 'group',
+ ...(indentationAtItemLevel ? { indentationAtItemLevel: true } : {}),
},
className: classes.groupTransition,
});
@@ -329,6 +351,15 @@ export const TreeItem = React.forwardRef(function TreeItem(
onBlur={handleBlur}
onKeyDown={handleKeyDown}
ref={handleRootRef}
+ style={
+ indentationAtItemLevel
+ ? ({
+ ...other.style,
+ '--TreeView-itemDepth':
+ typeof depthContext === 'function' ? depthContext(itemId) : depthContext,
+ } as React.CSSProperties)
+ : other.style
+ }
>
styles.content,
- shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'status',
-})(({ theme }) => ({
+ shouldForwardProp: (prop) =>
+ shouldForwardProp(prop) && prop !== 'status' && prop !== 'indentationAtItemLevel',
+})<{ status: UseTreeItem2Status; indentationAtItemLevel?: true }>(({ theme }) => ({
padding: theme.spacing(0.5, 1),
borderRadius: theme.shape.borderRadius,
width: '100%',
@@ -50,12 +52,13 @@ export const TreeItem2Content = styled('div', {
backgroundColor: 'transparent',
},
},
- [`& .${treeItemClasses.groupTransition}`]: {
- margin: 0,
- padding: 0,
- paddingLeft: 12,
- },
variants: [
+ {
+ props: { indentationAtItemLevel: true },
+ style: {
+ paddingLeft: `calc(${theme.spacing(1)} + 12px * var(--TreeView-itemDepth))`,
+ },
+ },
{
props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.disabled,
style: {
@@ -134,10 +137,17 @@ export const TreeItem2GroupTransition = styled(Collapse, {
name: 'MuiTreeItem2',
slot: 'GroupTransition',
overridesResolver: (props, styles) => styles.groupTransition,
-})({
+ shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel',
+})<{ indentationAtItemLevel?: true }>({
margin: 0,
padding: 0,
paddingLeft: 12,
+ variants: [
+ {
+ props: { indentationAtItemLevel: true },
+ style: { paddingLeft: 0 },
+ },
+ ],
});
export const TreeItem2Checkbox = styled(
@@ -250,7 +260,6 @@ export const TreeItem2 = React.forwardRef(function TreeItem2(
[classes.disabled]: status.disabled,
}),
});
-
const IconContainer: React.ElementType = slots.iconContainer ?? TreeItem2IconContainer;
const iconContainerProps = useSlotProps({
elementType: IconContainer,
diff --git a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx
index 93ed2461541f9..2b5387522cef1 100644
--- a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx
+++ b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx
@@ -4,9 +4,9 @@ import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewCon
function TreeItem2Provider(props: TreeItem2ProviderProps) {
const { children, itemId } = props;
- const { wrapItem } = useTreeViewContext<[]>();
+ const { wrapItem, instance } = useTreeViewContext<[]>();
- return wrapItem({ children, itemId });
+ return wrapItem({ children, itemId, instance });
}
TreeItem2Provider.propTypes = {
diff --git a/packages/x-tree-view/src/TreeView/TreeView.tsx b/packages/x-tree-view/src/TreeView/TreeView.tsx
index 8434b19200d0c..71a155acd522f 100644
--- a/packages/x-tree-view/src/TreeView/TreeView.tsx
+++ b/packages/x-tree-view/src/TreeView/TreeView.tsx
@@ -137,6 +137,14 @@ TreeView.propTypes = {
* Used when the item's expansion is controlled.
*/
expandedItems: PropTypes.arrayOf(PropTypes.string),
+ /**
+ * Unstable features, breaking changes might be introduced.
+ * For each feature, if the flag is not explicitly set to `true`,
+ * the feature will be fully disabled and any property / method call will not have any effect.
+ */
+ experimentalFeatures: PropTypes.shape({
+ indentationAtItemLevel: PropTypes.bool,
+ }),
/**
* This prop is used to help implement the accessibility logic.
* If you don't provide this prop. It falls back to a randomly generated id.
diff --git a/packages/x-tree-view/src/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.ts b/packages/x-tree-view/src/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.ts
new file mode 100644
index 0000000000000..e06f1da340382
--- /dev/null
+++ b/packages/x-tree-view/src/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.ts
@@ -0,0 +1,10 @@
+import * as React from 'react';
+import { TreeViewItemId } from '../../models';
+
+export const TreeViewItemDepthContext = React.createContext<
+ number | ((itemId: TreeViewItemId) => number)
+>(() => -1);
+
+if (process.env.NODE_ENV !== 'production') {
+ TreeViewItemDepthContext.displayName = 'TreeViewItemDepthContext';
+}
diff --git a/packages/x-tree-view/src/internals/TreeViewItemDepthContext/index.ts b/packages/x-tree-view/src/internals/TreeViewItemDepthContext/index.ts
new file mode 100644
index 0000000000000..aefec1d8a5c5c
--- /dev/null
+++ b/packages/x-tree-view/src/internals/TreeViewItemDepthContext/index.ts
@@ -0,0 +1 @@
+export { TreeViewItemDepthContext } from './TreeViewItemDepthContext';
diff --git a/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts b/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts
index 9ff9f5af735eb..2d42554e5d8d5 100644
--- a/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts
+++ b/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts
@@ -14,8 +14,8 @@ export type TreeViewContextValue;
publicAPI: TreeViewPublicAPI;
rootRef: React.RefObject;
- wrapItem: TreeItemWrapper;
- wrapRoot: TreeRootWrapper;
+ wrapItem: TreeItemWrapper;
+ wrapRoot: TreeRootWrapper;
runItemPlugins: (props: TProps) => Required;
};
diff --git a/packages/x-tree-view/src/internals/index.ts b/packages/x-tree-view/src/internals/index.ts
index c1ddf24a82748..9500d00e37f34 100644
--- a/packages/x-tree-view/src/internals/index.ts
+++ b/packages/x-tree-view/src/internals/index.ts
@@ -9,6 +9,7 @@ export type {
ConvertPluginsIntoSignatures,
MergePluginsProperty,
TreeViewPublicAPI,
+ TreeViewExperimentalFeatures,
} from './models';
// Plugins
diff --git a/packages/x-tree-view/src/internals/models/helpers.ts b/packages/x-tree-view/src/internals/models/helpers.ts
index e3ee527a4d1fa..d8b259e45f467 100644
--- a/packages/x-tree-view/src/internals/models/helpers.ts
+++ b/packages/x-tree-view/src/internals/models/helpers.ts
@@ -50,4 +50,5 @@ export interface MergePlugins {
slotProps: MergePluginsProperty;
events: MergePluginsProperty;
models: MergePluginsProperty;
+ experimentalFeatures: MergePluginsProperty;
}
diff --git a/packages/x-tree-view/src/internals/models/plugin.ts b/packages/x-tree-view/src/internals/models/plugin.ts
index 009c43d511f7d..0d85c8b61754d 100644
--- a/packages/x-tree-view/src/internals/models/plugin.ts
+++ b/packages/x-tree-view/src/internals/models/plugin.ts
@@ -1,6 +1,6 @@
import * as React from 'react';
import { EventHandlers } from '@mui/base/utils';
-import { TreeViewModel } from './treeView';
+import { TreeViewExperimentalFeatures, TreeViewInstance, TreeViewModel } from './treeView';
import type { MergePluginsProperty, OptionalIfEmpty } from './helpers';
import { TreeViewEventLookupElement } from './events';
import type { TreeViewCorePluginsSignature } from '../corePlugins';
@@ -12,6 +12,7 @@ export interface TreeViewPluginOptions;
slots: TSignature['slots'];
slotProps: TSignature['slotProps'];
+ experimentalFeatures: TreeViewUsedExperimentalFeatures;
models: TreeViewUsedModels;
setState: React.Dispatch>>;
rootRef: React.RefObject;
@@ -45,6 +46,7 @@ export type TreeViewPluginSignature<
slots?: { [key in keyof T['slots']]: React.ElementType };
slotProps?: { [key in keyof T['slotProps']]: {} | (() => {}) };
modelNames?: keyof T['defaultizedParams'];
+ experimentalFeatures?: string;
dependantPlugins?: readonly TreeViewAnyPluginSignature[];
},
> = {
@@ -64,6 +66,7 @@ export type TreeViewPluginSignature<
>;
}
: {};
+ experimentalFeatures: T['experimentalFeatures'];
dependantPlugins: T extends { dependantPlugins: Array } ? T['dependantPlugins'] : [];
};
@@ -78,6 +81,7 @@ export type TreeViewAnyPluginSignature = {
slots: any;
slotProps: any;
models: any;
+ experimentalFeatures: any;
publicAPI: any;
};
@@ -105,6 +109,9 @@ export type TreeViewUsedInstance
type TreeViewUsedState = TSignature['state'] &
MergePluginsProperty, 'state'>;
+type TreeViewUsedExperimentalFeatures =
+ TreeViewExperimentalFeatures<[TSignature, ...TSignature['dependantPlugins']]>;
+
type RemoveSetValue>> = {
[K in keyof Models]: Omit;
};
@@ -135,12 +142,16 @@ export type TreeViewItemPlugin = (
options: TreeViewItemPluginOptions,
) => void | TreeViewItemPluginResponse;
-export type TreeItemWrapper = (params: {
+export type TreeItemWrapper = (params: {
itemId: TreeViewItemId;
children: React.ReactNode;
+ instance: TreeViewInstance;
}) => React.ReactNode;
-export type TreeRootWrapper = (params: { children: React.ReactNode }) => React.ReactNode;
+export type TreeRootWrapper = (params: {
+ children: React.ReactNode;
+ instance: TreeViewInstance;
+}) => React.ReactNode;
export type TreeViewPlugin = {
(options: TreeViewPluginOptions): TreeViewResponse;
@@ -156,11 +167,11 @@ export type TreeViewPlugin = {
* @param {{ nodeId: TreeViewItemId; children: React.ReactNode; }} params The params of the item.
* @returns {React.ReactNode} The wrapped item.
*/
- wrapItem?: TreeItemWrapper;
+ wrapItem?: TreeItemWrapper<[TSignature, ...TSignature['dependantPlugins']]>;
/**
* Render function used to add React wrappers around the TreeView.
* @param {{ children: React.ReactNode; }} params The params of the root.
* @returns {React.ReactNode} The wrapped root.
*/
- wrapRoot?: TreeRootWrapper;
+ wrapRoot?: TreeRootWrapper<[TSignature, ...TSignature['dependantPlugins']]>;
};
diff --git a/packages/x-tree-view/src/internals/models/treeView.ts b/packages/x-tree-view/src/internals/models/treeView.ts
index 552dd29bf7e2c..80937aa10b554 100644
--- a/packages/x-tree-view/src/internals/models/treeView.ts
+++ b/packages/x-tree-view/src/internals/models/treeView.ts
@@ -8,7 +8,11 @@ export interface TreeViewItemMeta {
expandable: boolean;
disabled: boolean;
/**
- * Only defined for `RichTreeView`.
+ * Only defined for `RichTreeView` and `RichTreeViewPro`.
+ */
+ depth?: number;
+ /**
+ * Only defined for `RichTreeView` and `RichTreeViewPro`.
*/
label?: string;
}
@@ -24,3 +28,7 @@ export type TreeViewInstance =
MergePluginsProperty;
+
+export type TreeViewExperimentalFeatures<
+ TSignatures extends readonly TreeViewAnyPluginSignature[],
+> = { [key in MergePluginsProperty]?: boolean };
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx
similarity index 90%
rename from packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.ts
rename to packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx
index 25f4d660487d2..c704fd57fe513 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.ts
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx
@@ -8,6 +8,7 @@ import {
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
import { TreeViewBaseItem, TreeViewItemId } from '../../../models';
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './useTreeViewItems.utils';
+import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
interface UpdateNodesStateParameters
extends Pick<
@@ -28,7 +29,7 @@ const updateItemsState = ({
[TREE_VIEW_ROOT_PARENT_ID]: [],
};
- const processItem = (item: TreeViewBaseItem, parentId: string | null) => {
+ const processItem = (item: TreeViewBaseItem, depth: number, parentId: string | null) => {
const id: string = getItemId ? getItemId(item) : (item as any).id;
if (id == null) {
@@ -71,6 +72,7 @@ const updateItemsState = ({
idAttribute: undefined,
expandable: !!item.children?.length,
disabled: isItemDisabled ? isItemDisabled(item) : false,
+ depth,
};
itemMap[id] = item;
@@ -81,10 +83,10 @@ const updateItemsState = ({
}
itemOrderedChildrenIds[parentIdWithDefault].push(id);
- item.children?.forEach((child) => processItem(child, id));
+ item.children?.forEach((child) => processItem(child, depth + 1, id));
};
- items.forEach((item) => processItem(item, null));
+ items.forEach((item) => processItem(item, 0, null));
const itemChildrenIndexes: State['itemChildrenIndexes'] = {};
Object.keys(itemOrderedChildrenIds).forEach((parentId) => {
@@ -104,6 +106,7 @@ export const useTreeViewItems: TreeViewPlugin = ({
params,
state,
setState,
+ experimentalFeatures,
}) => {
const getItemMeta = React.useCallback(
(itemId: string) => state.items.itemMetaMap[itemId],
@@ -233,7 +236,10 @@ export const useTreeViewItems: TreeViewPlugin = ({
preventItemUpdates,
areItemUpdatesPrevented,
},
- contextValue: { disabledItemsFocusable: params.disabledItemsFocusable },
+ contextValue: {
+ disabledItemsFocusable: params.disabledItemsFocusable,
+ indentationAtItemLevel: experimentalFeatures.indentationAtItemLevel ?? false,
+ },
};
};
@@ -251,6 +257,14 @@ useTreeViewItems.getDefaultizedParams = (params) => ({
disabledItemsFocusable: params.disabledItemsFocusable ?? false,
});
+useTreeViewItems.wrapRoot = ({ children, instance }) => {
+ return (
+ instance.getItemMeta(itemId)?.depth ?? 0}>
+ {children}
+
+ );
+};
+
useTreeViewItems.params = {
disabledItemsFocusable: true,
items: true,
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.types.ts
index 327907342d612..5a27d1ed9992d 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.types.ts
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.types.ts
@@ -93,7 +93,9 @@ export interface UseTreeViewItemsState {
}
interface UseTreeViewItemsContextValue
- extends Pick, 'disabledItemsFocusable'> {}
+ extends Pick, 'disabledItemsFocusable'> {
+ indentationAtItemLevel: boolean;
+}
export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
params: UseTreeViewItemsParameters;
@@ -103,6 +105,7 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
events: UseTreeViewItemsEventLookup;
state: UseTreeViewItemsState;
contextValue: UseTreeViewItemsContextValue;
+ experimentalFeatures: 'indentationAtItemLevel';
}>;
export type TreeViewItemMetaMap = { [itemId: string]: TreeViewItemMeta };
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.tsx
index e28b703fbed81..a1bac68c02188 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.tsx
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.tsx
@@ -17,6 +17,7 @@ import {
import type { TreeItemProps } from '../../../TreeItem';
import type { TreeItem2Props } from '../../../TreeItem2';
import { UseTreeViewIdSignature } from '../useTreeViewId';
+import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
export const useTreeViewJSXItems: TreeViewPlugin = ({
instance,
@@ -181,12 +182,23 @@ const useTreeViewJSXItemsItemPlugin: TreeViewItemPlugin (
- {children}
-);
+useTreeViewJSXItems.wrapItem = ({ children, itemId }) => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const depthContext = React.useContext(TreeViewItemDepthContext);
+
+ return (
+
+
+ {children}
+
+
+ );
+};
useTreeViewJSXItems.wrapRoot = ({ children }) => (
- {children}
+
+ {children}
+
);
useTreeViewJSXItems.params = {};
diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts
index 3898b90cc2321..464da635e65e2 100644
--- a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts
+++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts
@@ -93,6 +93,7 @@ export const useTreeView = plugin.wrapItem)
- .filter((wrapItem): wrapItem is TreeItemWrapper => !!wrapItem);
+ .filter((wrapItem): wrapItem is TreeItemWrapper => !!wrapItem);
contextValue.wrapItem = ({ itemId, children }) => {
let finalChildren: React.ReactNode = children;
itemWrappers.forEach((itemWrapper) => {
- finalChildren = itemWrapper({ itemId, children: finalChildren });
+ finalChildren = itemWrapper({ itemId, children: finalChildren, instance });
});
return finalChildren;
@@ -160,11 +161,14 @@ export const useTreeView = plugin.wrapRoot)
- .filter((wrapRoot): wrapRoot is TreeRootWrapper => !!wrapRoot);
+ .filter((wrapRoot): wrapRoot is TreeRootWrapper => !!wrapRoot)
+ // The wrappers are reversed to ensure that the first wrapper is the outermost one.
+ .reverse();
+
contextValue.wrapRoot = ({ children }) => {
let finalChildren: React.ReactNode = children;
rootWrappers.forEach((rootWrapper) => {
- finalChildren = rootWrapper({ children: finalChildren });
+ finalChildren = rootWrapper({ children: finalChildren, instance });
});
return finalChildren;
diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.types.ts b/packages/x-tree-view/src/internals/useTreeView/useTreeView.types.ts
index 0ff481abccae7..a4221edcf5f46 100644
--- a/packages/x-tree-view/src/internals/useTreeView/useTreeView.types.ts
+++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.types.ts
@@ -8,6 +8,7 @@ import {
MergePluginsProperty,
TreeViewInstance,
TreeViewPublicAPI,
+ TreeViewExperimentalFeatures,
} from '../models';
export type UseTreeViewParameters<
@@ -25,6 +26,7 @@ export interface UseTreeViewBaseParameters<
plugins: TPlugins;
slots: MergePluginsProperty, 'slots'>;
slotProps: MergePluginsProperty, 'slotProps'>;
+ experimentalFeatures: TreeViewExperimentalFeatures>;
}
export type UseTreeViewDefaultizedParameters<
diff --git a/packages/x-tree-view/src/internals/utils/extractPluginParamsFromProps.ts b/packages/x-tree-view/src/internals/utils/extractPluginParamsFromProps.ts
index 6f0ae94843dbd..2f1f139aeb048 100644
--- a/packages/x-tree-view/src/internals/utils/extractPluginParamsFromProps.ts
+++ b/packages/x-tree-view/src/internals/utils/extractPluginParamsFromProps.ts
@@ -2,6 +2,7 @@ import * as React from 'react';
import {
ConvertPluginsIntoSignatures,
MergePluginsProperty,
+ TreeViewExperimentalFeatures,
TreeViewPlugin,
TreeViewPublicAPI,
} from '../models';
@@ -17,9 +18,10 @@ export const extractPluginParamsFromProps = <
apiRef?: React.MutableRefObject<
TreeViewPublicAPI> | undefined
>;
+ experimentalFeatures?: TreeViewExperimentalFeatures>;
},
>({
- props: { slots, slotProps, apiRef, ...props },
+ props: { slots, slotProps, apiRef, experimentalFeatures, ...props },
plugins,
rootRef,
}: {
@@ -39,6 +41,7 @@ export const extractPluginParamsFromProps = <
rootRef,
slots: slots ?? {},
slotProps: slotProps ?? {},
+ experimentalFeatures: experimentalFeatures ?? {},
apiRef,
} as UseTreeViewBaseParameters & PluginParams;
const otherProps = {} as Omit;
diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts
index 047bf9660b52f..7efc38056a9d4 100644
--- a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts
+++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts
@@ -15,6 +15,7 @@ import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewCon
import { DefaultTreeViewPlugins } from '../internals/plugins/defaultPlugins';
import { MuiCancellableEvent } from '../internals/models/MuiCancellableEvent';
import { useTreeItem2Utils } from '../hooks/useTreeItem2Utils';
+import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
export const useTreeItem2 = (
parameters: UseTreeItem2Parameters,
@@ -23,9 +24,11 @@ export const useTreeItem2 = ();
+ const depthContext = React.useContext(TreeViewItemDepthContext);
const { id, itemId, label, children, rootRef } = parameters;
@@ -134,7 +137,7 @@ export const useTreeItem2 = = {
...externalEventHandlers,
ref: handleRootRef,
role: 'treeitem',
@@ -148,6 +151,15 @@ export const useTreeItem2 = = {}>(
@@ -155,7 +167,7 @@ export const useTreeItem2 = => {
const externalEventHandlers = extractEventHandlers(externalProps);
- return {
+ const response: UseTreeItem2ContentSlotProps = {
...externalEventHandlers,
...externalProps,
ref: contentRef,
@@ -163,6 +175,12 @@ export const useTreeItem2 = = {}>(
@@ -213,7 +231,7 @@ export const useTreeItem2 = => {
const externalEventHandlers = extractEventHandlers(externalProps);
- return {
+ const response: UseTreeItem2GroupTransitionSlotProps = {
...externalEventHandlers,
unmountOnExit: true,
component: 'ul',
@@ -222,6 +240,12 @@ export const useTreeItem2 = >;
onKeyDown: MuiCancellableEventHandler>;
ref: React.RefCallback;
+ /**
+ * Only defined when the `indentationAtItemLevel` experimental feature is enabled.
+ */
+ style?: React.CSSProperties;
}
export type UseTreeItem2RootSlotProps = ExternalProps &
@@ -50,6 +54,10 @@ export interface UseTreeItem2ContentSlotOwnProps {
onMouseDown: MuiCancellableEventHandler;
ref: React.RefCallback | null;
status: UseTreeItem2Status;
+ /**
+ * Only defined when the `indentationAtItemLevel` experimental feature is enabled.
+ */
+ indentationAtItemLevel?: true;
}
export type UseTreeItem2ContentSlotProps = ExternalProps &
@@ -85,6 +93,10 @@ export interface UseTreeItem2GroupTransitionSlotOwnProps {
component: 'ul';
role: 'group';
children: React.ReactNode;
+ /**
+ * Only defined when the `indentationAtItemLevel` experimental feature is enabled.
+ */
+ indentationAtItemLevel?: true;
}
export type UseTreeItem2GroupTransitionSlotProps = ExternalProps &