Using React-Spring useSprings only on active button in array - javascript

I am trying to animate a position change of the active box. When the user clicks on the box, I would like the box to animate a margin-top change and the rest of the boxes to stay at 0px. When the user changes the active box, the previously active box will return to 0px and the new active box will acquire a margin-top of 50px.
I think I am nearly there but I haven't quite managed to get it to work yet. I would be really grateful if someone could help enlighten me as to what I am doing wrong.
import { useSpring, useSprings, animated } from "react-spring";
import styled from "styled-components";
const Nav = () => {
// Set Active Box
const [activeBox, setActiveBox] = useState(0);
const handleClick = (index) => {
setActiveBox(index);
};
// Boxes
const boxes = [box1, box2, box3, box4];
// useSprings
const springs = useSprings(
boxes.length,
boxes.map((box, index) => ({
marginTop: activeBox === index ? 50 : 0,
}))
);
return (
<NavContainer>
{springs.map((prop, index) => (
<StyledBox
style={prop}
key={index}
onClick={() => handleClick(index)}
currentActiveBox={activeBox === index}
>
<img src={boxes[index]} alt="" />
</StyledBox>
))}
</NavContainer>
);
};
export default Nav;
const NavContainer = styled.div`
position: absolute;
bottom: 0;
width: 200px;
height: 75px;
`;
const StyledBox = styled.div`
width: 25px;
height: 25px;
img {
width: 100%;
height: 100%;
}
`;
Thank you!

