I'm using Next js and implementing modal dialog using #headlessui/react package. I'm setting an overlay but it closes all components when clicked.
index.js
const [modal, setModal] = useState("");
const [openModal, setOpenModal] = useState(false)
return (
<div>
{openModal && <Modal vid={modal} onClose={setOpenModal}/>}
...{
<div onClick={()=> {setOpenModal(true), setModal(res.snippet.resourceId?.videoId)}}>
}
...
</div>
)
Modal.js
import { Dialog } from "#headlessui/react";
import { useRef } from "react";
import YouTube from 'react-youtube'
import { XIcon } from "#heroicons/react/outline";
export function Modal({ vid, onClose }) {
let overlayRef = useRef();
const opts ={
playerVars: {
autoplay: 1,
}
}
//onClick={(e)=> e.stopPropagation()}
return (
<Dialog
static
open={true}
onClose={onClose}
initialFocus={overlayRef}
className="fixed inset-0 z-10 flex items-center justify-center bg-bg-black bg-opacity-70 backdrop-blur-sm"
>
<Dialog.Overlay
ref={overlayRef}
className="fixed inset-0 bg-gray-800/60"
/>
<div className="flex flex-col items-end justify-center">
<button onClick={()=> onClose(false)} className=' pb-3'>
<XIcon className=" h-5 w-5 text-text-gray-color hover:accent-text-gray-color"/>
</button>
<YouTube videoId={vid} opts={opts} className=""/>
</Dialog>
);
}
export default Modal
So how can I fix the overlay to only close when clicked outside of the component?
Related
I am setting up a login page using google OAuth in react, I am following a youtube tutorial, everything seems to work fine but somehow after logging in, I am not being redirected to my homepage. here is the code (Login.jsx):
import React from 'react'
import GoogleLogin from 'react-google-login';
import { useNavigate } from 'react-router-dom';
import { FcGoogle } from 'react-icons/fc';
import carVideo from '../assets/car.mp4';
import logo from '../assets/speedograph white.png';
import { client } from '../client';
const Login = () => {
const navigate = useNavigate();
const responseGoogle = (response) => {
localStorage.setItem('user', JSON.stringify(response.profileObj));
if (response.profileObj) {
const { name, googleId, imageUrl } = response.profileObj;
const doc = {
_id: googleId,
_type: 'user',
userName: name,
image: imageUrl,
};
client.createIfNotExists(doc).then(() => {
navigate('/', { replace: true });
});
}
};
return (
<div className = "flex justify-start items-center flex-col h-screen">
<div className='relative w-full h-full'>
<video
src={carVideo}
type='video/mp4'
loop
controls={false}
muted
autoPlay
className='w-full h-full object-cover'
/>
<div className = "absolute flex flex-col justify-center items-center top-0 right-0 left-0 bottom-0 bg-blackOverlay">
<div className="p-5 ml-3">
<img src={logo} width="130px" alt="logo" />
</div>
<div className='shadow-2xl'>
<GoogleLogin
clientId={process.env.REACT_APP_GOOGLE_API_TOKEN}
render={(renderProps) => (
<button
type='button'
className='bg-white flex justify-center items-center p-3 rounded-lg cursor-pointer outline-none'
onClick={renderProps.onClick}
disabled={renderProps.disabled}
>
<FcGoogle className='mr-4' />Sign in with Google
</button>
)}
onSuccess={responseGoogle}
onFailure={responseGoogle}
cookiePolicy="single_host_origin"
/>
</div>
</div>
</div>
</div>
)
}
export default Login
I think the problem is due to the if condition, but I added it after reviewing a Stackoverflow tab that suggested adding it as a null check, before adding it I was getting the error:
Cannot destructure property 'name' of 'response.profileObj'
now the error is gone but it is not redirecting me to the home page (neither did it do so before the error). So where am I missing in my code exactly?
I want to render this popover panel
<Popover.Panel className="absolute z-10 inset-x-0 transform shadow-xl pb-2 w-max">
<div>
<Dropdown subCategory={mainCatArray}/>
</div>
</Popover.Panel>
same as this popover button render in this below code. Because I want to render a separate popover panel with the popover button, please help me to do that. (I cannot render in one categoryObj because I want to render this popover button horizontally, but if I use a single map for the entire popover component popover button renders it vertically.
(Simply What I want to do is I want to render one <Popover.Panel> to one <Popover.Button>)
import { Fragment, useEffect, useState } from "react";
import { Popover, Transition } from "#headlessui/react";
import Dropdown from "./dropdown";
import { categoryObj } from "../../components/item/categoriesobj";
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
const CatDropDown = () => {
const [mainCatArray, setMainCatArray] = useState([]);
return (
<>
<Popover className="z-0 relative">
{({ open }) => (
<>
<div>
<div className=" z-10 bg-white">
<div className="w-2/3 flex gap-5 px-4 py-6 sm:px-6 lg:px-8 ">
{categoryObj.map((singleMainCategory) => (
<Popover.Button
className={classNames(
open ? "text-gray-900" : "text-gray-900",
"group bg-white rounded-md inline-flex items-center text-sm font-susty focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white"
)}
>
<span
key={singleMainCategory.id}
onClick={() => {
setMainCatArray(singleMainCategory.subCategory);
}}
>
<span>{singleMainCategory.name}</span>
</span>
</Popover.Button>
))}
</div>
</div>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 -translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-1"
>
<Popover.Panel className="absolute z-10 inset-x-0 transform shadow-lg pb-2">
<div>
<Dropdown subCategory={mainCatArray}/>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
</>
);
};
export default CatDropDown;
I am implementing a modal in my react app but face the weird error, Error: Expected `onClick` listener to be a function, instead got a value of `object` type.. What's confusing here is the fact that I have implemented the modal in another part of the application using the same logic and it doesn't produce such error.
Below are important snippets of the code.
index.js
import React, { useState } from "react";
import ProfileSave from "../../components/modal/profileSave";
const test = () => {
const [saveModal, setSaveModal] = useState(false);
const [save, setSave] = useState(false);
return (
<>
<button className="w-[165px] h-[60px] text-center px-4 py-2 text-sm text-white bg-[#3A76BF] rounded-md font-bold text-[16px]" onClick={saveShowModal}>
Save
</button>
{saveModal && (
<ProfileSave
open={saveModal}
handleClose={() => setSaveModal(!saveModal)}
handleSave={handleSave}
/>
)}
</>
export default test
profileSave.js
import React from "react";
const ProfileSave = (open, handleClose, handleModal) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;
You neet to extract the props that you component recive
Try this :
import React from "react";
const ProfileSave = ({open, handleClose, handleModal}) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;
Im working on a custom toast notification. But the hard part to me is I've not been able to hide the toast notification correctly. When I make a click to show it the toast has an animation to the left the problem is when the toast is gonna hide just disapear and it does not slide to the right. Im working with react and tailwind css. Here is the code. also leave a link to the case. https://codesandbox.io/s/toast-notification-ucx2v?file=/src/components/toastNotification.jsx
I hope you can help me guys.
-----App component-----
import { useEffect, useState } from "react";
import ToastNotification from "./components/toastNotification";
import "./styles.css";
export default function App() {
const [showToast, setShowToast] = useState(false);
const addToCart = () => {
setShowToast(true);
};
useEffect(() => {
setTimeout(() => {
setShowToast(false);
}, 3000);
}, [showToast]);
return (
<div className="App">
<ToastNotification showNotification={showToast} />
<button
className="w-1/2 p-2 flex items-center justify-center rounded-md bg-indigo-400 text-white"
type="submit"
onClick={addToCart}
>
Add to cart
</button>
</div>
);
}
-----Toast component-----
import React, { useEffect, useState } from "react";
const ToastNotification = ({ showNotification }) => {
useEffect(() => {
setTimeout(() => {}, 5000);
}, []);
return (
<>
{showNotification === false ? null : (
<div className="static z-20">
<div
className={`${
showNotification
? "animate__animated animate__slideInRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg overflow-hidden mx-auto"
: "animate__animated animate__slideOutRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg mx-auto"
}`}
>
<div class="w-10 border-r px-2">
<i class="far fa-check-circle"></i>
</div>
<div class="flex items-center px-2 py-3">
<div class="mx-3">
<p>add to car</p>
</div>
</div>
</div>
</div>
)}
</>
);
};
export default ToastNotification;
I am trying to use antD's calendar picker inside of a modal that has been created using Material-UI. When I put the datepicker inside of the modal component. It shows me the selection box but when I click on it, nothing opens to choose from. I tried wrapping it in a div as well, but that didn't work. Here's an image of the modal:
Modal code:
import React, { useState,useEffect } from "react";
import { withStyles } from "#material-ui/core/styles";
// import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import MuiDialogTitle from "#material-ui/core/DialogTitle";
import MuiDialogContent from "#material-ui/core/DialogContent";
import MuiDialogActions from "#material-ui/core/DialogActions";
import IconButton from "#material-ui/core/IconButton";
import CloseIcon from "#material-ui/icons/Close";
import Typography from "#material-ui/core/Typography";
import { Link } from "#material-ui/core";
import Table from "../Cards/CardTable";
import moment from "moment";
import CardLineChart from "components/Cards/CardLineChart.js";
import CardLineChart2 from "components/Cards/CardLineChart2.js";
import CardStats from "components/Cards/CardStats.js";
import { DatePicker, Space } from "antd";
const { RangePicker } = DatePicker;
const styles = (theme) => ({
root: {
width: 300,
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
},
});
const DialogTitle = withStyles(styles)((props) => {
const { children, classes, onClose, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles((theme) => ({
root: {
// width: 200,
padding: theme.spacing(2),
},
}))(MuiDialogContent);
const DialogActions = withStyles((theme) => ({
root: {
margin: 0,
padding: theme.spacing(1),
},
}))(MuiDialogActions);
export default function CustomizedDialogs(props) {
const { name } = props;
console.log("Name is ======>", name)
const [open, setOpen] = useState(false);
const [colors, setColors] = useState(false);
const [deviceMsgs,setDeviceMsgs] = useState(null)
const [start, setStart] = useState();
const [end, setEnd] = useState();
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const onChange = (dates, dateStrings) => {
if (dateStrings.length > 0) {
setStart(dateStrings[0]);
setEnd(dateStrings[1]);
}
};
const DPicker = () => {
return (
<div>
<Space direction="vertical" size={12}>
<RangePicker
ranges={{
Today: [moment(), moment()],
"This Month": [
moment().startOf("month"),
moment().endOf("month"),
],
}}
onChange={onChange}
/>
</Space>
</div>
);
};
return (
<div>
<Link variant="outlined" color="primary" onClick={handleClickOpen}>
{name}
</Link>
<Dialog
maxWidth={"xl"}
// style={{ width: "100%" }}
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle
style={{ backgroundColor: colors ? "green" : "red" }}
id="customized-dialog-title"
onClose={handleClose}
>
{name}
</DialogTitle>
<DialogContent dividers>
<div>
<DPicker /> //////// Using the picker
<div className="relative w-full pr-4 max-w-full flex-grow flex-1">
<span className="font-semibold text-xl text-blueGray-700">
Last Update : 21 April 2021 17:00:00
</span>
</div>
<div className="m-4 px-4 md:px-10 mx-auto w-full">
<div>
{/* Card stats */}
<div className="flex flex-wrap">
<div className="w-full lg:w-6/12 xl:w-3/12 px-4">
<CardStats
statSubtitle="Total"
statTitle="10"
statArrow="up"
statPercent="3.48"
statPercentColor="text-emerald-500"
statDescripiron="Since last month"
statIconName="fas fa-percent"
statIconColor="bg-lightBlue-500"
/>
</div>
<div className="w-full lg:w-6/12 xl:w-3/12 px-4">
<CardStats
statSubtitle="ACTIVE"
statTitle="6"
statArrow="down"
statPercent="3.48"
statPercentColor="text-red-500"
statDescripiron="Since last week"
statIconName="fas fa-users"
statIconColor="bg-pink-500"
/>
</div>
<div className="w-full lg:w-6/12 xl:w-3/12 px-4">
<CardStats
statSubtitle="INACTIVE"
statTitle="4"
statArrow="down"
statPercent="1.10"
statPercentColor="text-orange-500"
statDescripiron="Since yesterday"
statIconName="far fa-chart-bar"
statIconColor="bg-red-500"
/>
</div>
<div className="w-full lg:w-6/12 xl:w-3/12 px-4">
<CardStats
statSubtitle="ALERT"
statTitle="--"
statArrow="up"
statPercent="12"
statPercentColor="text-emerald-500"
statDescripiron="Since last month"
statIconName="fas fa-chart-pie"
statIconColor="bg-orange-500"
/>
</div>
</div>
</div>
</div>
</Dialog>
</div>
);
}
Obviously, you should check my solution because it is just my assumption of your problem, but anyway it seems that it can help you:
<DatePicker
getPopupContainer={(triggerNode) => {
return triggerNode.parentNode;
}}
/>
By default, popup container is located nearby body but you can change it to your modal element. The my solution above covers that
I found a solution to the problem and it is straightforward.
after tinkering in the dev-tools, I found out that the Z-index of the antd Modal is set to 1055.
A simple override to the date-picker Z-index fixes the issue : I set it to 1056.
Its className is : ".ant-picker-dropdown"
.ant-picker-dropdown{
z-index: 1056;
}
solves the issue.
This will work.
Go to Node Module > antd > dist > antd.compact.css > change the z-index of elements accordingly.