Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TreeView] Improve customization of tree item #22846

Merged
merged 13 commits into from
Oct 10, 2020
8 changes: 4 additions & 4 deletions docs/pages/api-docs/tree-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ The `MuiTreeItem` name can be used for providing [default props](/customization/
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the component. |
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| <span class="prop-name">collapseIcon</span> | <span class="prop-type">node</span> | | The icon used to collapse the node. |
| <span class="prop-name">ContentComponent</span> | <span class="prop-type">element type</span> | <span class="prop-default">TreeItemContent</span> | The component used for the content node.<br>⚠️ [Needs to be able to hold a ref](/guides/composition/#caveat-with-refs). |
| <span class="prop-name">ContentProps</span> | <span class="prop-type">object</span> | | Props applied to ContentComponent |
| <span class="prop-name">disabled</span> | <span class="prop-type">bool</span> | | If `true`, the node will be disabled. |
| <span class="prop-name">endIcon</span> | <span class="prop-type">node</span> | | The icon displayed next to a end node. |
| <span class="prop-name">expandIcon</span> | <span class="prop-type">node</span> | | The icon used to expand the node. |
| <span class="prop-name">icon</span> | <span class="prop-type">node</span> | | The icon to display next to the tree node's label. |
| <span class="prop-name">label</span> | <span class="prop-type">node</span> | | The tree node label. |
| <span class="prop-name required">nodeId<abbr title="required">*</abbr></span> | <span class="prop-type">string</span> | | The id of the node. |
| <span class="prop-name">onFocus</span> | <span class="prop-type">unsupportedProp</span> | | This prop isn't supported. Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. |
| <span class="prop-name">onIconClick</span> | <span class="prop-type">func</span> | | `onClick` handler for the icon container. Call `event.preventDefault()` to prevent `onNodeToggle` from being called. |
| <span class="prop-name">onLabelClick</span> | <span class="prop-type">func</span> | | `onClick` handler for the label container. Call `event.preventDefault()` to prevent `onNodeToggle` from being called. |
| <span class="prop-name">TransitionComponent</span> | <span class="prop-type">elementType</span> | <span class="prop-default">Collapse</span> | The component used for the transition. [Follow this guide](/components/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. |
| <span class="prop-name">TransitionProps</span> | <span class="prop-type">object</span> | | Props applied to the transition element. By default, the element is based on this [`Transition`](http://reactcommunity.org/react-transition-group/transition) component. |

Expand All @@ -53,12 +53,12 @@ Any other props supplied will be provided to the root element (native element).
|:-----|:-------------|:------------|
| <span class="prop-name">root</span> | <span class="prop-name">.MuiTreeItem-root</span> | Styles applied to the root element.
| <span class="prop-name">group</span> | <span class="prop-name">.MuiTreeItem-group</span> | Styles applied to the `role="group"` element.
| <span class="prop-name">content</span> | <span class="prop-name">.MuiTreeItem-content</span> | Styles applied to the tree node content.
| <span class="prop-name">content</span> | <span class="prop-name">.MuiTreeItem-content</span> | Styles applied to the content element.
| <span class="prop-name">expanded</span> | <span class="prop-name">.Mui-expanded</span> | Pseudo-class applied to the content element when expanded.
| <span class="prop-name">selected</span> | <span class="prop-name">.Mui-selected</span> | Pseudo-class applied to the content element when selected.
| <span class="prop-name">focused</span> | <span class="prop-name">.Mui-focused</span> | Pseudo-class applied to the content element when focused.
| <span class="prop-name">disabled</span> | <span class="prop-name">.Mui-disabled</span> | Pseudo-class applied to the element when disabled.
| <span class="prop-name">iconContainer</span> | <span class="prop-name">.MuiTreeItem-iconContainer</span> | Styles applied to the tree node icon and collapse/expand icon.
| <span class="prop-name">iconContainer</span> | <span class="prop-name">.MuiTreeItem-iconContainer</span> | Styles applied to the tree node icon.
| <span class="prop-name">label</span> | <span class="prop-name">.MuiTreeItem-label</span> | Styles applied to the label element.

You can override the style of the component thanks to one of these customization points:
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/api-docs/tree-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The `MuiTreeView` name can be used for providing [default props](/customization/
| <span class="prop-name">expanded</span> | <span class="prop-type">Array&lt;string&gt;</span> | | Expanded node ids. (Controlled) |
| <span class="prop-name">id</span> | <span class="prop-type">string</span> | | 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. |
| <span class="prop-name">multiSelect</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If true `ctrl` and `shift` will trigger multiselect. |
| <span class="prop-name">onNodeFocus</span> | <span class="prop-type">func</span> | | Callback fired when tree items are focused.<br><br>**Signature:**<br>`function(event: object, value: string) => void`<br>*event:* The event source of the callback<br>*value:* of the focused node. |
| <span class="prop-name">onNodeFocus</span> | <span class="prop-type">func</span> | | Callback fired when tree items are focused.<br><br>**Signature:**<br>`function(event: object, value: string) => void`<br>*event:* The event source of the callback **Warning**: This is a generic event not a focus event.<br>*value:* of the focused node. |
| <span class="prop-name">onNodeSelect</span> | <span class="prop-type">func</span> | | Callback fired when tree items are selected/unselected.<br><br>**Signature:**<br>`function(event: object, value: array \| string) => void`<br>*event:* The event source of the callback<br>*value:* of the selected nodes. When `multiSelect` is true this is an array of strings; when false (default) a string. |
| <span class="prop-name">onNodeToggle</span> | <span class="prop-type">func</span> | | Callback fired when tree items are expanded/collapsed.<br><br>**Signature:**<br>`function(event: object, nodeIds: array) => void`<br>*event:* The event source of the callback.<br>*nodeIds:* The ids of the expanded nodes. |
| <span class="prop-name">selected</span> | <span class="prop-type">Array&lt;string&gt;<br>&#124;&nbsp;string</span> | | Selected node ids. (Controlled) When `multiSelect` is true this takes an array of strings; when false (default) a string. |
Expand Down
197 changes: 197 additions & 0 deletions docs/src/pages/components/tree-view/BarTreeView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles, fade } from '@material-ui/core/styles';
import TreeView from '@material-ui/lab/TreeView';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import TreeItem, { useTreeItem } from '@material-ui/lab/TreeItem';
import clsx from 'clsx';
import Typography from '@material-ui/core/Typography';

const useStyles = makeStyles({
root: {
height: 240,
flexGrow: 1,
maxWidth: 400,
position: 'relative',
},
});

const useContentStyles = makeStyles((theme) => ({
root: {
WebkitTapHighlightColor: 'transparent',
'&:hover, &$disabled, &$focused, &$selected, &$selected$focused, &$selected:hover': {
backgroundColor: 'transparent',
},
},
label: {
width: '100%',
paddingLeft: 4,
position: 'relative',
},
bar: {
position: 'absolute',
width: '100%',
height: 24,
left: 0,
'$root:hover &': {
backgroundColor: theme.palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
'$root$disabled &': {
opacity: theme.palette.action.disabledOpacity,
backgroundColor: 'transparent',
},
'$root$focused &': {
backgroundColor: theme.palette.action.focus,
},
'$root$selected &': {
backgroundColor: fade(
theme.palette.primary.main,
theme.palette.action.selectedOpacity,
),
},
'$root$selected:hover &': {
backgroundColor: fade(
theme.palette.primary.main,
theme.palette.action.selectedOpacity +
theme.palette.action.hoverOpacity,
),
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: fade(
theme.palette.primary.main,
theme.palette.action.selectedOpacity,
),
},
},
'$root$selected$focused &': {
backgroundColor: fade(
theme.palette.primary.main,
theme.palette.action.selectedOpacity +
theme.palette.action.focusOpacity,
),
},
},
expanded: {},
selected: {},
focused: {},
disabled: {},
}));

const CustomContent = React.forwardRef(function CustomContent(props, ref) {
const {
classes,
label,
nodeId,
icon: iconProp,
expansionIcon,
displayIcon,
} = props;

const contentClasses = useContentStyles();

const {
disabled,
expanded,
selected,
focused,
handleExpansion,
handleSelection,
preventSelection,
} = useTreeItem(nodeId);

const icon = iconProp || expansionIcon || displayIcon;

const handleMouseDown = (event) => {
preventSelection(event);
};

const handleClick = (event) => {
handleExpansion(event);
handleSelection(event);
};

return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
<div
className={clsx(classes.root, contentClasses.root, {
[contentClasses.expanded]: expanded,
[contentClasses.selected]: selected,
[contentClasses.focused]: focused,
[contentClasses.disabled]: disabled,
})}
onClick={handleClick}
onMouseDown={handleMouseDown}
ref={ref}
>
<div className={contentClasses.bar} />
<div className={classes.iconContainer}>{icon}</div>
<Typography component="div" className={classes.label}>
{label}
</Typography>
</div>
);
});

CustomContent.propTypes = {
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object.isRequired,
/**
* The icon to display next to the tree node's label. Either a parent or end icon.
*/
displayIcon: PropTypes.node,
/**
* The icon to display next to the tree node's label. Either an expansion or collapse icon.
*/
expansionIcon: PropTypes.node,
/**
* The icon to display next to the tree node's label.
*/
icon: PropTypes.node,
/**
* The tree node label.
*/
label: PropTypes.node,
/**
* The id of the node.
*/
nodeId: PropTypes.string.isRequired,
};

const CustomTreeItem = (props) => (
<TreeItem ContentComponent={CustomContent} {...props} />
);

export default function BarTreeView() {
const classes = useStyles();

return (
<TreeView
aria-label="icon expansion"
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
<CustomTreeItem nodeId="1" label="Applications">
<CustomTreeItem nodeId="2" label="Calendar" />
<CustomTreeItem nodeId="3" label="Chrome" />
<CustomTreeItem nodeId="4" label="Webstorm" />
</CustomTreeItem>
<CustomTreeItem nodeId="5" label="Documents">
<CustomTreeItem nodeId="10" label="OSS" />
<CustomTreeItem nodeId="6" label="Material-UI">
<CustomTreeItem nodeId="7" label="src">
<CustomTreeItem nodeId="8" label="index.js" />
<CustomTreeItem nodeId="9" label="tree-view.js" />
</CustomTreeItem>
</CustomTreeItem>
</CustomTreeItem>
</TreeView>
);
}