diff --git a/docs/data/charts/highlighting/ElementHighlights.js b/docs/data/charts/highlighting/ElementHighlights.js index 4ff360e2a58cc..7379647649bd9 100644 --- a/docs/data/charts/highlighting/ElementHighlights.js +++ b/docs/data/charts/highlighting/ElementHighlights.js @@ -7,7 +7,6 @@ import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import ToggleButton from '@mui/material/ToggleButton'; - import { BarChart } from '@mui/x-charts/BarChart'; import { LineChart } from '@mui/x-charts/LineChart'; import { ScatterChart } from '@mui/x-charts/ScatterChart'; @@ -130,6 +129,7 @@ export default function ElementHighlights() { }))} /> )} + {chartType === 'line' && ( )} + {chartType === 'scatter' && ( )} + {chartType === 'pie' && ( )} + {chartType === 'line' && ( )} + {chartType === 'scatter' && ( )} + {chartType === 'pie' && ( ( - -)); - -export default function LabelSlotProps() { - return ( - - - - ); -} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx deleted file mode 100644 index a94af1fab5afc..0000000000000 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; -import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; -import { TreeViewBaseItem } from '@mui/x-tree-view/models'; - -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' }, - ], - }, - { - id: 'charts', - label: 'Charts', - children: [{ id: 'charts-community', label: '@mui/x-charts' }], - }, - { - id: 'tree-view', - label: 'Tree View', - children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }], - }, -]; - -const CustomTreeItem = React.forwardRef( - (props: TreeItem2Props, ref: React.Ref) => ( - - ), -); - -export default function LabelSlotProps() { - return ( - - - - ); -} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js deleted file mode 100644 index acddadac792c2..0000000000000 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js +++ /dev/null @@ -1,168 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import { TreeItem2, TreeItem2Label } from '@mui/x-tree-view/TreeItem2'; -import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; - -import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; - -function CustomLabel(props) { - const { children, onChange, ...other } = props; - - const [isEditing, setIsEditing] = React.useState(false); - const [value, setValue] = React.useState(''); - const editingLabelRef = React.useRef(null); - - const handleLabelDoubleClick = () => { - setIsEditing(true); - setValue(children); - }; - - const handleEditingLabelChange = (event) => { - setValue(event.target.value); - }; - - const handleEditingLabelKeyDown = (event) => { - if (event.key === 'Enter') { - event.stopPropagation(); - setIsEditing(false); - onChange(value); - } else if (event.key === 'Escape') { - event.stopPropagation(); - setIsEditing(false); - } - }; - - React.useEffect(() => { - if (isEditing) { - editingLabelRef.current?.focus(); - } - }, [isEditing]); - - if (isEditing) { - return ( - - ); - } - - return ( - - {children} - - ); -} - -const TreeItemContext = React.createContext({ onLabelValueChange: () => {} }); - -const CustomTreeItem = React.forwardRef((props, ref) => { - const { interactions } = useTreeItem2Utils({ - itemId: props.itemId, - children: props.children, - }); - - const { onLabelValueChange } = React.useContext(TreeItemContext); - - const handleLabelValueChange = (newLabel) => { - onLabelValueChange(props.itemId, newLabel); - }; - - const handleContentClick = (event) => { - event.defaultMuiPrevented = true; - interactions.handleSelection(event); - }; - - const handleIconContainerClick = (event) => { - interactions.handleExpansion(event); - }; - - return ( - - ); -}); - -const DEFAULT_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' }, - ], - }, - { - id: 'charts', - label: 'Charts', - children: [{ id: 'charts-community', label: '@mui/x-charts' }], - }, - { - id: 'tree-view', - label: 'Tree View', - children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }], - }, -]; - -const DEFAULT_EXPANDED_ITEMS = ['pickers']; - -export default function LabelSlots() { - const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); - - const context = React.useMemo( - () => ({ - onLabelValueChange: (itemId, label) => - setProducts((prev) => { - const walkTree = (item) => { - if (item.id === itemId) { - return { ...item, label }; - } - if (item.children) { - return { ...item, children: item.children.map(walkTree) }; - } - - return item; - }; - - return prev.map(walkTree); - }), - }), - [], - ); - - return ( - - - - - - ); -} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx deleted file mode 100644 index a833704682aa2..0000000000000 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import { - TreeItem2, - TreeItem2Label, - TreeItem2Props, -} from '@mui/x-tree-view/TreeItem2'; -import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; -import { TreeViewBaseItem } from '@mui/x-tree-view/models'; -import { UseTreeItem2ContentSlotOwnProps } from '@mui/x-tree-view/useTreeItem2'; -import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; - -interface CustomLabelProps { - children: string; - className: string; - onChange: (value: string) => void; -} - -function CustomLabel(props: CustomLabelProps) { - const { children, onChange, ...other } = props; - - const [isEditing, setIsEditing] = React.useState(false); - const [value, setValue] = React.useState(''); - const editingLabelRef = React.useRef(null); - - const handleLabelDoubleClick = () => { - setIsEditing(true); - setValue(children); - }; - - const handleEditingLabelChange = (event: React.ChangeEvent) => { - setValue(event.target.value); - }; - - const handleEditingLabelKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - event.stopPropagation(); - setIsEditing(false); - onChange(value); - } else if (event.key === 'Escape') { - event.stopPropagation(); - setIsEditing(false); - } - }; - - React.useEffect(() => { - if (isEditing) { - editingLabelRef.current?.focus(); - } - }, [isEditing]); - - if (isEditing) { - return ( - - ); - } - - return ( - - {children} - - ); -} - -const TreeItemContext = React.createContext<{ - onLabelValueChange: (itemId: string, label: string) => void; -}>({ onLabelValueChange: () => {} }); - -const CustomTreeItem = React.forwardRef( - (props: TreeItem2Props, ref: React.Ref) => { - const { interactions } = useTreeItem2Utils({ - itemId: props.itemId, - children: props.children, - }); - - const { onLabelValueChange } = React.useContext(TreeItemContext); - - const handleLabelValueChange = (newLabel: string) => { - onLabelValueChange(props.itemId, newLabel); - }; - - const handleContentClick: UseTreeItem2ContentSlotOwnProps['onClick'] = ( - event, - ) => { - event.defaultMuiPrevented = true; - interactions.handleSelection(event); - }; - - const handleIconContainerClick = (event: React.MouseEvent) => { - interactions.handleExpansion(event); - }; - - return ( - - ); - }, -); - -const DEFAULT_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' }, - ], - }, - { - id: 'charts', - label: 'Charts', - children: [{ id: 'charts-community', label: '@mui/x-charts' }], - }, - { - id: 'tree-view', - label: 'Tree View', - children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }], - }, -]; - -const DEFAULT_EXPANDED_ITEMS = ['pickers']; - -export default function LabelSlots() { - const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); - - const context = React.useMemo( - () => ({ - onLabelValueChange: (itemId: string, label: string) => - setProducts((prev) => { - const walkTree = (item: TreeViewBaseItem): TreeViewBaseItem => { - if (item.id === itemId) { - return { ...item, label }; - } - if (item.children) { - return { ...item, children: item.children.map(walkTree) }; - } - - return item; - }; - - return prev.map(walkTree); - }), - }), - [], - ); - - return ( - - - - - - ); -} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview deleted file mode 100644 index 7ba0ef733a399..0000000000000 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview +++ /dev/null @@ -1,8 +0,0 @@ - - - \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/customization.md b/docs/data/tree-view/rich-tree-view/customization/customization.md index d8172900c1581..adfe395f9c196 100644 --- a/docs/data/tree-view/rich-tree-view/customization/customization.md +++ b/docs/data/tree-view/rich-tree-view/customization/customization.md @@ -38,26 +38,10 @@ Use `treeItemClasses` to target internal elements of the Tree Item component and {{"demo": "CustomStyling.js"}} -### Custom label +### Custom Tree Item -:::warning -This example is built using the new `TreeItem2` component -which adds several slots to modify the content of the Tree Item or change its behavior. - -You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). -::: - -Use the `label` slot to customize the Tree Item label or to replace it with a custom component. - -The `slotProps` prop allows you to pass props to the label component. -The demo below shows how to pass an `id` attribute to the Tree Item label: - -{{"demo": "LabelSlotProps.js", "defaultCodeOpen": false }} - -The `slots` prop allows you to replace the default label with your own component: -The demo below shows how to add a basic edition feature on the Tree Item label: - -{{"demo": "LabelSlots.js", "defaultCodeOpen": false}} +You can use the Tree Item's customization API to build new layouts and manage behaviors. +Learn more about the anatomy of the Tree Item components and the customization utilities provided in the [Tree Item customization doc](/x/react-tree-view/tree-item-customization/). ### Headless API diff --git a/docs/data/tree-view/rich-tree-view/editing/EditLeaves.js b/docs/data/tree-view/rich-tree-view/editing/EditLeaves.js index 041b884099746..f87ffa867579b 100644 --- a/docs/data/tree-view/rich-tree-view/editing/EditLeaves.js +++ b/docs/data/tree-view/rich-tree-view/editing/EditLeaves.js @@ -15,7 +15,7 @@ const MUI_X_PRODUCTS = [ }, { id: 'pickers', - label: 'Date and time pickers', + label: 'Date and Time pickers', children: [ { id: 'pickers-community', diff --git a/docs/data/tree-view/rich-tree-view/editing/EditLeaves.tsx b/docs/data/tree-view/rich-tree-view/editing/EditLeaves.tsx index 6d1a2ef9997e7..a0279d28eacfe 100644 --- a/docs/data/tree-view/rich-tree-view/editing/EditLeaves.tsx +++ b/docs/data/tree-view/rich-tree-view/editing/EditLeaves.tsx @@ -22,7 +22,7 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, { id: 'pickers', - label: 'Date and time pickers', + label: 'Date and Time pickers', children: [ { id: 'pickers-community', diff --git a/docs/data/tree-view/rich-tree-view/editing/editableProducts.ts b/docs/data/tree-view/rich-tree-view/editing/editableProducts.ts index ca7945031fb53..0da21730f810d 100644 --- a/docs/data/tree-view/rich-tree-view/editing/editableProducts.ts +++ b/docs/data/tree-view/rich-tree-view/editing/editableProducts.ts @@ -19,7 +19,7 @@ export const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, { id: 'pickers', - label: 'Date and time pickers', + label: 'Date and Time pickers', children: [ { diff --git a/docs/data/tree-view/rich-tree-view/editing/products.ts b/docs/data/tree-view/rich-tree-view/editing/products.ts index 97929673353e6..802607175c463 100644 --- a/docs/data/tree-view/rich-tree-view/editing/products.ts +++ b/docs/data/tree-view/rich-tree-view/editing/products.ts @@ -13,7 +13,7 @@ export const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, { id: 'pickers', - label: 'Date and time pickers', + label: 'Date and Time pickers', children: [ { diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js deleted file mode 100644 index 4908de784996d..0000000000000 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; - -const CustomTreeItem = React.forwardRef((props, ref) => ( - -)); - -export default function LabelSlotProps() { - return ( - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx deleted file mode 100644 index 99bb07a10be05..0000000000000 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; - -const CustomTreeItem = React.forwardRef( - (props: TreeItem2Props, ref: React.Ref) => ( - - ), -); - -export default function LabelSlotProps() { - return ( - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js deleted file mode 100644 index e5ff0f51a96d7..0000000000000 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js +++ /dev/null @@ -1,80 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Tooltip from '@mui/material/Tooltip'; -import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem2, TreeItem2Label } from '@mui/x-tree-view/TreeItem2'; - -function CustomLabel(props) { - const { tooltip, ...other } = props; - - return ( - - - - ); -} - -const CustomTreeItem = React.forwardRef((props, ref) => { - const { labelTooltip, ...other } = props; - - return ( - - ); -}); - -export default function LabelSlots() { - return ( - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx deleted file mode 100644 index 36bf15aae8862..0000000000000 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Tooltip from '@mui/material/Tooltip'; -import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { - TreeItem2, - TreeItem2Label, - TreeItem2Props, -} from '@mui/x-tree-view/TreeItem2'; - -interface CustomLabelProps { - children: React.ReactNode; - tooltip?: string; -} - -function CustomLabel(props: CustomLabelProps) { - const { tooltip, ...other } = props; - - return ( - - - - ); -} - -interface CustomTreeItemProps extends TreeItem2Props { - labelTooltip?: string; -} - -const CustomTreeItem = React.forwardRef( - (props: CustomTreeItemProps, ref: React.Ref) => { - const { labelTooltip, ...other } = props; - - return ( - - ); - }, -); - -export default function LabelSlots() { - return ( - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/docs/data/tree-view/simple-tree-view/customization/customization.md b/docs/data/tree-view/simple-tree-view/customization/customization.md index 3774ec10f6eaa..4a18e36b438b5 100644 --- a/docs/data/tree-view/simple-tree-view/customization/customization.md +++ b/docs/data/tree-view/simple-tree-view/customization/customization.md @@ -38,26 +38,11 @@ Use `treeItemClasses` to target internal elements of the Tree Item component and {{"demo": "CustomStyling.js"}} -### Custom label +### Custom Tree Item -:::warning -This example is built using the new `TreeItem2` component -which adds several slots to modify the content of the Tree Item or change its behavior. - -You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). -::: - -Use the `label` slot to customize the Tree Item label or to replace it with a custom component. - -The `slotProps` prop allows you to pass props to the label component. -The demo below shows how to pass an `id` attribute to the Tree Item label: - -{{"demo": "LabelSlotProps.js", "defaultCodeOpen": false }} - -The `slots` prop allows you to replace the default label with your own component: -The demo below shows how to add a tooltip on the Tree Item label: +You can use the Tree Item's customization API to build new layouts and manage behaviors. -{{"demo": "LabelSlots.js", "defaultCodeOpen": false}} +Learn more about the anatomy of the Tree Items and the customization utilities provided on the [Tree Item Customization page](/x/react-tree-view/tree-item-customization/). ### Headless API diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlot.js b/docs/data/tree-view/tree-item-customization/CheckboxSlot.js new file mode 100644 index 0000000000000..81245357631ee --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlot.js @@ -0,0 +1,34 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) { + return ; +}); + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + + ); +}); + +export default function CheckboxSlot() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx b/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx new file mode 100644 index 0000000000000..64966268c86f5 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomCheckbox = React.forwardRef(function CustomCheckbox( + props: React.InputHTMLAttributes, + ref: React.Ref, +) { + return ; +}); + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + return ( + + ); +}); + +export default function CheckboxSlot() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx.preview b/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx.preview new file mode 100644 index 0000000000000..2ac8bb2f53de1 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlot.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.js b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.js new file mode 100644 index 0000000000000..193fcc9e17edd --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; +import Favorite from '@mui/icons-material/Favorite'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + , + checkedIcon: , + }, + }} + /> + ); +}); + +export default function CheckboxSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx new file mode 100644 index 0000000000000..a78b6903b1f9f --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; +import Favorite from '@mui/icons-material/Favorite'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { + TreeItem2, + TreeItem2Props, + TreeItem2SlotProps, +} from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + return ( + , + checkedIcon: , + }, + } as TreeItem2SlotProps + } + /> + ); +}); + +export default function CheckboxSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx.preview b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx.preview new file mode 100644 index 0000000000000..2ac8bb2f53de1 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CheckboxSlotProps.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/ContentSlot.js b/docs/data/tree-view/tree-item-customization/ContentSlot.js new file mode 100644 index 0000000000000..78857c927c615 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlot.js @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { alpha, styled } from '@mui/material/styles'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; + +import { MUI_X_PRODUCTS } from './products'; + +const CustomContent = styled('div')(({ theme }) => ({ + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.borderRadius, + width: '100%', + border: '1px solid', + display: 'flex', + '&:hover': { + backgroundColor: alpha((theme.vars || theme).palette.primary.main, 0.2), + }, + variants: [ + { + props: ({ status }) => status.disabled, + style: { + opacity: 0.5, + backgroundColor: theme.palette.action.disabledBackground, + }, + }, + { + props: ({ status }) => status.selected, + style: { + backgroundColor: alpha((theme.vars || theme).palette.primary.main, 0.4), + }, + }, + ], +})); + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + + ); +}); + +export default function ContentSlot() { + return ( + + Boolean(item?.disabled)} + /> + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ContentSlot.tsx b/docs/data/tree-view/tree-item-customization/ContentSlot.tsx new file mode 100644 index 0000000000000..6dc524ffcc10d --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlot.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { alpha, styled } from '@mui/material/styles'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { UseTreeItem2ContentSlotOwnProps } from '@mui/x-tree-view/useTreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomContent = styled('div')(({ theme }) => ({ + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.borderRadius, + width: '100%', + border: '1px solid', + display: 'flex', + '&:hover': { + backgroundColor: alpha((theme.vars || theme).palette.primary.main, 0.2), + }, + variants: [ + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.disabled, + style: { + opacity: 0.5, + backgroundColor: theme.palette.action.disabledBackground, + }, + }, + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.selected, + style: { + backgroundColor: alpha((theme.vars || theme).palette.primary.main, 0.4), + }, + }, + ], +})); + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + return ( + + ); +}); + +export default function ContentSlot() { + return ( + + Boolean(item?.disabled)} + /> + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ContentSlot.tsx.preview b/docs/data/tree-view/tree-item-customization/ContentSlot.tsx.preview new file mode 100644 index 0000000000000..0f3bf96b25bca --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlot.tsx.preview @@ -0,0 +1,7 @@ + Boolean(item?.disabled)} +/> \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/ContentSlotProps.js b/docs/data/tree-view/tree-item-customization/ContentSlotProps.js new file mode 100644 index 0000000000000..2aff956f7aa40 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlotProps.js @@ -0,0 +1,31 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + + ); +}); + +export default function ContentSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx b/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx new file mode 100644 index 0000000000000..a5702f4ea5d20 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { + TreeItem2, + TreeItem2Props, + TreeItem2SlotProps, +} from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + return ( + + ); +}); + +export default function ContentSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx.preview b/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx.preview new file mode 100644 index 0000000000000..1333b932705fa --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/ContentSlotProps.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.js b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.js new file mode 100644 index 0000000000000..b5e9421b1df5f --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.js @@ -0,0 +1,215 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Popover from '@mui/material/Popover'; +import Typography from '@mui/material/Typography'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, + TreeItem2Checkbox, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; + +const ITEMS = [ + { + id: '1', + label: 'An item', + children: [ + { id: '1.1', label: 'An editable child', editable: true }, + { id: '1.2', label: 'Another child' }, + ], + }, +]; + +const AnnotationText = styled(Typography)(({ theme }) => ({ + color: theme.palette.primary.contrastText, + fontSize: theme.typography.pxToRem(11), +})); + +const CustomTreeItem2Transition = styled(TreeItem2GroupTransition)(({ theme }) => ({ + padding: 6, + border: '1px solid transparent', + '&:hover:not(:has(:hover))': { + borderColor: theme.palette.primary.main, + }, +})); + +const CustomTreeItem2LabelInput = styled(TreeItem2LabelInput)(({ theme }) => ({ + color: theme.palette.text.primary, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Content = styled(TreeItem2Content)(({ theme }) => ({ + border: '1px solid transparent', + '&:hover:not(:has(:hover))': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2IconContainer = styled(TreeItem2IconContainer)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Label = styled(TreeItem2Label)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Checkbox = styled(TreeItem2Checkbox)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { id, itemId, label, disabled, children, ...other } = props; + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleMouseOver = (event) => { + setAnchorEl(event.target); + }; + + const handleMouseLeave = () => { + setAnchorEl(null); + }; + + const { + getRootProps, + getContentProps, + getIconContainerProps, + getCheckboxProps, + getLabelProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + getLabelInputProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + return ( + + + + + + + + + {status?.editable ? ( + + ) : ( + + )} + + + + {children && ( + + )} + + + ({ + maxWidth: 'initial', + maxHeight: 'initial', + minHeight: 0, + backgroundColor: theme.palette.primary.main, + padding: '0 4px', + display: 'flex', + }), + }, + }} + anchorEl={anchorEl} + open={Boolean(anchorEl)} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + > + + {anchorEl && anchorEl.closest('[data-name]')?.dataset.name} + + + + ); +}); + +export default function CustomTreeItemDemo() { + return ( + + Boolean(item?.editable)} + /> + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx new file mode 100644 index 0000000000000..522707702114c --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx @@ -0,0 +1,228 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Popover from '@mui/material/Popover'; +import Typography from '@mui/material/Typography'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { useTreeItem2, UseTreeItem2Parameters } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, + TreeItem2Checkbox, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; + +type Editable = { + editable?: boolean; + id: string; + label: string; +}; +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'An item', + children: [ + { id: '1.1', label: 'An editable child', editable: true }, + { id: '1.2', label: 'Another child' }, + ], + }, +]; + +const AnnotationText = styled(Typography)(({ theme }) => ({ + color: theme.palette.primary.contrastText, + fontSize: theme.typography.pxToRem(11), +})); + +const CustomTreeItem2Transition = styled(TreeItem2GroupTransition)(({ theme }) => ({ + padding: 6, + border: '1px solid transparent', + '&:hover:not(:has(:hover))': { + borderColor: theme.palette.primary.main, + }, +})); + +const CustomTreeItem2LabelInput = styled(TreeItem2LabelInput)(({ theme }) => ({ + color: theme.palette.text.primary, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Content = styled(TreeItem2Content)(({ theme }) => ({ + border: '1px solid transparent', + '&:hover:not(:has(:hover))': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2IconContainer = styled(TreeItem2IconContainer)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Label = styled(TreeItem2Label)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); +const CustomTreeItem2Checkbox = styled(TreeItem2Checkbox)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + border: '1px solid transparent', + '&:hover': { + borderColor: theme.palette.primary.main, + }, +})); + +interface CustomTreeItemProps + extends Omit, + Omit, 'onFocus'> {} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: CustomTreeItemProps, + ref: React.Ref, +) { + const { id, itemId, label, disabled, children, ...other } = props; + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleMouseOver = (event: React.MouseEvent) => { + setAnchorEl(event.target as HTMLElement); + }; + + const handleMouseLeave = () => { + setAnchorEl(null); + }; + + const { + getRootProps, + getContentProps, + getIconContainerProps, + getCheckboxProps, + getLabelProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + getLabelInputProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + return ( + + + + + + + + + {status?.editable ? ( + + ) : ( + + )} + + + + {children && ( + + )} + + + ({ + maxWidth: 'initial', + maxHeight: 'initial', + minHeight: 0, + backgroundColor: theme.palette.primary.main, + padding: '0 4px', + display: 'flex', + }), + }, + }} + anchorEl={anchorEl} + open={Boolean(anchorEl)} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + > + + {anchorEl && + (anchorEl.closest('[data-name]') as HTMLElement)?.dataset.name} + + + + ); +}); + +export default function CustomTreeItemDemo() { + return ( + + Boolean(item?.editable)} + /> + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx.preview b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx.preview new file mode 100644 index 0000000000000..cd8ea376bd18e --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/CustomTreeItemDemo.tsx.preview @@ -0,0 +1,9 @@ + Boolean(item?.editable)} +/> \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.js b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.js new file mode 100644 index 0000000000000..7d9f509c9f557 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.js @@ -0,0 +1,46 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; + +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { interactions } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + + const doSomething = () => { + // Do something when the checkbox is clicked + }; + const handleCheckboxOnChange = (event) => { + event.defaultMuiPrevented = true; + doSomething(); + interactions.handleCheckboxSelection(event); + }; + + return ( + + ); +}); + +export default function HandleCheckboxSelectionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx new file mode 100644 index 0000000000000..c86ae88109bac --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; +import { UseTreeItem2CheckboxSlotOwnProps } from '@mui/x-tree-view/useTreeItem2'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { + TreeItem2, + TreeItem2Props, + TreeItem2SlotProps, +} from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + const { interactions } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + + const doSomething = () => { + // Do something when the checkbox is clicked + }; + + const handleCheckboxOnChange: UseTreeItem2CheckboxSlotOwnProps['onChange'] = ( + event, + ) => { + event.defaultMuiPrevented = true; + doSomething(); + interactions.handleCheckboxSelection(event); + }; + + return ( + + ); +}); + +export default function HandleCheckboxSelectionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx.preview b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx.preview new file mode 100644 index 0000000000000..2ac8bb2f53de1 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleCheckboxSelectionDemo.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.js b/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.js new file mode 100644 index 0000000000000..07f41849946f8 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.js @@ -0,0 +1,101 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import AddBoxOutlinedIcon from '@mui/icons-material/AddBoxOutlined'; +import IndeterminateCheckBoxOutlinedIcon from '@mui/icons-material/IndeterminateCheckBoxOutlined'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2Label, + TreeItem2Root, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }, + ref, +) { + const { + getRootProps, + getContentProps, + getLabelProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + const { interactions } = useTreeItem2Utils({ + itemId, + children, + }); + + const handleClick = (event) => { + interactions.handleExpansion(event); + }; + + return ( + + + {status.expandable && ( + + {status.expanded ? ( + + + + + + + ) : ( + + + + )} + + )} + + + + + + {children && } + + + ); +}); + +export default function HandleExpansionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.tsx b/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.tsx new file mode 100644 index 0000000000000..59d5869eeff84 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import AddBoxOutlinedIcon from '@mui/icons-material/AddBoxOutlined'; +import IndeterminateCheckBoxOutlinedIcon from '@mui/icons-material/IndeterminateCheckBoxOutlined'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2Label, + TreeItem2Root, + TreeItem2Props, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }: TreeItem2Props, + ref: React.Ref, +) { + const { + getRootProps, + getContentProps, + getLabelProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + const { interactions } = useTreeItem2Utils({ + itemId, + children, + }); + + const handleClick = (event: React.MouseEvent) => { + interactions.handleExpansion(event); + }; + + return ( + + + {status.expandable && ( + + {status.expanded ? ( + + + + + + + ) : ( + + + + )} + + )} + + + + + + + {children && } + + + ); +}); + +export default function HandleExpansionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/tree-item-customization/HandleExpansionDemo.tsx.preview similarity index 100% rename from docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview rename to docs/data/tree-view/tree-item-customization/HandleExpansionDemo.tsx.preview diff --git a/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.js b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.js new file mode 100644 index 0000000000000..09caf51788b1c --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import PanoramaFishEyeIcon from '@mui/icons-material/PanoramaFishEye'; +import CircleIcon from '@mui/icons-material/Circle'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; + +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +function CustomLabel({ children, status, onClick, ...props }) { + return ( + + {children} + + {status.selected ? ( + + ) : ( + + )} + + + ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { interactions, status } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + const handleContentClick = (event) => { + event.defaultMuiPrevented = true; + }; + + const handleIconButtonClick = (event) => { + interactions.handleSelection(event); + }; + + return ( + + ); +}); + +export default function HandleSelectionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx new file mode 100644 index 0000000000000..ecd4dd6ed0dd4 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx @@ -0,0 +1,92 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import PanoramaFishEyeIcon from '@mui/icons-material/PanoramaFishEye'; +import CircleIcon from '@mui/icons-material/Circle'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; +import { + UseTreeItem2ContentSlotOwnProps, + UseTreeItem2LabelSlotOwnProps, + UseTreeItem2Status, +} from '@mui/x-tree-view/useTreeItem2'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { + TreeItem2, + TreeItem2Props, + TreeItem2SlotProps, +} from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +interface CustomLabelProps extends UseTreeItem2LabelSlotOwnProps { + status: UseTreeItem2Status; + onClick: React.MouseEventHandler; +} + +function CustomLabel({ children, status, onClick, ...props }: CustomLabelProps) { + return ( + + {children} + + {status.selected ? ( + + ) : ( + + )} + + + ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + const { interactions, status } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + const handleContentClick: UseTreeItem2ContentSlotOwnProps['onClick'] = (event) => { + event.defaultMuiPrevented = true; + }; + + const handleIconButtonClick = (event: React.MouseEvent) => { + interactions.handleSelection(event); + }; + + return ( + + ); +}); + +export default function HandleSelectionDemo() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx.preview b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx.preview new file mode 100644 index 0000000000000..72352857dd793 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/HandleSelectionDemo.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js index 674e7b6a56373..6d5828aab1a2b 100644 --- a/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js +++ b/docs/data/tree-view/tree-item-customization/IndentationAtItemLevel.js @@ -35,7 +35,7 @@ const MUI_X_PRODUCTS = [ export default function IndentationAtItemLevel() { return ( - + + + + + {children} + {secondaryLabel && ( + + {secondaryLabel} + + )} + + ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { publicAPI } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + + const item = publicAPI.getItem(props.itemId); + + return ( + + ); +}); + +export default function LabelSlot() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/LabelSlot.tsx b/docs/data/tree-view/tree-item-customization/LabelSlot.tsx new file mode 100644 index 0000000000000..3bcf47c99caf2 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/LabelSlot.tsx @@ -0,0 +1,121 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks'; + +type TreeItemWithLabel = { + id: string; + label: string; + secondaryLabel?: string; +}; + +export const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { + id: 'grid-community', + label: '@mui/x-data-grid', + secondaryLabel: 'Community package', + }, + { + id: 'grid-pro', + label: '@mui/x-data-grid-pro', + secondaryLabel: 'Pro package', + }, + { + id: 'grid-premium', + label: '@mui/x-data-grid-premium', + secondaryLabel: 'Premium package', + }, + ], + }, + { + id: 'pickers', + label: 'Date and Time pickers', + + children: [ + { + id: 'pickers-community', + label: '@mui/x-date-pickers', + secondaryLabel: 'Community package', + }, + { + id: 'pickers-pro', + label: '@mui/x-date-pickers-pro', + secondaryLabel: 'Pro package', + }, + ], + }, + { + id: 'charts', + label: 'Charts', + + children: [{ id: 'charts-community', label: '@mui/x-charts' }], + }, + { + id: 'tree-view', + label: 'Tree View', + children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }], + }, +]; + +interface CustomLabelProps { + children: string; + className: string; + secondaryLabel: string; +} + +function CustomLabel({ children, className, secondaryLabel }: CustomLabelProps) { + return ( +
+ {children} + {secondaryLabel && ( + + {secondaryLabel} + + )} +
+ ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + const { publicAPI } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, + }); + + const item = publicAPI.getItem(props.itemId); + + return ( + + ); +}); + +export default function LabelSlot() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/LabelSlot.tsx.preview b/docs/data/tree-view/tree-item-customization/LabelSlot.tsx.preview new file mode 100644 index 0000000000000..1333b932705fa --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/LabelSlot.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/LabelSlotProps.js b/docs/data/tree-view/tree-item-customization/LabelSlotProps.js new file mode 100644 index 0000000000000..5de096ebed230 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/LabelSlotProps.js @@ -0,0 +1,31 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + + ); +}); + +export default function LabelSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx b/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx new file mode 100644 index 0000000000000..4713d99686863 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + return ( + + ); +}); + +export default function LabelSlotProps() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx.preview new file mode 100644 index 0000000000000..ebee34cfcc6f4 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/LabelSlotProps.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/products.ts b/docs/data/tree-view/tree-item-customization/products.ts new file mode 100644 index 0000000000000..4eff454801bbc --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/products.ts @@ -0,0 +1,44 @@ +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +type TreeItemType = { + id: string; + label: string; + disabled?: boolean; + editable?: boolean; +}; + +export const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + editable: true, + children: [ + { id: 'grid-community', label: '@mui/x-data-grid', editable: true }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro', editable: true }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium', editable: true }, + ], + }, + { + id: 'pickers', + label: 'Date and Time pickers', + children: [ + { + id: 'pickers-community', + label: '@mui/x-date-pickers', + disabled: true, + }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro', editable: true }, + ], + }, + { + id: 'charts', + label: 'Charts', + + children: [{ id: 'charts-community', label: '@mui/x-charts' }], + }, + { + id: 'tree-view', + label: 'Tree View', + children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }], + }, +]; 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 index ffc3be81307aa..93c39d69cd276 100644 --- a/docs/data/tree-view/tree-item-customization/tree-item-customization.md +++ b/docs/data/tree-view/tree-item-customization/tree-item-customization.md @@ -11,6 +11,66 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/

Learn how to customize the Tree Item component.

+## Anatomy + +Each Tree Item component is shaped by a series of composable slots. +Hover over them in the demo below to see each slot. + + + + +{{"component": "modules/components/TreeItem2Anatomy.js"}} + +### Content + +Use the content slot to customize the content of the Tree Item or replace it with a custom component. + +#### Slot props + +The `slotProps` prop lets you pass props to the content component. +The demo below shows how to pass an `sx` handler to the content of the Tree Item: + +{{"demo": "ContentSlotProps.js"}} + +#### Slot + +The demo below shows how to replace the content slot with a custom component. + +{{"demo": "ContentSlot.js"}} + +### Label + +Use the label slot to customize the Tree Item label or replace it with a custom component. + +#### Slot props + +The `slotProps` prop lets you pass props to the label component. +The demo below shows how to pass an `id` attribute to the Tree Item label: + +{{"demo": "LabelSlotProps.js"}} + +#### Slot + +The demo below shows how to replace the label slot with a custom component. + +{{"demo": "LabelSlot.js"}} + +### Checkbox + +The checkbox is present on the items when `checkboxSelection` is enabled on the Tree View. + +#### Slot props + +You can pass props to the checkbox slot using the `slotProps` on the Tree Item 2 component. + +{{"demo": "CheckboxSlotProps.js"}} + +#### Slot + +The demo below shows how to replace the checkbox slot with a custom component. + +{{"demo": "CheckboxSlot.js"}} + ## Basics ### Change nested item's indentation @@ -69,3 +129,115 @@ const CustomTreeItem2Content = styled(TreeItem2Content)(({ theme }) => ({ ``` ::: + +## Hooks + +### useTreeItem2 + +The `useTreeItem2` hook lets you manage and customize individual Tree Items. +You can use it to get the properties needed for all slots, the status of any given Item, or to tap into the interactive API of the Tree View. + +#### Slot properties + +The `useTreeItem2` hook gives you granular control over an Item's layout by providing resolvers to get the appropriate props for each slot. +This makes it possible to build a fully custom layout for your Tree Items. + +The demo below shows how to get the props needed for each slot, and how to pass them correctly. + +{{"demo": "useTreeItem2HookProperties.js"}} + +You can pass additional props to a slot—or override existing slots—by passing an object argument to the slot's props resolver, as shown below: + +```jsx + +``` + +#### Item status + +The `useTreeItem2` hook also returns a `status` object that holds boolean values for each possible state of a Tree Item. + +```jsx +const { + status: { expanded, expandable, focused, selected, disabled, editable, editing }, +} = useTreeItem2(props); +``` + +You can use these statuses to apply custom styling to the item or conditionally render subcomponents. + +{{"demo": "useTreeItem2HookStatus.js"}} + +#### Imperative API + +The `publicAPI` object provides a number of methods to programmatically interact with the Tree View. +You can use the `useTreeItem2` hook to access the `publicAPI` object from within a Tree Item. + +{{"demo": "useTreeItem2HookPublicAPI.js"}} + +See the **Imperative API** section on each feature page to learn more about the public API methods available on the Tree View. + +### `useTreeItem2Utils` + +The `useTreeItem2Utils` hook provides a set of interaction methods for implementing custom behaviors for the Tree View. +It also returns the status of the Item. + +```jsx +const { interactions, status } = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, +}); +``` + +To override the Tree Item's default interactions, set `event.defaultMuiPrevented` to `true` in the event handlers and then implement your own behavior. + +#### Selection + +You can select an Item in a Tree View by clicking its content slot. +The demo below shows how to handle selection when the user clicks on an icon. + +{{"demo": "HandleSelectionDemo.js"}} + +#### Checkbox selection + +By default, checkbox selection is skipped if an Item is disabled or if `disableSelection` is `true` on the Tree View. +You can create a custom handler for the `onChange` event on the checkbox slot to bypass these conditions. +The demo below shows how to implement custom checkbox selection behavior. + +{{"demo": "HandleCheckboxSelectionDemo.js"}} + +Visit the [Rich Tree View](/x/react-tree-view/rich-tree-view/selection/) or [Simple Tree View](/x/react-tree-view/simple-tree-view/selection/) docs, respectively, for more details on the selection API. + +#### Expansion + +By default, a Tree Item is expanded when the user clicks on its contents. +You can change the expansion trigger using the `expansionTrigger` prop on the `iconContainer`. +For more details, see [Expansion—Limit expansion to icon container](/x/react-tree-view/rich-tree-view/expansion/#limit-expansion-to-icon-container). + +Use the `handleExpansion` interaction method for deeper customization of the Item's expansion behavior. + +The demo below shows how to introduce a new element that expands and collapses the Item. + +{{"demo": "HandleExpansionDemo.js"}} + +#### Label editing + +The `useTreeItem2Utils` hook provides the following interaction methods relevant to label editing behavior: + +```jsx +const { + interactions: { + toggleItemEditing, + handleCancelItemLabelEditing, + handleSaveItemLabel, + }, +} = useTreeItem2Utils({ + itemId: props.itemId, + children: props.children, +}); +``` + +See [Editing—enable editing using only icons](/x/react-tree-view/rich-tree-view/editing/#enable-editing-using-only-icons) for more details on customizing this behavior. diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.js b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.js new file mode 100644 index 0000000000000..b93e60f39e001 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Checkbox, + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2Label, + TreeItem2Root, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }, + ref, +) { + const { + getRootProps, + getContentProps, + getIconContainerProps, + getCheckboxProps, + getLabelProps, + getLabelInputProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + return ( + + + + + + + + {status.editing ? ( + + ) : ( + + )} + + + + {children && } + + + ); +}); + +export default function useTreeItem2HookProperties() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx new file mode 100644 index 0000000000000..e2ec284be456a --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx @@ -0,0 +1,73 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Checkbox, + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2Label, + TreeItem2Root, + TreeItem2Props, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2DragAndDropOverlay } from '@mui/x-tree-view/TreeItem2DragAndDropOverlay'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; +import { MUI_X_PRODUCTS } from './products'; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }: TreeItem2Props, + ref: React.Ref, +) { + const { + getRootProps, + getContentProps, + getIconContainerProps, + getCheckboxProps, + getLabelProps, + getLabelInputProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + status, + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); + + return ( + + + + + + + + {status.editing ? ( + + ) : ( + + )} + + + + {children && } + + + ); +}); + +export default function useTreeItem2HookProperties() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx.preview b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx.preview new file mode 100644 index 0000000000000..5c65f878fe8fb --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookProperties.tsx.preview @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.js b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.js new file mode 100644 index 0000000000000..086d8d6ff0867 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.js @@ -0,0 +1,56 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +function CustomLabel({ children, className, numberOfChildren }) { + return ( + + {children} + + + + ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { publicAPI } = useTreeItem2(props); + + const childrenNumber = publicAPI.getItemOrderedChildrenIds(props.itemId).length; + + return ( + + ); +}); + +export default function useTreeItem2HookPublicAPI() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx new file mode 100644 index 0000000000000..271d94879967a --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import Stack from '@mui/material/Stack'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { MUI_X_PRODUCTS } from './products'; + +interface CustomLabelProps { + children: string; + className: string; + numberOfChildren: number; +} + +function CustomLabel({ children, className, numberOfChildren }: CustomLabelProps) { + return ( + + {children} + + + + ); +} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItem2Props, + ref: React.Ref, +) { + const { publicAPI } = useTreeItem2(props); + + const childrenNumber = publicAPI.getItemOrderedChildrenIds(props.itemId).length; + + return ( + + ); +}); + +export default function useTreeItem2HookPublicAPI() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx.preview b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx.preview new file mode 100644 index 0000000000000..1333b932705fa --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookPublicAPI.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.js b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.js new file mode 100644 index 0000000000000..6385bcb3f5ae3 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.js @@ -0,0 +1,152 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import AdjustIcon from '@mui/icons-material/Adjust'; +import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'; +import ExpandCircleDownOutlinedIcon from '@mui/icons-material/ExpandCircleDownOutlined'; +import ExpandCircleDownRoundedIcon from '@mui/icons-material/ExpandCircleDownRounded'; +import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import DrawOutlinedIcon from '@mui/icons-material/DrawOutlined'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2Root, + TreeItem2GroupTransition, + TreeItem2IconContainer, + TreeItem2Label, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; +import { MUI_X_PRODUCTS } from './products'; + +function StatusLegend() { + return ( + ({ + padding: 2, + background: theme.palette.grey[50], + ...theme.applyStyles('dark', { + background: theme.palette.grey[900], + }), + })} + > + + Legend + + + {STATUS_ICONS.focused} + focused + + + {STATUS_ICONS.selected} + selected + + + {STATUS_ICONS.expandable} + expandable + + + {STATUS_ICONS.expanded} + expanded + + + {STATUS_ICONS.disabled} + disabled + + + {STATUS_ICONS.editable} + editable + + + {STATUS_ICONS.editing} + editing + + + + ); +} + +const STATUS_ICONS = { + focused: , + selected: , + expandable: , + expanded: , + disabled: , + editable: , + editing: , +}; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }, + ref, +) { + const { + getRootProps, + getContentProps, + getLabelProps, + getGroupTransitionProps, + getIconContainerProps, + getLabelInputProps, + status, + } = useTreeItem2({ id, itemId, label, disabled, children, rootRef: ref }); + + return ( + + + + + + + + {status.editing ? ( + + ) : ( + + )} + + + {Object.keys(STATUS_ICONS).map((iconKey, index) => { + if (status[iconKey]) { + return ( + + {STATUS_ICONS[iconKey]} + + ); + } + return null; + })} + + + {children && } + + + ); +}); + +export default function useTreeItem2HookStatus() { + return ( + + + Boolean(item?.disabled)} + isItemEditable={(item) => Boolean(item?.editable)} + experimentalFeatures={{ + labelEditing: true, + }} + /> + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx new file mode 100644 index 0000000000000..2ea3153a42cef --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx @@ -0,0 +1,157 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import AdjustIcon from '@mui/icons-material/Adjust'; +import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'; +import ExpandCircleDownOutlinedIcon from '@mui/icons-material/ExpandCircleDownOutlined'; +import ExpandCircleDownRoundedIcon from '@mui/icons-material/ExpandCircleDownRounded'; +import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import DrawOutlinedIcon from '@mui/icons-material/DrawOutlined'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItem2, UseTreeItem2Status } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2Root, + TreeItem2Props, + TreeItem2GroupTransition, + TreeItem2IconContainer, + TreeItem2Label, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2LabelInput } from '@mui/x-tree-view/TreeItem2LabelInput'; +import { MUI_X_PRODUCTS } from './products'; + +function StatusLegend() { + return ( + ({ + padding: 2, + background: theme.palette.grey[50], + ...theme.applyStyles('dark', { + background: theme.palette.grey[900], + }), + })} + > + + Legend + + + {STATUS_ICONS.focused} + focused + + + {STATUS_ICONS.selected} + selected + + + {STATUS_ICONS.expandable} + expandable + + + {STATUS_ICONS.expanded} + expanded + + + {STATUS_ICONS.disabled} + disabled + + + {STATUS_ICONS.editable} + editable + + + {STATUS_ICONS.editing} + editing + + + + ); +} + +const STATUS_ICONS: { + [K in keyof UseTreeItem2Status]: React.ReactNode; +} = { + focused: , + selected: , + expandable: , + expanded: , + disabled: , + editable: , + editing: , +}; + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + { id, itemId, label, disabled, children }: TreeItem2Props, + ref: React.Ref, +) { + const { + getRootProps, + getContentProps, + getLabelProps, + getGroupTransitionProps, + getIconContainerProps, + getLabelInputProps, + status, + } = useTreeItem2({ id, itemId, label, disabled, children, rootRef: ref }); + + return ( + + + + + + + + {status.editing ? ( + + ) : ( + + )} + + + {(Object.keys(STATUS_ICONS) as [keyof UseTreeItem2Status]).map( + (iconKey, index) => { + if (status[iconKey]) { + return ( + + {STATUS_ICONS[iconKey]} + + ); + } + return null; + }, + )} + + + {children && } + + + ); +}); + +export default function useTreeItem2HookStatus() { + return ( + + + Boolean(item?.disabled)} + isItemEditable={(item) => Boolean(item?.editable)} + experimentalFeatures={{ + labelEditing: true, + }} + /> + + + + ); +} diff --git a/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx.preview b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx.preview new file mode 100644 index 0000000000000..bdd7c89341d10 --- /dev/null +++ b/docs/data/tree-view/tree-item-customization/useTreeItem2HookStatus.tsx.preview @@ -0,0 +1,14 @@ + + Boolean(item?.disabled)} + isItemEditable={(item) => Boolean(item?.editable)} + experimentalFeatures={{ + labelEditing: true, + }} + /> + + \ No newline at end of file diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts index a4a7b3f5cfa2f..4f11a03dc6cc3 100644 --- a/docs/next-env.d.ts +++ b/docs/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/docs/public/static/x/tree-view-illustrations/tree-item-dark.png b/docs/public/static/x/tree-view-illustrations/tree-item-dark.png new file mode 100644 index 0000000000000..9cd285c3d2797 Binary files /dev/null and b/docs/public/static/x/tree-view-illustrations/tree-item-dark.png differ diff --git a/docs/public/static/x/tree-view-illustrations/tree-item-light.png b/docs/public/static/x/tree-view-illustrations/tree-item-light.png new file mode 100644 index 0000000000000..052d8359525c4 Binary files /dev/null and b/docs/public/static/x/tree-view-illustrations/tree-item-light.png differ diff --git a/docs/src/modules/components/TreeItem2Anatomy.js b/docs/src/modules/components/TreeItem2Anatomy.js new file mode 100644 index 0000000000000..2f46bb4dce947 --- /dev/null +++ b/docs/src/modules/components/TreeItem2Anatomy.js @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { useTheme } from '@mui/material/styles'; +import Card from '@mui/material/Card'; +import CardMedia from '@mui/material/CardMedia'; + +export default function TreeItem2Anatomy() { + const { palette } = useTheme(); + + const src = + palette.mode === 'light' + ? '/static/x/tree-view-illustrations/tree-item-light.png' + : '/static/x/tree-view-illustrations/tree-item-dark.png'; + return ( + + + + ); +} diff --git a/packages/x-tree-view/src/useTreeItem2/index.ts b/packages/x-tree-view/src/useTreeItem2/index.ts index 94545e26b83b6..58154d6640e85 100644 --- a/packages/x-tree-view/src/useTreeItem2/index.ts +++ b/packages/x-tree-view/src/useTreeItem2/index.ts @@ -12,6 +12,7 @@ export type { UseTreeItem2ContentSlotOwnProps, UseTreeItem2LabelInputSlotOwnProps, UseTreeItem2LabelSlotOwnProps, + UseTreeItem2CheckboxSlotOwnProps, UseTreeItem2IconContainerSlotOwnProps, UseTreeItem2GroupTransitionSlotOwnProps, UseTreeItem2DragAndDropOverlaySlotOwnProps, diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts index 9457cc1572509..68080b4f4f735 100644 --- a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts @@ -100,7 +100,7 @@ export type UseTreeItem2LabelInputSlotProps = ExternalProps export interface UseTreeItem2CheckboxSlotOwnProps { visible: boolean; checked: boolean; - onChange: React.ChangeEventHandler; + onChange: MuiCancellableEventHandler>; disabled: boolean; ref: React.RefObject; tabIndex: -1; diff --git a/scripts/x-tree-view-pro.exports.json b/scripts/x-tree-view-pro.exports.json index ea69aaa8f832b..aa48d50761f16 100644 --- a/scripts/x-tree-view-pro.exports.json +++ b/scripts/x-tree-view-pro.exports.json @@ -64,6 +64,7 @@ { "name": "unstable_resetCleanupTracking", "kind": "Variable" }, { "name": "unstable_useTreeItem2", "kind": "Variable" }, { "name": "useTreeItem2", "kind": "Variable" }, + { "name": "UseTreeItem2CheckboxSlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2ContentSlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2DragAndDropOverlaySlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2GroupTransitionSlotOwnProps", "kind": "Interface" }, diff --git a/scripts/x-tree-view.exports.json b/scripts/x-tree-view.exports.json index 676aa7f67abea..ad00319179cf5 100644 --- a/scripts/x-tree-view.exports.json +++ b/scripts/x-tree-view.exports.json @@ -68,6 +68,7 @@ { "name": "unstable_resetCleanupTracking", "kind": "Variable" }, { "name": "unstable_useTreeItem2", "kind": "Variable" }, { "name": "useTreeItem2", "kind": "Variable" }, + { "name": "UseTreeItem2CheckboxSlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2ContentSlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2DragAndDropOverlaySlotOwnProps", "kind": "Interface" }, { "name": "UseTreeItem2GroupTransitionSlotOwnProps", "kind": "Interface" },