How to loop a Draggable slider in React - reactjs

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.

Related

How to stop propagation of scroll event in case of a modal view?

I have this modal React component:
import React, { useEffect, useRef } from 'react'
import useOnClickOutside from '../../hooks/useOnClickOutside'
import Button from '../codrop/Button'
import styles from './Modal.module.scss'
var Scroll = require('react-scroll')
var scroll = Scroll.animateScroll
interface Props {
children?: React.ReactNode
title?: string
completion?: Function
[key: string]: any
isMyFlex?: Boolean
}
const Modal = ({ children, title, completion, isMyFlex, ...rest }: Props) => {
return (
<div className="overlay">
<div className="cnt-box cnt-call2 modal">
<div className="caption" style={{ paddingRight: 'unset' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<h2 style={{ fontSize: '30px', fontWeight: '500' }}>{title}</h2>
<span
onClick={(e) => {
completion?.()
}}
style={{
fontFamily: 'Icons',
fontSize: '2rem',
cursor: 'pointer',
}}
>
c
</span>
</div>
{children}
</div>
</div>
</div>
)
}
export default Modal
And following two style:
.overlay {
position: fixed; /* Sit on top of the page content */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); /* Black background with opacity */
z-index: 2000; /* Specify a stack order in case you're using a different order for other elements */
overflow: hidden;
}
.modal {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 470px;
background-color: #fff;
border-radius: 6px;
padding: 30px 50px;
}
When modal / overlay is present, and I scroll in mobil, I see on the bottom, as some pixlel is visilbe from background, that the background i scrolling, though not the foreground.
Is it a way to prevent this scrolling?
I tried to add overflow: hidden; to the overlay style, did not help.
I tried hide overflow when component appears, but has no effect, why?
const Modal = ({ children, title, completion, isMyFlex, ...rest }: Props) => {
useEffect(() => {
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = 'unset'
}
}, [])
By default, trying to scroll an element that has already reached the bottom will scroll the parent. You can use the overscroll-behavior CSS property on the modal to prevent this behavior:
overscroll-behavior: contain;
See https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior for the documentation.

Move sibling elements to the right of target element right and elements left of target element left using styled components and React js

I have a container where I am mapping in multiple item components that contains an image. When I hover on an image, the image scales up 1.5 and I want the items on the right to all move right and the items on the left to all move left of the target component. I was using some code I found written in sass and I want to migrate it to Styled components but I can't get it to work. This is the original sass:
.slider {
display: flex;
position: relative;
&__container {
display: flex;
padding: 0 55px;
transition: transform 300ms ease 100ms;
z-index: 3;
width: 100%;
}
&:not(&--open) .item:hover .show-details-button {
opacity: 1;
}
&:not(&--open) .item:hover {
transform: scale(1.5) !important;
}
&:not(&--open):hover .item {
transform: translateX(-25%);
}
&:not(&--open) .item:hover ~ .item {
transform: translateX(25%);
}
}
The className that was being applied (&--open) here isn't relevant, I replaced it with props but I can only replicate half the behavior I want. Also the show details block is not important. So far I have :
export const ItemContainer = styled.div`
flex: 0 0 19.7%;
transition: transform 300ms ease 100ms;
margin: 0 2px;
position: relative;
img {
height: 100%;
width: 100%;
vertical-align: top;
}
&:hover {
transform: ${(props) => (props.isOpen ? null : 'scale(1.5)')} !important;
}
&:hover + & {
transform: ${(props) =>
(props.isActive && (props.isOpen) ? null : 'translateX(-25%);')};
}
&:hover ~ & {
transform: ${(props) => (props.isActive && (props.isOpen) ? null : 'translateX(25%);')};
}
`;
const Item = ({ show }) => {
const {onShowDetails, currentSlide, elementRef, isOpen } = useContext(SliderContext)
const isActive = currentSlide && currentSlide.id === show.id;
return (
<ItemContainer
ref={elementRef}
isActive={isActive}
isOpen={isOpen}
>
<img src={show.thumbnail.regular.medium} alt={`Show title: ${show.title}`} />
<ShowDetailsButton onClick={() => onShowDetails(show)} />
{isActive && <Mark />}
</ItemContainer>
);
};
export default Item;
So when I hover on an element it scales to 1.5 and all elements to the right will shift right but the ones to the left will not shift at all. The original sass code is applying the styles to the item component inside the parent component which I don't want, I was hoping to be able to do this all from the item component if possible. I just can't figure the correct syntax so any help would be greatly appreciated.

Why does my overlay not transition while rerendering?

I'm trying to transition a simple overlay if one of four thumbnails is clicked. My idea was to toggle the overlays opacity within the inline style of the element like this..
<div className='product-thumbnail-overlay' style={selectedThumbnail === id ? { opacity: 1 } : { opacity: 0 }}></div>
This is working until I wrap this code in a react function component to avoid repetitions of my code.
HTML
import { useState, useEffect } from 'react';
import './Product.scss';
import ProductImage1 from '../assets/images/image-product-1.jpg';
import ProductImage2 from '../assets/images/image-product-2.jpg';
import ProductImage3 from '../assets/images/image-product-3.jpg';
import ProductImage4 from '../assets/images/image-product-4.jpg';
import ProductThumbnail1 from '../assets/images/image-product-1-thumbnail.jpg';
import ProductThumbnail2 from '../assets/images/image-product-2-thumbnail.jpg';
import ProductThumbnail3 from '../assets/images/image-product-3-thumbnail.jpg';
import ProductThumbnail4 from '../assets/images/image-product-4-thumbnail.jpg';
const Product = () => {
const [selectedThumbnail, setSelectedThumbnail] = useState(1);
const getProductThumbnailById = (id) => {
// get the imported product thumbnail
};
const ProductThumbnail = ({ id }) => {
return (
<div className='product-thumbnail-container' onClick={() => setSelectedThumbnail(id)}>
<img src={getProductThumbnailById(id)} alt='thumbnail of product' className='product-thumbnail' />
<div
className='product-thumbnail-overlay'
style={selectedThumbnail === id ? { opacity: 1 } : { opacity: 0 }}
></div>
</div>
);
};
return (
<main>
<div className='product-images'>
<img src={ProductImage1} alt='product' className='product-image' />
<div className='product-thumbnails'>
<ProductThumbnail id={1} />
<ProductThumbnail id={2} />
<ProductThumbnail id={3} />
<ProductThumbnail id={4} />
</div>
</div>
<div className='product-details'></div>
</main>
);
};
export default Product;
important SCSS
.product-thumbnail-container {
position: relative;
flex: 20.5%;
cursor: pointer;
.product-thumbnail {
display: block;
max-width: 100%;
border-radius: variables.$br;
}
.product-thumbnail-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.6);
border: 2px solid variables.$clr_prim_orange;
border-radius: variables.$br;
transition: all 2000ms ease-in-out;
}
}
Codepen with what im trying to accomplish (but with repetitions of code): https://codepen.io/Sandhexer/pen/JjLrZOQ?editors=0110
Codepen with current code: https://codepen.io/Sandhexer/pen/gOeGKpP?editors=0110
I think that the rerendering causes the element not to transition because the opacity doesnt really change. Is this right and does anyone know a solution or good workaround for this problem?

