I'm new to react , and created a react modal component to show an image with its close button.
const BoxModal = ({children, modalIsOpen, setModalIsOpen}) => {
return (
<>
<Modal
isOpen={modalIsOpen}
onRequestClose={() => setModalIsOpen(false)}
style={{
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
zIndex: 2000,
},
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
border: 'none',
background: 'none',
transform: 'translate(-50%, -50%)',
padding: 0,
},
}}
>
{children}
<ButtonModal onClick={() => setModalIsOpen(false)}>
<img width={30} height={30} src={close_btn} alt="close" />
</ButtonModal>
</Modal>
</>
);
};
BoxModal.propTypes = {
children: PropTypes.element.isRequired,
modalIsOpen: PropTypes.bool,
setModalIsOpen: PropTypes.bool,
};
export default BoxModal;
Then, in another page I import I call it and import it like this:
<BoxModal>
<img
src={MapaRutas}
style={{
position: 'relative',
width: '100%',
}}
/>
</BoxModal>
<Button
width="260px"
height="60px"
color={'black'}
onClick={() => setModalIsOpen(true)}
>
<img width={13} height={13} src={map} alt="map" />
Ver cobertura de YoCargo
</Button>
I use react state to pass the onclick function in the button
const [modalIsOpen, setModalIsOpen] = useState(false);
But it's not working, nothing happens.Can someone tell me how to fix it ?
What i suggest is to set function to close your modal as prop in your BoxModal component, i call it 'handleClose', also you forget to pass 'modalIsOpen' to it.
import React, { useState } from 'react';
const Sample = () => {
const [modalIsOpen, setModalIsOpen] = useState(false);
return (
<>
<BoxModal modalIsOpen={modalIsOpen} handleClose={() => setModalIsOpen(false)}>
<img
src={MapaRutas}
style={{
position: 'relative',
width: '100%',
}}
/>
</BoxModal>
<Button
width="260px"
height="60px"
color={'black'}
onClick={() => setModalIsOpen(true)}
>
<img width={13} height={13} src={map} alt="map" />
Ver cobertura de YoCargo
</Button>
</>
);
}
In your BoxModel, you get the props like that
const BoxModal = ({children, modalIsOpen, handleClose }) => {
return (
<>
<Modal
isOpen={modalIsOpen}
onRequestClose={handleClose}
style={{
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
zIndex: 2000,
},
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
border: 'none',
background: 'none',
transform: 'translate(-50%, -50%)',
padding: 0,
},
}}
>
{children}
<ButtonModal onClick={handleClose}>
<img width={30} height={30} src={close_btn} alt="close" />
</ButtonModal>
</Modal>
</>
);
};
Related
in the following code, how do I define itemData as post.images[i]?
import clientPromise from "../lib/mongodb";
import Box from '#mui/material/Box';
import Avatar from '#mui/material/Avatar';
import IconButton from '#mui/material/IconButton';
import ChatBubbleOutlineRoundedIcon from '#mui/icons-material/ChatBubbleOutlineRounded';
import FavoriteBorderRoundedIcon from '#mui/icons-material/FavoriteBorderRounded';
import ShareRoundedIcon from '#mui/icons-material/ShareRounded';
import Button from '#mui/material/Button';
import ImageList from '#mui/material/ImageList';
import ImageListItem from '#mui/material/ImageListItem';
export default function Posts({ posts }) {
return (
<div>
<ul>
<div style={{ width: '100%' }}>
<Box sx={{ display: 'flex', justifyContent: 'center'}}>
<div style={{ width: '75%' }}>
{posts.map((post) => (
<li style={{ border: '1px solid grey', borderRadius: '12px', margin: "10px", padding: "10px" }}>
<Box sx={{ display: 'flex', justifyContent: 'start', alignItems: 'center'}}>
<Avatar alt={post.userName} src={post.userAvatar}/>
<h4 style={{ paddingLeft:'20px', paddingRight: '20px' }}>{post.userName}</h4>
<p style={{ color: 'grey', fontSize: '12px' }}>{post.timestamp}</p>
</Box>
<Box>
<p style={{ display: 'block', width: '100%', margin: '0'}}>{post.text}</p>
</Box>
<Box sx={{ my: 1, display: 'flex', justifyContent: 'end', alignItems: 'center'}}>
<IconButton><ChatBubbleOutlineRoundedIcon/></IconButton>
<IconButton><FavoriteBorderRoundedIcon/></IconButton>
<IconButton><ShareRoundedIcon/></IconButton>
</Box>
<Box><Button href={post.link} variant="outlined">Link</Button></Box>
<Box>
<ImageList sx={{ width: 500, height: 450 }} cols={3} rowHeight={164}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
</li>
))}
</div>
</Box>
</div>
</ul>
</div>
)
}
const itemData = [
{
img: 'https://images.unsplash.com/photo-1523217582562-09d0def993a6',
title: 'House',
},
{
img: 'https://images.unsplash.com/photo-1615874694520-474822394e73',
title: 'Living Room',
},
{
img: 'https://images.unsplash.com/photo-1616594039964-ae9021a400a0',
title: 'Bedroom',
},
{
img: 'https://images.unsplash.com/photo-1616486886892-ff366aa67ba4',
title: 'Dining Room',
},
{
img: 'https://images.unsplash.com/photo-1521783593447-5702b9bfd267',
title: 'Bathroom',
}
];
export async function getServerSideProps() {
try {
const client = await clientPromise;
const db = client.db("abode-app");
const posts = await db
.collection("posts")
.find({})
.sort({ metacritic: -1 })
.limit(20)
.toArray();
return {
props: { posts: JSON.parse(JSON.stringify(posts)) },
};
} catch (e) {
console.error(e);
}
}
I have tried loading the images one by one and they work, but prefer to loop over them.
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.
I need to get the left position of an element after elements got mounted to show a progress bar. It works on clicking links however when it got mounted the progressWidth is not getting calculated. basically, the useEffect seems running before the component is mounted!!!!
export default ({ task, selectedLocal, selectedScenarioId, taskId, selectedTaskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const isBefore = taskId <= selectedTaskId;
const isActive = taskId === selectedTaskId;
const navItemWidth = 100;
const [progressWidth, setProgressWidth] = useState(0);
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current) {
console.log(ref.current.getBoundingClientRect().left);
setProgressWidth(ref.current.getBoundingClientRect().left);
}
});
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold'
},
navTitle: {
width: `${navItemWidth}px`,
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: `${navItemWidth}px`
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative',
'z-index': '3'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
activeNavIndicator: { borderColor: theme.palette.themePrimary },
activeInnerIndicator: { backgroundColor: theme.palette.themePrimary },
progress: {
marginTop: '59px',
'z-index': '2',
position: 'fixed',
left: '0',
width: `${progressWidth}px`,
borderBottom: '2px solid',
borderColor: theme.palette.themePrimary
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(isActive ? [styles.navLink, styles.navLinkActive] : styles.navLink)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div
ref={ref}
className={css(
isBefore ? [styles.navIndicator, styles.activeNavIndicator] : styles.navIndicator
)}
>
<div
className={css(
isBefore ? [styles.innerIndicator, styles.activeInnerIndicator] : styles.innerIndicator
)}
/>
</div>
</div>
</NavLink>
{isActive && <div className={css(styles.progress)} />}
</div>
);
};
So when component is getting loaded I get image 1, when I click on the component I get image 2. What I need to happen is when component is getting loaded it should look like image 2.
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
I followed exactly the same style as the overlay example in the bootstrap website, and on the website, when I tried to add rootclose, it does not work properly(overlay does not dismiss on click on other places of screen), and same for my own work
const Example = React.createClass({
getInitialState() {
return { show: true };
},
toggle() {
this.setState({ show: !this.state.show });
},
render() {
const style = {
position: 'absolute',
backgroundColor: '#EEE',
boxShadow: '0 5px 10px rgba(0, 0, 0, 0.2)',
border: '1px solid #CCC',
borderRadius: 3,
marginLeft: -5,
marginTop: 5,
padding: 10
};
return (
<div style={{ height: 100, position: 'relative' }}>
<Button ref="target" onClick={this.toggle}>
I am an Overlay target
</Button>
<Overlay
show={this.state.show}
onHide={() => this.setState({ show: false })}
placement="right"
container={this}
rootclose
target={() => ReactDOM.findDOMNode(this.refs.target)}
>
<div style={style}>
<strong>Holy guacamole!</strong> Check this info.
</div>
</Overlay>
</div>
);`enter code here`
}
});
ReactDOM.render(<Example/>, mountNode);
Camelcase it. And supply the boolean.
rootClose={true}