Opening Dialog drawer half way - reactjs

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}
>
...

Related

Custom Menu Item in MUI Data Grid Toolbar

I'm currently using the Data Grid Toolbar (a feature of the Material-UI Data Grid component) because I want the Column Show/Hide component, but I also want to add my own menu item in the form of an IconButton with a Menu that opens when clicked. The issue is when you click said button, the Toolbar appears to re-render, which causes the Menu to lose its anchor and render in the upper left. Is there a special way to get an anchor within the Data Grid Toolbar for the Menu popper to appear in the correct location?
function CustomToolbar() {
return (
<GridToolbarContainer>
<Box
height="65px"
width="100%"
display="flex"
flexDirection="row"
justifyContent="center"
>
<Box width="300px" display="flex" justifyContent="flex-start" alignItems="center">
<GridToolbarColumnsButton sx={{ ml: 2 }} />
</Box>
<Box width="100%" alignSelf="center" textAlign="center">
<Typography sx={{ flex: "1 1 100%" }} variant="h6" component="div">
Title Goes Here
</Typography>
</Box>
<Box width="300px" display="flex" justifyContent="flex-end" alignItems="center">
<Tooltip title="Filter">
<IconButton
color="primary"
component="span"
disabled={loading}
sx={{ mr: 2 }}
onClick={handleMenuClick}
>
<FilterList />
</IconButton>
</Tooltip>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={() => handleClose(menuState, filters)}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
PaperProps={MenuProps}
>
<MenuItem /> //Clipped
<MenuItem /> //Clipped
<MenuItem /> //Clipped
</Menu>
</Box>
</Box>
</GridToolbarContainer>
);
}
You must create the Toolbar component outside of your component that declares the DataGrid, and get the properties you need through the DataGrid's componentsProps property.
GridToolbarCustom Component:
type Props = {
selectionModel: GridSelectionModel;
}
const GridToolbarCustom = ({ selectionModel }: Props) => {
const [anchorElMenu, setAnchorElMenu] = useState<null | HTMLButtonElement>(null);
const openMenu = Boolean(anchorElMenu);
return (
<GridToolbarContainer>
<Grid container item xs>
{/* default buttons */}
<GridToolbarColumnsButton />
<GridToolbarFilterButton />
<GridToolbarDensitySelector />
<GridToolbarExport />
</Grid>
<Grid>
<Button
variant="contained"
size="small"
disabled={selectionModel.length === 0}
startIcon={<MoreVertIcon />}
onClick={(event: MouseEvent<HTMLButtonElement>) => {
setAnchorElMenu(event.currentTarget);
}}
>
Actions
</Button>
<Menu
id="menu-options"
anchorEl={anchorElMenu}
open={openMenu}
onClose={() => {
setAnchorElMenu(null);
}}
>
<MenuItem /> //Clipped
<MenuItem /> //Clipped
<MenuItem /> //Clipped
</Menu>
</Grid>
</GridToolbarContainer>
);
}
export default GridToolbarCustom;
MyComponent:
import GridToolbarCustom from './GridToolbarCustom';
const MyComponent = () => {
const [selectionModel, setSelectionModel] = useState<GridSelectionModel>([]);
return (
<DataGrid
//Clipped
components={{
Toolbar: GridToolbarCustom,
}}
componentsProps={{
toolbar: {
selectionModel,
},
}}
checkboxSelection
onSelectionModelChange={(newSelectionModel) => {
setSelectionModel(newSelectionModel);
}}
selectionModel={selectionModel}
/>
);
};

Material-UI Responsive Cards

