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

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.

Related

React how to set image as background of navbar

i'm using create-react-app and have a navbar that I would like to add a background image to. I have tried a couple different things like add the backgroundImage styling and adding an image tag but have not been able to make it work. The picture I am using is a .jpg and was downloaded from a stock image site. I added the image to my project by dragging it in and adding it to an images folder.
The current path from my header (what I called my navbar) file to my image is ../../images/pexels-kai-pilger-1341279.jpg
Here is my header file:
import React from 'react';
import { makeStyles } from '#material-ui/core';
import { ReactComponent as MenuLogo } from '../../images/menu-logo.svg';
const useStyles = makeStyles(theme => {
return ({
header: {
backgroundColor: '#d9d9d9',
boxShadow: '0rem 0rem 0rem 0.05rem #666666',
padding: '0rem 1rem 0rem 1rem',
position: 'relative',
width: '100vw',
height: 70,
zIndex: '100',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
backgroundImage: "url('SpaceLogo')"
},
headerActive: {
left: 352,
backgroundColor: '#d9d9d9',
boxShadow: '0rem 0rem 0rem 0.05rem #666666',
padding: '0rem 1rem 0rem 1rem',
position: 'relative',
width: '81.7%',
height: 70,
zIndex: '100',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
},
menuLogo: {
backgroundColor: '#d9d9d9',
border: 'none',
'&:hover': {
cursor: 'pointer'
}
},
});
});
function Header(props) {
const { toggleMenu, isMenuOpen } = props;
const classes = useStyles(props);
return (
<div className={ isMenuOpen ? classes.headerActive : classes.header } >
<button
className={classes.menuLogo}
onClick={() => toggleMenu()}>
<MenuLogo />
</button>
</div>
);
}
export default (Header);
I believe that is the only file that is needed for this question, but I can update if I need to add more info.
If I could get some help on how to get this set up, that would be great.
Bonus question: Is there a way for me to choose what part of the picture is visible in the navbar? For example, the image is a large square but my navbar is a thin rectangle. I would like to use the middle of the image as the background as opposed to the top.
header: {
backgroundColor: '#d9d9d9',
boxShadow: '0rem 0rem 0rem 0.05rem #666666',
padding: '0rem 1rem 0rem 1rem',
position: 'relative',
width: '100vw',
height: 70,
zIndex: '100',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
backgroundImage: `url(${SpaceLogo})`
},
This should be work

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>

ReactJS + MaterialUI: SelectField height change

I'm using React material-ui SelectField on my page. I'm not able to adjust the height of the select field. Here is my code snippet:
<SelectField
underlineStyle={{
display: 'none'
}}
style={{
width: '49%',
padding: '0 0 5em 5em',
border: '1px solid #ccc',
borderRadius: '5px',
backgroundColor: '#fff',
margin: '16px 0px 0px 28px',
}}
floatingLabelText="Select Stack*"
value={this.state.stack}
onChange={this.handleStackChange.bind(this)}
>
{this.state.dropDownStack}
</SelectField>
My UI is looking as below
I need to reduce the height of dropdown box, how can I do it?
You have to set it using following:
<SelectField
className="custom-selectfield"
underlineStyle={{
visibility: 'hidden',
}}
floatingLabelStyle={{
top: '14px',
}}
style={{
border: '1px solid #ccc',
height: '45px',
}}
floatingLabelText="Select Stack*"
value={this.state.stack}
onChange={this.handleStackChange.bind(this)}>
{this.state.dropDownStack}
</SelectField>
There is some internal CSS applied in material UI so in order to overwrite it added a custom class to SelectPicker component named custom-selectfield
Add following lines in your CSS file
.custom-selectfield > div:nth-child(2) {
margin-top: -6px !important;
}

Resources