Changing a styled-component style based on state at the top of app - reactjs

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>

Related

Passing a direction prop into styled-components keyframe component?

I'm trying to implement a reusable text animation component where a direction prop can be passed in. I probably close but doesn't seem to be working. Also open to better ways of implementing it a better way.
import React from "react"
import styled from "styled-components"
import { GlobalStyles } from "./global"
import TextAnimations from "./Components/TextAnimations"
const Container = styled.div`
display: flex;
justify-content: space-between;
flex-wrap: wrap;
`
const NavBar = styled.nav`
background: #3a3a55;
padding: 0.25rem;
width: 100%;
height: 10vh;
`
const Main = styled.main`
background: #3a3a55;
color: white;
padding: 0.25rem;
flex: 10 1 auto;
height: 100vh;
`
const Break = styled.div`
flex-basis: 100%;
width: 0;
`
function App() {
return (
<>
<GlobalStyles />
<Container>
<NavBar>NavBar</NavBar>
<Break />
<Main>
<TextAnimations text='Sample text from the left' direction='left' />
<TextAnimations text='Sample text from the right' direction='right' />
</Main>
</Container>
</>
)
}
export default App
and then the animation component:
import { motion } from "framer-motion"
import styled, { keyframes } from "styled-components"
type TextAnimationProps = {
text: string
direction: string
}
const Left = keyframes`
0% { left: -3.125em; }
100% { left: 3em;}
`
const Right = keyframes`
0% { Right: -3.125em; }
100% { Right: 3em;}
`
const HomeHeader = styled.div`
h1 {
font-weight: lighter;
}
position: relative;
top: 0;
animation: ${(props) => (props.defaultValue === "left" ? Left : Right)} // is this right?
animation-duration: 3s;
animation-fill-mode: forwards;
`
const TextAnimations = ({ text, direction }: TextAnimationProps) => {
return (
<HomeHeader defaultValue={direction}>
<h1>{text}</h1>
</HomeHeader>
)
}
export default TextAnimations
I'd also like to make it more flexible so that I can add 'top', 'bottom', etc.
Is this the best way to handle text animations?
You can create a separate function to set the animation. The function will receive the props of the styled component from which the function will access the direction prop.
const setHomeHeaderAnimation = ({ direction = "left" }) => {
const animation = keyframes`
0% {
${direction}: -3.125em;
}
100% {
${direction}: 3em;
}
`;
return css`
position: relative;
animation: ${animation};
animation-duration: 3s;
animation-fill-mode: forwards;
`;
};
const HomeHeader = styled.div`
${setHomeHeaderAnimation}
h1 {
font-weight: lighter;
}
`;
const App = () => (
<div>
<HomeHeader>
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="right">
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="top">
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="bottom">
<div>Some text</div>
</HomeHeader>
</div>
);

How to add functions inside styled-components

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>
</>
);
}

React Carousel target div elements

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>
);
}

Transition on div doesn't work in react styled-component

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.

Styled-components: Styles are not applied when trying to style already styled component

I'm trying to style my component which was styled already. But styles in the new rules are not applied in output CSS.
Can I style component that I already styled?
Thanks you in advance for your help.
EDIT: Add rest of LanugageChooser definition
// COMPONENT THAT I'M TRYING TO STYLE
const LanguageChooser = () => {
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
return (
<Container>
<Flag { ...languages.pl }/>
<Flag { ...languages.en }/>
</Container>
)
}
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: .2s all;
&:hover {
font-size: 36px;
}
`;
return (
<Button onClick={ () => i18n.changeLanguage(language) }>{ flag }</Button>
)
}
// TRYING ADD MARGIN LEFT, BUT THERE IS NO RESULT.
// ANY OTHER CSS PROPERTY ARE NOT APPLYING
const Navbar = ({ color }) => {
...
const StyledLanguageChooser = styled(LanguageChooser)`
margin-left: auto;
`;
const Nav = styled.nav`
display: flex;
align-content:center;
background: ${ color };
padding: 2px 3px;
`;
return (
<Nav className="navbar">
<StyledNavLink to="/home">...</StyledNavLink>
<StyledNavLink to="/maps">...</StyledNavLink>
<StyledNavLink to="/charts">...</StyledNavLink>
<StyledLanguageChooser/>
</Nav>
)
}
First, move the styled component outside of function scope or your style will reset on every render and you will get heavy performance issues.
Secondly, in order to apply styles, you need to pass className property.
See Styling normal React components
If you use the styled(MyComponent) notation and MyComponent does not render the passed-in className prop, then no styles will be applied.
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
const LanguageChooser = ({ className }) => {
return (
<Container className={className}>
<Flag {...languages.pl} />
<Flag {...languages.en} />
</Container>
);
};
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: 0.2s all;
&:hover {
font-size: 36px;
}
`;
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
return <Button onClick={() => i18n.changeLanguage(language)}>{flag}</Button>;
};

Resources