I'm in the process of testing out Material-UI. I've been using Bootstrap for a long time, but am interested in adapting some React projects to Material-UI. Something I've been trying to figure out is how to create responsive cards in Material-UI. I've leaned pretty heavily on Bootstraps responsive containers in the past, so I can't understand why my cards expand with the page but don't shrink as the window is condensed...If we're meant to write custom css for this I'm cool with that, just need to be pointed in the right direction.
Questions:
Are responsive cards in Material-UI out of the gate a thing?
Or are we meant to write the css to make the cards responsive?
If so, where can I learn to do that? (leaned on Bootstrap a lot in the past)
Thanks in advance for your help!
...
useStyles = () => makeStyles(theme => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing.unit,
textAlign: "center",
color: theme.palette.text.secondary,
marginBottom: theme.spacing.unit
},
}));
render() {
const {baseData} = this.state;
const {hfcMetrics} = this.state;
const {stateMember} = this.state;
const {stateName} = this.state;
const {states} = this.state;
const {lineVizData} = this.state;
const classes = this.useStyles();
return (this.state.doneLoading === false ? (
<div className={classes.root}>
<Grid container>
<Grid item xs={12}>
<ReactLoading type={"spinningBubbles"} color={"black"} height={'10%'}
width={'10%'} id='spinner'/>
</Grid>
</Grid>
</div>
)
:
(stateMember === true ?
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Card>
<CardHeader
title="Options"
/>
<CardContent style={{ width: '100%', height: 300 }}>
<LineViz
data={lineVizData}
state={stateName}
source='compareTool'
states={states}
vizKey={this.state.vizKey}
/>
</CardContent>
<CardActions>
<Button size="small" color="primary">
Share
</Button>
<Button size="small" color="primary">
Learn More
</Button>
</CardActions>
</Card>
</Grid>
<Grid item xs={6}>
<Card ref={this.elRef}>
<CardHeader
title="Comparison Analysis"
action={
<ButtonGroup variant="text" color="primary" aria-label="text primary button group">
<YearDropDown2
year={this.state.year}
handleChange={this.toggleYear}
/>
<IconButton color='default' component="span"
onClick={() => this.resetStateToggle()}><AutorenewRoundedIcon/></IconButton>
</ButtonGroup>
}
/>
<CardContent style={{padding: 0}}>
<DataCompareTable
data={this.state.compareData}
metric={this.state.metric}
stateName={this.state.stateName}
compareCount={this.state.compareCount}
handleChange={this.toggleStateName}
toggleOne={this.state.toggleOne}
toggleTwo={this.state.toggleTwo}
toggleThree={this.state.toggleThree}
/>
</CardContent>
</Card>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
</Grid>
</Grid>
</div>
: ''
)
)
}
}
export default CompareTool
Thank you for your question
To answer your questions:
1) not that I know of
2) no you dont have to write alot of css, but yes some css in the usestyles
3) Below I explained in detail the css you have to write, in your code. You are using inline styles and useStyles at the same time, try putting all your styles in the usestyle hook API instead and make it responsive. Hope this helps let me know if I need to make it clearer. Thank you
as far as I know, you can use the "useMediaQuery" hook to make your design responsive.
Here is the component from the material UI Card component page, I only added the useTheme and useMediaQuery imports, and added a medium breakpoint inside useStyle under classes.root Here is a useful link
on "useMediaQuery"
https://material-ui.com/components/use-media-query/#usemediaquery
import { useTheme } from "#material-ui/styles";
import useMediaQuery from "#material-ui/core/useMediaQuery";
const useStyles = makeStyles(theme => ({
root: {
maxWidth: 345,
[theme.breakpoints.down("md")] : {
maxWidth: 200
}
},
media: {
height: 140
}
}));
const Card = () => {
const classes = useStyles();
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up("sm"));
return (
<Card className={classes.root}>
<CardActionArea>
<CardMedia
className={classes.media}
title="Contemplative Reptile"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
Lizard
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
Lizards are a widespread group of squamate reptiles, with over 6,000
species, ranging across all continents except Antarctica
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button size="small" color="primary">
Share
</Button>
<Button size="small" color="primary">
Learn More
</Button>
</CardActions>
</Card>
);
}
Hope this helps
You can wrap your card in a container with responsive options or use grids, which is very convenient.

Content not displayed when using full-screen Dialog in React Material-UI

Following the documentation of material-ui (https://material-ui.com/components/dialogs/),
I see that the dialog can be full screen. However, when I use it with AppBar and ToolBar, the DialogContent does not get displayed.
Below is the code for my dialog.
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open full-screen dialog
</Button>
<Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}>
<AppBar style={{ backgroundColor: "#182026" }} className={classes.appBar}>
<Toolbar>
<IconButton edge="start" color="inherit" onClick={handleClose} aria-label="close">
<CloseIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
Sound
</Typography>
</Toolbar>
</AppBar>
<DialogContent>
<DialogContentText>
Let Google help apps determine location. This means sending anonymous location data to
Google, even when no apps are running.
</DialogContentText>
</DialogContent>
</Dialog>
This just gives me
When I inspect this, I can see that the content is hidden inside the header which is pretty weird.
Since this code is mostly from the demo, I am not sure how to fix it. Thanks in advance.
You need to add position relative to your app bar class.
const useStyles = makeStyles((theme) => ({
appBar: {
position: 'relative',
},
title: {
marginLeft: theme.spacing(2),
flex: 1,
},
}));
And on the actual appear apply
<AppBar className={classes.appBar} />

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

Hide Menu Icon Button of Appbar of Material UI for only Desktop

<AppBar title="My AppBar" showMenuIconButton={false} />
This Hides the Menu Icon in all devices.
I need to hide only in Desktop.
How Can I achieve this?
You can try this-
showMenuIconButton={window.screen.width.<600? true:false}
where 600 is 600 pixels.
You can subscribe to resize event listener:
import React, { useState, useEffect } from 'react'
const Events = () => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth)
useEffect(() => {
function handleResize() {
setWindowWidth(window.innerWidth)
}
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return (
<div>
<AppBar title="My AppBar" showMenuIconButton={windowWidth < 600} />
</div>
)
you can use sx prop attribute for responsive style.
sx={{ mr: 2, display: {sm: 'none'} }}
<IconButton
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2, display: {sm: 'none'} }}
>
<MenuIcon />
</IconButton>
AppBar should look like,
<Box sx={{display: 'flex'}}>
<CssBaseline />
<AppBar
position="fixed"
elevation={1}
>
<Toolbar variant="dense">
<IconButton
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2, display: {sm: 'none'} }}
onClick={this.handleDrawerToggle}>
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" component="div">
Logo
</Typography>
</Toolbar>
</AppBar>
<LeftSideBar
handleDrawerToggle={this.handleDrawerToggle}
mobileOpen={this.state.mobileOpen}
/>
</Box>

Resources