Why 1st menu item selected by default at MenuAppBar - reactjs

In this menuAppBar the first item in the menu is selected by default. Where in this menu the first item is not selected by default, and that what I want is the same to be in the manuAppBar menu.

The first example is implemented with the material-ui Menu component and the second example is implemented using components from the react-popper library.
In the first example, the first item is highlighted because it has focus. This is because Menu sets focus if it is open when mounted or updated. Take a look at the source:
class Menu extends React.Component {
componentDidMount() {
if (this.props.open) {
this.focus();
}
}
componentDidUpdate(prevProps) {
if (!prevProps.open && this.props.open) {
// Needs to refocus as when a menu is rendered into another Modal,
// the first modal might change the focus to prevent any leak.
this.focus();
}
}
If you prefer react-popper and would like to use it in an AppBar, you can:
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography type="title" color="inherit" className={classes.flex}>
Title
</Typography>
{auth && (
<Manager>
<Target>
<IconButton
aria-owns={open ? 'menu-list' : null}
aria-haspopup="true"
onClick={this.handleMenu}
color="contrast"
>
<AccountCircle />
</IconButton>
</Target>
<Popper
placement="top-right"
eventsEnabled={open}
className={classNames({ [classes.popperClose]: !open })}
>
<ClickAwayListener onClickAway={this.handleClose}>
<Grow in={open} id="menu-list" style={{ transformOrigin: '0 0 0' }}>
<Paper>
<MenuList role="menu">
<MenuItem onClick={this.handleClose}>Profile</MenuItem>
<MenuItem onClick={this.handleClose}>My account</MenuItem>
<MenuItem onClick={this.handleClose}>Logout</MenuItem>
</MenuList>
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
</Manager>
)}
</Toolbar>
</AppBar>
Here is a mashup of your two cited examples on codesandbox. It needs work and was only added here to illustrate react-popper as a possibility.

Related

react material ui grow on left side

How to make the context menu open not in the center of the button, but to the left of the button? With using properties Material UI.
Working example from link:
https://codesandbox.io/s/2f33z
Now this:
I want this:
Here is a link to an example from the documentation:
https://material-ui.com/components/menus/#menulist-composition
<div>
<Button
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
Toggle Menu Grow
</Button>
<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 autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
Your Popper component should have a placement property of value bottom-start:
// ...
<Popper
placement="bottom-start"
// ...
>
// ...
For more options look here: https://material-ui.com/components/popper/#positioned-popper.

Opening Dialog drawer half way

I'm trying to trigger a dialog box to open when a button is clicked so that it takes up half the height of the blue area as shown in the picture below, while the remaining top half will have the backdrop. Currently i'm unable to force the dialog box to open up in the specific area (blue area), instead it takes up the fullscreen and i'm not sure how to change that. I've added a sample code in codesanbox to show what I have done to try to do this: https://codesandbox.io/s/material-demo-rryul
This is an image showing what I would like to achieve
export default function CenteredGrid() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className={classes.root}>
<Grid container spacing={1}>
<Grid item xs={6}>
<Paper style={{ height: 500, background: "blue" }}>
<div>
<Button
variant="outlined"
color="white"
onClick={handleClickOpen}
>
Open full-screen dialog
</Button>
<Dialog
fullScreen
open={open}
onClose={handleClose}
TransitionComponent={Transition}
>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
Sound
</Typography>
<Button autoFocus color="inherit" onClick={handleClose}>
save
</Button>
</Toolbar>
</AppBar>
<List>
<ListItem button>
<ListItemText
primary="Phone ringtone"
secondary="Titania"
/>
</ListItem>
<Divider />
<ListItem button>
<ListItemText
primary="Default notification ringtone"
secondary="Tethys"
/>
</ListItem>
</List>
</Dialog>
</div>
</Paper>
</Grid>
<Grid item xs={6}>
<Paper style={{ height: 500, background: "purple" }} />
</Grid>
</Grid>
</div>
);
}
If anything is unclear, just let me know
The material ui Dialog is rendered as portal. As per doc, Modal component's props are also available to Dialog.
So you can use the Modal's container prop in Dialog and provide a target which indicates where to display the portal.
The container will have the portal children appended to it.
By default, it uses the body of the top-level document object,
Working demo (codesandbox)
dialog style
dialog: {
background: "orange",
position: "relative !important",
height: "100%",
width: "100%",
padding: "100px 0px 0px 0px", //change this based on your needs
backgroundColor: "rgba(0,0,0,0.6)"
}
dialog jsx
...
const container = React.useRef(null);
...
...
<Dialog
container={container.current}//<---here
fullScreen
open={open}
onClose={handleClose}
TransitionComponent={Transition}
className={classes.dialog}
>
...

