React body scroll lock issue on IOS - reactjs

I'm literally fighting in finding a clean solution to the scroll issue in the IOS devices. In my App.js i've simply the background body and a modal with some contents. When the modal is shown i'd like to block the scroll in the background (myBodyContent) and still let the scroll in the modal component. I'm quite new to both javascript and React and this not helping me at all.
The cleanest solution (according to me) i was able to find is the body-scroll-lock package but it seems i'm not able to successfully use it. here is my code:
App.js
class App extends Component {
targetRef = React.createRef();
targetElement = null;
constructor(props) {
super(props);
}
componentDidMount() {
this.targetElement = this.targetRef.current;
disableBodyScroll(this.targetElement);
}
render() {
const myModal = (
<Modal ref={this.targetRef}>
// my long content here
</Modal>);
return (
<React.Fragment>
{myModal}
<Layout>
<myBodyContent>
</Layout>
</React.Fragment>
);
}
}
Modal.js
class Modal extends Component {
shouldComponentUpdate(nextProps, nextState){
return (nextProps.show !== this.props.show)
}
render () {
return (
<div>
<Auxi>
<Backdrop
show = {this.props.show}
clicked = {this.props.modalClosed}
/>
<div className={style.Modal}
style={{
transform: this.props.show ? 'translateY(0)' : 'translateY(-100vh)', // vh is special unit for outside screen
opacity: this.props.show ? '1': '0'
}}>
{this.props.children}
</div>
</Auxi>
</div>
);
}
}
Modal css
.Modal {
position: fixed;
z-index: 500;
background-color: white;
width: 80%;
overflow-y: hidden;
overflow: auto;
padding-right: 15px; /* Avoid width reflow */
border: 1px solid #ccc;
box-shadow: 1px 1px 1px black;
padding: 16px;
top: 5%;
left: 5%;
box-sizing: content-box;
transition: all 0.3s ease-out;
}
#media (min-width: 600px) {
.Modal {
width: 80%;
height: 80%;
left: 10%;
top: 10%
}
}
With the above code, simply everything is locked and i cannot scroll neither the modal nor the myBodyContent.
Can you help me understanding what i'm doing wrong? Or suggest me some other ways to achieve the same result?
Thanks in advance for your help.

You don't have targetElement (it's null) inside App componentDidMount because you try to set ref for React component but not HTML element.
To fix this you need to forward ref inside Modal component like that:
const myModal = (
<Modal forwardedRef={this.targetRef}>
// my long content here
</Modal>
);
and then :
class Modal extends Component {
shouldComponentUpdate(nextProps, nextState){
return (nextProps.show !== this.props.show)
}
render () {
return (
<div ref={this.props.forwardedRef}>
<Auxi>
<Backdrop
show = {this.props.show}
clicked = {this.props.modalClosed}
/>
<div className={style.Modal}
style={{
transform: this.props.show ? 'translateY(0)' : 'translateY(-100vh)', // vh is special unit for outside screen
opacity: this.props.show ? '1': '0'
}}>
{this.props.children}
</div>
</Auxi>
</div>
);
}
}

Thanks Max, i've tried but unfortunately the result is the same. I've also tried to enclose the Modal in a div directly in the App.js and apply the ref directly there without passing it as props...but it's the same. No way to scroll anything.

Related

How to loop a Draggable slider in React

