how to add a custom action menu in material table - reactjs

I need to display a menu in the actions column from material-table.
there is a post already here:
How show menu item in Material Table
which does it but I couldn't ask for the sandbox so I can see and play with the implementation
looking something like this
I would really appreciate any help on this

This is the working piece of what was looking for
this is the sandbox with the example
I hope this is useful in case you are looking for something similar
https://codesandbox.io/s/elastic-kalam-4987cm?file=/src/ActionMenu.js
here the code as well
import React, { useState } from "react";
import IconButton from "#mui/material/IconButton";
import Menu from "#mui/material/Menu";
import MenuItem from "#mui/material/MenuItem";
import MoreVertIcon from "#mui/icons-material/MoreVert";
export default function ActionMenu({ data }) {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const add = () => {
console.log("Add: ", data.name);
handleClose();
};
const remove = () => {
console.log("remove: ", data.name);
handleClose();
};
return (
<div>
<IconButton
aria-label="more"
id="long-button"
aria-controls={open ? "long-menu" : undefined}
aria-expanded={open ? "true" : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu
id="long-menu"
MenuListProps={{
"aria-labelledby": "long-button"
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem key="1" onClick={add}>
add
</MenuItem>
<MenuItem key="2" onClick={remove}>
remove
</MenuItem>
</Menu>
</div>
);
}

Related

TransformedProps not a recognized as a type in a Typescript/React project?

I'm getting an error when using an OOTB Material UI component in React18, Deno (TypeScript) and Material UI. Specifically on not finding the TransformedProps type:
https://mui.com/material-ui/react-button-group/
I cannot get it to load TransformedProps, here is the code from the above link:
import * as React from 'react';
import Button from '#mui/material/Button';
import ButtonGroup from '#mui/material/ButtonGroup';
import ArrowDropDownIcon from '#mui/icons-material/ArrowDropDown';
import ClickAwayListener from '#mui/material/ClickAwayListener';
import Grow from '#mui/material/Grow';
import Paper from '#mui/material/Paper';
import Popper from '#mui/material/Popper';
import MenuItem from '#mui/material/MenuItem';
import MenuList from '#mui/material/MenuList';
const options = ['Create a merge commit', 'Squash and merge', 'Rebase and merge'];
export default function SplitButton() {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef<HTMLDivElement>(null);
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleClick = () => {
console.info(`You clicked ${options[selectedIndex]}`);
};
const handleMenuItemClick = (
event: React.MouseEvent<HTMLLIElement, MouseEvent>,
index: number,
) => {
setSelectedIndex(index);
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event: Event) => {
if (
anchorRef.current &&
anchorRef.current.contains(event.target as HTMLElement)
) {
return;
}
setOpen(false);
};
return (
<React.Fragment>
<ButtonGroup variant="contained" ref={anchorRef} aria-label="split button">
<Button onClick={handleClick}>{options[selectedIndex]}</Button>
<Button
size="small"
aria-controls={open ? 'split-button-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-label="select merge strategy"
aria-haspopup="menu"
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu" autoFocusItem>
{options.map((option, index) => (
<MenuItem
key={option}
disabled={index === 2}
selected={index === selectedIndex}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</React.Fragment>
);
}
Due to Deno/TypeScript I cannot use npm style imports, so mine look like this:
import React from "react";
import {
Button,
ButtonGroup,
ClickAwayListener,
Grow,
MenuItem,
MenuList,
Paper,
Popper,
TransitionProps as TransitionPropsType
} from ""https://esm.sh/#mui/material#5.6.1";
import { ArrowDropDown } from ""https://esm.sh/#mui/icons-material#5.6.1";
I also changed the "TransformedProps" line:
{({ TransitionProps, placement }: { TransitionProps: TransitionPropsType, placement: string }) => (
<Grow
{...TransitionProps}
Deno prevents me from using any so I tried to import but it still fails. Since it is minified it is hard to see where it is exported and what file. Thoughts and thoughts on how to solve these issues in the future? It seems not using npm and just using strict TypeScript is difficult no matter what library I use.

Display a modal from Material UI dropdown selection

The goal is to open a delete option modal from a material ui dropdown menu.
The first step towards that is understanding how to simply select an item from a dropdown, and then trigger some form of action (opening the modal) depending on which item is selected.
The material UI docs all seem to offer samples in which onClick simply handles the closing of the menu. It seems there are rare examples of selecting something from a dropdown and then opening/doing things from there?
I'm having trouble seeing where I would insert the logic/event handling to handle 'if the user selects option x, open modal regarding option x' within the context of a material ui menu.
Here's my code to show the rabbit hole I'm down currently:
import * as React from 'react';
import IconButton from '#mui/material/IconButton';
import Menu from '#mui/material/Menu';
import MenuItem from '#mui/material/MenuItem';
import { useState } from 'react'
import Select from '#mui/material/Select';
interface IProps extends Omit<unknown, 'children'> {
children: any;
options: string[];
}
const ITEM_HEIGHT = 48;
const DropdownMenu = ({ children, options }: IProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const [value, setValue] = useState('');
const handleChange = (event: any) => {
setValue(event.target.value);
console.log(value)
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
id="long-button"
aria-controls="long-menu"
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
{children}
</IconButton>
<Menu
id="long-menu"
MenuListProps={{
'aria-labelledby': 'long-button'
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: '20ch'
}
}}
>
<Select value={value} onChange={handleChange}>
{options.map(option => (
<MenuItem key={option} onClick={handleClose} >
{option}
</MenuItem>
))}
</Select>
</Menu>
</div>
);
};
export default DropdownMenu;
How would I use the dropdown to trigger the invocation of a Modal component based on a string value?
If I understand your question right then this should be the solution. simply assign a ref to the triggering element (in this case it's the IconButton). then you can open the menu via setting the open state. BTW why should the button render children?
import * as React from 'react';
import IconButton from '#mui/material/IconButton';
import Menu from '#mui/material/Menu';
import MenuItem from '#mui/material/MenuItem';
import { FC, useState } from 'react';
import Select from '#mui/material/Select';
type DropdownMenuProps = {
options: string[];
};
const ITEM_HEIGHT = 48;
const DropdownMenu: FC<DropdownMenuProps> = ({ children, options }) => {
const anchorEl = React.useRef<null | HTMLButtonElement>(null);
const [open, setOpen] = useState<boolean>(false);
const handleClick: React.MouseEventHandler = () => {
setOpen(true);
};
const [value, setValue] = useState('');
const handleChange: React.ChangeEventHandler<HTMLSelectElement> = (event) => {
const value = event.target.value;
setValue(value);
if (value === 'the value you want to trigger the menu') {
setOpen(true);
}
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<IconButton
aria-label="more"
id="long-button"
aria-controls="long-menu"
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}>
{children}
</IconButton>
<Menu
id="long-menu"
MenuListProps={{
'aria-labelledby': 'long-button',
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: '20ch',
},
}}>
<Select value={value} onChange={handleChange}>
{options.map((option) => (
<MenuItem key={option} onClick={handleClose}>
{option}
</MenuItem>
))}
</Select>
</Menu>
</div>
);
};
export default DropdownMenu;

Customizing/Disabling Material-UI Dialog's Touch Mechanics

may somebody please help me customize/disable the Material-UI Dialog's touch mechanics? I have the Cancel and Confirm buttons, and that's just what I want to use to close the dialog. However, making a selection from a dropdown menu caused the dialog to autoclose according to the doc. And I couldn't see how to do that in the doc itself.
The problem
The problem I have is that the auto-closing is doing the job I want the cancel button to do. i.e: Closing the dialog and empty an array.
I'd appreciate your help.
Any special reason to use the Material-UI Simple Dialog?
From your question, seems like a Confirmation dialog is what you're looking for :)
Simple dialog touch mechanism
Touch mechanics:
Choosing an option immediately commits the option and closes the menu
Touching outside of the dialog, or pressing Back, cancels the action and closes the dialog
In the other hand, matching your needs:
Confirmation dialogs
Confirmation dialogs require users to explicitly confirm their choice before an option is committed. For example, users can listen to multiple ringtones but only make a final selection upon touching “OK”.
Touching “Cancel” in a confirmation dialog, or pressing Back, cancels the action, discards any changes, and closes the dialog.
import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import DialogTitle from '#material-ui/core/DialogTitle';
import DialogContent from '#material-ui/core/DialogContent';
import DialogActions from '#material-ui/core/DialogActions';
import Dialog from '#material-ui/core/Dialog';
import RadioGroup from '#material-ui/core/RadioGroup';
import Radio from '#material-ui/core/Radio';
import FormControlLabel from '#material-ui/core/FormControlLabel';
const options = [
'None',
'Atria',
'Callisto',
'Dione',
'Ganymede',
'Hangouts Call',
'Luna',
'Oberon',
'Phobos',
'Pyxis',
'Sedna',
'Titania',
'Triton',
'Umbriel',
];
function ConfirmationDialogRaw(props) {
const { onClose, value: valueProp, open, ...other } = props;
const [value, setValue] = React.useState(valueProp);
const radioGroupRef = React.useRef(null);
React.useEffect(() => {
if (!open) {
setValue(valueProp);
}
}, [valueProp, open]);
const handleEntering = () => {
if (radioGroupRef.current != null) {
radioGroupRef.current.focus();
}
};
const handleCancel = () => {
onClose();
};
const handleOk = () => {
onClose(value);
};
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<Dialog
disableBackdropClick
disableEscapeKeyDown
maxWidth="xs"
onEntering={handleEntering}
aria-labelledby="confirmation-dialog-title"
open={open}
{...other}
>
<DialogTitle id="confirmation-dialog-title">Phone Ringtone</DialogTitle>
<DialogContent dividers>
<RadioGroup
ref={radioGroupRef}
aria-label="ringtone"
name="ringtone"
value={value}
onChange={handleChange}
>
{options.map((option) => (
<FormControlLabel value={option} key={option} control={<Radio />} label={option} />
))}
</RadioGroup>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel} color="primary">
Cancel
</Button>
<Button onClick={handleOk} color="primary">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
ConfirmationDialogRaw.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
value: PropTypes.string.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper,
},
paper: {
width: '80%',
maxHeight: 435,
},
}));
export default function ConfirmationDialog() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState('Dione');
const handleClickListItem = () => {
setOpen(true);
};
const handleClose = (newValue) => {
setOpen(false);
if (newValue) {
setValue(newValue);
}
};
return (
<div className={classes.root}>
<List component="div" role="list">
<ListItem button divider disabled role="listitem">
<ListItemText primary="Interruptions" />
</ListItem>
<ListItem
button
divider
aria-haspopup="true"
aria-controls="ringtone-menu"
aria-label="phone ringtone"
onClick={handleClickListItem}
role="listitem"
>
<ListItemText primary="Phone ringtone" secondary={value} />
</ListItem>
<ListItem button divider disabled role="listitem">
<ListItemText primary="Default notification ringtone" secondary="Tethys" />
</ListItem>
<ConfirmationDialogRaw
classes={{
paper: classes.paper,
}}
id="ringtone-menu"
keepMounted
open={open}
onClose={handleClose}
value={value}
/>
</List>
</div>
);
}
you can have a look of the working dialog here:
https://4zgol.csb.app/
Hope it helps and if not, feel free to explain more about the problem or even add a code snippet :)

how to give space in between user icon and User name in material UI?

I'm new to material UI, i've created a User icon and User name but i couldn't able to give space in between them. I've tried by giving p={1} and m={1} but didn't worked.
Can anyone assist me how to give space in between them?
import React from "react";
import Button from "#material-ui/core/Button";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import Avatar from "#material-ui/core/Avatar";
import Fade from "#material-ui/core/Fade";
export default function FadeMenu() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<Button
aria-controls="fade-menu"
aria-haspopup="true"
onClick={handleClick}
>
<Avatar mx="auto">AJ</Avatar>
Anthony Johnson
</Button>
<Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
);
}
Here is the Code: "https://codesandbox.io/s/material-demo-7vvuy"
Here you go with a solution
import React from "react";
import Button from "#material-ui/core/Button";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import Avatar from "#material-ui/core/Avatar";
import Fade from "#material-ui/core/Fade";
const styles = () => ({
avatar: {
marginRight: 15
}
});
export default function FadeMenu() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const {classes} = this.props;
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<Button
aria-controls="fade-menu"
aria-haspopup="true"
onClick={handleClick}
>
<Avatar mx="auto" className={classes.avatar}>AJ</Avatar>
Anthony Johnson
</Button>
<Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
);
}
Use className and pass classes as props.
For reference please use https://material-ui.com/api/avatar/

