Can't set my grid card content to equal height - reactjs

I want all cards in my grid to have equal height. I've been trying for hours with chrome, testing various combinations but I can't achieve the desired result. I want the content part of the card (pink) to expand so that everything is in the same line.
Grid cells are perfect. But if I flex grow the card or set it to 100% height, it fills the grid item (which is shown in picture 1) with paper (picture 2). I did the same thing for all the children (especially the text area), but they won't expand.
Tried stretching, changing displays, heights etc. Want to avoid a fixed height (even dependent on viewport) or fixed max height at all costs. Any ideas?
Edit: Maybe it's worth mentioning that the closest I've gotten to the desired result is setting display: content at the CardActionArea sub-element. Card is exactly as I want it except that it pushes my text at the bottom.
Here's the code of my styles and my first card - I'll leave the comments in to show some of the things that have been tried and tested (some code is also irrelevant but I'll leave it in in case something sabotages what I'm trying to do):
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1
},
grid: {}, //grid works exactly as I want it
gridItem: {
[theme.breakpoints.up("md")]: {
padding: "32px !important"
}
// display: "flex",
// flexDirection: "column",
// justifyContent: "space-between",
// alignItems: "stretch"
// [theme.breakpoints.down("sm")]: {
// padding: theme.spacing(0)
// }
},
card: {
"&:hover": {
boxShadow:
"0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(255, 255, 255, 0.23)",
// margin: "-10px auto 0",
// transform: "scale(1.01)",
// boxShadow: "-6px 4px 3px rgba(38, 38, 38, 0.2)",
// top: "-4px",
transition: ["boxShadow", "margin"],
transitionDuration: 300
},
display: "flex",
flexFlow: "column"
// flexDirection: "column",
// justifyContent: "space-between",
// alignItems: "stretch",
// height: "100%"
// maxWidth: "300px",
// maxHeight: 345
// boxShadow:
// "10px 10px 5px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.43)"
// display: "block",
// width: "100%",
},
media: {
height: "auto",
width: "100%",
objectFit: "cover"
// margin: "-70px auto 0",
// width: "80%",
// height: 140,
// borderRadius: "4px",
// boxShadow: "0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23)",
// position: "relative",
// zIndex: 1000
},
content: {
background: "linear-gradient(to right, #ee9ca7, #ffdde1)",
padding: "10px",
flexGrow: 1
// display: "flex"
// flexDirection: "column",
// justifyContent: "space-between",
// alignItems: "stretch",
// height: "100%"
},
contHead: {
color: "black",
marginBottom: "3px"
},
contText: {
color: "black"
},
buttonArea: {
background: "linear-gradient(to right, #000000, #434343)",
// display: "flex", //card has flex by default
flexFlow: "wrap",
justifyContent: "center",
padding: "0"
},
buttons: {
// backgroundColor: "white",
padding: "0",
minHeight: "29px",
marginLeft: "0 !important",
marginTop: "5px",
marginBottom: "5px"
},
icons: { color: "white", fontSize: "1.8rem" },
iconsTBA: { color: "#9c7e82", fontSize: "1.3rem" },
toolTips: {
// backgroundColor: "#f5f5f9",
backgroundColor: "rgba(0, 0, 0, 1)",
maxWidth: 350,
// // maxHeight: 100,
fontSize: theme.typography.pxToRem(14),
// border: "1px solid #dadde9",
// // transform: "translate(100px, 200px) rotate(50deg)"
// // transform: "translate(50%,90%)"
// marginLeft: "23vw",
// marginTop: "90vh",
[theme.breakpoints.down("sm")]: {
marginBottom: "35vh"
}
},
toolTipsCard: {
fontSize: theme.typography.pxToRem(20)
}
}));
//Tooltip info
const rpgTT = "test";
const Games = () => {
const classes = useStyles();
return (
<div className="full-container-2">
<div className={classes.root}>
<Grid className={classes.grid} container spacing={3}>
<Grid className={classes.gridItem} item xs={12} sm={6} md={4} lg={4}>
<Card className={classes.card}>
<Tooltip
classes={{ tooltip: classes.toolTipsCard }}
TransitionComponent={Zoom}
title="Play"
placement="top"
>
<CardActionArea>
<CardMedia className={classes.media} image={pic1} />
<CardContent className={classes.content}>
<Typography
className={classes.contHead}
gutterBottom
variant="h5"
component="h2"
>
Game Title
</Typography>
<Typography
className={classes.contText}
variant="body2"
color="textSecondary"
component="p"
>
This is a small string
</Typography>
</CardContent>
</CardActionArea>
</Tooltip>
<CardActions className={classes.buttonArea}>
<Button className={classes.buttons}>
<FontAwesomeIcon
title="Not Available Yet"
className={classes.iconsTBA}
icon={faWindows}
/>
</Button>
<Button className={classes.buttons}>
<FontAwesomeIcon
title="Not Available Yet"
className={classes.iconsTBA}
icon={faLinux}
/>
</Button>
<Button className={classes.buttons}>
<FontAwesomeIcon className={classes.icons} icon={faGithub} />
</Button>
<Tooltip
classes={{ tooltip: classes.toolTips }}
TransitionComponent={Zoom}
title={rpgTT}
placement="bottom"
>
<Button className={classes.buttons}>
<FontAwesomeIcon
className={classes.icons}
icon={faInfoCircle}
/>
</Button>
</Tooltip>
</CardActions>
</Card>
</Grid>

Make each card a flexbox and give the info panel a flex-grow: 1:
.grid{
display:grid;
grid-template-columns: 1fr 1fr;
}
.card {
display: flex;
flex-flow: column;
margin-right: 10px;
}
.card img{
width: 100%;
}
.card .info{
background-color: pink;
flex-grow: 1;
}
<div class="grid">
<div class="card">
<img src="http://placekitten.com/200/200" />
<div class="info">
<h1>Title</h1>
<p>Text</p>
</div>
</div>
<div class="card">
<img src="http://placekitten.com/200/200" />
<div class="info">
<h1>Title</h1>
<p>Text</p>
<p>Text</p>
<p>Text</p>
</div>
</div>
</div>

Found the culprit. It was the CardActionArea (a subcomponent of the card that make it clickable and contains the ripple effect of MUI).
The desired result is achieved only when this area has all of the following properties, so that the content fits into that area:
flexGrow: 1,
display: "flex",
flexDirection: "column",
alignItems: "stretch"
Thanks to everyone for discussing it, helped me approach the problem in a better way and started commenting out the sub-elements of the card one by one and found the problem. Cheers!

Tried to achieve wanted card behavior in a very simple way, I hope that would help:
.container {
display: flex;
flex-wrap: wrap;
}
.card {
border: 4px solid #827d7d;
border-radius: 5px;
margin: 8px;
display: flex;
flex-direction: column;
}
.card_header {
background-color: grey;
}
.card_content {
background-color: yellow;
height: 100%;
}
h1, p {
margin: 0;
}
<div class="container">
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
</div>
</div>
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
</div>
</div>
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
</div>
</div>
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
</div>
</div>
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
</div>
</div>
<div class="card">
<div class="card_header">
<h1>I'm header</h1>
</div>
<div class="card_content">
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
<p>I'm different amount of content</p>
</div>
</div>
</div>

Related

Rendering a chart with colored tiles in React

I would like to how I can render this kind of chart in React. Especially I am wondering how I can create such a tiled background with different colors and have one or more points with coordinates where x and y can be any number from 1-4.
The Grid can be done like this.
function Rect() {
return <div style={{
height: "96px",
width: "96px",
backgroundColor: "transparent",
border: "solid 2px white"
}}></div>;
}
function App() {
return (
<div style={{
background: "linear-gradient(45deg, #53A658, yellow 50%, #ED564D)",
height: "400px",
width: "400px",
display: "flex",
flexWrap: "wrap"
}}>
{new Array(16).fill(1).map(n => <Rect/>)}
</div>
);
}
export default App;

How to add a label to a border in mui?

I would like to have a list wrapped in a border which looks and behaves the same as a textfield border:
Example textfield and list which should have both same border.
In the image, the border around the list looks similar than the one around the textfield but most notably, the label is missing. How can I add the label and how would I set up the focus listeners to get the same hover and selection behaviour?
The typescript code for the list:
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600'}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>
I am also open for alternative approaches. Thanks for the help.
Here is my answer using React and Mui (only for icon).
It relies on flex.
We have a main container that only draws its left, bottom and right borders.
Then we have a header container in charge of drawing the top border in two parts (before and after) and a section with an icon and title.
You can either pass an icon and a title, just a title, just an icon, or nothing at all.
borderedSection.js:
import React from "react";
import SvgIcon from "#mui/material/SvgIcon";
import styles from "./borderedSection.module.scss";
function BorderedSection({ icon, title, children }) {
return (
<div className={styles.mainContainer}>
<div className={styles.header}>
<div className={styles.headerBorderBefore}></div>
{(icon || title) && (
<div className={styles.headerTitle}>
{icon && <SvgIcon component={icon} />}
{title && <span className={styles.title}>{title}</span>}
</div>
)}
<div className={styles.headerBorderAfter}></div>
</div>
<div className={styles.childrenContainer}>{children}</div>
</div>
);
}
export default BorderedSection;
borderedSection.module.scss:
$border-color: #b2b2b2;
.mainContainer {
display: flex;
flex-direction: column;
max-width: 100%;
border-left: 1px solid $border-color;
border-bottom: 1px solid $border-color;
border-right: 1px solid $border-color;
border-radius: 5px;
margin: 1em;
.childrenContainer {
padding: 1em;
}
.header {
display: flex;
flex-direction: row;
width: 100% !important;
.headerBorderBefore {
border-top: 1px solid $border-color;
width: 1em;
border-top-left-radius: 5px;
}
.headerTitle {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
gap: 0.25em;
width: fit-content;
height: 2em;
margin: -1em 0.5em 0em 0.5em;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1em;
font-weight: 600;
}
.headerBorderAfter {
border-top: 1px solid $border-color;
width: 1em;
flex-grow: 2;
border-top-right-radius: 5px;
}
}
}
usage:
import React from "react";
import BorderedSection from "./borderedSection";
import InfoIcon from "#mui/icons-material/Info";
function Example() {
return (
<div style={{ padding: "2em" }}>
<BorderedSection icon={InfoIcon} title="Icon and title">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection title="Title only">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection icon={InfoIcon} >
<div>Icon only</div>
<div>a second child with quite a long text</div>
</BorderedSection>
<BorderedSection >
<div>No icon and no title</div>
<div>a second child with quite a long text</div>
</BorderedSection>
</div>
);
}
Here is how it looks:
I hope it helps
I now managed to hack a solution which looks the same. I do still hope though that there is a clean way to do this: result.
<FormLabel style={{marginLeft: "0.71em", marginTop: "-0.71em", paddingLeft: "0.44em", zIndex: 2, width: "4.2em", backgroundColor: "#383838", position: "absolute", fontSize: "0.75em"}}>Damage</FormLabel>
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600', "&:hover": { borderColor: 'grey.200' }}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>
I needed the same thing. As I was poking around I noticed that MUI accomplished this by using the fieldset tag. I created a quick and dirty component (OutlinedBox) to get this effect:
import React from "react";
import {Box, FormLabel} from "#mui/material";
const OutlinedBox = (props) => {
const {
label,
children
} = props;
return (
<Box>
<FormLabel
sx={{
marginLeft: "0.71em",
marginTop: "-0.71em",
paddingLeft: "0.44em",
paddingRight: '0.44em',
zIndex: 2,
backgroundColor: (theme) => theme.palette.background.default,
position: "absolute",
fontSize: "0.75em",
width: 'auto',
}}>{label}</FormLabel>
<Box
sx={{
position: 'relative',
borderRadius: theme => theme.shape.borderRadius + 'px',
fontSize: '0.875rem',
}}
>
<Box
sx={{
padding: (theme) => theme.spacing(1),
display: 'flex',
gap: (theme) => theme.spacing(1),
flexWrap: 'wrap',
overflow: 'auto'
}}
>
{children}
</Box>
<fieldset aria-hidden={"true"} style={{
textAlign: 'left',
position: 'absolute',
bottom: 0,
right: 0,
top: '-5px',
left: 0,
margin: 0,
padding: '0 8px',
pointerEvents: 'none',
borderRadius: 'inherit',
borderStyle: 'solid',
borderWidth: '1px',
overflow: 'hidden',
minWidth: '0%',
borderColor: 'rgba(255, 255, 255, 0.23)',
}}
>
<legend style={{
float: 'unset',
overflow: 'hidden',
display: 'block',
width: 'auto',
padding: 0,
height: '11px',
fontSize: '0.75em',
visibility: 'hidden',
maxWidth: '100%',
'-webkit-transition': 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
transition: 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
whiteSpace: 'nowrap',
}}><span>{label}</span></legend>
</fieldset>
</Box>
</Box>
);
}
export { OutlinedBox };
// Example usage: <OutlinedBox label="Test">Some content here</OutlinedBox>
I figured I'd post it here in case anyone needs the same thing and comes across this question. All the styling stuff was copied from the styles MUI was using. There may be a better way to read some of this off of the theme, so if anyone decides to use this you may want to tweak it some.

'absolute' Positioned View Position is Changing After the Initial Render

I'm using NextJs and one of my view has absolute positioning.
That view's position is changing after initial render according to this block:
.medal {
height: 15vw;
position: absolute;
top: 20px;
left: -20px;
}
Why my view's position is changing 20 px to top, -20px to left after the initial render?
What I expect is that my view should be rendered initially 20px to top, -20px to left.
Edit:
<main style={{ padding: 20, }}> //If I remove the padding, issue disappears
<Grow
in={secondScreenActive}
style={{ transformOrigin: '0 0 0' }}
{...(secondScreenActive ? { timeout: 1000 } : {})}
>
<div className={styles.cardPerson}>
<div style={{ flex: 1, display: 'flex', alignItems: 'center', width: '100%', justifyContent: 'space-between', flexDirection: 'row' }}>
<div style={{width:'1vw'}}>
<Medal style={{height:'15vw',position: 'absolute', top: 20, left: -30}}/>
</div>
</div>
</div>
</Grow>
</main>
CSS
.cardPerson {
height: 30vh;
display: flex;
padding: 1.5rem;
border-radius: 5px;
background: linear-gradient(to left, rgb(86, 136, 143), Black);
flex-direction: column;
align-items: center;
justify-content: center;
}
SOLVED
I moved <main style={{ padding: 20, }}> to after <Grow>, issue solved.
<Grow
in={secondScreenActive}
style={{ transformOrigin: '0 0 0' }}
{...(secondScreenActive ? { timeout: 1000 } : {})}
>
<main style={{ padding: 20, }}>
<div className={styles.cardPerson}>
<div style={{ flex: 1, display: 'flex', alignItems: 'center', width: '100%', justifyContent: 'space-between', flexDirection: 'row' }}>
<div style={{width:'1vw'}}>
<Medal style={{height:'15vw',position: 'absolute', top: 20, left: -30}}/>
</div>
</div>
</div>
</main>
</Grow>

How to keep react modal open when the popup window is clicked?

I'm very new to React js. After a series of youtube videos, I am working on a project.
My project searches for recipes with some keywords. I am trying to add a filter to filter out some recipes with a react modal. I know I can create a popup with React modal but not sure if this is the best way to have a popup.
Here's a screenshot of my filter popup:
The problem I'm having now is when I clicked on the input bar, the window closed.
Here's the modal component:
class Filter extends Component {
render() {
return ReactDOM.createPortal(
<div className="background"
style={{
position: 'absolute',
top: '0',
bottom: '0',
left: '0',
right: '0',
display: 'grid',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.3)',
}}
onClick={this.props.onClose}
>
<div
style={{
padding: 20,
background: '#fff',
borderRadius: '2px',
display: 'inline-block',
minHeight: '300px',
margin: '1rem',
position: 'relative',
minWidth: '300px',
boxShadow: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',
justifySelf: 'center',
}}
>
<form className="filter-form">
<div>
<p>Max Calories: </p>
<input type="text" value={this.state.input} onChange={this.props.handler}></input>
</div>
</form>
<hr />
<button onClick={this.props.onClose}>Save</button>
</div>
</div>,
document.getElementById('filter-popup')
)
}
}
Please let me know if other code is needed.

Adding text over image in ReactJS by relative height and width

I am trying the following code using HTML and CSS:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.container {
position: relative;
text-align: center;
color: white;
}
.top-asad {
color: blue;
position: absolute;
top: 10%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<h2>Image Text</h2>
<p>How to place text over an image:</p>
<div class="container">
<img src="http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png" alt="Snow" style="width:100%;">
<div class="top-asad">Top Left</div>
</div>
</body>
</html>
I am trying to convert the above code to ReactJS using the following:
import React from "react"
import CardMedia from '#material-ui/core/CardMedia';
import { withStyles } from '#material-ui/core/styles';
import CardActionArea from '#material-ui/core/CardActionArea';
import { Card, CardContent } from "#material-ui/core";
const styles = theme => ({
card: {
display: 'flex',
},
details: {
display: 'flex',
flexDirection: 'column',
},
content: {
flex: '1 0 auto',
},
cover: {
width: 151,
},
controls: {
display: 'flex',
alignItems: 'center',
paddingLeft: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
},
container: {
position: 'relative',
textAlign: 'center',
color: 'white',
},
topasad: {
color: 'blue',
position: 'absolute',
top: '10%',
left: '50%',
},
media: {
display: 'flex',
height: 100,
objectFit: 'contain',
alignItems: 'left',
},
})
function Header(props) {
const { classes } = props;
return (
<Card className={classes.card}>
<div className={classes.con}>
<CardContent className={classes.content}>
<CardMedia
component="img"
className={classes.media}
image="http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png"
/>
</CardContent>
</div>
</Card>
)
}
// export default Header
export default withStyles(styles)(Header);
But I have been unable to place the text on top center of image as I have done using basic HTML and CSS. Can someone please help, please note that I am using relative position while placing text in my working HTML and CSS codes?
Edit:
I am doing this as a learning exercise. I am seeking an answer using ReactJS and MaterialUI
How about just add a div into your header that wraps the <CardMedia>, sets some styles, and includes the text you want to center?
function Header(props) {
const { classes } = props;
return (
<Card className={classes.card}>
<div className={classes.con}>
<CardContent className={classes.content}>
<div style={{position: 'relative'}} >
<CardMedia
component="img"
className={classes.media}
image="https://www.w3schools.com/css/img_lights.jpg"
/>
<div style={{
position: 'absolute',
color: 'white',
top: 8,
left: '50%',
transform: 'translateX(-50%)'
}} >Your text</div>
</div>
</CardContent>
</div>
</Card>
);
}
https://stackblitz.com/edit/react-b7esqk
Result:
Of course "Your text" could be whatever you want, including a prop on <Header> or the children of the header component.

Resources