Position Draft js emoji popover on top

I'm using the #draft-js-plugins/emoji but I'm having problem positioning the emoji popover on top of the editor. Currently it's showing on bottom like this I want to show this popover on top rather than the bottom.
I found an answer here, but it's not working for me.
const {
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
plugins,
} = useMemo(() => {
const mentionPlugin = createMentionPlugin();
// eslint-disable-next-line no-shadow
const { MentionSuggestions } = mentionPlugin;
const emojiPlugin = createEmojiPlugin({
positionSuggestions: (settings) => {
return {
left: settings.decoratorRect.left + 'px',
top: settings.decoratorRect.top - 40 + 'px', // change this value (40) for manage the distance between cursor and bottom edge of popover
display: 'block',
transform: 'scale(1) translateY(-100%)', // transition popover on the value of its height
transformOrigin: '1em 0% 0px',
transition: 'all 0.25s cubic-bezier(0.3, 1.2, 0.2, 1)',
position: 'fixed',
};
},
useNativeArt: true,
});
// eslint-disable-next-line no-shadow
const { EmojiSuggestions, EmojiSelect } = emojiPlugin;
// eslint-disable-next-line no-shadow
const plugins = [mentionPlugin, emojiPlugin];
return {
plugins,
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
};
}, []);
<div className="flex w-100 items-center">
<div
className={`${editorStyles.editor} flex w-100 pa2`}
onClick={() => {
ref.current!.focus();
}}
>
<Editor
editorKey={'editor'}
editorState={editorState}
handleKeyCommand={(cmd) => handleKeyCommand(cmd)}
keyBindingFn={myKeyBindingFn}
onChange={onChange}
plugins={plugins}
ref={ref}
placeholder="Type your message and hit ENTER to send"
/>
<MentionSuggestions
open={open}
onOpenChange={onOpenChange}
suggestions={suggestions}
onSearchChange={onSearchChange}
onAddMention={() => {
// get the mention object selected
}}
/>
<EmojiSuggestions />
</div>
<div style={{ margin: '0 .5rem' }}>
<EmojiSelect closeOnEmojiSelect />
</div>
<SendIcon
className="pointer"
style={{ color: '#3F61C5', margin: '0 .5rem' }}
onClick={handleSend}
/>
</div>
I also inspected the elements, the css I wrote in
positionSuggestions are not there. Any way I can place this popover on top?
I had the same issue and I have resolved it using the CSS option.
Create a copy of this file in your project: https://github.com/draft-js-plugins/draft-js-plugins/blob/master/packages/emoji/src/theme.ts
Find emojiSelectPopover class and replace it with below:
emojiSelectPopover: css`
padding: 0 0.3em;
position: absolute;
z-index: 1000;
box-sizing: content-box;
background: #fff;
border: 1px solid #e0e0e0;
box-shadow: 0 4px 30px 0 gainsboro;
transform: scale(1) translateY(-100%);
transform-origin: 1em 0% 0px;
top: 65%;
#media (min-width: 320px) and (max-width: 480px) {
right: 0;
top: 50%;
}
Use your custom theme file(which you have created in #1) in the configuration:
const emojiPlugin = createEmojiPlugin({
selectButtonContent: '😊',
theme: defaultTheme // This should be imported from your created theme file
});
For your scenario, you need to move right: 0; outside of the media query as your emoji button is close to the right side of the browser.

How to hide with animation? React + SCSS

I think is just something very simple, but I can't think of anything;
import React, { useState } from "react"
import "./header.scss"
export default function(){
let [curtain, setCurtain] = useState(false);
return (
<div className="header"
onMouseEnter={() => setCurtain(true)}
onMouseLeave={() => setCurtain(false)}
>
{ curtain && <div className="header__curtain__black header__curtain"/> }
</div>
)
}
And here is SCSS
.header{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 70px;
&__curtain{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
animation: slide_down 0.2s linear;
}
}
#keyframes slide_down {
0%{
transform: translateY(-100%);
}
100%{
transform: translateY(0);
}
}
Code is super simple and task is super simple too, I want to make a reverse on closing but what do I do ?
You can assign different class names to the curtain div according to curtain state value. I would also suggest to use transition property in your css styles instead of keyframe animations.
Here is the edited code.

Resources