Reputation: 1766
I'm having an issue with my Material UI <Menu>
. Basically, I'm mapping over a <Card>
to display some data and interface functionality. I'm trying to add a <IconButton>
on each card. Once, clicked, the button opens a menu. Currently the issue I'm facing is, when I click on the IconButton, all menu's open up on top of each other (due to mapping over menu items with the same state prop).
<CardHeader
action={
<>
<IconButton
onClick={handleClick}
aria-label="settings">
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Edit </MenuItem>
<MenuItem onClick={handleClose}>Delete</MenuItem>
</Menu>
</>
}
/>
You can tell due to the large shadow behind the Menu, that's not CSS by choice... thats all my menus stacked up from my <Card>
component. I know the culprit is having one open close state property. Is there any "quickfix" to this solution? I can't hardcode the states themselves.
Here is the rest of the code where I return my map function that produces <Card>
. Please check my <CardHeader>
. Note that I have two <CardHeaders>
, that's because I have a conditional that dictates which CardHeader to use for the card.
const UserBuckets = (props) => {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
if (!buckets || buckets.length === 0) return <p>Can not find any buckets, make one below!</p>;
return (
<React.Fragment style={{width:"90%"}}>
<Container style={{width:"90%"}} maxWidth="md" component="main">
<Grid container spacing={5} alignItems="stretch">
{buckets.map((bucket) =>
{
return (
<Grid item key={bucket.id} xs={12} sm={6} md={4} lg={4}>
<Card
className={classes.root}
classes={{ root: state.raised ? classes.cardHovered : "" }}
onMouseOver={() => setState({ shadow: 3 })}
onMouseOut={() => setState({ shadow: 1 })}
zdepth={state.shadow}
style={{ height: "100%", borderRadius:"30px"}}
>
{(!bucket || bucket.stock_list === null) &&
<CardHeader className={classes.bucketTitle} classes={{ title: classes.bucketTitle }}
title={
<>
<Link
color="textPrimary"
href={'dash/' + bucket.slug}
className={classes.link}
style={{ textDecoration: 'none' }}
>
{bucket.name.substr(0, 50)}
</Link>
</>
}
subheader="Add Stocks to get started!"
action={
<>
<IconButton
onClick={handleClick}
aria-label="settings">
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
style={{boxShadow: 'none'}}
elevation={0}
>
<MenuItem onClick={handleClose}>Edit </MenuItem>
<MenuItem onClick={handleClose}>Delete</MenuItem>
</Menu>
</>
}
/>}
{bucket && bucket.stock_list != null &&
<CardHeader className="cardHeaderBucket"
title={bucket.name.substr(0, 20)}
subheader={bucket.about}
action={
<>
<IconButton
onClick={handleClick}
aria-label="settings">
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Edit </MenuItem>
<MenuItem onClick={handleClose}>Delete</MenuItem>
</Menu>
</>
}
style={{margin:0}}
/>}
<CardContent className={classes.cardContent}>
{(!bucket || bucket.bucket_pos_neg === null) &&
<p style={{ textAlign: "center" }} >
Your Bucket is empty...
</p>}
{bucket && bucket.bucket_pos_neg != null &&
<div className={classes.bucketText}>
<Grid>
<Typography variant="subtitle1" color="textSecondary">
{/* {bucket.stock_list.join(",").substr(0, 15)}... */}
{"Total Stocks: " + bucket.stock_count}
</Typography>
<Typography variant="overline">
Return Donut
</Typography>
<BucketDoughnutDisplay data={bucket.bucket_pos_neg} />
</Grid>
</div>
}
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
</Container>
</React.Fragment>
);
};
How can I fix my Menu
to be unique for each individual card? My one state for my approach is causing major issues. Thank you for the help.
EDIT: Right now the menu is not popping up and no consle errors to work from. I have made the second round of revision. Please see this code
const UserBuckets = (props) => {
const [anchorEl, setAnchorEl] = React.useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const handleClick = (index) => (event) => {
setAnchorEl(event.currentTarget);
setCurrentIndex(index);
};
const handleClose = () => {
setAnchorEl(null);
};
const classes = useStyles();
if (!buckets || buckets.length === 0) return <p>Can not find any buckets, make one below!</p>;
return (
<React.Fragment>
<Container style={{width:"90%"}} maxWidth="md" component="main">
<Grid container spacing={5} alignItems="stretch">
{buckets.map((bucket, index) =>
{
return (
<Grid item key={bucket.id} xs={12} sm={6} md={4} lg={4}>
<Card
className={classes.root}
classes={{ root: state.raised ? classes.cardHovered : "" }}
onMouseOver={() => setState({ shadow: 3 })}
onMouseOut={() => setState({ shadow: 1 })}
zdepth={state.shadow}
style={{ height: "100%", borderRadius:"30px"}}
>
{(!bucket || bucket.stock_list === null) &&
<CardHeader className={classes.bucketTitle} classes={{ title: classes.bucketTitle }}
title={
<>
<Link
color="textPrimary"
href={'dash/' + bucket.slug}
className={classes.link}
style={{ textDecoration: 'none' }}
>
{bucket.name.substr(0, 50)}
</Link>
</>
}
subheader="Add Stocks to get started!"
action={
<>
<IconButton
onOpen={handleClick(index)}
aria-label="settings">
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl) && currentIndex}
onClose={handleClose}
style={{boxShadow: 'none'}}
elevation={0}
>
<MenuItem onClick={handleClick(index)}>Edit </MenuItem>
<MenuItem onClick={handleClick(index)}>Delete</MenuItem>
</Menu>
</>
}
/>}
{bucket && bucket.stock_list != null &&
<CardHeader className="cardHeaderBucket"
title={bucket.name.substr(0, 20)}
subheader={bucket.about}
action={
<>
<IconButton
onOpen={handleClick(index)}
aria-label="settings">
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl) && currentIndex}
onClose={handleClose}
>
<MenuItem onClick={handleClick(index)}>Edit </MenuItem>
<MenuItem onClick={handleClick(index)}>Delete</MenuItem>
</Menu>
</>
}
style={{margin:0}}
/>}
<CardContent className={classes.cardContent}>
{(!bucket || bucket.bucket_pos_neg === null) &&
<p style={{ textAlign: "center" }} >
Your Bucket is empty...
</p>}
{bucket && bucket.bucket_pos_neg != null &&
<div className={classes.bucketText}>
<Grid>
<Typography variant="subtitle1" color="textSecondary">
{/* {bucket.stock_list.join(",").substr(0, 15)}... */}
{"Total Stocks: " + bucket.stock_count}
</Typography>
<Typography variant="overline">
Return Donut
</Typography>
<BucketDoughnutDisplay data={bucket.bucket_pos_neg} />
</Grid>
</div>
}
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
</Container>
</React.Fragment>
);
};
Upvotes: 3
Views: 1105
Reputation: 1
const Navbar = () => {
const classes = useStyles();
const [anchorEl, setAnchorEl] = useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const handleClick = (index) => (e) => {
setAnchorEl(e.currentTarget);
setCurrentIndex(index);
console.log("index", currentIndex);
};
const handleClose = () => {
setAnchorEl(null);
};
const invoiceItem = [
{
id: 0,
title: "New Invoice",
link: "",
},
{
id: 1,
title: "Recent Invoices",
link: "",
},
{
id: 2,
title: "List All invoices",
link: "",
},
];
const companyItem = [
{
id: 0,
title: "New Company",
link: "",
},
{
id: 1,
title: "All Company List",
link: "",
},
];
return (
<AppBar
className={classes.root}
style={{ background: "#282423" }}
position="static"
>
<Toolbar>
<Typography variant="h6" className={classes.title}>
SSENTERPRISES
</Typography>
<Tabs textColor="inherit">
<Tab
onClick={handleClick(1)}
aria-controls="invoices"
label="Invoices"
/>
<Menu
id="invoices"
keepMounted
anchorEl={anchorEl}
open={currentIndex === 1 ? Boolean(anchorEl) : null}
onClose={handleClose}
>
{invoiceItem.map((item) => (
<MenuItem
key={item.id}
style={{ fontWeight: item.id === 0 ? "bold" : "normal" }}
onClick={handleClose}
>
{item.title}
</MenuItem>
))}
</Menu>
<Tab
onClick={handleClick(2)}
aria-controls="company"
label="Company"
/>
<Menu
id="company"
keepMounted
anchorEl={anchorEl}
open={currentIndex === 2 ? Boolean(anchorEl) : null}
onClose={handleClose}
>
{companyItem.map((item) => (
<MenuItem
key={item.id}
style={{ fontWeight: item.id === 0 ? "bold" : "normal" }}
onClick={handleClose}
>
{item.title}
</MenuItem>
))}
</Menu>
<Tab label="Material" />
<Tab label="Reports" />
</Tabs>
</Toolbar>
</AppBar>
);
};
Upvotes: 0
Reputation: 81430
Try storing the current index alongside with your open state to identify the current Menu
to open:
const [currentIndex, setCurrentIndex] = useState(0);
const handleClick = (index) => (event) => {
setAnchorEl(event.currentTarget);
setCurrentIndex(index);
};
buckets.map((bucket, index) => (
<>
<Grid>
<Menu open={Boolean(anchorEl) && currentIndex} {...props} />
</Grid>
<IconButton onOpen={handleClick(index)}/>
</>
))
Upvotes: 3