How to use MUI to stack components on top of eachother? - css

I want a div that looks like this on Desktop:
------------------------------------------------------------------
| (icon) | (content) |(button here)|
------------------------------------------------------------------
to look like this on mobile:
--------------------------
|(icon)| (content) |
--------------------------
| (button here) |
--------------------------
I have tried using Card and CardHeader, but they don't give me the results I want on mobile:
<Card sx={{ width: "90%", marginBottom: 2 }}>
<CardHeader
avatar={<img src={icon} style={{ maxWidth: "100%" }} />}
action={
<Button
onClick={onClick}
variant="contained"
sx={{
width: "173px",
height: "48px",
letterSpacing: 1.4,
}}
>
{buttonText}
</Button>
}
title={children}
subheader={subContent}
/>
</Card>
I would prefer an alternative to Card. Grid doesn't seem to have stackable capabilities, so not sure what to do there... Thank you.

From your illustration above, it sounds like Grid is exactly what you're looking for, as Grid is specifically for responsive layouts. You can use a combination of the xs, sm, md, lg, and xl props to define how much space, in the 12 column layout, that you want each item to occupy at a given breakpoint. For example, in your case:
<Grid container>
<Grid item xs={4} sm={2}>
(icon)
</Grid>
<Grid item xs={8} sm={8}>
(content)
</Grid>
<Grid item xs={12} sm={2}>
(button here)
</Grid>
</Grid>
sm+:
xs:
Working example for your use case: https://codesandbox.io/s/basicgrid-material-demo-forked-q795q?file=/demo.js:0-725

MUI Stack component is usually great for stacking
[1]: https://mui.com/material-ui/react-stack/
I have played with it below a little bit but check out the spec maybe its may save you a lot
import { Stack } from '#mui/material'
{ isMobile ? <Stack justifyItems="center" sx={{ width: `100%` }}>
<Stack direction="row" spacing={12} alignItems="center" sx={{ margin: `auto` }}>
<p>Icon</p>
<p>Content</p>
</Stack>
<Stack sx={{ margin: `auto auto` }}>
<button>Button here</button>
</Stack>
</Stack> : <Stack direction="row" spacing={12} alignItems="center">
<p>Icon</p>
<p>Content</p>
<button>Button here</button>
</Stack> }

Related

How to fit 2 products in the screen when it's in mobile?

I've been stuck on this part, and I've been struggling how can I manage to make it row, I tried to make it flexWrap, but it's going down instead of going its side.
<Box sx={{display: {xs: 'flex'},
gap: {xs: '0px', md:'20px',
flexWrap: 'wrap',
alignItems: 'center' }}}
>
{products.map((product) => (
<CardProduct key={product._id} product={product}/>
))}
</Box>
outcome
this is I want to achieve, the first and second product must be align, and then have a gap between or spacing, then the 3rd product will automatically wrap... I tried to call the
flexWrap:'wrap'
but it failed. should I just the height ? or is it because the button is too big?
want to achieve
This is what it looks like when it only have two products
two images
Try using a Grid:
<Grid container spacing={2}>
{products.map((product) => (
<Grid item xs={6} key={product._id}>
<CardProduct product={product} />
</Grid>
))}
</Grid>;
To make 2 columns you can use flex-basis property, flex-basis: 50%; , do remove any padding or margin before.
with flex-basis property you can set any width or get number of columns.
<Box sx={{display: {xs: 'flex'},
gap: {xs: '0px', md:'20px',
flex-basis: '50%',
alignItems: 'center' }}}
>
{products.map((product) => (
<CardProduct key={product._id} product={product}/>
))}
There is on more property in css that helps us to achieve any number of coumns without using flexbox.
.div {
column-count: 2;
}
you can try Grid. Material-UI has a very good and clear example exactly as you want.
link
<Grid container spacing={2}>
<Grid item xs={12}>
<Grid container justifyContent="center" spacing={spacing}>
{[0, 1, 2].map((value) => (
<Grid key={value} item>
<Paper className={classes.paper} />
</Grid>
))}
</Grid>
</Grid>

