I have created several CSS animations in my React.js project
I'm looking to put a general button to pause them if the user wants, because they can disturb or simply slow down the pc.
I have done this by targeting classes but it doesn't work at all :
Button.js :
import {useRef, useEffect} from 'react';
function Button() {
const ref = useRef(null);
useEffect(() => {
const el2 = ref.current;
console.log(el2);
const jstoggle = document.getElementById('js-toggle');
console.log(jstoggle)
jstoggle.addEventListener('click', () => {
const animations = document.querySelectorAll('.person-name', '.background-penny');
animations.forEach(animation => {
const running = animation.style.animationPlayState || 'running';
animation.style.animationPlayState = running === 'running' ? 'paused' : 'running';
})
});
}, []);
return (
<>
<button id="js-toggle" ref={ref} className="btn" type="button">PAUSE ANIMATION</button>
</>
);
}
export default Button
Animations CSS :
.person-name{
text-transform: capitalize;
background-image: linear-gradient(
-225deg,
#231557 0%,
#44107a 29%,
#ff1361 67%,
#fff800 100%
);
background-size: auto auto;
background-clip: border-box;
background-size: 200% auto;
color: #fff;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: textclip 2s linear infinite;
display: inline-block;
font-size: 19px;
padding-top: 5px;
}
#keyframes textclip {
to {
background-position: 200% center;
}
}
.background-penny{
padding: 20px;
animation: changecolor 3s infinite;
transition: none;
}
Thank you for the help !
It is working fine for me only difference is i have attached onClick event handler to button.
const handleClick = () => {
const animations = document.querySelectorAll('.person-name', '.background-penny');
animations.forEach(animation => {
const running = animation.style.animationPlayState || 'running';
animation.style.animationPlayState = running === 'running' ? 'paused' : 'running';
})
}
<button id="js-toggle" onClick={()=>handleClick()} ref={ref} className="btn" type="button">PAUSE ANIMATION</button>
Can you also check if changecolor animation is correctly implemented, textclip animation is working correctly.
Related
I want to change the
id="navbar"
font color while the page is in dark mode, and make the font color back when the page switches to light mode. The switch is made with js:
const onClick = () => {
theme.value = theme.value === 'light'
? 'dark'
: 'light'
setPreference()
}
const getColorPreference = () => {
if (localStorage.getItem(storageKey))
return localStorage.getItem(storageKey)
else
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
}
const setPreference = () => {
localStorage.setItem(storageKey, theme.value)
reflectPreference()
}
const reflectPreference = () => {
document.firstElementChild
.setAttribute('data-theme', theme.value)
document
.querySelector('#theme-toggle')
?.setAttribute('aria-label', theme.value)
}
const theme = {
value: getColorPreference()
}
and the background is set here
html {
background: linear-gradient(135deg, #a1c4fd 10%, #c2e9fb 90%);
block-size: 100%;
color-scheme: light;
background-attachment: fixed;
}
html[data-theme=dark] {
background: linear-gradient(135deg, #061c43 10%, #08101f 90%);
color-scheme: dark;
background-attachment: fixed;
}
#navbar ul li[data-theme=dark] {
padding: 10px;
border-radius: 25px;
float: right;
margin-left: 3px;
margin-right: 8px;
font-weight: 500;
color: white;
box-shadow: -5px -5px 8px #ffffff60,5px 5px 10px #00000060;
}
that's not doing anything. what am i missing?
Simply do this:
const reflectPreference = () => {
document.firstElementChild.setAttribute('data-theme', theme.value);
document.querySelector('#theme-toggle')?.setAttribute('aria-label', theme.value);
document.getElementById("navbar").style.fontColor = theme.value === "dark" ? "white" : "black";
}
Read more here.
if you want to change the color of an element using js , you gotta learn about DOM HTML
in this exemple i'm trying to change the color of some elements
<!DOCTYPE html>
<html>
<body>
<h2 id="myH2">This is an example h2</h2>
<p id="myP">This is an example paragraph.</p>
<p id="myP2">This is also an example paragraph.</p>
<div id="myDiv">This is an example div.</div>
<br>
<button type="button" onclick="myFunction()">Set text color</button>
<script>
function myFunction() {
document.getElementById("myH2").style.color = "#ff0000";
document.getElementById("myP").style.color = "magenta";
document.getElementById("myP2").style.color = "blue";
document.getElementById("myDiv").style.color = "lightblue";
}
</script>
</body>
</html>
My custom react toast component was working well until I tried to implement automatic dismissal of notifications after a set time.
I am trying to make it so that after a set time, the pop-up "toast" notifications will have a CSS fade-out animation play and then be deleted — unless the user is hovering over that notification, in which case it will hold off on dismissing that one until the user moves their mouse off of it.
Sometimes it works properly, other times it stops displaying anything and adds the notifications back one by one, other times it... well, it just behaves in a very strange and unexpected manner.
Here is my code:
Toast.css
.toast-container {
font-size: 24px;
box-sizing: border-box;
position: fixed;
z-index: 10;
}
.toast-popup {
padding: 12px;
display: flex;
align-items: center;
justify-content: space-between;
width: 500px;
border: solid #f2f2f2;
border-radius: 8px;
box-shadow: 0 0 10px #999;
margin-bottom: 1rem;
opacity: 0.9;
}
.toast-popup:hover {
box-shadow: 0 0 12px deepskyblue;
opacity: 1 !important;
animation-play-state: paused;
}
.success {
background-color: #5cb85c;
}
.info {
background-color: #5bc0de;
}
.warning {
background-color: #f0ad4e;
}
.danger {
background-color: #d9534f;
}
.toast-text {
justify-self: flex-start;
width: 100%;
padding: 6px 0 6px 6px;
opacity: 0.9;
}
.toast-title {
font-weight: 700;
font-size: 32px;
text-align: left;
padding-bottom: 0px;
color: #f2f2f2;
}
.toast-message {
padding-top: 0px;
text-align: left;
color: #f2f2f2;
}
.toast-icon {
float: left;
margin: 0 20px 0 10px;
opacity: 0.9;
}
.toast-icon img {
width: 50px;
height: 50px;
fill: #f2f2f2;
opacity: 0.9;
}
.close-button {
float: right;
align-self: flex-start;
font-weight: 600;
color: #f2f2f2;
background: none;
border: none;
opacity: 0.9;
cursor: pointer;
}
.top-right {
top: 2rem;
right: 2rem;
}
.top-right-slide {
top: 2rem;
right: 2rem;
transition: transform .6s ease-in-out;
animation: toast-in-right .7s;
}
.bottom-right {
bottom: 2rem;
right: 2rem;
}
.bottom-right-slide {
bottom: 2rem;
right: 2rem;
transition: transform .6s ease-in-out;
animation: toast-in-right .7s;
}
.top-left {
top: 2rem;
left: 2rem;
}
.top-left-slide {
top: 2rem;
left: 2rem;
transition: transform .6s ease-in;
animation: toast-in-left .7s;
}
.bottom-left {
bottom: 2rem;
left: 2rem;
}
.bottom-left-slide {
bottom: 2rem;
left: 2rem;
transition: transform .6s ease-in;
animation: toast-in-left .7s;
}
.fadeout {
animation: 4s linear 5s 1 normal forwards running toast-fadeout;
}
#keyframes toast-in-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
#keyframes toast-in-left {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
#keyframes toast-fadeout {
from { opacity: 0.9; }
to { opacity: 0; }
}
Toast.js - Please excuse the generous peppering of console.logs...
import React, {useEffect, useState} from 'react';
import icon_success from './icons/feathericons/check-circle.svg';
import icon_info from './icons/feathericons/info.svg';
import icon_warning from './icons/feathericons/alert-triangle.svg';
import icon_danger from './icons/feathericons/alert-octagon.svg';
import './Toast.css';
const Toast = (props) => {
const {toastList, position} = props;
const [list, setList] = useState(toastList);
const [prevId, setPrevId] = useState(0);
// This useEffect updates the list of toasts to display
useEffect(() => {
console.log('useEffect()');
console.log('useEffect() toastList:');
console.log(toastList);
setList([...toastList]);
}, [toastList]);
const markForDeletion = (toast) => {
if( toast.isDeleting ) {
return;
}
console.log(`toast ${toast.id} marked for deletion`)
toast.isDeleting = true;
setTimeout(() => {attemptDeletion(toast)}, 5000);
}
const attemptDeletion = (toast) => {
console.log(`attempting to delete toast ${toast.id}. canDelete = ${toast.canDelete}`);
if( toast.canDelete ) {
deleteToast(toast.id);
}
else {
console.log(`cannot delete toast ${toast.id}. `);
}
}
const getIcon = (variant) => {
switch( variant ) {
case 'success':
return icon_success;
break;
case 'info':
return icon_info;
break;
case 'warning':
return icon_warning;
break;
case 'danger':
return icon_danger;
break;
}
}
const generateId = (toast) => {
if( typeof(toast.id) === 'number' ) {
return toast.id;
}
toast.id = prevId + 1;
setPrevId(toast.id);
return toast.id;
}
const deleteToast = (id) => {
console.log(`deleting toast ${id}`);
const deletionIdxList = list.findIndex(e => e.id === id);
const deletionIdxToastList = toastList.findIndex(e => e.id === id);
console.log(`deletionIdxToastList: ${deletionIdxToastList}`);
if(deletionIdxList == null || deletionIdxList === -1) {
console.log(`cannot find list idx of id ${id}`);
console.log('list:');
console.log(list);
return;
}
if(deletionIdxToastList == null || deletionIdxToastList === -1) {
console.log(`cannot find toastList idx of id ${id}`);
console.log('toastList:');
console.log(toastList);
return;
}
console.log('list before deletion:');
console.log(list);
console.log('toastList before deletion:');
console.log(toastList);
console.log('list[deletionIdxList]:');
console.log(list[deletionIdxList]);
list.splice(deletionIdxList, 1);
console.log('toastList[deletionIdxToastList]:');
console.log(toastList[deletionIdxToastList]);
toastList.splice(deletionIdxToastList, 1);
setList([...list]);
console.log(`toast ${id} deleted successfully`);
console.log('list after deletion:');
console.log(list);
console.log('toastList after deletion:');
console.log(toastList);
}
return (
<>
<div className={`toast-container ${position}`} >
{
list.map((toast, i) => (
<div
key={i}
className={`toast-popup ${toast.variant} ${toast.isDeleting ? (position + ' fadeout') : (position + '-slide')}`}
onLoad={() => {
if( !toast.isLoaded ) {
toast.Id = generateId(toast);
toast.canDelete = true;
toast.isDeleting = false;
toast.isLoaded = true;
console.log(`on load ${toast.id}`);
setTimeout(() => markForDeletion(toast), 500);
}
}}
onMouseOver={() => {
toast.canDelete === true ? toast.canDelete = false : null;
toast.isDeleting === true ? toast.isDeleting = false : null;
console.log(`mouse over ${toast.id}`);
}}
onMouseLeave={() => {
toast.canDelete === false ? toast.canDelete = true : null;
markForDeletion(toast);
console.log(`mouse leave ${toast.id}`);
}}
>
<div className={'toast-icon'}>
<img src={getIcon(toast.variant)} />
</div>
<div className={'toast-text'}>
<div className={'toast-title'}>
{toast.variant.charAt(0).toUpperCase() + toast.variant.slice(1)}
</div>
<div className={'toast-message'}>{toast.message}</div>
</div>
<button
className={'close-button'}
onClick={() => {
toast.canDelete = true;
deleteToast(toast.id)
}
}>
X
</button>
</div>
))
}
</div>
</>
)
}
Toast.defaultProps = {
position: 'bottom-right'
}
export default Toast;
Snippet of Home.js where I am testing this new Toast component - A class component as I'm working on updating a pre-existing application to remove dependency on the react-toastify library
// Leaving out constructor and other irrelevant code...
toastSuccess() {
const newToast = {
variant: 'success',
message: 'This is a test of the success variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastInfo() {
const newToast = {
variant: 'info',
message: 'This is a test of the info variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastWarning() {
const newToast = {
variant: 'warning',
message: 'This is a test of the warning variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastDanger() {
const newToast = {
variant: 'danger',
message: 'This is a test of the danger variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
render() {
return (
<div className="Home" style={{height:'100%'}}>
<Toast
toastList={this.state.toastList}
position={'bottom-right'}
/>
<div style={{display:'flex', justifyContent:'center'}}>
<Button onClick={() => this.toastSuccess()}>Success</Button>
<Button onClick={() => this.toastInfo()}>Info</Button>
<Button onClick={() => this.toastWarning()}>Warning</Button>
<Button onClick={() => this.toastDanger()}>Danger</Button>
</div>
{// ...}
</div>
);
}
Let me know if there's a way to get this code running here on StackOverflow using that Code Snippet feature, as that would be really helpful so that you readers can see the issue first-hand. Unfortunately I've never had any luck getting it to work, but I'll keep trying for a bit to see if I can figure it out.
EDIT:
Thanks to #Cristian-FlorinCalina for recommending StackBlitz as a good shareable test environment. I've got it set up there now, here's a link:
https://react-ts-ybunlg.stackblitz.io
First problem that I see with your code is that you are keeping two sources of truth for toast list. One is passed from the parent via props, and one is the internal state list in the Toast component. This is an antipattern that can generate a lot of issues.
Second BIG issue is that you are altering the list that you receive from the parent. That is a huge antipattern in React since props are readonly -- All React components must act like pure functions with respect to their props. (since you are altering an object inside an array apparently it works for the load update but it does not work when you are trying to call splice on the list -- this is why even if you deleted the element and applied the deletion effect, when it gets updated on the parent (next render) -> it will come back without it being removed and clicking again on another toast generate button will show you the previously deleted toast as well).
I think the big problem here is that you are not using composition properly. Instead of passing the toast list to the Toast component, you should keep the list on the parent, move the map from the child inside the parent. You will have one instance of Toast component per each element in the list.
Maybe you can have a ToastList component as well, that handles Toast compoonents based on their position... So when you click on Upper Left Toast Generator for example, it will add a new entry inside an array of toasts, with a position key. That array will be sent to the ToastList component, which will generate Toast components that handle their state internally (deletion, etc) and do not update the actual list. You can pass a function to the Toast component called onDelete that will be called by the Toast component on deletion, and you will update the ToastList state based on those events (probably propagate the delete event to the parent to update the list there).
Hope it makes sense.
Following the screenshot below I'm trying to move the cursor through the string which I have no idea how to do.
I'm trying to achieve the effect of an old-phone UI. I'm already managed to make it blink.
I'm using ReactJs and styled-components. Follow the code below:
import console from 'console';
import { useContext, useEffect, useState } from 'react'
import { PhonewordsContext } from '../../PhonewordsContext';
import { Container, Keyboard, Screen, Cursor } from './styles'
export function Phone() {
const { getWords } = useContext(PhonewordsContext);
const [number, setNumber] = useState<string>('');
const [position, setPosition] = useState<number>(0);
useEffect(() => {
getWords(number)
},[number]); // #todo: warning
function onBtnClicked(char: string) {
// in case is not in the end of string add substring in the index
if (position !== number.length){
setNumber(number.slice(0, position) + char + number.slice(position))
} else {
setNumber(`${number}${char}`)
}
setPosition(position +1)
}
function onRemoveChar() {// #todo: how remove words box when empty. re-render?
const rightPosition = position - 1
if (position > 0) {
// concatenate slices of the string before and after the current index
setNumber(number.slice(0, rightPosition) + number.slice(rightPosition + 1))
setPosition(position -1)
}
}
function onUpClicked() {
// position never be negative
if (position > 0)setPosition(position - 1)
}
function onDownClicked() {
// check for max position
if (position < number.length) setPosition(position + 1)
}
return (
<Container>
<Screen>
{/* MOVE CURSOR */}
<span>
{number.split('').map(i =>
alert(`here ${i}`)
)}
</span>
<Cursor />
{number}
</Screen>
{position}
<Keyboard>
<button onClick={() => onUpClicked()}>⬆</button>
<button onClick={() => onDownClicked()}>⬇</button>
<button onClick={() => onRemoveChar()}>⌫</button>
<button disabled>1</button>
<button onClick={() => onBtnClicked('2')}>2 abc</button>
<button onClick={() => onBtnClicked('3')}>3 def</button>
<button onClick={() => onBtnClicked('4')}>4 ghi</button>
<button onClick={() => onBtnClicked('5')}>5 jkl</button>
<button onClick={() => onBtnClicked('6')}>6 mno</button>
<button onClick={() => onBtnClicked('7')}>7 pqrs</button>
<button onClick={() => onBtnClicked('8')}>8 tuv</button>
<button onClick={() => onBtnClicked('9')}>9 wxyz</button>
<button disabled>*</button>
<button disabled>0 ⌴</button>
<button disabled>#</button>
</Keyboard>
</Container>
)
}
and the css file using styled-components:
import styled from "styled-components"
export const Container = styled.div`
display: flex;
align-items: center;
flex-direction: column;
width: 100%;
`
export const Screen = styled.div`
padding: 1rem 2rem;
border: 0;
border-radius: 0.25rem;
background: var(--white);
width: 15rem;
height: 8rem;
`
export const Keyboard = styled.div`
display: grid;
padding: 2rem 0;
grid-template-columns: repeat(3, 64px);
grid-template-rows: 32px repeat(4, 64px);
gap: 8px;
button {
border-radius: 0.25rem;
border: 0;
box-shadow: #777 2px 1px 10px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
transition: 0.4s;
&:active {
box-shadow: 2px 2px 2px #777;
transform: translateY(3px);
}
}
`
export const Cursor = styled.span`
animation: blink 1.5s linear infinite alternate;
border-color: #333;
border-left: 1px solid;
margin-left: -1px;
#keyframes blink {
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
`
Thanks for any help!
You should substring number based Cursor position.
if (position == 0)
<Cursor /> {number}
else if(position > 0)
{number.substring(0, position)} <Cursor /> {number.substring(position + 1, number.length)}
like this.
<Screen>
{/* MOVE CURSOR */
<span>{number.slice(0, position)} <Cursor /> {number.slice(position)}</span>
</Screen>
Managed to implement it using the solution above!
i started a simple project with react.in my project i have a paragraph and when mouse hover on paragraph (mouse enter event) a square appears under the paragraph and when hover out from paragraph(mouse leave event) that square disapear.but this occure so fast so i want changing this smoothly and i want use opacity and change that from 0 to 1 and reverse when my events occure.but I do not know what to do to change the opacity with animation in react.
this is my appjs
import './index.css';
import React, {useState} from "react";
function App() {
const [isShowSquare, setIsShowSquare] = useState(false);
const showSquare = () => {
setIsShowSquare(true);
}
const hideSquare = () => {
setIsShowSquare(false);
}
return (
<div>
<p onMouseEnter={showSquare} onMouseLeave={hideSquare} style={{display:'inline-block'}}>Hover Me</p>
{isShowSquare ?
<div className='square'>
</div>
: null
}
</div>
);
}
export default App;
and this is my index.css
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.square{
width: 50px;
height: 50px;
background-color: #61dafb;
}
I would be very grateful if anyone could help me
Here is a method without using useState though.
I don't know if this part is important, but have a look at my
sandbox
First of all you need a css class to define the opacity the method and how much time it will take. Also, your first square class should have opacity: 0, meaning non visible.
When mouse is over text, then you add the extra class to the element.
const showSquare = () => {
div.current.classList.add("square-full");
};
const hideSquare = () => {
div.current.classList.remove("square-full");
};
.square.square-full {
opacity: 0.5;
transition: opacity 1s ease-out;
}
.square {
width: 50px;
height: 50px;
background-color: #61dafb;
opacity: 0;
}
Updated answer: No need for ref
Just use the following code
export default function App() {
const [ isShown, setShown ] = useState(false)
return (
<div>
<p
onMouseEnter={() => setShown(true)}
onMouseLeave={() => setShown(false)}
style={{ display: "inline-block" }}
class="paragraph"
>
Hover Me
</p>
<div className={`square ${isShown ? 'square-full' : ''}`}></div>
</div>
);
}
along with the extra class i mentioned before
Appearing is easy, and for disappearing I found a solution like this;
import { useState } from "react";
import "./styles.css";
export default function App() {
const [visible, setVisible] = useState(false)
const [disappear, setDisappear] = useState(false)
return (
<div className="App">
<p onMouseLeave={()=> {
setDisappear(true)
setTimeout(()=>{setVisible(false)
setDisappear(false)}
, 1000)
}} onMouseEnter={()=> setVisible(true)}>Hide/Show square </p>
{visible && <div className="square"
style={{
width: 100,
height: 100,
animation: disappear ? "disappear 1s ease" : "appear 1s ease"
}}> </div> }
</div>
);
}
For the animations in css ;
//style.css
.square {
background-color: red;
animation: appear 1s ease;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes disappear {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
So what happens here is that we have a keyframe on our css for the opacity initially. So this works fine, tricky part is disappearing. When we set the visible state to false React immideately removes our element, so we have setTimeOut to stop React for 1 second. In that 1 second we apply our animations, it runs smoothly on me. Go ahead and try.
I have made a modal in a component, the data works fine, it's dynamic which is perfect. Problem is, when I open it, it seems that whenever I click anywhere inside the modal, it closes it again.
The modal is managed using useState hook. I think the problem lies in my onClick calls further down. Any advise please?
const LeaveRequestsUnits = () => {
let [data, setData] = useState([]);
let [modalState, setModalState] = useState(false);
let modalOnOff = () => {
setModalState(!modalState);
};
let [selectedUnit, setSelectedUnit] = useState('');
let updateSelectedUnit = (item) => {
setSelectedUnit(item);
const getLeaveUnits = data.map((item) => {
// fct to update the modalState and display-block the modal
const openModal = (item) => {
updateSelectedUnit(item);
modalOnOff();
$('.modalBackground').css('display', 'block');
};
const modal = () => {
return (
<div>
<p>{selectedUnit.note}</p>
<p>{selectedUnit.start}</p>
<p>{selectedUnit.end}</p>
Google
<h1>Close</h1>
</div>
);
};
// display:none the modal if the modalState is false
if (!modalState) {
$('.modalBackground').css('display', 'none');
}
if (item.end >= today && item.approved !== false) {
return (
<div
className={unitColour}
key={item.reqID}
onClick={() => openModal(item)}
>
<div className='unitLeft'>
<img src={statusIcon} alt='Status Icon' id='statusIcon' />
</div>
<div className='unitMiddle'>
<p id='unitLeaveType'>{leaveTypeName}</p>
<p id='unitDate'>{startEndDate(item.start, item.end)}</p>
</div>
<div className='unitDivider'></div>
<div className='unitRight'>
<p id='unitDuration'>
{convertTimestamp(item.duration, item.type)}
</p>
</div>
{/* modal */}
<div className={`modalBackground modalShowing-${modalState}`}>
{modal()}
</div>
{/* end modal */}
</div>
);
}
});
return <div className='requestsContainer
CSS below:
.modalBackground {
display: none;
z-index: 10000;
width: 80vw;
height: 250px;
background-color: white;
border-radius: 15px;
position: absolute;
top: 10vh;
overflow: hidden;
color: black;
opacity: 0;
pointer-events: none;
cursor: auto;
}
.modalShowing-true {
/* display: none;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: black;
opacity: 0.3; */
opacity: 1;
pointer-events: auto;
}
When you define the modal component, you need to tell it to prevent clicks on it from bubbling up to the parent elements click listener that will try to close the modal.
const cancelClick = useEffect( (event) => {
event && event.stopPropagation();
}, []) // only need to create this function once
const modal = () => {
return (
<div onClick={cancelClick}>
<p>{selectedUnit.note}</p>
<p>{selectedUnit.start}</p>
<p>{selectedUnit.end}</p>
Google
<h1>Close</h1>
</div>
);
};
I would HIGHLY recommend you stop using jquery here as well. In this component its a super easy change, just remove the jquery calls to change the display css property on the backdrop and instead use the state variable to control showing that.
<div className={`modalBackground${!!modalState ? ' open' : ''}`}>
{modal()}
</div>
and then you can clean up the css for it. I dropped the display: none; style and went with transform: scale(0);. This gives more flexibility in how you decide how you want to show the modal (fade in would do nicely).
.modalBackground {
position: absolute;
top: 10vh;
z-index: 10000;
overflow: hidden;
width: 80vw;
height: 250px;
opacity: 0;
transform: scale(0);
background-color: white;
color: black;
border-radius: 15px;
cursor: auto;
/* here you can add a transition on both opacity and scale to make the modal animate in. */
}
.modalBackground.open {
opacity: 1;
transform: scale(1);
}