Initially I have three div In first div I am able to upload pdf in second div I am able upload excel file in third div I am able to upload audio file. now What I am trying to do is If I uploaded pdf file successfully then I need to disable Excel file div so I cannot upload excel file please help to achieve this
This is my code
This is App.js
import React, { useState } from "react";
import 'antd/dist/antd.css';
import './index.css';
import { Row, Col, Button, Modal, Upload, message, Divider } from 'antd';
import { FilePdfOutlined, FileExcelOutlined, AudioOutlined } from '#ant-design/icons';
import "./App.css";
const App = () => {
const [visible, setVisible] = useState(false)
const [disableDiv, setDisableDiv] = useState(disable)
const showPopUp = () => {
setVisible(true)
}
const closePopUp = () => {
setVisible(false)
}
const props = {
beforeUpload: file => {
const characterValidation = file
// setFile(characterValidation)
console.log(characterValidation)
}
}
return (
<div>
<Row>
<Col span={24}>
<Button onClick={() => showPopUp()} type="primary">Show PopUp</Button>
<Modal
visible={visible}
onCancel={closePopUp}
onOk={closePopUp}
>
<div style={{ display: "flex" }}>
<div style={{ marginLeft: "5px" }}>
<Upload accept=".pdf" {...props}>
<FilePdfOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Pdf</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }} disabled={disableDiv}>
<Upload accept=".xls .xlsx" {...props}>
<FileExcelOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload accept="audio/*" {...props}>
<AudioOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
</div>
</Modal>
</Col>
</Row>
</div>
)
}
export default App
This is App.css
.ant-modal-close-x > span {
display: none;
}
.ant-upload-list:before {
content:"Attachments:";
}
.ant-upload-list {
display: block;
margin-bottom: 30pt;
margin-left: 0;
font-size: 16px;
font-weight: bold;
border-top:1px solid red;
}
Ok so as far as I could understand you want to prevent the user from interacting with a specific input under a certain condition.
In your case you want to disable the Upload input component when the user has uploaded the pdf file in the previous Upload component.
One way to do that is to set an onChange event handler on the pdf Upload component and when the file is successfully uploaded you can then disable the excel Upload component.
Here is how this would look in your example (without changing how your code worked before):
import React, { useState } from "react";
import 'antd/dist/antd.css';
import './index.css';
import { Row, Col, Button, Modal, Upload, message, Divider } from 'antd';
import { FilePdfOutlined, FileExcelOutlined, AudioOutlined } from '#ant-design/icons';
import "./App.css";
const App = () => {
const [visible, setVisible] = useState(false)
const [isExcelUploadDisabled, setIsExcelUploadDisabled] = useState(false);
const showPopUp = () => {
setVisible(true)
}
const closePopUp = () => {
setVisible(false)
}
const props = {
beforeUpload: file => {
const characterValidation = file
// setFile(characterValidation)
console.log(characterValidation)
}
}
const PDFUploadProps = {
accept: '.pdf',
name: 'PDF file name',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
headers: {
authorization: 'authorization-text'
},
onChange(info) {
console.log(info);
if (info.file.status !== 'uploading') {
console.log('uploading');
}
if (info.file.status === 'done') {
setIsExcelUploadDisabled(true);
}
//bonus: enable the excel upload again when the user removes the file
if (info.file.status === 'removed') {
setIsExcelUploadDisabled(false);
} else if (info.file.status === 'error') {
console.error('error');
}
},
...props
};
const excelUploadProps = {
accept: '.xls, .xlsx',
name: 'excel file name',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
headers: {
authorization: 'authorization-text'
},
disabled: isExcelUploadDisabled
...props
};
return (
<div>
<Row>
<Col span={24}>
<Button onClick={() => showPopUp()} type="primary">Show PopUp</Button>
<Modal
visible={visible}
onCancel={closePopUp}
onOk={closePopUp}
>
<div style={{ display: "flex" }}>
<div style={{ marginLeft: "5px" }}>
<Upload {...PDFUploadProps}>
<FilePdfOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Pdf</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload {...excelUploadProps}>
<FileExcelOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload accept="audio/*" {...props}>
<AudioOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
</div>
</Modal>
</Col>
</Row>
</div>
)
}
export default App
Here is also a minimal example of the same functionality here.
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.
I am mapping over an array and rendering a custom card component for each index in the array. However, I am receiving the error "Each child in a list should have a unique "key" prop" 1. Although, I am passing the index as the key. I have tried with a React.fragment and passing the index down to the card component and adding the key there. Both methods are still throwing the same error.
Main Component
import React from "react";
import { useRouter } from "next/router";
import { Button, Container } from "#mui/material";
import { makeStyles } from "#mui/styles";
import { InstructionsCard } from "../layout/directory";
import {
RiNumber1 as OneIcon,
RiNumber2 as TwoIcon,
RiNumber3 as ThreeIcon,
} from "react-icons/ri";
function InstructionSection() {
const router = useRouter();
const classes = useStyles();
const instructions = [
{
id: 1,
icon: OneIcon,
title: "step one",
text: [
"Navigate to the",
<Button
onClick={() => router.push("/requirements")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
requirements
</Button>,
"page for our most frequently asked questions and specific requirements before booking any activity. ",
],
},
{
id: 2,
icon: TwoIcon,
title: "step two",
text: [
"Find the activity you are interested in and read through the information carefully. Be sure to fully understand the,",
<Button
onClick={() => router.push("/#upgrades")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
entry fee
</Button>,
" and",
<Button
onClick={() => router.push("/#upgrades")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
upgrade
</Button>,
" packages",
],
},
{
id: 3,
icon: ThreeIcon,
title: "step three",
text: [
"Please, be sure to verify we are ",
<Button
onClick={() => router.push("/locations")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
located
</Button>,
" in your area. Select an experience, date, time-slot, toggle any upgrades, and continue through checkout.",
],
},
];
return (
<Container className={classes.root}>
{/* instructions iteration */}
{instructions.map((_instruction, index) => {
return (
<React.Fragment key={index}>
<InstructionsCard item={_instruction} />
</React.Fragment>
);
})}
</Container>
);
}
// custom styles
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("md")]: {
flexDirection: "column",
},
width: "100%",
display: "flex",
justifyContent: "space-evenly",
},
}));
export default InstructionSection;
Card Component
import { makeStyles } from "#mui/styles";
import { Card, CardContent, Typography, Divider } from "#mui/material";
const InstructionsCard = ({ item }) => {
const classes = useStyles();
const Icon = item.icon;
return (
<Card className={classes.root}>
<CardContent>
<Icon className={classes.icon} />
<Typography className={classes.title} variant="h5" component="h6">
{item.title.toUpperCase()}
</Typography>
<Divider className={classes.divider} />
<Typography
variant="subtitle1"
component="p"
sx={{ mb: 1.5 }}
color="text.secondary"
>
{item.text}
</Typography>
</CardContent>
</Card>
);
};
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("md")]: {
margin: theme.spacing(4, 0, 4, 0),
},
background: theme.palette.primary.main,
borderRadius: theme.spacing(5),
padding: theme.spacing(2),
margin: theme.spacing(5),
width: "100%",
textAlign: "center",
boxShadow: `0px 0px 10px 10px ${theme.palette.offset.main}`,
},
icon: {
background: theme.palette.secondary.dark,
width: "50px",
height: "50px",
padding: "15px",
borderRadius: theme.spacing(5),
},
divider: {
background: theme.palette.secondary.dark,
padding: "2px",
width: "20%",
margin: theme.spacing(1, "auto", 1, "auto"),
},
title: {
fontWeight: 800,
},
}));
export default InstructionsCard;
Change like this in your main component
React.Fragment we need to use One time it can't use multiple time
return (
<Container className={classes.root}>
<React.Fragment>
{instructions.map((_instruction, index) => {
<InstructionsCard key={index} item={_instruction} />;
})}
</React.Fragment>
</Container>
);
Thank You
I wish to remove all the styles from my main component.
All the JS style of the material UI is on the same file and it is starting to get long code.
I wish to create a new file that contains all the styles on other file and on Navbar.js I will just call the components, just same as CSS file that I call to class
import styled from "#emotion/styled";
import { Notifications, Pets } from "#mui/icons-material";
import {
AppBar,
Avatar,
Badge,
Box,
InputBase,
Menu,
MenuItem,
Toolbar,
Typography,
} from "#mui/material";
import MailIcon from "#mui/icons-material/Mail";
import React, { useState } from "react";
const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
const Search = styled("div")(({ theme }) => ({
backgroundColor: "white",
padding: "0 10px",
borderRadius: theme.shape.borderRadius,
width: "40%",
}));
const Icons = styled(Box)(({ theme }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
const UserBox = styled(Box)(({ theme }) => ({
display: "flex",
gap: "10px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "none",
},
}));
const Navbar = () => {
const [open, setOpen] = useState(false);
return (
<AppBar position="sticky">
<StyledToolbar>
<Typography variant="h6" sx={{ display: { xs: "none", sm: "block" } }}>
PALSAM
</Typography>
<Pets sx={{ display: { xs: "block", sm: "none" } }} />
<Search>
<InputBase placeholder="search..." />
</Search>
<Icons>
<Badge badgeContent={4} color="error">
<MailIcon />
</Badge>
<Badge badgeContent={4} color="error">
<Notifications />
</Badge>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
onClick={(e) => setOpen(true)}
/>
</Icons>
<UserBox onClick={(e) => setOpen(true)}>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
<Typography variant="span">Jhon</Typography>
</UserBox>
</StyledToolbar>
<Menu
id="demo-positioned-menu"
aria-labelledby="demo-positioned-button"
open={open}
onClose={(e) => setOpen(false)}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<MenuItem>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</Menu>
</AppBar>
);
};
export default Navbar;
You can create a new js file and add export before each const then you can import them from that file
export const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
I have a Search button, and some other button, but only Search button text font size not get reduced, do you have any idea why?
import React from "react";
import event1 from "../assets/images/event1.png";
import event2 from "../assets/images/event2.png";
import event3 from "../assets/images/event3.png";
import event4 from "../assets/images/event4.png";
import scene from "../assets/images/scene.png";
import styles from "../assets/styles/HomePage.module.css";
import { Container, Box, TextField, Button } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyle = makeStyles({
root: {
backgroundColor: "#f6d7dc",
fontFamily: "ASMBold",
height: "100%"
},
container: {
width: "80%",
paddingTop: "20px"
},
search: {
border: "5px solid #184cdf",
padding: "22px 0",
display: "flex",
justifyContent: "center",
alignItems: "center",
fontSize: "20px",
margin: 0
},
longInput: {
width: "400px"
},
searchButton: {
backgroundColor: "#184cdf",
color: "#ec8562",
textTransform: "uppercase",
fontWeight: "bold",
height: "55px",
width: "100px"
}
});
function HomePage(props) {
const classes = useStyle();
return (
<div className={classes.root}>
<Container maxWidth="xl" className={classes.container} disableGutters>
<Box className={classes.search}>
<TextField variant="outlined" placeholder="city or place" />
<TextField variant="outlined" placeholder="dates" />
<TextField
variant="outlined"
placeholder="artists / venues / events"
className={classes.longInput}
/>
<Button varian="contained" className={classes.searchButton}>
Search
</Button>
</Box>
</Container>
</div>
);
}
export default HomePage;
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