Dynamic Image List from MUI do not respect max height of the React container

I am trying to create a dynamic image list where I can change the number of images that are being displayed, where the images have to occupy the max space in the component without change the image ratio and also without make the component scrollable. The following images show the result that is expected:
Although what really happens now is, in the first case, with only 2 images, it overflows the parent component due to the spacing added between the flexbox elements.
For the 6 images example, it goes worst:
It overflows the parent even more because it ignores the component's max height and always tries to occupy the max width possible.
To reach the correct behaviour, I'm hard coding the flex-basis of the image list for each case because it is a flex item, and only that way does it not grow more than it should. Although, that is not desirable because I want to make the images grow as much as possible without depending on the screen size.
I do not understand why they ignore the parent's max height.
I do not understand why they do ignore the max height of the parent.
Main component:
<>
<Grid
container
direction="column"
wrap="nowrap"
rowSpacing={1}
sx={{ alignSelf: 'flex-end', margin: 0, height: '100%', justifyContent: 'flex-end' }}
>
<Grid
container
wrap="nowrap"
item
sx={{
alignSelf: 'flex-start',
flex: '1 0 auto',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<CustomImageList ref={ref} />
</Grid>
<Grid item>
<CustomPagination onClick={keyMap.move} />
</Grid>
<Grid width="100%" item>
<Stack
spacing={1}
direction="row"
sx={{
overflowX: 'auto',
justifyContent: 'space-between',
'& .MuiFormControl-root': { marginTop: '1px' },
}}
divider={<Divider orientation="vertical" flexItem />}
>
{Buttons.map((button) => (
<Stack spacing={1} flex={1} key={button.buttonId}>
<Stack direction="row" spacing={1}>
{button.text ? (
<ButtonText button={button} />
) : (
<Button />
)}
</Stack>
<Stack spacing={1} direction="row">
<ButtonBox button={button} />
</Stack>
</Stack>
))}
</Stack>
</Grid>
</Grid>
</>
The component CustomImageList is sketch like the next code:
<>
<Stack justifyContent="center">
<IconButton
ref={leftArrowRef}
size="large"
onClick={(e) => {
e.preventDefault();
handleClick({
key: 'ArrowLeft',
keyCode: 37,
});
}}
>
<ArrowBackIosNewIcon />
</IconButton>
</Stack>
<ImageList
sx={{
height: '100%',
alignContent: 'center'
}}
cols={imageColumnCount}
>
{images.map((image, index) =>
createOrdinaryListItem(image, index)
)}
</ImageList>
<Stack justifyContent="center">
<IconButton
onClick={(e) => {
e.preventDefault();
handleClick({
key: 'ArrowRight',
keyCode: 39,
});
}}
ref={rightArrowRef}
size="large"
>
<ArrowForwardIosIcon />
</IconButton>
</Stack>
</>
The ImageList component is a MUI ImageList
What would be the best approach to create the scenario shown in the first and second figures? I have already tried to create my component. It was a flexbox where I used the breakpoint from the grid to distribute the columns across the images. I had the same behaviour that I presented here.
Sorry about the verbose post and kinda "hard to understand" question, but I was not capable of creating a runnable script to show what is aforementioned

React Material UI responsive Grid with Card

I am trying to create a responsive grid layout using cards that should have the same height including an image. I am using React and Material-UI.
I am fiddling around now for hours and cannot come up with a solution. The "flex" part seems super unintuitive to me. Please help.
It is a very small and simple code piece:
const useStyles = makeStyles((theme: Theme) =>
createStyles({
cardGrid: {
flexGrow: 1,
paddingTop: 16,
},
card: {
flex: 1,
height: '100%',
backgroundColor: 'red',
},
cardActionArea: {
flex: 1,
height: '100%',
backgroundColor: 'green',
},
cardMedia: {
height: '0',
paddingTop: '56.25%', // 16:9
},
cardContent: {
flex: 1,
backgroundColor: blue[600],
},
cardContentTitle: {
flex: 1,
backgroundColor: blue[400],
},
cardContentRating: {
backgroundColor: blue[300],
},
cardContentDistance: {
backgroundColor: blue[200],
}
}),
);
// ...
<Container className={classes.cardGrid} maxWidth="xl">
<Grid
container
direction="row"
justify="space-evenly"
alignItems="stretch"
spacing={2}
>
{
dataList.map(data => (
<Grid item
key={`GridItem-${data.id}`} xs={12} sm={6} md={4} lg={2} xl={1}
>
<Card className={classes.card}
onClick={handleCardClick}
key={`OverviewItem-${data.id}`}
>
<CardActionArea className={classes.cardActionArea}>
{
data.previewURL &&
<CardMedia
className={classes.cardMedia}
image={data.previewURL}
title={data.title}
/>
}
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant="h5" className={classes.cardContentTitle}>
{data.title}
</Typography>
<Typography className={classes.cardContentRating}>
Some text
</Typography>
<Typography className={classes.cardContentDistance}>
Some other text
</Typography>
</CardContent>
</CardActionArea>
</Card>
</Grid>
))
}
</Grid>
</Container>
What I am trying to achieve:
Image should be at the top of each card
"Some text" and "Some other text" should get moved to the bottom
It seems like I have to write "flex:1" and "height:'100%'" multiple times when going down the tree and at some point it breaks completely like in following screenshot:
With the code snippet from above it looks like this:
What is the magic to simply expand the childs of a Grid Item to its maximum height and to still be able to align them as I want.
E.g. the entire card should stretch over the area of the Grid Item and the title part below the image should expand to move the image to the top and "Some text" and "Some other text" to the bottom.
Any hints/ideas are appreciated!
I was able to achieve what I wanted without a Card and without the image. But making the tree bigger and therefore the nesting deeper, breaks everything and I don't understand why.
Here is the simple snippet and the result:
<Container className={classes.cardGrid} maxWidth="xl">
<Grid
container
direction="row"
justify="space-evenly"
alignItems="stretch"
spacing={2}
>
{
toiletOverviews.map(data => (
<Grid item
key={`GridItem-${data.id}`} xs={12} sm={6} md={4} lg={2} xl={1}
>
<Box
display="flex"
flexDirection="column"
style={{height: '100%', backgroundColor: 'purple'}}
>
<Typography
variant="h5"
style={{flex:1, backgroundColor: blue[800]}}
>
{data.title}
</Typography>
<Typography
style={{backgroundColor: blue[600]}}
>
Some text
</Typography>
<Typography
style={{backgroundColor: blue[400]}}
>
Some other text
</Typography>
</Box>
</Grid>
))
}
</Grid>
</Container>
I figured it out! The important part that I did not know is that display:"flex" is only applied to the current element and its direct children. For childrens of its childrens it will not be applied.
The solution now is to correctly apply it to all necessary elements. The sideeffect was that the image was no longer displayed. It had a width/height of zero. The fix here is to add width:"100%" to the CardMedia element.

MUI Grid item not working with overflowY: "auto"

I am using MUI with React and I have a <Paper> element which wraps a <Grid> with 3 children elements. When setting the overflowY property of the bottom grid item to "auto", when the content becomes too big, the scroll bar doesn't show up but it breaks the parent container. This is my code:
<Paper
elevation={0}
style={{
height: "776px",
width: 342,
overflowY: "hidden"
}}
>
<Grid
container={true}
direction="column"
style={{ overflowY: "hidden" }}
>
{
<Grid
container={true}
item={true}
style={{
flexGrow: 1,
padding: "16px",
width: "100%",
flexWrap: "nowrap",
position: "sticky",
top: 0
}}
>
{props.pageTitle && (
<Grid item={true} style={{ marginTop: 6 }}>
{!filterOpen && (
<Typography variant="h6">
<span
style={{
paddingLeft: 8
}}
>
{props.pageTitle}
</span>
</Typography>
)}
</Grid>
)}
{props.allowFilter && (
<Grid justify={"flex-end"} container={true} item={true}>
<FmsFilterBox
filterText={filterText}
onChange={setFilterText}
cssFilterBoxWidth="100%"
onFilterOpenChanged={setFilterOpen}
/>
</Grid>
)}
</Grid>
}
<Grid item={true} style={{ flexGrow: 1 }}>
<Divider style={{ width: "100%" }} />
</Grid>
<Grid
item={true}
style={{
flexGrow: 1,
overflowY: "auto"
}}
>
{props.children(filteredData)}
</Grid>
</Grid>
</Paper>
I tried everything but I can't get the scroll bar appear for the 3rd (bottom) grid item (the one rendering {props.children(filteredData)} which is a list with list items fetched from the back end).
If I remove the overflowY: "hidden" from the <Paper>, the content of the 3rd gets hidden, but still no scroll bar - please see the photos attached.
I am really out of ideas, did anyone have this issue before? Thank you.
Fixed it. Apparently I had to add a height of 100% to the upper most grid (main container) and a flex-wrap of "no-wrap".

How to center a button in Material-UI

I couldn't figure out how to center buttons in Material-UI. This is the code I have:
function BigCard(props) {
const { classes } = props;
return (
<div>
<Card className={classes.card}>
<CardContent>
<Typography variant="display1" className={classes.title}>
Start Funding!
</Typography>
</CardContent>
<CardActions >
<Button size="small" color="primary" className={classes.actions}>
Share
</Button>
<Button size="small" color="primary">
Learn More
</Button>
</CardActions>
</Card>
</div>
);
How can I center my button?
You can use Box element
<Box textAlign='center'>
<Button variant='contained'>
My button
</Button>
</Box>
you could use the override with classes in the material ui doc,
FIRST WAY
You can do something like :
//Add your justify css in your styles const
const styles = {
...
root: {
justifyContent: 'center'
}
};
And use the classes props to add this to your CardActions component :
<CardActions classes={{root: classes.root}}>
SECOND WAY (easier)
OR You can use the style directly on your component, but i advise you to train how to use classes if you're working alot with material-ui
Just do something like :
<CardActions style={{justifyContent: 'center'}}>
For use cases that avoid defining css
<Grid container justify="center">
{props.children}
</Grid>
Here is how I did it with Material - UI
import {Typography, Button} from '#material-ui/core';
<Typography align='center'>
<Button
color='primary'
size='large'
type='submit'
variant='contained'
>
Sign Up
</Button>
</Typography>
Quick and dirty:
<Button style={{margin: '0 auto', display: "flex"}}>
NEXT
</Button>
From MUI v5, you can apply the custom styles using the sx props. Because CardActions layout is already flex, you only need to set its justify-content to center.
Using sx instead of inline styles like the other answer help reduce the CSS specificity, and make it easier to override the CSS in the future
<CardActions sx={{ justifyContent: "center" }}>
<Button size="small">Learn More</Button>
</CardActions>
Live Demo
What I tried is below and is also shown in official videos of material ui on youtube.
<Grid item xs={12} sm={12} md={4} lg={4}
style={{
textAlign:'center' // this does the magic
}}
>
<Button>
NEXT
</Button>
</Grid>
Thanks and best wishes
You have to use two properties: display and justify-content
<CardActions display='flex' justifyContent='center'>
This will centre your button horizontally
<Button
size="medium"
variant="contained"
color="primary"
style={{
display: "flex",
flexDirection: "row",
justifyContent:"center",
backgroundColor: purple[500],
'&:hover': {
backgroundColor: purple[700],
},
}}
>
Get Hired
</Button>
The easiest way is to use a Box wrapping around the button
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', md: 'row' },
alignItems: 'center',
justifyContent: 'center',
}}
>
<Button>Click me</Button>
</Box>
This may sound counter-intuitive but what worked for my case (slightly different than yours) was to use the fullWidth attribute in the Button element.
Here is my scenario (MUI v5):
<Grid container alignItems={'center'} >
<Grid item>
<Button fullWidth variant='contained' >Click Me!</Button>
</Grid>
</Grid>
wrap element in this center
.center{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
}

Resources