diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 3977bf50b95af..ee4684125d361 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -482,7 +482,6 @@ const pages: MuiPage[] = [ children: [ { pathname: '/x/react-tree-view', title: 'Overview' }, { pathname: '/x/react-tree-view/getting-started' }, - { pathname: '/x/react-tree-view/accessibility' }, { pathname: '/x/react-tree-view/simple-tree-view', subheader: 'Simple Tree View', @@ -505,6 +504,14 @@ const pages: MuiPage[] = [ { pathname: '/x/react-tree-view/rich-tree-view/focus' }, ], }, + { + pathname: '/x/react-tree-view/common-features', + subheader: 'Common features', + children: [ + { pathname: '/x/react-tree-view/accessibility' }, + { pathname: '/x/react-tree-view/tree-item-customization', title: 'Item customization' }, + ], + }, { pathname: '/x/api/tree-view-group', title: 'API Reference', diff --git a/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js new file mode 100644 index 0000000000000..5a8f88b88527b --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +const MUI_X_PRODUCTS = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +export default function IndentationAtItemLevel() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx new file mode 100644 index 0000000000000..5e9dfc8e5f1b2 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +export default function IndentationAtItemLevel() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx.preview b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx.preview new file mode 100644 index 0000000000000..659fb32599af2 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.js b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.js new file mode 100644 index 0000000000000..64309aa42b3d6 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +const MUI_X_PRODUCTS = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +export default function ItemChildrenIndentationProp() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx new file mode 100644 index 0000000000000..840be0a94d14c --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +export default function ItemChildrenIndentationProp() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx.preview b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx.preview new file mode 100644 index 0000000000000..3f52945434cdb --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ItemChildrenIndentationProp.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/tree-item-customization.md b/docs/data/tree-view/tree-item-customization/tree-item-customization.md new file mode 100644 index 0000000000000..ffc3be81307aa --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/tree-item-customization.md @@ -0,0 +1,71 @@ +--- +productId: x-tree-view +title: Tree Item Customization +components: SimpleTreeView, RichTreeView, TreeItem, TreeItem2 +packageName: '@mui/x-tree-view' +githubLabel: 'component: tree view' +waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ +--- + +# Tree Item Customization + +

Learn how to customize the Tree Item component.

+ +## Basics + +### Change nested item's indentation + +Use the `itemChildrenIndentation` prop to change the indentation of the nested items. +By default, a nested item is indented by `12px` from its parent item. + +{{"demo": "ItemChildrenIndentationProp.js"}} + +:::success +This feature is compatible with both the `TreeItem` and `TreeItem2` components +If you are using a custom Tree Item component, and you want to override the padding, +then apply the following padding to your `groupTransition` element: + +```ts +const CustomTreeItem2GroupTransition = styled(TreeItem2GroupTransition)(({ theme }) => ({ + // ...other styles + paddingLeft: `var(--TreeView-itemChildrenIndentation)`, +} +``` + +If you are using the `indentationAtItemLevel` prop, then instead apply the following padding to your `content` element: + +```ts +const CustomTreeItem2Content = styled(TreeItem2Content)(({ theme }) => ({ + // ...other styles + paddingLeft: + `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`, +} +``` + +::: + +### Apply the nested item's indentation at the item level + +By default, the indentation of nested items is applied by the `groupTransition` slot of its parent (i.e.: the DOM element that wraps all the children of a given item). +This approach is not compatible with upcoming features like the reordering of items using drag & drop. + +To apply the indentation at the item level (i.e.: have each item responsible for setting its own indentation using the `padding-left` CSS property on its `content` slot), +you can use the `indentationAtItemLevel` experimental feature. +It will become the default behavior in the next major version of the Tree View component. + +{{"demo": "IndentationAtItemLevel.js"}} + +:::success +This feature is compatible with both the `TreeItem` and `TreeItem2` components and with the `itemChildrenIndentation` prop. +If you are using a custom Tree Item component, and you want to override the padding, +then apply the following padding to your `content` element: + +```ts +const CustomTreeItem2Content = styled(TreeItem2Content)(({ theme }) => ({ + // ...other styles + paddingLeft: + `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`, +} +``` + +::: 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 bc08974346195..7da4e28d56f04 100644 --- a/docs/pages/x/api/tree-view/rich-tree-view.json +++ b/docs/pages/x/api/tree-view/rich-tree-view.json @@ -46,6 +46,10 @@ "returned": "boolean" } }, + "itemChildrenIndentation": { + "type": { "name": "union", "description": "number
| string" }, + "default": "12px" + }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, "onExpandedItemsChange": { "type": { "name": "func" }, @@ -138,6 +142,6 @@ "forwardsRefTo": "HTMLUListElement", "filename": "/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } 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 6003d38a33a93..1c52f9e03646b 100644 --- a/docs/pages/x/api/tree-view/simple-tree-view.json +++ b/docs/pages/x/api/tree-view/simple-tree-view.json @@ -21,6 +21,10 @@ "type": { "name": "shape", "description": "{ indentationAtItemLevel?: bool }" } }, "id": { "type": { "name": "string" } }, + "itemChildrenIndentation": { + "type": { "name": "union", "description": "number
| string" }, + "default": "12px" + }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, "onExpandedItemsChange": { "type": { "name": "func" }, @@ -103,6 +107,6 @@ "forwardsRefTo": "HTMLUListElement", "filename": "/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/pages/x/api/tree-view/tree-item-2.json b/docs/pages/x/api/tree-view/tree-item-2.json index 6b9f00accb0b8..5a3135b2b98b4 100644 --- a/docs/pages/x/api/tree-view/tree-item-2.json +++ b/docs/pages/x/api/tree-view/tree-item-2.json @@ -94,6 +94,6 @@ "muiName": "MuiTreeItem2", "filename": "/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/pages/x/api/tree-view/tree-item.json b/docs/pages/x/api/tree-view/tree-item.json index b25b49c3e6515..6173f6442d930 100644 --- a/docs/pages/x/api/tree-view/tree-item.json +++ b/docs/pages/x/api/tree-view/tree-item.json @@ -108,6 +108,6 @@ "forwardsRefTo": "HTMLLIElement", "filename": "/packages/x-tree-view/src/TreeItem/TreeItem.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/pages/x/api/tree-view/tree-view.json b/docs/pages/x/api/tree-view/tree-view.json index 45a574640afff..ae8f37306dda4 100644 --- a/docs/pages/x/api/tree-view/tree-view.json +++ b/docs/pages/x/api/tree-view/tree-view.json @@ -21,6 +21,10 @@ "type": { "name": "shape", "description": "{ indentationAtItemLevel?: bool }" } }, "id": { "type": { "name": "string" } }, + "itemChildrenIndentation": { + "type": { "name": "union", "description": "number
| string" }, + "default": "12px" + }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, "onExpandedItemsChange": { "type": { "name": "func" }, diff --git a/docs/pages/x/react-tree-view/tree-item-customization.js b/docs/pages/x/react-tree-view/tree-item-customization.js new file mode 100644 index 0000000000000..af937e3ce0202 --- /dev/null +++ b/docs/pages/x/react-tree-view/tree-item-customization.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/tree-view/tree-item-customization/tree-item-customization.md?muiMarkdown'; + +export default function Page() { + return ; +} 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 17c5fb3d6eb9d..b866f4dcc07a5 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 @@ -42,6 +42,9 @@ "boolean": "true if the item should be disabled." } }, + "itemChildrenIndentation": { + "description": "Horizontal indentation between an item and its children. Examples: 24, "24px", "2rem", "2em"." + }, "multiSelect": { "description": "If true, ctrl and shift will trigger multiselect." }, 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 2523d471b4e72..a12a983517785 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 @@ -28,6 +28,9 @@ "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." }, + "itemChildrenIndentation": { + "description": "Horizontal indentation between an item and its children. Examples: 24, "24px", "2rem", "2em"." + }, "multiSelect": { "description": "If true, ctrl and shift will trigger multiselect." }, 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 2e1fed84729f8..c29df21217338 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 @@ -28,6 +28,9 @@ "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." }, + "itemChildrenIndentation": { + "description": "Horizontal indentation between an item and its children. Examples: 24, "24px", "2rem", "2em"." + }, "multiSelect": { "description": "If true, ctrl and shift will trigger multiselect." }, diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx index 7a3ea33fbf288..15cea0fe567f1 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx @@ -232,6 +232,12 @@ RichTreeView.propTypes = { * @returns {boolean} `true` if the item should be disabled. */ isItemDisabled: PropTypes.func, + /** + * Horizontal indentation between an item and its children. + * Examples: 24, "24px", "2rem", "2em". + * @default 12px + */ + itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), items: PropTypes.array.isRequired, /** * If `true`, `ctrl` and `shift` will trigger multiselect. diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx index d25cb79422364..297792cd511cf 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx @@ -174,6 +174,12 @@ SimpleTreeView.propTypes = { * If you don't provide this prop. It falls back to a randomly generated id. */ id: PropTypes.string, + /** + * Horizontal indentation between an item and its children. + * Examples: 24, "24px", "2rem", "2em". + * @default 12px + */ + itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * If `true`, `ctrl` and `shift` will trigger multiselect. * @default false diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx index fdf9820fd0d67..a330de1af8d98 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx @@ -139,7 +139,7 @@ const StyledTreeItemContent = styled(TreeItemContent, { { props: { indentationAtItemLevel: true }, style: { - paddingLeft: `calc(${theme.spacing(1)} + 12px * var(--TreeView-itemDepth))`, + paddingLeft: `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`, }, }, ], @@ -153,7 +153,7 @@ const TreeItemGroup = styled(Collapse, { })({ margin: 0, padding: 0, - paddingLeft: 12, + paddingLeft: 'var(--TreeView-itemChildrenIndentation)', variants: [ { props: { indentationAtItemLevel: true }, diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx index 572d07a5dbaf9..fd905cda2caa3 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx @@ -56,7 +56,7 @@ export const TreeItem2Content = styled('div', { { props: { indentationAtItemLevel: true }, style: { - paddingLeft: `calc(${theme.spacing(1)} + 12px * var(--TreeView-itemDepth))`, + paddingLeft: `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`, }, }, { @@ -141,7 +141,7 @@ export const TreeItem2GroupTransition = styled(Collapse, { })<{ indentationAtItemLevel?: true }>({ margin: 0, padding: 0, - paddingLeft: 12, + paddingLeft: 'var(--TreeView-itemChildrenIndentation)', variants: [ { props: { indentationAtItemLevel: true }, diff --git a/packages/x-tree-view/src/TreeView/TreeView.tsx b/packages/x-tree-view/src/TreeView/TreeView.tsx index 71a155acd522f..b6944f2df05aa 100644 --- a/packages/x-tree-view/src/TreeView/TreeView.tsx +++ b/packages/x-tree-view/src/TreeView/TreeView.tsx @@ -150,6 +150,12 @@ TreeView.propTypes = { * If you don't provide this prop. It falls back to a randomly generated id. */ id: PropTypes.string, + /** + * Horizontal indentation between an item and its children. + * Examples: 24, "24px", "2rem", "2em". + * @default 12px + */ + itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * If `true`, `ctrl` and `shift` will trigger multiselect. * @default false diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx index c704fd57fe513..c2a77e35bd972 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.tsx @@ -222,6 +222,14 @@ export const useTreeViewItems: TreeViewPlugin = ({ }; return { + getRootProps: () => ({ + style: { + '--TreeView-itemChildrenIndentation': + typeof params.itemChildrenIndentation === 'number' + ? `${params.itemChildrenIndentation}px` + : params.itemChildrenIndentation, + } as React.CSSProperties, + }), publicAPI: { getItem, }, @@ -255,6 +263,7 @@ useTreeViewItems.getInitialState = (params) => ({ useTreeViewItems.getDefaultizedParams = (params) => ({ ...params, disabledItemsFocusable: params.disabledItemsFocusable ?? false, + itemChildrenIndentation: params.itemChildrenIndentation ?? '12px', }); useTreeViewItems.wrapRoot = ({ children, instance }) => { @@ -271,4 +280,5 @@ useTreeViewItems.params = { isItemDisabled: true, getItemLabel: true, getItemId: true, + itemChildrenIndentation: 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 0b659a1be3420..dc234010c8550 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 @@ -105,11 +105,17 @@ export interface UseTreeViewItemsParameters { * @default (item) => item.id */ getItemId?: (item: R) => TreeViewItemId; + /** + * Horizontal indentation between an item and its children. + * Examples: 24, "24px", "2rem", "2em". + * @default 12px + */ + itemChildrenIndentation?: string | number; } export type UseTreeViewItemsDefaultizedParameters = DefaultizedProps< UseTreeViewItemsParameters, - 'disabledItemsFocusable' + 'disabledItemsFocusable' | 'itemChildrenIndentation' >; interface UseTreeViewItemsEventLookup {