App bar menu items not opening correct sub menu

I am building a front-end application using React16 and the Material UI library.
I am trying to build a simple navigation bar at the top containing multiple menu items. I took the "simple menu" example from the material-ui.com website.
I tried to add a second menu item in the app bar.
However, clicking on either one of the menu items opens the sub-menus for profile-menu. So in other words clicking on simple-menu opens up Profile, My Account, and Logout but it should open up New, List, and Report.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import IconButton from '#material-ui/core/IconButton';
import AccountCircle from '#material-ui/icons/AccountCircle';
import Button from '#material-ui/core/Button';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import MenuIcon from '#material-ui/icons/Menu';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}));
function MenuAppBar() {
const classes = useStyles();
const [auth, setAuth] = React.useState(true);
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
function handleMenu(event) {
setAnchorEl(event.currentTarget);
}
function handleClose() {
setAnchorEl(null);
}
return (
<div className={classes.root}>
<AppBar color="default" position="static">
<Toolbar>
<Typography variant="h6" className={classes.title}>
App
</Typography>
{auth && (
<div>
<Button
aria-owns={anchorEl ? 'simple-menu' : undefined}
aria-haspopup="true"
onClick={handleMenu}
>
Open Menu
</Button>
<Menu id="simple-menu" anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={handleClose}>New</MenuItem>
<MenuItem onClick={handleClose}>List</MenuItem>
<MenuItem onClick={handleClose}>Report</MenuItem>
</Menu>
</div>
)}
{auth && (
<div>
<IconButton
aria-owns={anchorEl ? 'profile-menu' : undefined}
aria-haspopup="true"
onClick={handleMenu}
>
<AccountCircle />
</IconButton>
<Menu id="profile-menu" anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
)}
</Toolbar>
</AppBar>
</div>
);
}
export default MenuAppBar;
First you need to define the menus in an enum / object
const MenuTypes = Object.freeze({
Simple: 'simple',
Profile: 'profile'
})
Add another state item to track your active menu
const [activeMenu, setActiveMenu] = React.useState(null);
then update handleMenu to accept the menu type and set it in state.
I can't remember off the top of my head if the menuType will be the first or second argument so verify this.
function handleMenu(menuType, event) {
setActiveMenu(menuType);
setAnchorEl(event.currentTarget);
}
Then your click callbacks need to reflect the correct menu
<Button
aria-owns={anchorEl ? 'simple-menu' : undefined}
aria-haspopup="true"
onClick={handleMenu.bind(null, MenuTypes.Simple}
>
Then you need to reference that type to determine which is currently active
open={!!activeMenu && activeMenu === MenuTypes.Simple}
Dont forget to update the close handler as well
function handleClose() {
setActiveMenu(null);
setAnchorEl(null);
}
Let me know if anything here doesn't make sense and I'll try and explain in more detail what is confusing :)

Resources