I am learning to make custom Carousel by using React and Typescript. For styling I used styled components and scss. I found from one Article how to make Carousel. My carousel works fine.
I have made four div elements. when the carousel image slide will change, I want to change background-color of the div elements inot orange color. But Don't know how to do that.
I share my code in codesandbox
This is my Carousel component
import React, { useState, useEffect, useRef, memo, useCallback } from "react";
import styled from "styled-components";
interface ICarousel {
children: JSX.Element[];
currentSlide?: number;
autoPlay?: boolean;
dots?: boolean;
interval?: number;
arrow?: boolean;
}
const IMG_WIDTH = 320;
const IMG_HEIGHT = 700;
export default memo(
({
children,
autoPlay = false,
dots = false,
interval = 3000,
arrow = false
}: ICarousel) => {
const [currentSlide, setSlide] = useState(0);
const [isPlaying, setIsPlaying] = useState(autoPlay);
const timer = useRef<any>(undefined);
const slides = children.map((slide, index) => (
<CarouselSlide key={index}>{slide}</CarouselSlide>
));
const handleSlideChange = useCallback(
(index: number) =>
setSlide(
index > slides.length - 1 ? 0 : index < 0 ? slides.length - 1 : index
),
[slides]
);
const createInterval = useCallback(() => {
timer.current = setInterval(() => {
handleSlideChange(currentSlide + 1);
}, interval);
}, [interval, handleSlideChange, currentSlide]);
const destroyInterval = useCallback(() => {
clearInterval(timer.current);
}, []);
useEffect(() => {
if (autoPlay) {
createInterval();
return () => destroyInterval();
}
}, [autoPlay, createInterval, destroyInterval]);
return (
<CarouselContainer
onMouseEnter={() => {
if (autoPlay) {
destroyInterval();
}
}}
onMouseLeave={() => {
if (autoPlay) {
createInterval();
}
}}
>
<CarouselSlides currentSlide={currentSlide}>{slides}</CarouselSlides>
{arrow ? (
<div>
<LeftButton onClick={() => handleSlideChange(currentSlide - 1)}>
❮
</LeftButton>
<RightButton onClick={() => handleSlideChange(currentSlide + 1)}>
❯
</RightButton>
</div>
) : null}
{dots ? (
<Dots>
{slides.map((i, index) => (
<Dot
key={index}
onClick={() => handleSlideChange(index)}
active={currentSlide === index}
/>
))}
</Dots>
) : null}
</CarouselContainer>
);
}
);
const Buttons = styled.a`
cursor: pointer;
position: relative;
font-size: 18px;
transition: 0.6s ease;
user-select: none;
height: 50px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
top: calc(50% - 25px);
position: absolute;
&:hover {
background-color: rgba(0, 0, 0, 0.8);
}
`;
const RightButton = styled(Buttons)`
border-radius: 3px 0 0 3px;
right: 0;
`;
const LeftButton = styled(Buttons)`
border-radius: 0px 3px 3px 0px;
left: 0;
`;
const Dots = styled.div`
display: flex;
justify-content: center;
align-items: center;
align-content: center;
margin-top: 10px;
`;
const Dot = styled.span<{ active: boolean }>`
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 10px;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
background-color: ${({ active }) => (active ? `red` : `#eeeeee`)};
`;
const CarouselContainer = styled.div`
overflow: hidden;
position: relative;
width: ${IMG_WIDTH}px;
height: ${IMG_HEIGHT}px;
img {
/* change the margin and width to fit the phone mask */
width: ${IMG_WIDTH - 20}px;
height: ${IMG_HEIGHT - 50}px;
margin-left: 10px;
margin-top: 15px;
}
z-index: 1;
`;
const CarouselSlide = styled.div`
flex: 0 0 auto;
transition: all 0.5s ease;
width: 100%;
`;
const CarouselSlides = styled.div<{
currentSlide: ICarousel["currentSlide"];
}>`
display: flex;
${({ currentSlide }) =>
` transform: translateX(-${currentSlide ? currentSlide * 100 + `%` : 0})`};
transition: transform 300ms linear;
cursor: pointer;
`;
This where I am using the Carousel component
import * as React from "react";
import "./styles.scss";
import Carousel from "./carousel";
export const imgUrls = [
`https://images.unsplash.com/photo-1455849318743-b2233052fcff?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1508138221679-760a23a2285b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1494253109108-2e30c049369b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`
];
export default function App() {
return (
<div className="App">
<main className="Testeru">
<div className="phone-left">
// I want change background color of this div element
<div className="phone-left-upper">left upper</div>
<div className="phone-left-lower">left-lower</div>
</div>
<div className="phone-slider">
<div className="mobile_overlay">
<Carousel autoPlay>
{imgUrls.map((i) => {
return (
<img
key={i}
src={i}
alt=""
style={{
borderRadius: `20px`
}}
/>
);
})}
</Carousel>
</div>
</div>
// I want change background color of this div element
<div className="phone-right">
<div className="phone-right-upper">right upper</div>
<div className="phone-right-lower">right-lower</div>
</div>
</main>
</div>
);
}
Related
On click on the Text Area, button should add relevant text area in a pag. By clicking on the close button, system should remove the added textarea in a React page. By default, the close button shouldn't display. In this example, system is adding the text area, but once it is removed, I am not able to add the textarea anymore. Could someone please advise the issue here.
CSB link:
https://codesandbox.io/s/nervous-currying-jojdhi?file=/src/App.js
import "./styles.css";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function App() {
const [multiInput, setMultiInput] = useState("");
const [createCode, setCreateCode] = useState("");
const [createImageTag, setImageTag] = useState("");
const [visible, setVisible] = useState(true);
const createCodeSection = () => {
const newElement = React.createElement("blockquote", {
contenteditable: "true",
className: "codehighlight",
name: "codesection" + new Date().getTime()
});
setCreateCode((createCode) => [...createCode, newElement]);
};
const createMultilineTextSection = () => {
const newElement = React.createElement("textarea", {
contenteditable: "true",
className: "defaultTextArea",
name: "textarea" + new Date().getTime()
});
setCreateCode((multiInput) => [...multiInput, newElement]);
};
const createImageSection = () => {
const newElement = React.createElement("img", {
key: "ele" + new Date().getTime()
});
setCreateCode((createImageTag) => [...createImageTag, newElement]);
};
const removeElement = () => {
setVisible((prev) => !prev);
};
return (
<div id="App">
<div className="adminSection">
<div className="row">
<div className="logout"></div>
<div className="createBlogSection">
<div className="row">
<button onClick={createMultilineTextSection}>Text Area</button>
<button onClick={createCodeSection}>Code</button>
<button>Image</button>
</div>{" "}
<br></br>
<div className="row">
<textarea className="defaultTextArea"></textarea>
</div>
<br></br>
<div className="row">
{visible && <span className="dtextArea">{createCode}</span>}
</div>
<div className="row">
{visible && (
<div>
<span className="closeElement">
<button onClick={removeElement}>X</button>
</span>
<span>{multiInput}</span>
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
}
//css
.App {
font-family: sans-serif;
text-align: center;
}
.adminSection {
width: 100%;
}
.adminSection h1 {
margin: 25px 0px 0px 550px !important;
color: black !important;
}
.logout img {
height: 30px;
width: 30px;
margin: -100px 50px 0px 0px;
float: right;
}
.createBlogSection {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(700px, 1fr));
grid-gap: 25px !important;
margin: 100px 20px 10px 20px !important;
/* border: solid 1px; */
border-color: #e2e8f0;
border-radius: 0px 5px 5px 0px !important;
background-color: #fff;
}
.defaultTextArea {
height: 100px;
width: 300px;
margin: 10px 0px 10px 40px;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
background-color: #fff !important;
}
.codehighlight {
width: 100%;
color: #fff;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
background-color: #353232 !important;
}
.closeElement {
margin-left: 320px;
display: none;
}
Instead of how you've put the added elements, use:
const removeElement = (index) => {
const newElements = multiInput.filter((_, i) => i == index);
setMultiInput(newElements);
};
// Inside "return" (createBlogSection div)
multiInput.map((_input, index) => {
<div key={index}>
{_input}
<span className="closeElement">
<button onClick={removeElement(index)}>X</button>
</span>
</div>
})
Edit:
Add const [multiInput, setMultiInput] = useState([]); and instead of pushing new elements into separate variables add all into multiInput only. Using setMultiInput(multiInput) => [...multiInput, newElement]); in each case.
I have a working ToastList that enables me to click a button multiple times and generate a toast each time. On entry, I have an animation, but when I remove the toast, I do not get an animation. I am using Typescript and functional components.
My component is as follows:
import React, { useCallback, useEffect, useState } from 'react';
import * as Styled from './Toast.styled';
export interface ToastItem {
id: number;
title: string;
description: string;
backgroundColor: string;
}
export interface ToastProps {
toastList: ToastItem[];
setList: React.Dispatch<React.SetStateAction<ToastItem[]>>;
}
export default function Toast(props: ToastProps) {
const deleteToast = useCallback(
(id: number) => {
const toastListItem = props.toastList.filter((e) => e.id !== id);
props.setList(toastListItem);
},
[props.toastList, props.setList]
);
useEffect(() => {
const interval = setInterval(() => {
if (props.toastList.length) {
deleteToast(props.toastList[0].id);
}
}, 2000);
return () => {
clearInterval(interval);
};
}, [props.toastList, deleteToast]);
return (
<Styled.BottomRight>
{props.toastList.map((toast, i) => (
<Styled.Notification
key={i}
style={{ backgroundColor: toast.backgroundColor }}
>
<button onClick={() => deleteToast(toast.id)}>X</button>
<div>
<Styled.Title>{toast.title}</Styled.Title>
<Styled.Description>{toast.description}</Styled.Description>
</div>
</Styled.Notification>
))}
</Styled.BottomRight>
);
}
And my styling is done using styled-components and is as follows:
import styled, { keyframes } from 'styled-components';
export const Container = styled.div`
font-size: 14px;
position: fixed;
z-index: 10;
& button {
float: right;
background: none;
border: none;
color: #fff;
opacity: 0.8;
cursor: pointer;
}
`;
const toastEnter = keyframes`
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
`;
export const BottomRight = styled(Container)`
bottom: 2rem;
right: 1rem;
`;
export const Notification = styled.div`
width: 365px;
color: #fff;
padding: 15px 15px 10px 10px;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 0 10px #999;
opacity: 0.9;
transition .1s ease;
animation: ${toastEnter} 0.5s;
&:hover {
box-shadow: 0 0 12px #fff;
opacity: 1;
}
`;
export const Title = styled.p`
font-weight: 700;
font-size: 16px;
text-align: left;
margin-bottom: 6px;
`;
export const Description = styled.p`
text-align: left;
`;
When I click a button, I just add an element to the state list, like:
toastProps = {
id: list.length + 1,
title: 'Success',
description: 'Sentence copied to clipboard!',
backgroundColor: '#5cb85c',
};
setList([...list, toastProps]);
My component is rendered like:
<Toast toastList={list} setList={setList}></Toast>
I would like to add animation when a toast exits, but do not know how. I have tried changing the style according to an additional prop I would send to the styled components, but this way all the toasts animate at the same time. My intuition is that I should use useRef(), but I am not sure how. Thanks in advance for any help you can provide.
Could you please help me with one issue? I'd like to make a closing modal window by clicking on backdrop (using ReactJS). But in result window is closing even i click on modal window (at any position).
Here is my code:
import React from "react";
import { Fragment } from "react";
import ReactDOM from "react-dom";
import "./_Modal.scss";
const Backdrop = (props) => {
return (
<div className="backdrop" onClick={props.onClose}>
{props.children}
</div>
);
};
const ModalOverlay = (props) => {
return <div className="modal">{props.children}</div>;
};
const portalItem = document.getElementById("overlays");
const Modal = (props) => {
return (
<Fragment>
{ReactDOM.createPortal(
<Backdrop onClose={props.onClose}>
<ModalOverlay>{props.children}</ModalOverlay>
</Backdrop>,
portalItem
)}
</Fragment>
);
};
export default Modal;
And here is CSS:
.backdrop {
position: fixed;
top:0;
left: 0;
width: 100%;
height: 100vh;
z-index: 20;
background-color: rgba(0,0,0, 0.7);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
padding-bottom: 15vh;
}
.modal {
position: relative;
max-width: 70%;
top: 5vh;
background-color: $darkgrey;
padding: 1rem;
border-radius: 1.5rem;
box-shadow: 0 1rem 1rem rgba(0,0,0, 0.25);
z-index: 30;
}
}
I'm just start to learning frontend, therefore do not judge strictly )
Just for understanding other people: after adding event.stopPropagation() in ModalOverlay everything works!
const ModalOverlay = (props) => {
return (
<div
className="modal"
onClick={(event) => {
event.stopPropagation();
}}
>
{props.children}
</div>
);
};
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
}
So I have a simple navbar that when hits a certain media query it turns into a hamburger, and when I click on hamburger-icon, I want the new navbar element slide over. Here is how I have it setup.
Layout Component
const Layout = ({ children }) => {
const [isOpen, setIsOpen] = React.useState(false);
const toggleSidebar = () => {
setIsOpen(!isOpen)
}
return (
<>
<Navbar toggleSidebar={toggleSidebar} />
<Sidebar isOpen={isOpen} toggleSidebar={toggleSidebar}/>
{children}
<Footer />
</>
)
}
Sidebar Component
const SidebarAside = styled.aside`
background: ${props => props.theme.grey10};
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
display: grid;
place-items: center;
opacity: 0;
transition: ${props => props.theme.transition};
transform: translateX(-100%);
${props =>
props.showsidebar && css`
opacity: 1;
tranform: translateX(0);
`
}
`
const CloseButton = styled.button`
position: absolute;
right: 4.75%;
top: 2.75%;
font-size: 2.5rem;
background: transparent;
border-color: transparent;
color: ${props => props.theme.redDark};
cursor: pointer;
`
const Sidebar = ({isOpen, toggleSidebar,}) => {
return (
<SidebarAside showsidebar={isOpen}>
<CloseButton onClick={toggleSidebar}>
<FaTimes />
</CloseButton>
</SidebarAside>
)
}
First time using styled-components, and not totally sure what my angle for this should be.
Ultimately I figured it out, with just changing the syntax in the styled component to...
Sidebar Styled Component
opacity: ${props => props.showsidebar ? '1' : '0'};
transform: ${props => props.showsidebar ? 'translateX(0)' : 'translateX(-100%)'};
`
const Sidebar = ({isOpen, toggleSidebar,}) => {
return (
<SidebarAside showsidebar={isOpen}>
<CloseButton onClick={toggleSidebar}>
<FaTimes />
</CloseButton>
</SideBarAside>