I have a draggable horizontal slider in my current project and I would like to setting up it also to loop continuously. By loop continuously I mean it should respond to the process of showing images one after another when dragging? Right now, I do have only 3 images in my slider and when I drag slider to the left, slider with its 3rd image and a blank white space starts showing just after. Here at this point I want images to get start again continuously from the very beginning i.e. from the 1st image with aim to cover the white blank space.
Apart, one error I'm getting with my existing code is that when I start to drag slider to right side, suddenly a scroll comes up on browser and keep going in never ending state. By never ending state, I mean it still remain on screen when I drag all my 3 images fully in right direction.
So these are the two things I want to apply and want to resolve in my current project. I'm sharing my code below.
src > Routes > Home > Components > Carousel > Components > SliderDataItems > index.js
import React, { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { ZoomInOutlined } from '#ant-design/icons'
import { Images } from '../../../../../../Shared/Assets';
import ImagesIcon from '../../../../../../Components/Cells/ImagesIcon'
gsap.registerPlugin(Draggable);
const pictures = [
{
img: Images.xgallery1,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery2,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery4,
icon: <ZoomInOutlined />
},
];
const Slide = ({ img, icon }) => {
return (
<div className="slide">
<div className="image">
<ImagesIcon src={img} />
<div className="icon">
{icon}
</div>
</div>
</div>
);
};
export const Slider = () => {
const sliderRef = useRef(null);
useEffect(() => {
Draggable.create(sliderRef.current, {
type: "x"
});
}, []);
return (
<div className="slider" ref={sliderRef}>
{pictures.map((item, index) => {
return (
<Slide key={index} img={item.img} icon={item.icon} />
);
})}
</div>
);
};
export default Slider;
src > Routes > Home > Components > Carousel > style.scss
.slider {
display: flex;
cursor: unset !important;
overflow: hidden !important;
.slide {
.image {
position: relative;
img {
width: 100% !important;
height: auto !important;
object-fit: cover;
}
.icon {
transition: 0.5s ease;
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
text-align: center;
span {
svg {
font-size: 30px;
color: #fff;
}
}
}
}
}
.image:hover .icon {
opacity: 1;
}
}
.image:after {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(211, 208, 208, 0.6);
opacity: 0;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.image:hover:after {
opacity: 1;
}
Here's the link of demo (kindly see just above the footer section) for your reference.
Thank you for any help.
For draggle Slider there is a very lightweight JS Carousel package - siema
It is a great, lightweight carousel that is made with JS. There are also other packages built on top of this purely made for React.
In your case, I would offer to try out react-siema.
With it, you can simply use the carousel like that and it will be draggable by default. Plus, no need to load any css.

Conditional rendering + React

i tried making a method that holds the previous state, and changes back to the the previous state on click. Yet I checked if the method was working, and it was properly changing between true and false.
Yet, when I do a ternary operator in the className, it stays as the true value, and does not let me toggle between two classes. The first being the regular border, and the second having the position absolute with the checkmark to indicate it was selected. Even when I check dev tools, 'checkmark' is the className but nothing changes onClick...
import React from 'react';
import './Questions.css'
import girl_sweat from './girl_sweat.jpg'
class Questions extends React.Component {
constructor(props){
super(props);
this.state = {
isChoiceClicked: false
}
this.handleChoice = this.handleChoice.bind(this);
}
handleChoice(){
this.setState(prevState => ({isChoiceClicked: !prevState.isChoiceClicked}));
}
render(){
const isChoiceClicked = this.state;
return <div className="banner_column">
<div className="banner_column_1"><img src={girl_sweat}/></div>
<div className="banner_column_2"><div className="survey_enter"><h2 className="title">What are you interested in?</h2>
<p className="description">Select up to <strong>3 areas</strong></p>
<div className="choices">
<div className={`choice_container ${isChoiceClicked ? 'checkmark': 'null'}`} onChange={this.handleChoice}><h5>Yoga</h5><p className="activities">Vinyasa, Ashtanga, Kundalini, Hatha, Restorative, Prenatal</p></div>
<div className={`choice_container ${isChoiceClicked ? 'checkmark': 'choice_container'}`} onChange={this.handleChoice}><h5>Fitness</h5><p className="activities">Strength, Barre, Pilates, HIIT, Core, Stretching</p></div>
<div className={`choice_container ${isChoiceClicked ? 'checkmark': 'choice_container'}`} onChange={this.handleChoice}><h5>Mindfullness</h5><p className="activities">Meditation, Breathwork, Sound Bath, Yoga Nidra</p></div>
<div className={`choice_container ${isChoiceClicked ? 'checkmark': 'choice_container'}`} onChange={this.handleChoice}><h5>Skills</h5><p className="activities">Handstands, Arm Balances, Flexibility, Mobility</p></div>
</div>
<div className="next"><button className="next_question">next question</button></div>
</div>
</div>
</div>
}
}
export default Questions; ```
.choice_container {
margin: 0 auto;
width: 250px;
padding: 1rem;
border: solid 0.5px black;
position: relative;
}
.choice_container .checkmark {
display: hidden;
position: absolute;
border: solid 2px black;
right: -8px;
top: -8px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #000;
color: #fff;
content: "✓";
}```
Even when I check dev tools, 'checkmark' is the className but nothing changes onClick
The function you activate should be onClick rather than onChange.
Usually onChange can be used in <input> and <select>. However, if you are using div, using onChange seems to be a problem.
<div className={`choice_container ${isChoiceClicked ? 'checkmark': 'null'}`} onClick={this.handleChoice}><h5>Yoga</h5><p className="activities">Vinyasa, Ashtanga, Kundalini, Hatha, Restorative, Prenatal</p></div>
Besides, I guess you wanna destructure from this.state. Therefore, you can do the following thing.
const isChoiceClicked = this.state;
const { isChoiceClicked } = this.state;

Transitions with React - should I use TransitionGroup?

I want page contents in my application to transition smoothly. I have been attempting to do this using react-transition-group but I have struggled to achieve the correct implementation. The following link was informative:
https://coursework.vschool.io/react-transitions-with-react-transition-group/
It shows how to make modularize and use TransitionGroup (although not both at the same time, unfortunately).
I created a demo project (based on the above link) to troubleshoot this issue. I have two items in an array ‘contactComponents’. All I am trying to do at the moment is make this information appear and disappear using the show/hide button.
Here is the main body of the code:
const contactDetails = ['Gryffindor Tower, Hogwarts','Gryffindor Tower, Hogwarts'];
const contacts = ['Harry', 'Ron'];
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0,
showMyContact: false
};
this.showContact = this.showContact.bind(this);
}
showContact() {
this.setState({showMyContact: !this.state.showMyContact})
}
render() {
const styles = {
container: { display: 'flex', justifyContent: 'center', width: '100vw', height: 100, flexDirection: 'column', padding: 100 },
btn: { width: '100%', display: 'flex', justifyContent: 'center'},
h1: { border: '2px solid blue', padding: 5, display: 'flex'}
};
let contactComponents = [contacts[this.state.count], contactDetails[this.state.count]];
console.log(this.state.showMyContact)
return (
<div>
<div style={ styles.container }>
<TransitionGroup component={null}>
{ contactComponents.map((item, key) =>
<CSSTransition
in={this.state.showMyContact}
key={key}
timeout={800}
classNames={"fade"}>
<h1 style={styles.h1}>
{
item
}
</h1>
</CSSTransition>
)}
</TransitionGroup>
<div style={ styles.btn }>
<button onClick={ this.showContact }>show/hide</button>
</div>
</div>
</div>
)
}
}
scss file:
.fade-appear,
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-appear-active,
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 600ms linear 200ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit.fade-exit-active {
opacity: 0;
transition: opacity 200ms linear;
}
Currently, the contents appears even though showMyContact is false when the render function first calls. Changing the state of showMyContact with the show/hide button has no effect. The content does not fade in and out as expected.
This post:
page transitions without React-Router
suggests it might be better to use pure css to carry out transitions rather than react-transition-group. Am I just barking up the wrong tree?
I found out that using pure css transitions provides the desired solution. I do not know if a solution using TransitionGroup and CSSTransition is feasible but it doesn't look like it.
By changing the contents of the render function to:
render() {
let contactComponents = [contacts[this.state.count], contactDetails[this.state.count]];
let cssList = [
"List",
this.state.showMyContact ? "ListShow" : "ListHide"
];
console.log(this.state.showMyContact);
return (
<div>
<div className={"container"}>
<List show={cssList.join(' ')} myContent={contactComponents}/>
<div className={"btn"}>
<button onClick={ this.showContact }>show/hide</button>
</div>
</div>
</div>
)
}
...and adding the following const:
const List = (props) => {
return (
<div className={props.show}>
<h1 className={"h1"}> { props.myContent[0] } </h1>
<h1 className={"h1"}> { props.myContent[1] } </h1>
</div>
)};
...and importing the following css file:
.container {
display: flex;
justify-content: center;
width: 500px;
height: 100px;
flex-direction: column;
padding: 100px;
}
.h1 {
border: 2px solid blue;
padding: 5px;
display: flex;
}
.btn {
width: 100%;
display: flex;
justify-content: center;
}
.List {
display: flex;
flex-direction: column;
transition: all 0.4s ease-out;
}
.ListShow {
opacity: 1;
}
.ListHide {
opacity: 0;
}
...I can get the desired behaviour.

