Skip to content

Commit

Permalink
[TreeView] Improve customization of tree item (#22846)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding committed Oct 10, 2020
1 parent 9f303a4 commit 3d949c1
Show file tree
Hide file tree
Showing 19 changed files with 1,004 additions and 151 deletions.
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>
);
}

0 comments on commit 3d949c1

Please sign in to comment.