I extended material ui tree view demo by adding a context menu for each node, using the following .tsx code (sandbox demo here).
import * as React from "react";
import { Typography, Menu, MenuItem } from "@mui/material";
import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import TreeItem from "@mui/lab/TreeItem";
interface RenderTree {
id: string;
name: string;
children?: readonly RenderTree[];
}
const data: RenderTree = {
id: "root",
name: "Parent",
children: [
{
id: "1",
name: "Child - 1"
},
{
id: "3",
name: "Child - 3",
children: [
{
id: "4",
name: "Child - 4"
}
]
}
]
};
const NodeWithContextMenu = (props) => {
const [contextMenu, setContextMenu] = React.useState<{
mouseX: number;
mouseY: number;
} | null>(null);
const handleContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
// Other native context menus might behave different.
// With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
null
);
};
const handleClose = () => {
setContextMenu(null);
};
return (
<div onContextMenu={handleContextMenu} style={{ cursor: "context-menu" }}>
<Typography>{props.label}</Typography>
<Menu
open={contextMenu !== null}
onClose={handleClose}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
<MenuItem onClick={handleClose}>Copy</MenuItem>
<MenuItem onClick={handleClose}>Print</MenuItem>
<MenuItem onClick={handleClose}>Highlight</MenuItem>
<MenuItem onClick={handleClose}>Email</MenuItem>
</Menu>
</div>
);
};
export default function RichObjectTreeView() {
const renderTree = (nodes: RenderTree) => (
<TreeItem
key={nodes.id}
nodeId={nodes.id}
label={<NodeWithContextMenu label={nodes.name} />}
>
{Array.isArray(nodes.children)
? nodes.children.map((node) => renderTree(node))
: null}
</TreeItem>
);
return (
<TreeView
aria-label="rich object"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpanded={["root"]}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 110, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
>
{renderTree(data)}
</TreeView>
);
}
On selecting a right-click context menu item the underlying tree node is toggled (expanded or contracted). This happens when either clicking on the context menu item or anywhere else to close the context menu.
Expected behavior: The use of the context menu should not toggle the underlying node.
Here is the code sandbox for the basic mui.com demo and my code sample above.
