React Material UI responsive Grid with Card - css

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.

Related

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

MUI: how to align text side by side?

I want the numbers and text to line up side-by-side like this picture.
But there is a margin to the right of that text(MASTER I 406), so the numbers don't come in the right place. Currently I'm using transform: 'translateY()' to force the number into position. In this way, the position of the number is broken when the size of the screen is changed.
Please tell me how to solve this.
https://codesandbox.io/s/still-morning-0q92mh?file=/src/App.js
MUI Grid layout is a very powerful system you can use for your customized UI.
You need one Grid container with 2 Grid items inside.
Your code should be like this:
import { Card, CardContent, Typography, Grid } from "#mui/material";
import RiceBowlIcon from "#mui/icons-material/RiceBowl";
export default function App() {
return (
<Card
sx={{
borderRadius: 7,
marginTop: 3
}}
>
<CardContent>
<Grid container sx={{ alignItems: "center" }}>
<Grid item xs={10}>
<RiceBowlIcon />
<Typography
component="span"
sx={{
fontSize: 20,
fontWeight: 600
}}
>
JohnDoe
</Typography>
<Typography
sx={{
fontSize: 10,
color: "#909090",
ml: 4
}}
>
MASTER I 100
</Typography>
</Grid>
<Grid item xs={2} sx={{ textAlign: "center" }}>
<Typography
sx={{
fontSize: 20,
fontWeight: 540,
mr: 2
}}
>
(+10.00%)
</Typography>
<Typography
component="span"
sx={{
fontSize: 20,
fontWeight: 540
}}
>
10,000
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
);
}
You can use the Grid system like the solution below:
import { Card, CardContent, Typography, Grid } from "#mui/material";
import RiceBowlIcon from "#mui/icons-material/RiceBowl";
export default function App() {
return (
<Card
sx={{
borderRadius: 7,
marginTop: 3
}}
>
<CardContent>
<Grid container spacing={2}>
<Grid item xs={6}>
<RiceBowlIcon />
<Typography
component="span"
sx={{
fontSize: 20,
marginLeft: 1,
fontWeight: 600
}}
>
JohnDoe
</Typography>
<Typography
sx={{
fontSize: 10,
ml: 4,
color: "#909090",
// width: "20%" // You don't need to specify the width as it wil break the text in two lines
}}
>
MASTER I 100
</Typography>
</Grid>
<Grid item xs={6}>
<Typography
sx={{
float: "right",
fontSize: 20,
fontWeight: 540
}}
>
(+10.00%)
</Typography>
<Typography
component="span"
sx={{
float: "right",
fontSize: 20,
fontWeight: 540
}}
>
10,000
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
);
}
To explain a little more, you can create a Grid with two Grid Items. In the left Grid item you have the icon with the texts so it will work as a container and on the right one, the numbers '10.000'. With that way the content of the left Grid shouldn't affect the content of the right one.
Here is the updated sandbox based on your code.
<ListItemText
primary={
<div>
<span>{comment.UserName}</span>
<span style={{ float: "right" }}>10.10.2022</span>
</div>
}
secondary={comment.Comment}
/>

how to align a grid item center in material-ui in react?

I have a grid element with maxWidth so that it has horizontal margin when displayed in a big screen. I want the grid element to be centered and a long paragraph should be aligned to the left side. How would you do? I'm using MUI v5. Thank you.
<Box
style={{
backgroundColor:"rgb(234, 237, 242)"
}}
>
<Grid container alignItems='center' justifyContent='center' maxWidth='md'>
<Grid item xs={12} md={12} justifyContent="center">
<Typography align="center" variant="h4" style={{ fontWeight: 800 }} sx={{mb:4}}>
Nice title
</Typography>
<Typography sx={{ px: 4 }} paragraph>very very long line. very very long line. very very long line. very very long line. very very long line. very very long line. very very long line. very very long line. very very long line. very very long line. very very long line.</Typography>
</Grid>
</Grid>
</Box>
It's simple enough using system properties.
<Box
display="flex"
justifyContent="center"
style={{
backgroundColor:"rgb(234, 237, 242)"
}}
>
...
You can try the following code. I have checked this code it is working perfectly.
import * as React from 'react';
import { styled } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Paper from '#mui/material/Paper';
import Grid from '#mui/material/Grid';
import Typography from '#mui/material/Typography';
const Item = styled(Paper)(({ theme }) => ({
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
export default function BasicGrid() {
return (
<Box sx={{ flexGrow: 1, backgroundColor: 'rgb(234, 237, 242)' }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Item>
<Typography align="center" variant="h4" style={{ fontWeight: 800 }} sx={{ mb: 4 }}>
Hello
</Typography>
<Typography align="center" sx={{ px: 4 }} paragraph>
very very long line
</Typography>
</Item>
</Grid>
</Grid>
</Box>
);
}

Using Material-UI's Grid component, how do I get one cell to take up the remaining horizontal width?

I'm trying to use material-ui's Grid component in my React 16.13.0 application. I want to have a row with three items. The first two items should only take up as much space as they need to (I don't want to hard code pixel values if possible). I would like the third item to take up the remaining horizontal space and float the furthest to the rigth (I discovered React doesn't like "float: right" as a style, though). I have this
const styles = (theme) => ({
root: {
textAlign: "left",
margin: theme.spacing(2),
paddingBottom: theme.spacing(1),
color: theme.color.secondary,
},
cardHeader: {
paddingBottom: theme.spacing(0),
},
cardContent: {
width: "100%",
paddingBottom: theme.spacing(1),
},
rowBody: {
width: "100%",
flexWrap: "nowrap",
alignItems: "center",
},
});
...
<CardContent className={classes.cardContent}>
<Grid container className={classes.rowBody}>
<Grid item>
<img height="20" src={require('../../img/apple.svg')} alt="" />
</Grid>
<Grid item>
{title}
</Grid>
<Grid item>
<InfoIcon />
</Grid>
</Grid>
But unfortunately, this renders everything bunched up together
How can I adjust the styles to do what I want?
Edit: This is what is displayed based on the answer given by #Mohsen007 ...
Follow the code below:
rowBody: {
width: "100%",
flexWrap: "nowrap",
alignItems: "center",
display: "flex",
},
<Grid container className={classes.rowBody}>
<Grid item>
<img height="20" src={require('../../img/apple.svg')} alt="" />
</Grid>
<Grid item style={{flex: 1,}}>
{title}
</Grid>
<Grid item>
<InfoIcon />
</Grid>
</Grid>

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

Resources