The issue was that animated was not added to the styled component!
const StyledBox = styled(animated.div)`
width: 25px;
height: 25px;
img {
width: 100%;
height: 100%;
}

Related

Creating a smooth fixed / sticky slideshow layout with react-intersection-observer

I am trying to build a react component with a large fixed div and a navbar below. Initially, the div should remain fixed at the top left and scroll through a number of slides as the user scrolls, before its display property reverts to absolute and it scrolls out of view. Meanwhile, the nav's position becomes sticky and scrolls to the top of the window where it remains.
I've managed to build something hacky using react-intersection-observer and a number of hidden 'ghost' elements which allow the slides to cycle between opacity 0 or 1, but the outcome is not great and I feel there must be an easier way to achieve this effect. In particular it is problematic having to account for the page position when the main component becomes absolute positioned and requires a 'top' value.
Any advice is appreciated thanks. Sample code is below:
import * as React from 'react';
import { useInView } from 'react-intersection-observer';
import styled from 'styled-components';
const OuterContainer = styled.div`
height: 500vh;
`;
const Card = styled.div`
box-sizing: border-box;
position: fixed;
top: 0;
width: 100%;
height:64vh;
transition: 1s;
z-index: 5;
&.hidden {
opacity: 0;
}
&.show {
opacity: 1;
}
&.border {
position: absolute;
top: 0;
}
`;
const AltCard = styled(Card)`
position: static;
`;
const CardContainer = styled.div`
height: 64vh;
`;
const FirstCard = styled(Card)`
opacity: 1;
`;
const CardGhost = styled.div`
height:64vh;
`;
const LastGhost = styled.div`
box-sizing: border-box;
`;
const GhostContainer = styled.div`
width: 100%;
position: absolute;
top: 0;
`;
const MainContainer = styled.div`
position: fixed;
width: 100%;
top: 0;
&.moving {
position: absolute;
top: 156.5vh;
}
`;
const HeaderBar = styled.div`
top: 64vh;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 8rem;
background-color: #44ff44;
`;
export default function IntroSection() {
const options = { threshold: 0.66 };
const [ref0, visible0] = useInView({ threshold: 0 });
const [ref1, visible1] = useInView(options);
const [ref2, visible2] = useInView(options);
const [ref3, visible3] = useInView(options);
const [ref4, visible4] = useInView({ threshold: 0.33 });
return (
<>
<OuterContainer>
<GhostContainer>
<CardGhost ref={ref0} />
<CardGhost ref={ref1} />
<CardGhost ref={ref2} />
<CardGhost ref={ref3} />
<LastGhost ref={ref4} />
</GhostContainer>
<CardContainer>
<FirstCard
className={visible0 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightBlue' }}
/>
<Card
className={visible1 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightGreen' }}
/>
<Card
className={visible2 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightYellow' }}
/>
</CardContainer>
</OuterContainer>
<MainContainer
className={visible4 && 'moving'}
>
<AltCard
className={visible3 ? 'show' : 'hidden'}
style={{ backgroundColor: '#ffBBFF' }}
/>
<HeaderBar><h1>THIS IS THE NAVBAR</h1></HeaderBar>
</MainContainer>
</>
);
}

Div does not follow Mouse due to parent div

I have a problem where the mouse does not follow the div closely due to parent div left and top. Assume parent.jsx is developed by another developer, and I solely can control child.jsx, is there a way for me to make the div follow the mouse?
Here's the parent jsx
import "./styles.css";
import Child from "./child";
export default function Parent() {
return (
<div
style={{
top: "25px",
left: "20px",
position: "relative"
}}
>
<Child />
</div>
);
}
The child jsx
import "./styles.css";
import { useRef } from "react";
export default function Child() {
const descBox = useRef(null);
const handleMove = (e) => {
console.log(e.clientX, e.clientY);
descBox.current.style.left = `${e.clientX}px`;
descBox.current.style.top = `${e.clientY}px`;
};
return (
<>
<div
onMouseMove={handleMove}
style={{
height: "300px",
width: "100%",
backgroundColor: "red"
}}
></div>
<div className="dropdown1" ref={descBox}>
123asdfsfdafffasdfasfdsajkj
</div>
</>
);
}
The css for child
.dropdown1 {
position: absolute;
left: 150px;
top: 10px;
background: #2b4557;
padding: 4px 8px;
font-size: 12px;
line-height: 18px;
color: #fff;
}
Codesandbox to run the code.
As the child is positioned absolute within the relative positioned parent you can work out its left and top positions relative to the parent and use those.
This code adds a small offset to give a slight gap between the cursor and the child (which you may or may not want).
const handleMove = (e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left + 10;
const y = e.clientY - rect.top + 10;
descBox.current.style.left = `${x}px`;
descBox.current.style.top = `${y}px`;
};

unable to make the animation(fade) work on a modal when clicking the next and previous button from inside the modal in a react app

I am using a ReactJS application which is mapping over an array of objects displayed in a div. When I click each div then a modal appears which shows the corresponding objects data on modal.
It has a previous and next button on the modal. When you click on the next button then the modal displays the second objects data and further clicking it displays the third objects data.
Same goes for the previous button but in a reverse direction.
I am using a react-reveal library for showing Fade animation on texts inside a modal. When I click individual DIV then the modal appears with corresponding object data and animation works on that.
But when I click Previous and Next button from INSIDE the modal then corresponding data of next object do appear but the animation doesnot work on the texts.
How can I make the animation work upon clicking Prev and Next button from inside the MODAL.
The working code is::
App.js
import React, {useState} from 'react';
import Fade from 'react-reveal/Fade';
import 'react-responsive-modal/styles.css';
import { Modal } from 'react-responsive-modal';
import "./App.css";
const App = () => {
const [open, setOpen] = useState(false);
const [index, setIndex] = useState(0);
const [profile, setProfile] = useState({});
const [profiles, setProfiles] = useState([
{title: "First title", description: "It is first description"},
{title: "Second title", description: "It is second description"},
{title: "third title", description: "It is third description"},
]);
const onOpenModal = (item) => {
setOpen(true);
setProfile(item);
}
const onCloseModal = () => setOpen(false);
const handlePrev = () => {
if(index > 0){
let i = index - 1;
setProfile(profiles[i]);
setIndex(i);
}
}
const handleNext = () => {
if(index < profiles.length - 1){
let i = index + 1;
setProfile(profiles[i]);
setIndex(i);
}
}
return (
<div>
<h1>Application..............</h1>
<div className="container">
{
profiles.map((p, i) => (
<div key={i} className="item" onClick={() => onOpenModal(p)}>
<h1>{p.title}</h1>
</div>
))
}
</div>
<Modal open={open} onClose={onCloseModal} center
classNames={{
overlay: 'customOverlay',
modal: 'customModal',
}}>
<h2>Simple centered modal</h2>
<Fade bottom delay={300}>
<h5>{profile.title}</h5>
</Fade>
<Fade bottom delay={800}>
<p>{profile.description}</p>
</Fade>
<div className="btn-group">
<button className="btn" onClick={handlePrev}>Prev</button>
<button className="btn" onClick={handleNext}>Next</button>
</div>
</Modal>
</div>
)
}
export default App;
App.css
.container{
display: flex;
justify-content: space-around;
margin-top: 8rem;
}
.item{
border: 2px solid;
padding: 4rem;
}
.customOverlay{
background: rgba(36, 123, 160, 0.7);
}
.customModal{
background: #b2dbbf;
max-width: 700px;
width: 100%;
font-size: 1.8rem;
}
.btn{
padding: 0.5rem 1.3rem;
margin-right: 20px;
margin-left: 8px;
cursor: pointer;
}
.btn-group{
width: 34%;
display: flex;
justify-content: flex-end;
margin-left: auto;
}
The sandbox working version is here:
https://codesandbox.io/s/long-sun-9u00h
you have to force the animation by adding key to Fade elements.
see the codebox

State working only in console and not changed color in the page

I am trying to change background color of Navbar to white on scrolling, but my code changes the state only in the console.
How to solve it?
Here is React and CSS code
import React, { useState, useEffect } from 'react';
import '../index';
export default function Header() {
const [headerColor, setHeaderColor] = useState('.header container');
const listenScrollEvent = () => {
window.scrollY > 250 ? setHeaderColor('#29323c') : setHeaderColor('transparent');
};
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
window.addEventListener('scroll', listenScrollEvent);
});
console.log(headerColor);
return (
<section id="header" style={{ color: headerColor }}>
<div className="header container" style={{ color: headerColor }}>
/* Header section */
#header {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100vw;
height: auto;
}
#header .header {
min-height: 8vh;
background-color: rgba(31, 30, 30, 0.24);
transition: 0.3s ease background-color;
}
#header .nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
max-width: 1300px;
padding: 0 10px;
}
I would rephrase your question to: "How does state work in React?"
You seem to be using a component named "Header".
import React, { useState, useEffect } from 'react';
import '../index';
export default function Header() {
const headerColor = useState({ headerColor : 'transparant' });
const listenScrollEvent = () => {
window.scrollY > 250 ? setHeaderColor('#29323c') : setHeaderColor('transparent');
};
//well lets create a func for setHeaderColor
setHeaderColor(color){
this.setState({
headerColor: color
});
}
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
window.addEventListener('scroll', listenScrollEvent);
});
console.log(headerColor);
return (
<section id="header" style={{ background-color: headerColor }}>
<div className="header container" style={{ background-color: headerColor }}>
"Hooks are a new feature in React v16.7.0-alpha useState is the “Hook”. useState() set the default value of the any variable and manage in function component(PureComponent functions). ex : const [count, setCount] = useState(0); set the default value of count 0. and u can use setCount to increment or decrement the value. onClick={() => setCount(count + 1)} increment the count value.DOC
"
Base answer on:
https://stackoverflow.com/a/53166036/4095631
Please also look at:
"Updating an object with setState in React"
Updating an object with setState in React
and the official documents about state and hooks:
https://reactjs.org/docs/hooks-overview.html

How do I stop my modal from closing when I click anywhere inside it

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);
}

Categories

Resources