MUI Modal doesnt close - reactjs

I have an e-commerce website and I added a cart icon in the navbar. When clicked, a MUI modal should open (and it does) but it doesn't close anymore. I also added an "X" button with close function, but still nothing. What is happening?
Also, the component Navbar (that you can see in the code), is rendered in all the other components (pages) of the app.
//imports
const Container=styled.div`
height:100px;
//background-color:#d1bda4;
${mobile({ height: "50px" })}
`
const Wrapper=styled.div`
padding:15px 20px;
display: flex;
justify-content:space-between;
text-align:center;
align-items: center;
${mobile({ padding: "10px 0px" })}
`
const Left=styled.div`
flex:1;
display:flex;
align-items:center;
${mobile({ flex: 2, justifyContent: "center" })}
`
const MenuItem=styled.span`
font-size:14;
cursor:pointer;
paddingLeft:20px;
flex:1;
${mobile({ fontSize: "12px", marginLeft: "10px" })}
`
const Center=styled.div`
flex:1;
${mobile({ fontSize: "12px", marginLeft: "10px" })}
`
const Brand=styled.h1`
font-weight:bold;
cursor:pointer;
${mobile({ fontSize: "24px" })}
`
const Right=styled.div`
flex:1;
display:flex;
align-items:center;
${mobile({ flex: 2, justifyContent: "center" })}
`
const MenuItemCart=styled.div`
paddingLeft:10px;
cursor:pointer;
`
const StyledLink = styled(Link)`
font-size:14;
cursor:pointer;
paddingLeft:20px;
flex:1;
text-decoration:none;
color:black;
${mobile({ fontSize: "12px", marginLeft: "10px" })}
`;
const MainContainer=styled.div`
display:flex;
flex-direction:column;
justify-content:space-between;
height:100%;
overflow-y: auto;
width:100%;
overflow-y:scroll;
`
const HeaderContainer=styled.div`
display: flex;
flex:0.1;
width:100%;
height:1%;
justify-content:space-between;
flex-direction:row;
padding:20px;
`
const LeftContainer=styled.div`
display: flex;
flex:0.5;
flex-direction:column;
width:100%;
margin-right:50px;
`
const Button=styled.button`
width:30px;
height:30px;
border:0px #dcd3ca;
border-radius: 50px;
cursor:pointer;
`
const DeleteButton=styled.button`
width:30px;
height:30px;
border:0px white;
border-radius: 50px;
background-color:white;
cursor:pointer;
`
export const Navbarr = ({items}) => {
const [user, error] = useAuthState(auth);
const [cartItems, setCartItems]=React.useState(0);
const [open, setOpen] = React.useState(false);
const [currentUserCart, setCurrentUserCart]=React.useState([]);
const [currentProducts, setCurrentProducts]=React.useState([]);
const [cartSubTotal, setCartSubtotal] = React.useState(0);
const handleOpen = () => {
fetchProducts();
setOpen(true);
};
const fetchProducts=async()=>{
let cart=[];
let subTotal=0;
let taxes=0;
const productsRef=collection(db, "carts");
const q=query(productsRef, where("userUID", "==", user?.uid));
const docs=await getDocs(q);
docs.forEach((doc)=>{
//cart.push(doc.data());
//subTotal+=doc.data().price;
console.log(doc.data());
cart=doc.data().products;
cart.forEach((product)=>{
subTotal+= product.quantity * product.price;
})
})
setCurrentProducts(cart);
setCartSubtotal(subTotal.toFixed(2));
}
const handleClose = (e) =>{
console.log('click');
setOpen(false);
};
const fetchUserCartItems=async()=>{
const q = query(collection(db, "carts"), where("userUID", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
let cartItemsClone=data.cartItems;
setCartItems(cartItemsClone);
}
React.useEffect(() => {
fetchUserCartItems();
fetchProducts();
}, [user], []);
return (
<Container>
<Wrapper>
<Left>
<StyledLink to='/quiz'>QUIZ</StyledLink>
<StyledLink to='/products'>PRODUCTS</StyledLink>
<MenuItem>REVIEWS</MenuItem>
</Left>
<Center>
<Link to='/' style={{textDecoration:'none', color:'black'}}>
<Brand to="/">SKINCE</Brand>
</Link>
</Center>
<Right>
<MenuItem>ABOUT</MenuItem>
<StyledLink to='/login'>ACCOUNT</StyledLink>
<StyledLink to='/cart'>CART</StyledLink>
<MenuItemCart onClick={()=>handleOpen()}>
<Badge badgeContent={items} sx={{
"& .MuiBadge-badge": {
color: "#FFFFFF",
backgroundColor: "#D6C0B4"
}
}} ></Badge>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={{position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 700,
height: 700,
bgcolor: 'background.paper',
boxShadow: 24,
overflowY: 'initial !important',
p: 4,}}>
<button onClick={(e)=>handleClose(e)}>X</button>
<Typography id="modal-modal-title" variant="h6" component="h2" sx={{fontFamily:'Montserrat', textAlign:'center', paddingBottom:'10px'}}>
Your cart
</Typography>
{currentProducts.length==0 && (
<div style={{textAlign:'center', height:'50vh', paddingTop:'10%'}}>
<h2 > Your cart is empty! </h2>
<h2>Check our products and choose the right one for you.</h2>
</div>
)}
{currentProducts.length>0 && (
<MainContainer>
<LeftContainer>
<HeaderContainer>
{/* <h3>Your cart</h3> */}
<h5>{cartItems} items</h5>
</HeaderContainer>
<TableContainer sx={{paddingTop:'10px'}} >
<Table sx={{ minWidth: 700}} aria-label="simple table">
<TableBody>
{currentProducts.map((row, key)=>(
<TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell sx={{width:'80px'}}>
<img src={row.img.url} style={{width:'100%', height:'100%'}} />
</TableCell>
<TableCell align='left' sx={{width:'80px', fontFamily:'Montserrat', fontSize:'12px'}}> {row.productName}
<h6>{row.productBrand}</h6></TableCell>
<TableCell align='right' sx={{fontFamily:'Montserrat', fontSize:'12px'}}>{row.size}</TableCell>
<TableCell align='center' sx={{fontFamily:'Montserrat'}}>
<Button>-</Button>
</TableCell>
<TableCell align='center' sx={{fontFamily:'Montserrat', fontSize:'12px'}}> {row.quantity}</TableCell>
<TableCell align='left' sx={{fontFamily:'Montserrat'}}>
<Button>+</Button>
</TableCell>
<TableCell align='right' sx={{fontFamily:'Montserrat', fontSize:'12px'}}>{row.price} €</TableCell>
<TableCell align='right' sx={{fontFamily:'Montserrat'}}>
<DeleteButton>X</DeleteButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</LeftContainer>
<Divider orientation="horizontal" flexItem/>
<HeaderContainer>
<h5>Subtotal</h5>
<h5 style={{paddingRight:'70px'}}>{cartSubTotal} € </h5>
</HeaderContainer>
</MainContainer>
)}
)
</Box>
</Modal>
<ShoppingCartOutlinedIcon/>
</MenuItemCart>
</Right>
</Wrapper>
</Container>
);
}

Related

How to change dropdown in each row of mui table cell in a loop?

import * as React from "react";
import {
Select,
MenuItem,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper
} from "#mui/material";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
function createData(name, mobile, access) {
return { name, mobile, access };
}
const rows = [
createData("Sam", 9865745159),
createData("Amily", 8723879237),
createData("Eva", 9432671262),
createData("Jack", 7898083305),
createData("Diana", 8973667356)
];
export default function DenseTable() {
const [access, setAccess] = React.useState(1);
const handleChange = (event, index, data) => {
setAccess(event.target.value);
};
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell align="center">Name</TableCell>
<TableCell align="center">Mobile</TableCell>
<TableCell align="center">Access</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row" align="center">
{row.name}
</TableCell>
<TableCell align="center">{row.mobile}</TableCell>
<TableCell align="center">
<Select
value={access}
onChange={handleChange}
MenuProps={{
MenuListProps: { disablePadding: true }
}}
fullWidth
size="small"
IconComponent={() => (
<KeyboardArrowDownIcon
sx={{
position: "absolute",
right: 10,
width: "20px",
pointerEvents: "none"
}}
/>
)}
sx={{
fontSize: "14px",
width: "100px",
height: "28px"
}}
>
<MenuItem
value={1}
sx={{
fontSize: "14px",
height: "25px",
width: "100%"
}}
>
Allow
</MenuItem>
<MenuItem
value={2}
sx={{
fontSize: "14px",
height: "30px",
width: "100%"
}}
>
Decline
</MenuItem>
</Select>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
I am using ReactJs with Material UI table with dropdown in each row of the table cell. Whenever I am changing the dropdown option of one row then automatically changes to same option in dropdown of all rows.I have to handle each row separately. How to change dropdown in each row of mui table cell in a loop? file.
Move the dropdown to a new jsx component and manage the state there
function dropdownComponent (){
const [access, setAccess] = React.useState(1);
const handleChange = (event, index, data) => {
setAccess(event.target.value);
};
return
(<Select
value={access}
onChange={handleChange}
MenuProps={{
MenuListProps: { disablePadding: true }
}}
fullWidth
size="small"
IconComponent={() => (
<KeyboardArrowDownIcon
sx={{
position: "absolute",
right: 10,
width: "20px",
pointerEvents: "none"
}}
/>
)}
sx={{
fontSize: "14px",
width: "100px",
height: "28px"
}}
>
<MenuItem
value={1}
sx={{
fontSize: "14px",
height: "25px",
width: "100%"
}}
>
Allow
</MenuItem>
<MenuItem
value={2}
sx={{
fontSize: "14px",
height: "30px",
width: "100%"
}}
>
Decline
</MenuItem>
</Select>
)
}
Call it like this
<TableCell align="center">
<dropdownComponent />

React close previous modals, before opening a new one. now its not closing previous modals

const Tile = ({name, value}) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => {
setShow(true);
}
return (<Style>
<a href="#" id="myBtn" onClick={handleShow}>{name}</a>
<Modal show={show} onHide={handleClose} centered size="lg"
aria-labelledby="contained-modal-title-vcenter"
style={{
left: "25%",
top: "50%",
position: "fixed",
overflow: "hidden",
width: "50%",
height: "auto",
background: "lightgrey",
}}>
<Modal.Body
style={{
margin: "10px",
padding: "20px",
border: "2px",
paddingBottom: "0px"
}}>
<p>{value}</p></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}
style={{
padding: "5px",
marginBottom: "5px",
marginLeft: "5px"
}}>
Close
</Button>
</Modal.Footer>
</Modal>
</Style>)
}
##################################################################################################
const Glossary = () => {
let data = useGlossaryData();
data = data.allFile.edges;
console.log(data);
let freshData = [];
data.map((x) => (freshData.push(x.node.childMdx.frontmatter.title + "!!" + x.node.childMdx.internal.content.substring(x.node.childMdx.internal.content.indexOf("#") + 1, x.node.childMdx.internal.content.length - 1))))
return (
<Style>
{freshData.map((x, index) => (
<Tile key={index} name={x.split('!!')[0]} value={x.split('!!')[1]}
/>
)
)}
</Style>
)
}
Its creating modals one top pf modals, I want to close previous modals while showing the current modal.

Make entire cell contents clickable MUI table

I've got a MUI table with cells that link to another part of the app when clicked. Rather than wrap the entire row, I'm selectively wrapping certain cells. The link/routing is working - but I'm left with this annoying dead unclickable space due to some styling interactions.
In the picture below I've opened the 'inspect element' view on dev tools to show specifically the dead zone I'm dealing with in the first cell, and to show some mui styling details that might be relevant. Code with styles included further down.
Styles:
const useStyles = makeStyles(theme => ({
table: {
minWidth: 650,
position: 'relative',
fontSize: 10
},
largeIcon: {
width: 60,
height: 60
},
tableContainer: {
minHeight: 320
},
tableBodyContainer: {
minHeight: 265
},
tableHeadRow: {
'& .MuiTableCell-root': {
borderRight: `1px solid ${COLORS.WHITE}`,
borderBottom: `none`,
padding: '8px 5px 8px',
fontSize: 10,
cursor: 'pointer'
}
},
arrow: {
color: theme.palette.grey[500]
},
arrowActive: {
transform: 'rotate(-180deg)',
color: theme.palette.primary.main,
display: 'inline-block'
},
tableRow: {
'& .MuiTableCell-root': {
borderRight: `1px solid ${theme.palette.grey[200]}`,
borderBottom: 'none',
cursor: 'pointer',
padding: 5,
paddingTop: 8,
paddingBottom: 8,
minWidth: 25
},
'& .MuiTableCell-root:first-child': {
border: 'none'
}
},
selectedRow: {
backgroundColor: `${COLORS.SECONDARY} !important`,
'& .MuiTableCell-root': {
color: COLORS.WHITE
}
},
editIcon: {
backgroundImage: `url(${EditIcon})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
width: 18,
height: 18
}
}));
And the table:
return (
<div className={classes.tableContainer}>
<TableContainer className={classes.tableBodyContainer}>
<Table className={classes.table} size="small">
<TableHead>
<TableRow className={classes.tableHeadRow}>
<TableCell />
{tableHeadElements.map(e => (
<TableCell key={e.key} align="center">
{e.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{folders?.items.map((folder: IFolderDTO, index: number) => {
const { id, name, updatedAt } = folder;
return (
<TableRow
className={classes.tableRow}
classes={{ selected: classes.selectedRow }}
selected={selectedRow === id}
onClick={() => setSelectedRow(id)}
key={index}
>
<TableCell align="center" >
<Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
<Box>
<IconButton color="default" size={'medium'}>
<FolderIcon fontSize="default" />
</IconButton>
</Box>
</Link>
</TableCell>
{[name, new Date(updatedAt)].map(cell => (
<TableCell key={index} align="center">
<Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
<Box>{getCorrectFormat(cell)}</Box>
</Link>
</TableCell>
))}
<FolderActionsMenu
folderId={id}
onDeleteFolder={onDeleteFolder}
openDialogWithId={openDialogWithId}
/>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<FolderFormDialog />
</div>
Kill the dead unclickable space! (please)
Thanks

MDXRenderer - Auto adjust image to container

I have created a simple default page that gets rendered with content (images and text) from GraphCMS.
I can query and display the content, but I'm not sure how to make the images adjust to the view/container, as I don't know how to access the image and set a CSS class.
I'm using the plugin "gatsby-plugin-mdx" to render the following:
page.content.markdownNode.childMdx.body
Here is my DefaultPage.tsx file:
import React from "react"
import Layout from "../layout/layout"
import styled from "styled-components"
import { H1, BodyMain } from "../styles/TextStyles"
import { MDXRenderer } from "gatsby-plugin-mdx"
export default function DefaultPageTemplate({ pageContext: { page } }) {
return (
<Layout>
<Wrapper>
<HeaderWrapper>
<TextWrapper>
<TitleWrapper>{page.title}</TitleWrapper>
<SubtitleWrapper>{page.subtitle}</SubtitleWrapper>
</TextWrapper>
</HeaderWrapper>
<ContentWrapper>
<MDXRenderer>{page.content.markdownNode.childMdx.body}</MDXRenderer>
</ContentWrapper>
</Wrapper>
</Layout>
)
}
const Wrapper = styled.div``
const HeaderWrapper = styled.div`
min-height: 300px;
color: #ffffff;
background-color: #339861;
display: grid;
justify-content: center;
align-items: center;
padding: 30px;
`
const TextWrapper = styled.div`
text-align: center;
`
const TitleWrapper = styled(H1)``
const SubtitleWrapper = styled(BodyMain)``
const ContentWrapper = styled.div`
margin: 1rem;
`
Below is a screenshot of the behaviour:
Below is a screenshot of when I'm inspecting the element in the browser. It appears that the image is wrapped in a paragraph:
Can you help me to understand how to scale the image for smaller views/screens?
It seems like I was able to target the image within the gatsby-browser.js and give it a width of 100%.
my gatsby-browser.js file:
import "./src/components/layout/layout.css"
import React from "react"
import { MDXProvider } from "#mdx-js/react"
const H1 = props => (
<h1 style={{ fontSize: "60px", fontWeight: "bold" }} {...props} />
)
const H2 = props => (
<h2 style={{ fontSize: "40px", fontWeight: "bold" }} {...props} />
)
const H3 = props => (
<h2 style={{ fontSize: "30px", fontWeight: "bold" }} {...props} />
)
const H4 = props => (
<h4 style={{ fontSize: "25px", fontWeight: "bold" }} {...props} />
)
const H5 = props => (
<h4 style={{ fontSize: "20px", fontWeight: "bold" }} {...props} />
)
const H6 = props => (
<h4 style={{ fontSize: "18px", fontWeight: "bold" }} {...props} />
)
const MyParagraph = props => (
<p style={{ fontSize: "20px", lineHeight: 1.6 }} {...props} />
)
const Strong = props => <strong style={{ fontWeight: "bold" }} {...props} />
const MyImage = props => (
<img style={{ maxWidth: "100%", borderRadius: "15px" }} {...props} />
)
const components = {
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
p: MyParagraph,
strong: Strong,
img: MyImage,
}
export const wrapRootElement = ({ element }) => (
<MDXProvider components={components}>{element}</MDXProvider>
)
Any other or better recommendations/solutions?

Creating Tweet Display Box in ReactJS

I am using the following code to generate a Tweet input box which takes in text/video/image/emoji. And they can be in different combinations.
I am not sure how to generate a tweet display box which shows the final display containing text/image/emoji ? I understand I might need to put the different inputs in an array or some sort but what after that. My current code for display side is performing nothing and I am not sure where to go from here.
I am looking for display box to be of following form after a Submit Button:
Code components/EmojiPicker.js has:
import React, {useState} from 'react'
import ReactDOM from "react-dom";
import { Picker } from "emoji-mart";
import Button from "#material-ui/core/Button";
const EmojiPicker = ({ onSelect }) => {
const [show, setShow] = useState(false);
return (
<>
<Button
onClick={() => setShow(oldState => !oldState)}
style={{ width: "30px", height: "30px", borderRadius: "4px", border: "3px solid", display: "flex", alignItems: "center", justifyContent: "center",
background: "transparent"}}>
ej
</Button>
{ReactDOM.createPortal(
show && <Picker onSelect={onSelect} />,
document.body
)}
</>
);
};
export default EmojiPicker
Code components/FileInput.js has:
import React, {useRef} from 'react'
const FileInput = ({ onChange, children }) => {
const fileRef = useRef();
const onPickFile = event => {
onChange([...event.target.files]);
};
return (
<div
style={{
width: "35px",
height: "35px",
borderRadius: "3px"
}}
onClick={() => fileRef.current.click()}
>
{children}
<input
multiple
ref={fileRef}
onChange={onPickFile}
type="file"
style={{ visibility: "hidden" }}
/>
</div>
);
};
export default FileInput
Code components/tweetboxImgInp.js as:
import React, {useState, useEffect} from 'react'
const ImgIcon = () => (
<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M14 13l4 5H6l4-4 1.79 1.78L14 13zm-6.01-2.99A2 2 0 0 0 8 6a2 2 0 0 0-.01 4.01zM22 5v14a3 3 0 0 1-3 2.99H5c-1.64 0-3-1.36-3-3V5c0-1.64 1.36-3 3-3h14c1.65 0 3 1.36 3 3zm-2.01 0a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h7v-.01h7a1 1 0 0 0 1-1V5z" />
</svg>
);
export const Img = ({ file, onRemove, index }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
if (file) {
setFileUrl(URL.createObjectURL(file));
}
}, [file]);
return fileUrl ? (
<div style={{ position: "relative", maxWidth: "230px", maxHeight: "95px" }}>
<img
style={{
display: "block",
maxWidth: "230px",
maxHeight: "95px",
width: "auto",
height: "auto"
}}
alt="pic"
src={fileUrl}
/>
<div
onClick={() => onRemove(index)}
style={{
position: "absolute",
right: 0,
top: 0,
width: "20px",
height: "20px",
borderRadius: "50%",
background: "black",
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
x
</div>
</div>
) : null;
};
export default ImgIcon
And App.js has:
import React, { useRef, useState } from "react";
import ImgIcon, {Img} from './components/tweetboxImgInp'
import EmojiPicker from './components/EmojiPicker'
import FileInput from './components/FileInput'
import "emoji-mart/css/emoji-mart.css";
import "./styles.css";
function App() {
const [text, setText] = useState("");
const [pics, setPics] = useState([]);
const textAreaRef = useRef();
const insertAtPos = value => {
const { current: taRef } = textAreaRef;
let startPos = taRef.selectionStart;
let endPos = taRef.selectionEnd;
taRef.value =
taRef.value.substring(0, startPos) +
value.native +
taRef.value.substring(endPos, taRef.value.length);
};
return (
<div style={{display: "flex", flexDirection: "column", border: "3px solid", borderRadius: "5px", width: "600px", minHeight: "200px", padding: "20px"}} >
<div style={{ display: "flex", flexDirection: "column", flex: 1, border: "1px solid", borderRadius: "5px", margin: "0px"}}>
<textarea
ref={textAreaRef}
value={text}
style={{ flex: 1, border: "none", minHeight: "150px" }}
onChange={e => setText(e.target.value)}
/>
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", background: "fbfbfb"}} >
{pics.map((picFile, index) => (
<Img key={index} index={index} file={picFile} onRemove={rmIndx =>
setPics(pics.filter((pic, index) => index !== rmIndx))}/>))}
</div>
</div>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", marginTop: "20px" }}>
<div style={{ marginRight: "20px" }}>
<FileInput onChange={pics => setPics(pics)}>
{/* <ImgIcon /> */}
Tes
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
export default App
Edit: I am good with the display box accepting only 1 media file, text and few emoji. It will surprise me if I am the only one in 2019 looking to do it for fun.
Working Example
Click the codesandbox button to view the demo
The tweet display component is pretty straightforward. Its a flexbox column with two parts. First part of the column contains the tweet. The second part of the column contains the list of images/media elements. Emoji is part of the text component.
Tweet Display Component
const Tweet = ({ tweet: { text, images } }) => (
<div
style={{
margin: "20px",
border: "1px solid grey",
width: "600px",
padding: "20px",
borderRadius: "3px"
}}
>
<div>{text}</div>
{images.length > 0 && (
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
background: "fbfbfb",
padding: "30px 0"
}}
>
{images.map((img, i) => (
<Img key={i} file={img} index={i} isSingle={images.length === 1} />
))}
</div>
)}
</div>
);
For more info checkout this css-tricks article to get more info on css flex layout

Resources