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

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>

Related

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.

Smooth transition with React Modal

I am trying to make React modal have a smooth transition when it appears on the screen but can't seem to get the transition property to do anything. Right now, the modal pops onto the screen in a jarring manner but I'd like to get it to slowly fade in. Here is my modal with the styles:
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
transition: "opacity .5s",
width: "90%",
maxHeight: "600px",
overflow: "auto",
padding: "40px",
maxWidth: "500px",
borderRadius: "10px",
boxShadow: "0px 0px 15px 1px gray",
},
};
<Modal
ariaHideApp={false}
isOpen={modalIsOpen}
onRequestClose={() => setIsOpen(false)}
style={customStyles}
contentLabel="Example Modal"
>
Anyone have any suggestions?
I think you need to put opacity as a property itself.
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
opacity: "20%",
transition: ".5s",
width: "90%",
maxHeight: "600px",
overflow: "auto",
padding: "40px",
maxWidth: "500px",
borderRadius: "10px",
boxShadow: "0px 0px 15px 1px gray",
},
};

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.

position absolute top:0, bottom: 0, right:0, left:0 and parent paddings

position absolute with all its properties in 0 makes the child element expand in the parent element, and if I put a padding in the parent element without a height the child with the position absolute cover until the of the parent padding, my theory is right or I am forgotting something here?there could be another different case?
import React from 'react';
import {View, StyleSheet} from 'react-native';
import Video from 'react-native-video';
export default function Reproductor(props) {
return (
<View style={styles.container}>
<View style={styles.videoContainer}>
<Video
source={{
uri:
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
}}
resizeMode="cover"
style={styles.video}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
height: 250,
justifyContent: 'center',
alignItems: 'center',
},
videoContainer: {
width: '90%',
paddingTop: '56.25%',
borderRadius: 20,
overflow: 'hidden',
borderWidth: 3,
backgroundColor: 'black',
},
video: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
the child (video) covers the padding
An absolutely positioned element is an element whose computed
position value is absolute or fixed. The top, right,
bottom, and left properties specify offsets from the edges of the element's containing block.
https://developer.mozilla.org/en-US/docs/Web/CSS/position
position absolute related to the edges of the parent element (= ignores parent padding/border).
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white; height: 50px;"></div>
<div style="position: absolute; background: blue; top:0; left:0; color: white;">child</div>
</div>
top: 0; right: 0; bottom: 0; left: 0; is the way to set overlay:
Example: https://www.w3schools.com/howto/howto_css_overlay.asp
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">div</div>
<div style="position: absolute; background: rgba(0, 0, 0, 0.5); top:0px; left:0px; right: 0; bottom: 0; color: white;">overlay</div>
</div>
Add space by top: 10px; for example (or em % and so on)
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:10px; left:10px; color: white;">child</div>
</div>
Or add a margin for the absolute element:
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:0px; left:0px; color: white;margin: 10px;">margin: 10px around</div>
</div>
You find more tricks/ideas but these are the basics ideas.
Thank you I understand, but you said that it cover padding and border (position absolute related to the edges of the parent element (= ignores parent padding/border).), and I try to cover the border too but it just ignore the padding not the border, what is my mistake here?
export default function MainScreen(props) {
return (
<View style={style.container}>
<View style={style.element} />
</View>
);
}
const style = StyleSheet.create({
container: {
borderWidth: 3,
padding: 40,
alignSelf: 'center',
width: 200,
height: 200,
backgroundColor: 'grey',
},
element: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: 100,
height: 100,
backgroundColor: 'purple',
},
});

Can't set my grid card content to equal height

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>

Resources