In the stepper component that I am working on, I am using the React Styled Components library. Unfortunately, I can't use Material UI or another similar solution. Originally, I was using CSS which rendered all of the UI for the stepper:
{index !== steps.length - 1 && (
<div
className={`divider-line divider-line_${steps.length}`}
/>
)}
This worked, but now I have to use this library, so I converted the I've now converted the horizontal line CSS as styled-components.
styled.js
export const DividerLine = styled.div`
height: 1px;
background-color: blue;
position: absolute;
top: 20%;
left: 70%;
`;
export const DividerLine_2 = styled.div`
width: 296%;
`;
export const DividerLine_3 = styled.div`
width: 125%;
`;
export const DividerLine_4 = styled.div`
width: 70%;
`;
export const DividerLine_5 = styled.div`
width: 60%;
`;
The rest of the stepper animations work but the original code does not render the horizontal lines.
I ran into a similar problem with rendering the numbered bubbles in various states, which I resolved by creating variables to use within inline styling:
let stepColor = '#65CC34';
let stepText = '#ffffff';
let stepTextActive = '#65CC34';
Stepper.js
<StepContainer key={index}>
<StepWrapper>
<StepNumber
style={{
background: `${
step.selected ? stepColor : ''
}`,
color: `${
step.selected ? stepText : stepTextActive
}`,
}}
>
{step.completed ? <span>✓</span> : index + 1}
</StepNumber>
{index !== steps.length - 1 && (
<div
className={`divider-line divider-line_${steps.length}`}
/>
)}
</StepWrapper>
<DividerLine />
</StepContainer>
);
});
return <StepWrapper>{stepsJSX}</StepWrapper>;
}
}
However, I'm not sure how to use {`divider-line divider-line_${steps.length}`} inside of the styled component. Any suggestions?
you don't need to use multiple classes with styled-components. Here's how you can do it:
const dividerLineWidths = {
'2': '296%',
'3': '125%',
'4': '70%',
'5': '50%'
}
export const DividerLine = styled.div`
height: 1px;
background-color: blue;
position: absolute;
top: 20%;
left: 70%;
${({steps}) => steps && `
width: ${dividerLineWidths[steps.length]}
`}
`;
Then:
...
{index !== steps.length - 1 && <DividerLine steps={steps} />}
...
Styled components will dynamically generate a new className for you depending on the props being passed to it. Think of the styled-components consts as full components as opposed to blobs of css properties.
Related
I am trying to extent react component in styled-component and trying to add custom style on extended component but unable to see the style changes that I am applying
I have created a button component in /src/newbutton.js with following code
import styled from "styled-components";
const Button = styled.button`
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
export const NewButton = ({ className, children }) => {
return (
<Button primary>Primary</Button>
)
}
And extending and creating another button component with custom style in /src/custom-button.js with following code
import styled from "styled-components";
import { NewButton } from './button'
const ButtonWrapper = styled(NewButton)`
width: 100%;
color: red
`;
const ExtendedButton = ({ className, children }) => {
return (
<ButtonWrapper />
)
}
I have added the custom style like width: 100% & color: red but it is not applying on ExtendedButton. Infect colour and width is same as NewButton
You need to pass a className to your NewButton in order to customize it, using styled-components.
Styled components works by creating a unique className that associated with a component and its CSS.
export const NewButton = ({ className, children }) => {
return (
<Button className={className} primary>Primary</Button>
)
}
I am posting the complete working code for future reference based on #Flat Globe solution. And it is working fine as expected.
I have modified the Button component code just by adding className in /src/newbutton.js with following code
import styled from "styled-components";
const Button = styled.button`
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
export const NewButton = ({ className, children }) => {
return (
<Button primary className={className}>Primary</Button>
)
}
I have also modified the extended-button code by just passing the className in /src/custom-button.js. check the full code below
import styled from "styled-components";
import { NewButton } from './button'
const ButtonWrapper = styled(NewButton)`
width: 100%;
color: red
`;
const ExtendedButton = ({ className, children }) => {
return (
<ButtonWrapper className="extended-button"/>
)
}
I am trying to build a react component with a large fixed div and a navbar below. Initially, the div should remain fixed at the top left and scroll through a number of slides as the user scrolls, before its display property reverts to absolute and it scrolls out of view. Meanwhile, the nav's position becomes sticky and scrolls to the top of the window where it remains.
I've managed to build something hacky using react-intersection-observer and a number of hidden 'ghost' elements which allow the slides to cycle between opacity 0 or 1, but the outcome is not great and I feel there must be an easier way to achieve this effect. In particular it is problematic having to account for the page position when the main component becomes absolute positioned and requires a 'top' value.
Any advice is appreciated thanks. Sample code is below:
import * as React from 'react';
import { useInView } from 'react-intersection-observer';
import styled from 'styled-components';
const OuterContainer = styled.div`
height: 500vh;
`;
const Card = styled.div`
box-sizing: border-box;
position: fixed;
top: 0;
width: 100%;
height:64vh;
transition: 1s;
z-index: 5;
&.hidden {
opacity: 0;
}
&.show {
opacity: 1;
}
&.border {
position: absolute;
top: 0;
}
`;
const AltCard = styled(Card)`
position: static;
`;
const CardContainer = styled.div`
height: 64vh;
`;
const FirstCard = styled(Card)`
opacity: 1;
`;
const CardGhost = styled.div`
height:64vh;
`;
const LastGhost = styled.div`
box-sizing: border-box;
`;
const GhostContainer = styled.div`
width: 100%;
position: absolute;
top: 0;
`;
const MainContainer = styled.div`
position: fixed;
width: 100%;
top: 0;
&.moving {
position: absolute;
top: 156.5vh;
}
`;
const HeaderBar = styled.div`
top: 64vh;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 8rem;
background-color: #44ff44;
`;
export default function IntroSection() {
const options = { threshold: 0.66 };
const [ref0, visible0] = useInView({ threshold: 0 });
const [ref1, visible1] = useInView(options);
const [ref2, visible2] = useInView(options);
const [ref3, visible3] = useInView(options);
const [ref4, visible4] = useInView({ threshold: 0.33 });
return (
<>
<OuterContainer>
<GhostContainer>
<CardGhost ref={ref0} />
<CardGhost ref={ref1} />
<CardGhost ref={ref2} />
<CardGhost ref={ref3} />
<LastGhost ref={ref4} />
</GhostContainer>
<CardContainer>
<FirstCard
className={visible0 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightBlue' }}
/>
<Card
className={visible1 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightGreen' }}
/>
<Card
className={visible2 ? 'show' : 'hidden'}
style={{ backgroundColor: 'lightYellow' }}
/>
</CardContainer>
</OuterContainer>
<MainContainer
className={visible4 && 'moving'}
>
<AltCard
className={visible3 ? 'show' : 'hidden'}
style={{ backgroundColor: '#ffBBFF' }}
/>
<HeaderBar><h1>THIS IS THE NAVBAR</h1></HeaderBar>
</MainContainer>
</>
);
}
I am trying to animate a position change of the active box. When the user clicks on the box, I would like the box to animate a margin-top change and the rest of the boxes to stay at 0px. When the user changes the active box, the previously active box will return to 0px and the new active box will acquire a margin-top of 50px.
I think I am nearly there but I haven't quite managed to get it to work yet. I would be really grateful if someone could help enlighten me as to what I am doing wrong.
import { useSpring, useSprings, animated } from "react-spring";
import styled from "styled-components";
const Nav = () => {
// Set Active Box
const [activeBox, setActiveBox] = useState(0);
const handleClick = (index) => {
setActiveBox(index);
};
// Boxes
const boxes = [box1, box2, box3, box4];
// useSprings
const springs = useSprings(
boxes.length,
boxes.map((box, index) => ({
marginTop: activeBox === index ? 50 : 0,
}))
);
return (
<NavContainer>
{springs.map((prop, index) => (
<StyledBox
style={prop}
key={index}
onClick={() => handleClick(index)}
currentActiveBox={activeBox === index}
>
<img src={boxes[index]} alt="" />
</StyledBox>
))}
</NavContainer>
);
};
export default Nav;
const NavContainer = styled.div`
position: absolute;
bottom: 0;
width: 200px;
height: 75px;
`;
const StyledBox = styled.div`
width: 25px;
height: 25px;
img {
width: 100%;
height: 100%;
}
`;
Thank you!
The issue was that animated was not added to the styled component!
const StyledBox = styled(animated.div)`
width: 25px;
height: 25px;
img {
width: 100%;
height: 100%;
}
I'm trying to pass some props when I use my custom component with Styled-component.
This is the container I want to focus on, and I want to change the flex-direction via props:
const InputInnerContainer = styled.View`
width: 100%;
height: 42px;
padding-horizontal: 5px;
flex-direction: ${props => props.right ? "row-reverse": "row"};
align-items: center;
justify-content: space-between;
border-radius: 4px;
border-width: 1px;
background-color: ${inputBackgroundColor};
border: 2px solid ${inputBorderColor};
`;
This is my custom component "Input":
const Input = ({ onChangeText, icon, value, label,iconPosition}) => {
return (
<InputContainer>
{label && <LabelText>{label}</LabelText>}
<InputInnerContainer {...iconPosition}>
<View>{icon && <LabelText>{icon}</LabelText>}</View>
<InputField onChangeText={onChangeText} value={value} />
</InputInnerContainer>
</InputContainer>
);
};
And this is where I call the custom component:
<Input
label="Password"
onChangeText={(text) => onChangeText(text)}
value={value}
icon="HIDE"
iconPosition="right"
/>
The props I want to pass is "iconPosition". But I'm not sure how to pass it into the Styled-component. I'm still relatively new to Styled-component, so any ideas are welcome.
Try without destructuring it. Not this way:
<InputInnerContainer {...iconPosition}/>
But this way:
<InputInnerContainer iconPosition={iconPosition}/>
You also need to update your styled component:
const InputInnerContainer = styled.View`
...
flex-direction: ${props => props.iconPosition === "right" ? "row-reverse" : "row"}
...
`
I would like to add some transition styling to my side navigation on my app. I am able to do this using normal classes however in this tutorial they use css modules and i am unsure how to do this using css modules.
I would like my nav to glide in and out, at the moment it jumps statically when the onClick function fires - toggleSideDrawer.
I have used this logic but I am not sure if it is doing anything:
className={props.toggleSideDrawer ? classes.SideDrawerOpen : classes.SideDrawer
Essentially i want that when the user clicks the toggle, the transform property switches from translateX(-100%) to translateX(0) but this is not happening.
Side nav code:
import React from "react";
import Logo from "../../Logo/Logo";
import NavigationItems from "../NavigationItems/NavigationItems";
import Backdrop from "../../UI/Backdrop/Backdrop";
import Aux from "../../../hoc/Aux";
import classes from "./SideDrawer.css";
const SideDrawer = props => {
return (
<Aux classname={classes.SideDrawer}>
<Backdrop
showBackdrop={props.showSideDrawer}
clicked={props.toggleSideDrawer}
/>
{props.showSideDrawer && (
<div
onClick={props.toggleSideDrawer}
className={
props.toggleSideDrawer ? classes.SideDrawerOpen : classes.SideDrawer
}
>
<div className={classes.Logo}>
<Logo />
</div>
<nav>
<NavigationItems />
</nav>
</div>
)}
</Aux>
);
};
export default SideDrawer;
Where the code is used in my Layout component:
import React, { useState } from "react";
import Aux from "../Aux";
import classes from "./Layout.css";
import Toolbar from "../../components/Navigation/Toolbar/Toolbar";
import SideDrawer from "../../components/Navigation/SideDrawer/SideDrawer";
const layout = props => {
const [showSideDrawer, setShowSideDrawer] = useState(false);
return (
<Aux>
<SideDrawer
showSideDrawer={showSideDrawer}
toggleSideDrawer={() => {
setShowSideDrawer(!showSideDrawer);
}}
/>
<Toolbar
onMenuClick={() => {
setShowSideDrawer(!showSideDrawer);
}}
/>
<main className={classes.mainContent}> {props.children} </main>
</Aux>
);
};
export default layout;
CSS:
.SideDrawer {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
background-color: white;
padding: 32px 16px;
box-sizing: border-box;
transform: translateX(-100%);
}
#media (min-width: 500px) {
.SideDrawer {
display: none;
}
}
.Logo {
height: 11%;
text-align: center;
}
.SideDrawerOpen {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
padding: 32px 16px;
box-sizing: border-box;
background-color: red;
transform: translateX(0);
transition: transform 0.3s ease-out;
}
The thing is that you need the element will has the transition rule all the time.
My suggestion is to set a static class which which will hold all the styles and addd another one only for overriding transform to make it move.
Something like that (it uses scss but it's easy to do it with css)
.SideDrawer {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
background-color: white;
padding: 32px 16px;
box-sizing: border-box;
transition: transform .3s ease;
transform: translateX(-100%);
&.show {
transform: translateX(0);
}
}
export const App = () => {
const [showSideDrawer, setShowSideDrawer] = useState(false);
const sidebarClasses = classname([
styles.SideDrawer,
{
[styles.show]: showSideDrawer
}
]);
const ToggleSidebar = () => {
return (
<button onClick={() => setShowSideDrawer(!showSideDrawer)}>
Toggle Sidebar
</button>
);
};
return (
<Fragment>
<h1>App</h1>
<div className={sidebarClasses}>
<div>Sidebar content</div>
<ToggleSidebar />
</div>
<ToggleSidebar />
</Fragment>
);
};
https://codesandbox.io/s/xenodochial-framework-04sbe?file=/src/App.jsx
#MoshFeu helped me fix this.
The problem is that you render the drawer only when showSideDrawer so before it becomes true, the sidebar is not in the DOM yet so the transition is not affecting it.
The solution is to keep it in the DOM all the time but toggle . Open class to change the style.
There are libraries that knows to make the transition works even for elements that are not in the DOM but it's a bit more complicated.
code fix for SideDrawer.js without the conditional within the return
class SideDrawer extends Component {
render() {
let sideDrawerClass = [classes.SideDrawer];
// SideDrawer will now be an array with the side drawer classes and the open class
if (this.props.showSideDrawer) {
sideDrawerClass.push(classes.Open);
}
return (
<Aux classname={classes.SideDrawer}>
<Backdrop
showBackdrop={this.props.showSideDrawer}
clicked={this.props.toggleSideDrawer}
/>
<div
className={sideDrawerClass.join(" ")}
onClick={this.props.toggleSideDrawer}
>
<div className={classes.Logo}>
<Logo />
</div>
<nav>
<NavigationItems />
</nav>
</div>
</Aux>
);
}
}
export default SideDrawer;