Mapped buttons that have opacity of 0 are showing, and are overlapping when resizing the window - reactjs

I've successfully mapped over text and have styled it with transition effects when going from one slide to the next as you can see here:
Following a similar concept with buttons isn't working. There should only be one button active per slide like you see here:
I want the buttons to have the same effect as the text, but I'm getting behaviors like you see here:
As you can see, there is no transition effect on the button when clicking to the second slide, and it also appears in a lower spot.
And lastly, when resizing the window, buttons are overlapping like you see here:
Don't know what to try next.
Here's the ImageSlider component:
import { useState } from "react";
import { SliderData } from "../data";
import { categories } from "../data";
import ShopNowButtonActive from "./ShopNowButtonActive";
import { IoIosArrowBack } from "react-icons/io";
import { IoIosArrowForward } from "react-icons/io";
import "./ImageSlider.css";
import ShopNowButton from "./ShopNowButton";
const ImageSlider = ({ slides }) => {
const [current, setCurrent] = useState(0);
const length = slides.length;
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1);
};
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1);
};
return (
<div className="slider">
<IoIosArrowBack className="left-arrow" onClick={prevSlide} />
{SliderData.map((slide, index) => (
<div key={slide.id}>
<img
src={slide.img}
alt=""
className={index === current ? "slide active" : "slide"}
/>
<div className="info-container">
<div className={index === current ? "title active" : "title"}>
{slide.title}
</div>
<div className={index === current ? "desc active" : "desc"}>
{slide.desc}
</div>
{categories.map((item, index) =>
index === current ? (
<ShopNowButtonActive item={item} />
) : (
<ShopNowButton item={item} />
)
)}
</div>
</div>
))}
<IoIosArrowForward className="right-arrow" onClick={nextSlide} />
</div>
);
};
export default ImageSlider;
The css file:
.slider {
height: 90vh;
margin-bottom: 0.5rem;
}
.left-arrow {
position: absolute;
top: 45%;
left: 32px;
font-size: 2rem;
cursor: pointer;
opacity: 0.5;
z-index: 1;
}
.slide.active {
opacity: 1;
width: 100%;
height: 88%;
object-fit: cover;
object-position: center;
-webkit-clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
}
.slide {
opacity: 0;
transition: 500ms opacity ease-in-out;
width: 100%;
height: 88%;
object-fit: cover;
object-position: center;
position: absolute;
-webkit-clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
}
.info-container {
display: flex;
flex-direction: column;
width: 40%;
height: 100%;
position: absolute;
top: 45%;
right: 30px;
}
.title.active {
opacity: 1;
transition-delay: 700ms;
font-size: 4rem;
}
.title {
opacity: 0;
transition: 200ms opacity ease-in-out;
font-size: 4rem;
}
.desc.active {
opacity: 1;
padding-top: 1.5em;
transition-delay: 700ms;
font-size: 1.25rem;
font-weight: 500;
letter-spacing: 3px;
}
.desc {
opacity: 0;
padding-top: 1.5em;
transition: 200ms opacity ease-in-out;
font-size: 1.25rem;
font-weight: 500;
letter-spacing: 3px;
}
.right-arrow {
position: absolute;
top: 45%;
right: 32px;
font-size: 2rem;
cursor: pointer;
opacity: 0.5;
z-index: 1;
}
The ShopNowButtonActive component:
import React from "react";
import styled from "styled-components/macro";
import { Link } from "react-router-dom";
const ButtonActive = styled.button`
opacity: 1;
padding: 0.5rem;
margin-top: 2.5rem;
width: 8rem;
font-size: 20px;
background-color: transparent;
cursor: pointer;
transition-delay: 700ms;
`;
const ShopNowButtonActive = ({ item }) => {
return (
<Link to={`/products/${item.cat}`}>
<ButtonActive>SHOP NOW</ButtonActive>
</Link>
);
};
export default ShopNowButtonActive;
And finally, the ShopNowButton component:
import React from "react";
import styled from "styled-components/macro";
import { Link } from "react-router-dom";
const Button = styled.button`
opacity: 0;
/* display: none; */
padding: 0.5rem;
margin-top: 2.5rem;
width: 8rem;
font-size: 20px;
background-color: transparent;
cursor: pointer;
transition: 200ms opacity ease-in-out;
`;
const ShopNowButton = ({ item }) => {
return (
<Link to={`/products/${item.cat}`}>
<Button>SHOP NOW</Button>
</Link>
);
};
export default ShopNowButton;
(Sorry for the use of both an external css file and styled components.)
Any suggestions?

