I am developing an ecommerce application and I am using a modal for both registration and login.
the form is very long and overflows on the page
I want to modal to be able to scroll like bootstrap modal.
How do I make it scrollable?
Modal Component
const MODAL_STYLES = {
position: "fixed",
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: '#FFF',
padding: '30px',
zIndex: '1000',
width: '50%',
borderRadius: '.5em'
}
const OVERLAY_STYLE={
position: "fixed",
top: '0px',
left: '0px',
bottom: '0px',
right: '0px',
backgroundColor: 'rgba(0,0,0, .8)',
zIndex: '1000'
}
login page
import React, {useState} from 'react'
import Modal from '../Components/Modal';
const Modal = ({open, children}) => {
if(!open) return null
return ReactDom.createPortal(
<>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>
{children}
</div>
</div>
</>,
document.getElementById('portal')
)
}
const [openLoginModal, setOpenLoginModal] = useState(false)
{
openLoginModal && (
<Modal open={openLoginModal}>
<form action="">
<div className="form-group mb-2">
<label htmlFor="" className="mb-2">Full Name <span>*</span></label>
<input type="text" className="form-control" />
</div>
<div className="form-group mb-2">
<label htmlFor="" className="mb-2">Emil Address <span>*</span></label>
<input type="text" className="form-control" />
</div>
<button></button>
</form>
</Modal>
)
}
link to codesandbox
https://codesandbox.io/s/muddy-voice-lf0ft
You should change your styles same as bellow:
const MODAL_STYLES = {
position: "absolute",
backgroundColor: "#FFF",
padding: "15px",
zIndex: "1000",
width: "35%",
borderRadius: ".5em"
};
const OVERLAY_STYLE = {
position: "fixed",
display: "flex",
justifyContent: "center",
top: "0",
left: "0",
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0, .8)",
zIndex: "1000",
overflowY: "auto"
};
Here's the code: https://codesandbox.io/s/quirky-almeida-dik6v
Related
suppose i have a div element
<div
style={{
borderTopLeftRadius: "10px",
borderTopRightRadius: "10px",
background: "#edf2f8",
border: "1px solid #edf2f8",
borderBottom: "0px",
width:"300px",
marginLeft:"20px",
backgroundPosition: "center",
}}
>
hello world
</div>
I want to use the onclick function to hide/show state of the div . For example when a user clicks in the div I hide it .. How can I achieve that in react ?
Store a display value in component state and toggle it via the onClick handler. Conditionally render the entire div.
Example:
function App() {
const [display, setDisplay] = React.useState(true);
return (
<div className="App">
{display && (
<div
onClick={() => setDisplay(false)}
style={{
borderTopLeftRadius: "10px",
borderTopRightRadius: "10px",
background: "#edf2f8",
border: "1px solid #edf2f8",
borderBottom: "0px",
width: "300px",
marginLeft: "20px",
backgroundPosition: "center"
}}
>
hello world
</div>
)}
<button type="button" onClick={() => setDisplay(true)}>
Reply
</button>
</div>
);
}
The video loader overrides the fixed bottom element, thus makes it quite unpleasant. I'am streaming the videos online and player used for it is React HLS player. What would be the best solution to prevent the overriding of loader. Following is the code reference
React HLS Player
<ReactHlsPlayer
url={video_url}
autoplay={false}
controls={true}
width="100%"
height="auto"
config={{
file: {
forceHLS: true,
}
}}
/>
Bottom Navbar Code
const useStyles = makeStyles({
root: {
width: "100%",
bottom: "0px",
position: "sticky"
},
gridList: {
flexWrap: "nowrap",
position: "fixed",
bottom: "0px",
background: "white",
border: "1px solid grey",
width: "100%"
}
});
<GridList className={classes.gridList}>
{itemList.map((tile, index) => {
return (
<GridListTile
key={tile.icon}
style={{ height: "70px", width: "25%" }}
>
<ListItem button key={tile.text}
onClick={(tile.text == "DirectLine") ? directLineFunc : ''}
>
<NavLink
exact
to={tile.link}
key={tile.key}
activeClassName="main-nav-active"
style={{ textAlign: "center" }}
isActive={(match, location) => {
match && setNewActiveLink(index)
return match;
}}
>
<ListItemText
disableTypography
primary={
<Typography
style={{
fontSize: "10px",
fontWeight: "bold",
fontFamily: "Nunito"
}}
>
{tile.text}
</Typography>
}
/>
</NavLink>
</ListItem>
</GridListTile>
);
})}
</GridList>
See the image below
And this the codesandbox link: https://codesandbox.io/s/react-material-forked-dtx6w
Finally I was able to sort out by adding 'zIndex:999' to gridList of BottomNavbar with the following changes in useStyles as:
const useStyles = makeStyles({
root: {
width: "100%",
bottom: "0px",
position: "sticky"
},
gridList: {
flexWrap: "nowrap",
position: "fixed",
bottom: "0px",
background: "white",
border: "1px solid grey",
width: "100%",
zIndex: 999 {/* <-- Here I added this and issue solved */}
}
});
I added a style={{marginBottom: "100px"}} property in the <ReactHlsPlayer /> and it seems to fix your problem
I have a layout file where I made footer and navigation and I insert these two functions in Layout const (code below). In the new file, I just need the Navigation function so how I can insert it without a footer? Because when I write in my new file import Navigation from "../components/layout" and the in code insert I've got error...
const Layout = ({ children }) => {return (
<div>
<Navigation></Navigation>
<Global
styles={{
html: {
backgroundColor: "#fff",
color: "#111",
fontFamily: `'Poppins', sans-serif`,
fontSize: 14,
[Screen.S]: {
fontSize: 16,
},
[Screen.M]: {
fontSize: 18,
},
[Screen.L]: {
fontSize: 20,
},
},
a: {
color: "unset",
},
}}
/>
{children}
<Footer></Footer>
</div>
)
}
function Navigation() { const [navbarOpen, setNavbarOpen] = useState(false) return (
<header
css={{
width: "100%",
maxWidth: "100%",
padding: "0 24px",
position: "fixed",
background: "#fff",
boxShadow: "0 0 0.35rem rgba(0,0,0,.25)",
zIndex: "100",
top: 0,
}}
>
<div
css={{
gridAutoFlow: "column",
minHeight: "4.5rem",
display: "grid",
maxWidth: 1200,
margin: "0 auto",
gridTemplateColumns: "auto 1fr",
alignItems: "center",
paddingLeft: 35,
}}>
<Link to="/ ">
<img style={{ height: "2.5rem" }} src={logo}/>
</Link>
<Toggle
navbarOpen={navbarOpen}
onClick={() => setNavbarOpen(!navbarOpen)}
>
{navbarOpen ? <Hamburger open /> : <Hamburger />}
</Toggle>
{navbarOpen ? (
<NavBoxIcons>
<NavbarSocialLinks />
</NavBoxIcons>
) : (
<NavBox open>
<div>
<HeaderLink>About</HeaderLink>
<HeaderLink>Blog</HeaderLink>
</div>
<div>
<NavbarLinks />
</div>
</NavBox>
)
}
</div>
</header >
)
}
function Footer() { return (
<footer
css={{
padding: "6rem 2rem",
fontSize: "1rem",
minHeight: 160,
fontFamily: "sans-serif",
...Css.container,
}}
>
<div
css={{
display: "flex",
flexDirection: "column",
marginBottom: "3.6rem",
}}
>
<div
css={{
fontSize: "1.2rem",
display: "grid",
gridGap: "0.8rem",
}}>
<a>
<span>Privacy police</span>
</a>
</div>
</div>
<div
css={{
display: "grid",
gridTemplateColumns: "1fr auto",
alignItems: "center",
fontWeight: "lighter",
}}>
<div css={{ display: "flex", flexDirection: "column" }}>
<span>My Page</span>
</div>
</div>
</footer>
)
}
try exporting both Navigation and Footer like this
//at bottom of file
export {Navigation, Footer}
import individual component like this
import {Navigation} from 'components/layout'
or
import {Navigation,Footer} from 'components/layout'
lookup exporting in js
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 am learning ReactJS and Material-UI and hence I have been working to build a semi twitter clone.
I am now hitting a road block in the sense that I can't figure how to build this input dialog box which can take all of text, video, photo, gif in same box.
Using <Input /> I can provide individually what kind of input I have i.e. email, password, etc using type. But I am not sure how to design this particular dialog box to take multiple inputs.
Can you please show me a working code example ?
Its 2019 and a lot of things have changed. This is a very rough and hacky implementation of how twitter has implemented. Its pretty simple.
Working Demo Link
For folks who want to just look at the code head over to codesandbox
Note: this was done very quickly to demonstrate what twitter has done under the hood.
The editor mainly consists of a <textarea /> where text for the tweets are added.
Below the text area is an expanding div block which loops image files that are selected from file system.
As for the emoji, a standard emoji picker is used to select emojis and plain old javascript is used to add emojis at the current cursor position in the textarea.
I have skipped the gif picker, as its similar to the image picker, the only difference being, a modal is open to populate gifs from giphy. An api from giphy. can be easily integrated to achieve this.
The following are the components written
FileInput Component
The FileInput component is used as the button and file picker to select images. Its a plain <input type="file" />. The native styles are hidden and a custom icon is displayed
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>
);
};
Img Component
The Img component displays the images selected from the input. It uses URL.createObjectURL to create a temporary local url that can be populated inside an img tag. This is the image preview seen in the tweet sheet below the textarea.
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;
};
App (Tweet Sheet)
This is the root component that stitches everything together.
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 />
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
EmojiPickerModal
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
)}
</>
);
};
Note: For the emoji picker a popular open source emoji picker component emoji-mart was used