I'm new to styled-components; I have a hamburger component and I'm trying to make a transition when the burger menu turns into an X using a styled-component.
I added a transition: all 0.3s linear to the div but I just can't figure out what is going wrong with this transition.
export default (props) => {
const StyledBurger = styled.div`
width: 2rem;
height: 2rem;
position: fixed;
top: 15px;
left: 20px;
z-index: 10;
display: none;
div {
transition: all 0.3s linear;
width: 2rem;
height: 0.25rem;
background-color: ${({ open }) => (open ? "#ccc" : "#333")};
border-radius: 10px;
transform-origin: 1px;
&:nth-child(1) {
transform: ${({ open }) => (open ? "rotate(45deg)" : "rotate(0)")};
}
&:nth-child(2) {
transform: ${({ open }) =>
open ? "translateX(100%)" : "translateX(0)"};
opacity: ${({ open }) => (open ? 0 : 1)};
}
&:nth-child(3) {
transform: ${({ open }) => (open ? "rotate(-45deg)" : "rotate(0)")};
}
}
`;
const [open, setOpen] = useState(false);
return (
<>
<StyledBurger open={open} onClick={() => setOpen(!open)}>
<div />
<div />
<div />
</StyledBurger>
</>
);
};
You have "display: none" in your StyledBurger. That's probably why you don't see children.
Related
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
i pass the open a prop to the component which is a boolean
i want to add a setTimeout function to hide a component but it shows syntax error
Timeout' is not assignable to parameter of type
'Interpolation<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>,
HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>>
here is what i tried
const DrawerContent = styled.div<{ open: boolean; visible?: any }>`
transition: 0.3s all;
${({ open, visible }) =>
open
? css`
display: flex;
width: 200px;
height: 100px;
background-color: brown;
position: absolute;
top: 0;
`
: setTimeout(() => { // i want to add this line
css`
display: none;
`;
}, 200)}
`;
You can't get a return value from the setTimeout callback function. If you are trying to hide the node after some time, you should use keyframes:
const hide = keyframes`
to {
width:0;
height:0;
overflow:hidden;
}
`;
const DrawerContent = styled.div`
transition: 0.3s all;
${({ open, visible }) =>
open &&
css`
display: flex;
width: 200px;
height: 100px;
background-color: brown;
position: absolute;
top: 0;
animation: ${hide} 0s ease-in 2s forwards;
`}
`;
Another solution is to move the timeout function outside the DrawerContent component:
const DrawerContent = styled.div`
transition: 0.3s all;
${({ open, visible }) =>
open &&
css`
display: flex;
width: 200px;
height: 100px;
background-color: brown;
position: absolute;
top: 0;
`}
`;
function App () {
const [show, setShow] = useState(true);
useEffect(() => {
let timeout;
if (show) {
timeout = setTimeout(() => {
setShow(false);
}, 2000);
}
return () => clearTimeout(timeout);
}, [show]);
return (
<>
<div>
<DrawerContent open={show} visible />
</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
}
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>
);
}
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>