I have recreated the above scenario using some static data. I have modified some of the css. It is working as expected. I also observed that the only major difference between ShopNowButton and ShopNowButtonActive was opacity property which hides the element. The reason you are observing 2 buttons because they were all there in the dom actually.(They were not hiding properly due to which we are observing more shop now buttons and every time we click on next icon the corresponding button is being displayed. Basically all the buttons are there on the page itself.)
Please find the sandbox url below.
https://codesandbox.io/s/stackoverflow-6u05e7?file=/src/ImageSlider/ImageSlider.js

Related

REACT + TypeScript Accordion transition doesn't works

Sup guys i have a problem, i would like to do an animated accordion, it should to have a transition animated when it opens and collapse, and in icon switching
I'm creating by myself an accordion in react + typescript but this transition doesnt works and idk why, code below:
This my index.tsx
import { useState } from "react";
import { AccordionButton, AccordionContent, Wrapper } from "./styles";
import { AccordionProps } from "./interfaces";
import { BsChevronDown, BsChevronUp } from "react-icons/bs";
export default function Accordion({ title, text }: AccordionProps) {
const [isOpen, setIsOpen] = useState(false);
const handleClick = () => {
setIsOpen(!isOpen);
returnIcon();
};
const returnIcon = () => {
return isOpen ? <BsChevronUp /> : <BsChevronDown />;
};
return (
<Wrapper>
<AccordionButton onClick={handleClick}>
{title} {returnIcon()}
</AccordionButton>
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>
);
}
and this is my styled component below:
import styled from "styled-components";
import { AccordionContentProps } from "./interfaces";
export const AccordionButton = styled.button`
background-color: #606582;
color: #ffffff;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.6s;
&:hover {
background-color: #60658295;
}
> svg {
float: right;
}
`;
export const AccordionContent = styled.div<AccordionContentProps>`
display: ${(props) => (props.isOpen === false ? "none" : "block")};
padding: 0 18px;
background-color: white;
overflow: hidden;
`;
export const Content = styled.div`
padding: 10px 0px;
flex-wrap: wrap;
max-width: 750px;
margin-right: auto;
margin-left: auto;
#media only screen and (max-width: 600px) {
max-width: 80%;
}
`;
export const Wrapper = styled.div`
padding: 10px 0px;
`;
I've tried to add this code but still doesnt working
-webkit-transition: all 0.4s ease-in;
-moz-transition: all 0.4s ease-in;
-o-transition: all 0.4s ease-in;
transition: all 0.4s ease-in;
Try to replace the return block with the following:
return (
<Wrapper>
{isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronUp />
</AccordionButton>}
{!isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronDown />
</AccordionButton>}
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>)
and we can also delete the function returnIcon - we don't need it

Custom arrow Swiper Slider + Next.js + Sass