How to show Vertical Progress Bar in react-transition-group with animation

Here is a jQuery example for a progress bar animation. and I want this feature in Reactjs without jQuery. How to implement this feature.
I hope you are still interested in this question. I just tinker with react-spring and I really love it. The best animation library in React IMHO.
You can create a neat component with the Spring component. It will always animate to the to property of the Spring component. First time from the from value of the from property.
import React from "react";
import { Spring } from "react-spring";
const VerticalProgress = ({ progress }) => {
return (
<Spring from={{ percent: 0 }} to={{ percent: progress }}>
{({ percent }) => (
<div className="progress vertical">
<div style={{ height: `${percent}%` }} className="progress-bar">
<span className="sr-only">{`${progress}%`}</span>
</div>
</div>
)}
</Spring>
);
};
export default VerticalProgress;
Here is the complete code: https://codesandbox.io/s/mqo1r9wo4j
Horizontal Example
Here is how to do it.
make 2 divs(container, progressing one)
you can change the height of progressing div based on state change.
const styled = styled.default;
const Bar = styled.div`
position: relative;
height: 500px;
width: 100%;
border-radius: 3px;
border: 1px solid #ccc;
margin: 1rem auto;
`
const Fill = styled.div`
background: #0095da;
width: 100%;
border-radius: inherit;
transition: height 0.2s ease-in;
height: ${(props) => `${props.percentual}%`};
`
const ProgressBar = ({ percentage }) => {
return (
<div>
<Bar>
<Fill percentage={percentage} />
</Bar>
</div>
);
}
ReactDOM.render(<ProgressBar percentage={you state for progress percentage} />, document.getElementById('bar'));
you don't even need react for that tho.
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_progressbar_label_js

