122 lines
3.7 KiB
JavaScript
122 lines
3.7 KiB
JavaScript
// MenuItem.jsx
|
|
import React, { useState } from "react";
|
|
import {
|
|
ListItem,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Collapse,
|
|
List,
|
|
styled,
|
|
Menu,
|
|
MenuItem as MuiMenuItem
|
|
} from "@mui/material";
|
|
import { ExpandLess, ExpandMore, Folder, FolderOpen } from "@mui/icons-material";
|
|
import StatusIndicator from "./StatusIndicator";
|
|
|
|
const StyledListItem = styled(ListItem)(({ theme, level }) => ({
|
|
cursor: "pointer",
|
|
paddingLeft: theme.spacing(2 + level * 2),
|
|
position: 'relative',
|
|
'&:hover': {
|
|
backgroundColor: theme.palette.action.hover,
|
|
},
|
|
'&.Mui-selected': {
|
|
backgroundColor: theme.palette.custom.sidebarHover,
|
|
},
|
|
}));
|
|
|
|
const MenuItem = ({ item, onSelectItem, level = 0, collapsed, onEdit }) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [contextMenu, setContextMenu] = useState(null);
|
|
const hasChildren = Array.isArray(item.items) && item.items.length > 0;
|
|
|
|
const handleContextMenu = (e) => {
|
|
e.preventDefault();
|
|
setContextMenu(
|
|
contextMenu === null
|
|
? { mouseX: e.clientX - 2, mouseY: e.clientY - 4 }
|
|
: null
|
|
);
|
|
};
|
|
|
|
const handleCloseContextMenu = () => {
|
|
setContextMenu(null);
|
|
};
|
|
|
|
const handleToggle = (e) => {
|
|
e.stopPropagation();
|
|
setIsOpen(!isOpen);
|
|
};
|
|
|
|
const handleClick = () => {
|
|
if (onSelectItem) {
|
|
onSelectItem(item);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<StyledListItem
|
|
component="div"
|
|
onClick={hasChildren ? handleToggle : handleClick}
|
|
onContextMenu={handleContextMenu}
|
|
level={level}
|
|
sx={{
|
|
pl: collapsed ? 2 : 2 + level * 2,
|
|
justifyContent: collapsed ? 'center' : 'flex-start',
|
|
}}
|
|
>
|
|
{!collapsed && <StatusIndicator status={item.status} />}
|
|
|
|
<ListItemIcon sx={{ minWidth: collapsed ? 'auto' : 56 }}>
|
|
{hasChildren ? (isOpen ? <FolderOpen /> : <Folder />) : <Folder />}
|
|
</ListItemIcon>
|
|
|
|
{!collapsed && (
|
|
<>
|
|
<ListItemText
|
|
primary={item.title}
|
|
primaryTypographyProps={{
|
|
color: 'custom.sidebarText'
|
|
}}
|
|
/>
|
|
{hasChildren && (isOpen ? <ExpandLess /> : <ExpandMore />)}
|
|
</>
|
|
)}
|
|
</StyledListItem>
|
|
|
|
<Menu
|
|
open={contextMenu !== null}
|
|
onClose={handleCloseContextMenu}
|
|
anchorReference="anchorPosition"
|
|
anchorPosition={
|
|
contextMenu !== null
|
|
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
|
|
: undefined
|
|
}
|
|
>
|
|
|
|
</Menu>
|
|
|
|
{hasChildren && !collapsed && (
|
|
<Collapse in={isOpen} timeout="auto" unmountOnExit>
|
|
<List component="div" disablePadding>
|
|
{item.items.map((child, index) => (
|
|
<MenuItem
|
|
key={child.id ?? index}
|
|
item={child}
|
|
onSelectItem={onSelectItem}
|
|
onEdit={onEdit}
|
|
level={level + 1}
|
|
collapsed={collapsed}
|
|
/>
|
|
))}
|
|
</List>
|
|
</Collapse>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default MenuItem;
|