I'm using the slider swiper in a project developed in Next.JS and I'm using Sass to do the styling. But when I'm going to use the swiper classes, as mandated by the documentation, to style the arrows, it doesn't work.
I need the arrows to be outside the component, not overlapping.
CSS
.swiper-button-next,
.swiper-button-prev {
background: red;
position: absolute;
top: 50%;
width: calc(var(--swiper-navigation-size) / 44 * 27);
height: var(--swiper-navigation-size);
margin-top: calc(0px - (var(--swiper-navigation-size) / 2));
z-index: 10;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--swiper-navigation-color, var(--swiper-theme-color));
}
.swiper-button-next.swiper-button-disabled,
.swiper-button-prev.swiper-button-disabled {
opacity: 0.35;
cursor: auto;
pointer-events: none;
}
.swiper-button-next:after,
.swiper-button-prev:after {
background: red;
font-family: swiper-icons;
font-size: var(--swiper-navigation-size);
text-transform: none !important;
letter-spacing: 0;
text-transform: none;
font-variant: initial;
line-height: 1;
}
.swiper-button-prev,
.swiper-container-rtl .swiper-button-next {
left: 10px;
right: auto;
}
.swiper-button-prev:after,
.swiper-container-rtl .swiper-button-next:after {
content: "prev";
color: #000;
}
.swiper-button-next,
.swiper-container-rtl .swiper-button-prev {
right: 10px;
left: auto;
}
.swiper-button-next:after,
.swiper-container-rtl .swiper-button-prev:after {
content: "next";
}
.swiper-button-next.swiper-button-white,
.swiper-button-prev.swiper-button-white {
--swiper-navigation-color: #ffffff;
}
.swiper-button-next.swiper-button-black,
.swiper-button-prev.swiper-button-black {
--swiper-navigation-color: #000000;
}
.swiper-button-lock {
display: none;
}
I've tried changing the styles, but nothing reflects on the component. If I change styles by browser it works normally.
Changing the basic arrows styles is pretty simple - take a look at the following codesandbox: https://codesandbox.io/s/stoic-shaw-q35wq?file=/src/styles.scss
.swiper-button-prev, .swiper-button-next {
top: 45%;
width: 40px;
height: 40px;
background: #fff;
border: 1px solid gray;
border-radius: 50%;
color: blue;
font-weight: 700;
outline: 0;
transition: background-color .2s ease, color .2s ease;
&::after {
font-size: 16px;
}
}
.swiper-button-prev {
&:after {
position: relative;
left: -1px;
}
}
.swiper-button-next {
&:after {
position: relative;
left: 1px;
}
}
.swiper-button-prev, .swiper-container-rtl .swiper-button-next {
left: 10px;
right: auto;
}
.swiper-button-next, .swiper-container-rtl .swiper-button-prev {
right: 10px;
left: auto;
}
.swiper-button-prev.swiper-button-disabled, .swiper-button-next.swiper-button-disabled {
opacity: 0;
cursor: auto;
pointer-events: none;
}
Moving the arrows outside, however, is a bit more tricky. In the example above I've used a little CSS tricks (negative margin and corresponding padding) and some overflows to get it working but it might not be enough for your use case.
You would have to create your own next/previous elements:
import React from "react";
import { Navigation, Pagination, Scrollbar, A11y, Controller } from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";
// Import Swiper styles
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import "swiper/css/scrollbar";
import "./styles.scss";
const image = "https://source.unsplash.com/featured/300x201";
export default function App() {
const images = new Array(6).fill({ url: image });
const [swiper, setSwiper] = React.useState();
const prevRef = React.useRef();
const nextRef = React.useRef();
React.useEffect(() => {
if (swiper) {
console.log("Swiper instance:", swiper);
swiper.params.navigation.prevEl = prevRef.current;
swiper.params.navigation.nextEl = nextRef.current;
swiper.navigation.init();
swiper.navigation.update();
}
}, [swiper]);
return (
<div className="App">
<div className="carousel-container">
<div className="swiper-button" ref={prevRef}>
prev
</div>
<Swiper
modules={[Navigation, Pagination, Scrollbar, A11y, Controller]}
className="external-buttons"
spaceBetween={24}
slidesPerView={1}
navigation={{
prevEl: prevRef?.current,
nextEl: nextRef?.current
}}
updateOnWindowResize
observer
observeParents
initialSlide={2}
onSwiper={setSwiper}
>
{images.map((image, index) => (
<SwiperSlide key={index}>
<img
height="200"
width="300"
alt="img"
className="image"
src={image.url}
/>
</SwiperSlide>
))}
</Swiper>
<div className="swiper-button" ref={nextRef}>
next
</div>
</div>
</div>
);
}
Complete example - https://codesandbox.io/s/prod-darkness-o483y?file=/src/App.js
Important - the examples are using Swiper v7
Here is the best way to add custom arrows:
import React, { useRef } from "react";
// For Typescript
// import SwiperCore from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
const SliderComponent = () => {
const swiperRef = useRef();
// For Typescript!
// const swiperRef = useRef<SwiperCore>();
const sliderSettings = {
440: {
slidesPerView: 1,
spaceBetween: 30,
},
680: {
slidesPerView: 2,
spaceBetween: 30,
},
1024: {
slidesPerView: 3,
spaceBetween: 30,
},
};
return (
<div>
<button onClick={() => swiperRef.current?.slidePrev()}>Prev</button>
<Swiper
slidesPerView={3}
breakpoints={sliderSettings}
onBeforeInit={(swiper) => {
swiperRef.current = swiper;
}}
>
<SwiperSlide>
Slide 1
</SwiperSlide>
<SwiperSlide>
Slide 2
</SwiperSlide>
<SwiperSlide>
Slide 3
</SwiperSlide>
<SwiperSlide>
Slide 4
</SwiperSlide>
<SwiperSlide>
Slide 5
</SwiperSlide>
</Swiper>
<button onClick={() => swiperRef.current?.slideNext()}>Next</button>
</div>
);
};
export default SliderComponent;
hamza liaqat's answer is correct, but for typescript you need to add
import { Swiper as SwiperCore } from 'swiper/types';

