Reputation: 490
I'm trying to override the style of the selected TreeItem inside a Material-UI TreeView component. According to the CSS API docs, there is a selected
selector, but when I use that, I see the entire subtree getting styled, and not just the selected item.
What is the correct selector to use to only style the selected tree item?
Code Sandbox: https://codesandbox.io/s/nostalgic-flower-e85cd?file=/src/App.js
import React from "react";
import { makeStyles } 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 MuiTreeItem from "@material-ui/lab/TreeItem";
import { withStyles } from "@material-ui/core/styles";
const useStyles = makeStyles({
root: {
height: 240,
flexGrow: 1,
maxWidth: 400
}
});
const TreeItem = withStyles({
selected: {
color: "red"
}
})(MuiTreeItem);
export default function FileSystemNavigator() {
const classes = useStyles();
return (
<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
<TreeItem nodeId="1" label="Applications">
<TreeItem nodeId="2" label="Calendar" />
<TreeItem nodeId="3" label="Chrome" />
<TreeItem nodeId="4" label="Webstorm" />
</TreeItem>
<TreeItem nodeId="5" label="Documents">
<TreeItem nodeId="10" label="OSS" />
<TreeItem nodeId="6" label="Material-UI">
<TreeItem nodeId="7" label="src">
<TreeItem nodeId="8" label="index.js" />
<TreeItem nodeId="9" label="tree-view.js" />
</TreeItem>
</TreeItem>
</TreeItem>
</TreeView>
);
}
Upvotes: 4
Views: 14981
Reputation: 81036
When you have questions about how to override the default Material-UI styles, the best resource is to look at how the default styles are defined.
Below are the default styles for TreeItemContent:
const StyledTreeItemContent = styled(TreeItemContent, {
name: 'MuiTreeItem',
slot: 'Content',
overridesResolver: (props, styles) => {
return [
styles.content,
styles.iconContainer && {
[`& .${treeItemClasses.iconContainer}`]: styles.iconContainer,
},
styles.label && {
[`& .${treeItemClasses.label}`]: styles.label,
},
];
},
})(({ theme }) => ({
padding: '0 8px',
width: '100%',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
WebkitTapHighlightColor: 'transparent',
'&:hover': {
backgroundColor: (theme.vars || theme).palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
[`&.${treeItemClasses.disabled}`]: {
opacity: (theme.vars || theme).palette.action.disabledOpacity,
backgroundColor: 'transparent',
},
[`&.${treeItemClasses.focused}`]: {
backgroundColor: (theme.vars || theme).palette.action.focus,
},
[`&.${treeItemClasses.selected}`]: {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})`
: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
'&:hover': {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))`
: alpha(
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: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})`
: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
},
},
[`&.${treeItemClasses.focused}`]: {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))`
: alpha(
theme.palette.primary.main,
theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity,
),
},
},
[`& .${treeItemClasses.iconContainer}`]: {
marginRight: 4,
width: 15,
display: 'flex',
flexShrink: 0,
justifyContent: 'center',
'& svg': {
fontSize: 18,
},
},
[`& .${treeItemClasses.label}`]: {
width: '100%',
// fixes overflow - see https://github.com/mui/material-ui/issues/27372
minWidth: 0,
paddingLeft: 4,
position: 'relative',
...theme.typography.body1,
},
}));
The overall structure of TreeItem can be found by looking at TreeItem and TreeItemContent and is as follows (simplified slightly):
<li className="MuiTreeItem-root">
<div className="MuiTreeItem-content">
<div className="MuiTreeItem-iconContainer">
{icon}
</div>
<div className="MuiTreeItem-label">
{label}
</div>
</div>
{children}
</li>
Additionally, the Mui-selected
, Mui-expanded
, Mui-focused
, and Mui-disabled
classes get added to the MuiTreeItem-content
div when applicable.
You can target the MuiTreeItem-content
div as follows:
const TreeItem = styled(MuiTreeItem)`
& > .MuiTreeItem-content.Mui-selected {
color: red;
}
`;
If you don't want to include the icon in the styling, then you can just target the label within the content:
const TreeItem = styled(MuiTreeItem)`
& > .MuiTreeItem-content.Mui-selected .MuiTreeItem-label {
color: red;
}
`;
If you want to change the background color, you need to pay attention to a few more details in the default styling (since the default styling does a lot with background color) in order to deal appropriately with the hover and focused states.
Upvotes: 10
Reputation: 9883
The answers so far use withStyles
from MUI v4 because that was the current version when they were posted. I'm pretty sure there is a MUI v5 approach, but I gave up on trying to get that to work.
The technique used doesn't really matter as much, anyway: The problem is getting all the selectors right.
I ended up adding this to index.css
which seems to disable any most focused
/ hover
/ selected
styles (some edge cases seem to persist):
.MuiTreeItem-root:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content:hover > .MuiTreeItem-label {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected > .MuiTreeItem-label {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected.Mui-focused {
background: transparent
}
Yes, as far as I can tell, they are all required. Probably still a few more.
NOTE: That's focused
(with one s
), using e.g. MUI-focussed
fails silently.
Upvotes: 2
Reputation: 367
import MuiTreeItem from "@material-ui/lab/TreeItem";
import { withStyles } from "@material-ui/core/styles";
const TreeItem = withStyles({
root: {
"&.Mui-selected > .MuiTreeItem-content": {
background: "#89CFF0"
},
"&.MuiTreeItem-root > .MuiTreeItem-content:hover": {
background: "gray",
},
"&.MuiTreeItem-root > .MuiTreeItem-content:hover > .MuiTreeItem-label": {
background: "#89CFF0",
},
'@media (hover: none)': {
backgroundColor: 'transparent',
}
}
})(MuiTreeItem);
Upvotes: 0