How to dynamically create button for calling specific action using map array in react

I tried to map an array in react and tried to generate a button that will perform a specific action ,that is referencing another object generated by the same array using map() function.I'm using material-ui to speed up my development process.
I am very new to react (actually this is my first project with react), so maybe this is just simple question to implement 'state' in react, but i'm a little bit confusing to use this and bind syntax properly.
P.S -So excuse me for my stupidity :>
Follow this link to reproduce the code
and this is the code i got trouble with:
const products = [
{
id: 1,
img: "https://image.flaticon.com/icons/png/512/676/676434.png",
title: "Pineaple",
price: "Rp. 14.000",
desc: "Pineaple is one of nutritious food"
},
{
id: 2,
img: "https://image.flaticon.com/icons/png/512/676/676433.png",
title: "Banana",
price: "Rp. 14.000",
desc: "Banana is one of nutritious food"
},
{
id: 3,
img: "https://image.flaticon.com/icons/png/512/676/676441.png",
title: "Dragonfruit",
price: "Rp. 14.000",
desc: "Dragonfruit is one of nutritious food"
},
];
export default function Posts(props) {
const [open, setOpen] = React.useState(false);
function handleClickOpen() {
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<div>
<Grid container spacing={1} justify="center">
{products.map(product => (
<Grid item xs={6} sm={3} md={2} key={product.id}>
<Card>
<CardActionArea>
<CardMedia
component="img"
width="auto"
height="auto"
image={product.img}
/>
<CardContent>
<Typography component="h2"> {product.title} </Typography>
<Typography variant="body2" color="primary" component="p">
{" "}{product.price}{" "}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button onClick={handleClickOpen}>
Buy
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
{products.map(product => (
<Dialog
key={product.id}
fullScreen
open={open}
onClose={handleClose}
>
<AppBar position="sticky">
<Toolbar>
<IconButton onClick={handleClose}>
<CloseIcon />
</IconButton>
<Typography> {product.title} </Typography>
<Button onClick={handleClose}> buy </Button>
</Toolbar>
</AppBar>
<List className={classes.dialogue}>
<img src={product.img} alt={product.title} />
<ListItem button>
<ListItemText primary={product.title} secondary={product.desc}/>
</ListItem>
</List>
</Dialog>
))}
</div>
);
}
I want to make onclick button generated by mapped array to reference to specific action (show specific dialog within array list). I also want to implement same method for onSubmit on 'buy' button in the Dialog.
Screenshoot: https://imgur.com/a/M4v5LOu
(I click buy on 'pineaple' but react render all list and show the latest object in a the list which is 'dragonfruit'.)
I guess i'll use redux but maybe not right now.
Anyway that's it, I really appreciate any response and helps :)
Thanks!
There are several ways you can solve this but I will show you one. You are making use of React Hooks and you have a hook for setting the open/close state. In my solution, I make slight modification by adding another hook to set the selected product and then checking if both open and the product are set.
export default function Posts(props) {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [product, setProduct] = React.useState(null);
function handleClickOpen(event, item) {
event.persist();
setProduct(item);
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<div style={{ margin: 0, padding: 0 }}>
<Grid container spacing={1} justify="center">
{products.map(product => (
<Grid item xs={6} sm={3} md={2} key={product.id}>
<Card elevation={0}>
<CardActionArea>
<CardMedia
component="img"
width="auto"
height="auto"
image={product.img}
/>
<CardContent>
<Typography component="h2"> {product.title} </Typography>
<Typography variant="body2" color="primary" component="p">
{' '}
{product.price}{' '}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button
variant={'outlined'}
size="small"
color="primary"
onClick={event => handleClickOpen(event, product)}
>
Buy
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
{open && product && (
<Dialog
key={product.id}
className={classes.dialogue}
fullScreen
open={open}
onClose={handleClose}
BackdropProps={{ classes: { root: classes.root } }}
PaperProps={{ classes: { root: classes.paper } }}
>
<AppBar position="sticky">
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="Close"
>
<CloseIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
{product.title}
</Typography>
<Button color="inherit" onClick={handleClose}>
buy
</Button>
</Toolbar>
</AppBar>
<List className={classes.dialogue}>
<Image
className={classes.images}
src={product.img}
alt={product.title}
/>
<ListItem button>
<ListItemText primary={product.title} secondary={product.desc} />
</ListItem>
</List>
</Dialog>
)}
</div>
);
}
In your code, you didn't have a way to track the currently selected product hence you always get the last item in the loop. By using another hook for the selected product, I can track the selected product. I hope this helps you and good luck in your mastery of React.
You are having two states open and close.
You are using map on array and showing the dialog box.
The dialog box will open when open state is true.
This will be true for all elements in the array. Dialog box will be shown for all elements.
Now, they will overlap on each other and you can only see the last one.
When you click on close dialog your open state set to false and all the dialogs are closed.
Hint :-
Maintain a state that will contain the id of element for which dialog is to be shown. Show dialog only when id state matches with the element's id

How to remove margin / padding from MaterialUI drawer?

I am working on a React app using MaterialUI to create an Appbar and Drawer.
There is an extra space between the edge of the window and the Drawer.
The extra space is also covering up an IconButton I use to toggle opening and expanding the drawer. It looks like this:
Here's how I'm rendering the Appbar and Drawer:
render(){
const { classes,drawerOpen,theme } = this.props;
return(
<React.Fragment>
<AppBar
position="absolute"
className={classNames(classes.appBar,drawerOpen && classes.appBarShift)}
>
<Toolbar disableGutters={!this.state.open}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, drawerOpen && classes.hide)}
>
<ToggleLeft />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
MYAPP
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: classNames(classes.drawerPaper, !drawerOpen && classes.drawerPaperClose),
}}
open={this.state.open}
>
<div className={classes.toolbar}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === 'rtl' ? <ToggleLeft />:<ToggleRight />}
</IconButton>
</div>
<Divider />
<List><SideAccountsList {...this.props}/></List>
</Drawer>
</React.Fragment>
)
}

Both right and left aligned icons in AppBar with material-ui next

If I have an AppBar, how can I make it so one group of icons plus the logo is on the left, and another group of icons are on the right of it?
Ex:
Left: (from left to right) 1 menu icon, logo
Right: (from right to left) 1 menu icon, 1 save icon, 1 edit icon
AppBar component:
<AppBar
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
[classes[`appBarShift-left`]]: open,
[classes[`appBarShift-right`]]: !tools,
})}
position='static'
>
<Toolbar className={classNames(classes.topBar)}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={this.handleDrawerToggle}
className={classNames(classes.menuButton)}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>React App</Typography>
<IconButton
color="inherit"
aria-label="open tool drawer"
onClick={this.handleToolDrawerToggle}
className={classNames(classes.menuButton)}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
You can use flexbox to control the alignment of elements in the toolbar...
One option is to add flex: 1 to the logo element. It will expand to fill the available space in container. All the elements after logo will be aligned to the right.
OR
Use margin-left: auto to align the second group of buttons to the right side of the flex container.
Here is a live example
const styles = {
// this group of buttons will be aligned to the right side
toolbarButtons: {
marginLeft: 'auto',
},
};
const Demo = ({ classes }) => (
<AppBar position="static">
<Toolbar>
<IconButton color="inherit">
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit">Title</Typography>
<div className={classes.toolbarButtons}>
<IconButton color="inherit"><EditIcon /></IconButton>
<IconButton color="inherit"><SaveIcon /></IconButton>
<IconButton color="inherit"><MoreVertIcon /></IconButton>
</div>
</Toolbar>
</AppBar>
);
I know it's been a while since you asked the question, I would like to provide an alternative solution. Add Box tag (similar to flexbox in CSS) around the components on the left and adjust the flexGrow attribute and it works for me:
import Box from '#material-ui/core/Box';
{/* BEFORE APPBAR*/}
<AppBar>
<Toolbar>
<Box display='flex' flexGrow={1}>
{/* whatever is on the left side */}
</Box>
{/* whatever is on the right side */}
</Toolbar>
</AppBar>
{/* AFTER APPBAR*/}
I tried using inline css inside Toolbar component itself, it worked for me;
<Toolbar style={{display:'flex', justifyContent:"space-between", width:'100%'}}>

Resources