ReactDOM.createPortal modal is mounted on DOM but nothing is displayed on the screen

this is a typescript-next.js project. I have this Modal component:
interface ModalProps {
onCancelModal: () => void;
onAcceptModal: () => void;
acceptEnabled: boolean;
isLoading?: boolean;
title: string;
}
const Modal: React.FC<ModalProps> = (props) => {
let containerRef = useRef<HTMLDivElement | null>(null);
console.log("container", containerRef);
useEffect(() => {
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
if (!containerRef.current) {
containerRef.current = rootContainer;
}
return () => rootContainer.remove();
}, []);
return containerRef.current
? ReactDOM.createPortal(
<div className="modal">
<header className="modal__header">
<h1>{props.title}</h1>
</header>
<div className="modal__content">{props.children}</div>
<div className="modal__actions">
<Button design="danger" mode="flat" onClick={props.onCancelModal}>
Cancel
</Button>
<Button
mode="raised"
onClick={props.onAcceptModal}
disabled={!props.acceptEnabled}
loading={props.isLoading}
>
Accept
</Button>
</div>
</div>,
containerRef.current
)
: null;
};
export default Modal;
I pass a custom error to ErrorHandler component:
const ErrorHandler: React.FC<ErrorHandlerProps> = (props) => (
<Fragment>
{props.error && <Backdrop onClick={props.onHandle} />}
{props.error && (
<Modal
title="An Error Occurred"
onCancelModal={props.onHandle}
onAcceptModal={props.onHandle}
acceptEnabled
>
<p>{props.error}</p>
</Modal>
)}
</Fragment>
);
However, Modal component is successfully mounted on the DOM but nothing displays on the screen.
EDIT
I have backdrop and modal components.
// css for backdrop
.backdrop {
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
z-index: 100;
position: fixed;
left: 0;
top: 0;
transition: opacity 0.3s ease-out;
opacity: 1;
}
// css for Modal
.modal {
position: fixed;
width: 90%;
left: 5%;
top: 20vh;
background: white;
border-radius: 5px;
z-index: 200;// I changed this to 999999 but didnot solve the issue
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
}
.modal__header {
border-bottom: 2px solid #3b0062;
}
.modal__header h1 {
font-size: 1.5rem;
color: #3b0062;
margin: 1rem;
}
.modal__content {
padding: 1rem;
}
.modal__actions {
padding: 1rem;
text-align: right;
}
.modal__actions button {
margin: 0 0.5rem;
}
#media (min-width: 768px) {
.modal {
width: 40rem;
left: calc((100% - 40rem) / 2);
}
}
I found the answer after i refresh my memory. I realized that there is another .modal className on elements-styles tab. It points me to the /node_modules/bootstrap/scss/_modal.scss file which also has modal className and it was overriding my custom className.
.modal {
position: fixed;
top: 0;
left: 0;
z-index: $zindex-modal;
display: none;
width: 100%;
height: 100%;
overflow: hidden;
// Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951.
outline: 0;
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
// See also https://github.com/twbs/bootstrap/issues/17695
}

pseudo styled component h2 not showing in html