simple css animation not working on dynamic reactjs element

Check the snippet at codepen http://codepen.io/anon/pen/EZJjNO
Click on Add button, it will add another item but its appearing immediately without any fade effect.
JS:
class App extends React.Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
items: [1,2,3]
}
}
addItem() {
var items = this.state.items;
items.push(4);
this.setState({
items: items
})
}
render() {
return (
<div className="App">
{
this.state.items.map(function(i) {
return <div className="item fade">Testing</div>
})
}
<button onClick={this.addItem}>Add</button>
</div>
);
}
}
React.render(<App />, document.body);
CSS:
.App {
background-color: rgba(0,0,0, 0.5);
text-align: center;
height: 100vh;
}
div.item {
border: 1px solid #ccc;
padding: 10px;
background: #123456;
color: #fff;
opacity: 0;
transition: all 0.4s ease-out;
}
.fade {
opacity: 1 !important;
}
Since the fade class is added by default, you don't get the transition effect. If you open your browser's developer tools and remove the class, you'll see it fade away nicely.
There's a few ways to get what you want, but I'd just use a keyframe CSS animation like so:
.fade {
animation: 0.4s ease-out fadeIn 1;
}
#keyframes fadeIn {
0% {
opacity: 0;
visibility: hidden;
}
100% {
opacity: 1;
visibility: visible;
}
}
Here's a fork of your code pen showing how it works :)

Resources