I'm currently trying to create an animation on an H2, using the pseudos ::before and after. But the ::before and ::after are not showing in my HTML.
What am I doing wrong here? Looking at styled components docs this should just work. I'm aware of the weird animation function. But this does not have any influence in the before after. I already completely removed it but it still doesn't render.
import React from 'react'
import styled, { keyframes, css } from 'styled-components'
import PropTypes from 'prop-types'
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1,1,1);
opacity: 1;
&::before, &::after{
content: ${(props) => props.text};
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
clip: rect(0, 900px, 0, 0);
}
&::before {
left: 7px;
text-shadow: 1px 0 green;
animation: ${glitchEffect} 3s infinite linear alternate-reverse;
}
&::after {
left: 3px;
text-shadow: -1px 0 red;
animation: ${glitchEffect} 2s infinite linear alternate-reverse;
}
`
const glitchEffect = keyframes`
${setInterval(createAnimation, 200)}
`
function createAnimation(){
const single = `clip: rect(${(Math.floor(Math.random() * 100 + 1))}px, 9999px, ${(Math.floor(Math.random() * 100 + 1))}px, 0);`
return css`${single}`
}
export default function Glitch({ text }){
return (
<Wrapper text={text}>{text}</Wrapper>
)
}
Glitch.propTypes = {
text: PropTypes.string.isRequired
}
A few things:
content: ${(props) => props.text}; wouldn't work, you need to add double quotes around the text like so content: "${(props) => props.text}";
The second issue is setInterval(createAnimation, 200). This will return an integer (a handle to the interval that you've just created). This will be needed to clear the interval once you're done with the animation for example.
If what you want to do is to generate a some keyframes, then you need to call createAnimation manually like so
import React from "react";
import styled, { keyframes, css } from "styled-components";
const glitchEffect = keyframes`
from {
${createAnimation()}
}
to {
${createAnimation()}
}
`;
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1, 1, 1);
opacity: 1;
> .before,
> .after {
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
clip: rect(0, 900px, 0, 0);
}
> .before {
left: 7px;
text-shadow: 1px 0 green;
animation: ${glitchEffect} 3s infinite linear alternate-reverse;
}
> .after {
left: 3px;
text-shadow: -1px 0 red;
animation: ${glitchEffect} 2s infinite linear alternate-reverse;
}
`;
function createAnimation() {
const single = `clip: rect(${Math.floor(
Math.random() * 100 + 1
)}px, 9999px, ${Math.floor(Math.random() * 100 + 1)}px, 0);`;
return css`
${single}
`;
}
export default function Glitch({ text }) {
return (
<Wrapper>
<div className="before">{text}</div>
{text}
<div className="after">{text}</div>
</Wrapper>
);
}
If you want to generate random animation, then you'll need to create the interval from Glitch
// First transform your "animation: ${glitchEffect} 3s infinite linear alternate-reverse;"
// into a "transition: text-shadow 500ms linear;"
// Since we're manually changing its value
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1, 1, 1);
opacity: 1;
> .before,
> .after {
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
}
> .before {
left: 7px;
text-shadow: 1px 0 green;
transition: clip 300ms linear;
}
> .after {
left: 3px;
text-shadow: -1px 0 red;
transition: clip 200ms linear;
}
`;
function createAnimation() {
return {
clip: `rect(${Math.floor(Math.random() * 100 + 1)}px, 9999px, ${Math.floor(
Math.random() * 100 + 1
)}px, 0)`
};
}
export default function Glitch({ text }) {
const [glitchEffect, setGlitchEffect] = useState(createAnimation());
useEffect(() => {
const interval = setInterval(() => setGlitchEffect(createAnimation()), 500);
return () => {
clearInterval(interval);
};
}, []);
// now pass glitchEffect to a "style" prop to your pseudo elements
// See https://stackoverflow.com/a/28269950/3877913
return (
<Wrapper>
<div className="before" style={glitchEffect}>
{text}
</div>
{text}
<div className="after" style={glitchEffect}>
{text}
</div>
</Wrapper>
);
}

How to create multi-items slider in React?

I created a multi-items carousel and it's fetching all images from the database but only displaying 3 images. I need this carousel to display all images from the database when one click on side arrows. How can I make it work as "https://react-multi-carousel.now.sh/" this carousel? Can you help me with my custom carousel?
.home__wrapper {
.categoryslider__wrapper {
display: flex;
width: 100%;
margin-top: 15px;
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.05s, height 0.05s linear;
height: auto;
.category {
position: relative;
height: 14vh;
margin-bottom: 1em;
cursor: pointer;
& >img {
height: 100%;
width: 100%;
object-fit: cover;
}
p {
position: absolute;
top: 0.2em;
left: 1em;
color: rgb(255, 255, 255);
font-size: 2vh;
font-weight: 900;
letter-spacing: 0.5px;
line-height: 24px;
width: 80%;
text-align: left;
}
}
}
}
import React from 'react'
import {withRouter} from 'react-router'
import './index.scss'
import {times} from 'lodash'
import Carousel from 'react-bootstrap/Carousel'
import {Col} from 'react-bootstrap';
import {API} from '../../../constants';
class CategorySlider extends React.Component {
render() {
let {stores} = this.props;
return (
<>
<Carousel className='categoryslider__wrapper'>
{times(4, (index) => {
return (
<Carousel.Item key={index}>
{stores && stores.length && stores.map(store => {
if (store.featured_products.length){
return store.featured_products[0].image_url;
}
return stores[0].name;
}).filter(image=>image!==undefined).map((image_link, i )=> {
return (
<Col key={image_link + "" + i} className="col-4 category" >
<img src={API.files.detail.replace('/files/:fileUUID/', image_link)} alt=""></img>
<p>{stores[i].name}</p>
</Col>
)
})}
</Carousel.Item >
)
})}
</Carousel>
</>
);
}
}
export default withRouter(CategorySlider);

Resources