Hi I'm trying to configure the colour of a button base on it's button type which will be put in a modal. In this case, either "Success" or "Danger":
import React from "react";
import styled from "styled-components";
const ButtonStyled = styled.button`
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;
const Button = (props) => {
console.log(props.btnType);
return (
<ButtonStyled btnType={props.btnType} onClick={props.clicked}>
{props.children}
</ButtonStyled>
);
};
export default Button;
Here's where I called the button:
import React from "react";
import Button from "../../UI/Button/Button";
const OrderSummary = (props) => {
return (
<div>
<Button btnType="Danger" clicked={props.cancelPurchase}>
Cancel
</Button>
<Button btnType="Success" clicked={props.continuePurchase}>
Continue
</Button>
</div>
);
};
export default OrderSummary;
Here is where the Order Summary being called:
import React, { useState } from "react";
import Modal from "../../components/UI/Modal/Modal";
import OrderSummary from "../../components/Burger/OrderSummary/OrderSummary";
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7,
};
const BurgerBuilder = () => {
const [ingredients, setIngredients] = useState({
salad: 0,
bacon: 0,
cheese: 0,
meat: 0,
});
const [totalPrice, setTotalPrice] = useState(4);
const [purchasing, setPurchasing] = useState(false);
const purchaseCancelHandler = () => {
setPurchasing(false);
};
const purchaseContinueHandler = () => {
alert("continue");
};
return (
<div>
<Modal show={purchasing} modalClosed={purchaseCancelHandler}>
<OrderSummary
continuePurchase={purchaseContinueHandler}
cancelPurchase={purchaseCancelHandler}
ingredients={ingredients}
price={totalPrice.toFixed(2)}
/>
</Modal>
</div>
);
};
export default BurgerBuilder;
Down here is the modal where the Button being applied
import React from "react";
import styled from "styled-components";
import Backdrop from "../Backdrop/Backdrop";
const ModalWrapper = styled.div`
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
justify-content: center;
align-items: center;
color: #000;
display: grid;
grid-template-columns: 1fr 1fr;
position: fixed;
z-index: 500;
border-radius: 10px;
transition: all 0.3s ease-out;
padding: 16px;
left: 15%;
top: 30%;
#media (min-width: 600px) {
width: 500px;
left: calc(50% - 250px);
}
`;
const ModalContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1.8;
color: #141414;
p {
margin-bottom: 1rem;
}
button {
padding: 10px 24px;
background: #141414;
// color: #fff;
border: none;
}
`;
const Modal = (props) => {
return (
<div>
<Backdrop show={props.show} clicked={props.modalClosed} />
<ModalWrapper
style={{
transform: props.show ? "translateY(0)" : "translateY(-100vh)",
opacity: props.show ? "1" : "0",
}}
>
<ModalContent>{props.children}</ModalContent>
</ModalWrapper>
</div>
// <ModalStyled>{props.children}</ModalStyled>
);
};
export default Modal;
As I did some testing, the values in the styled component Modal Content which state the button in that content will be that. Hence it affected the value of the styled component Button Styled. However, if I removed the value of button in Modal Content, the value of the color in ButtonStyled won't be accepted. Does anyone happen to know why?
The button style defined in the ModalContent is overriding the button style you've defined in your Button component.
button {
padding: 10px 24px;
background: #141414; // <-- overrides button background color
border: none;
}
With CSS the most specific selector will be the one that sets the style and I suspect the ModalContent styled component's computed style results with a more specific selector for button elements than the one from ButtonStyled.
What you can do is bump the selector specificity of the ButtonStyled background color.
Pseudoelements, pseudoselectors, and nesting, the last section.
Finally, the ampersand can be used to increase the specificity of
rules on the component; this can be useful if you are dealing with a
mixed styled-components and vanilla CSS environment where there might
be conflicting styles
const ButtonStyled = styled.button`
&& {
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
}
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;
Related
I am building my own SpeedDial which is inspired by the Material UI one, but can't currently figure out how I can make the hidden buttons transition smoothly like they do in the example link above when the SpeedDial is hovered. It's not like they are sliding out, but each button appears one after the other smoothly. If you go to the link above, you will see what I mean. Select the "left" direction, since it is most like my example code.
Here is a quick screenshot of what the SpeedDial looks like, just in case you are wondering.
I have a working CodeSandbox where I have the entire Speed Dial built, but it's lacking the animation I am looking for. My initial thought was to not conditionally render the components, and have them always visible, but use visibility: hidden. However, I don't think doing that solves my problem, and won't allow for any transitions to be set.
For sake of completeness, I will include all of the code, but I highly recommend that you just mess around with the CodeSandbox, as it already works.
App.js
import { useState } from "react";
import SpeedDial from "./components/SpeedDial";
import SpeedDialAction from "./components/SpeedDialAction";
import "./styles.css";
const actions = [
{ label: "share", icon: "share " },
{ label: "print", icon: "print" },
{ label: "save", icon: "floppy-o" },
{ label: "copy", icon: "copy" }
];
export default function App() {
const [activeAction, setActiveAction] = useState("");
return (
<div className="App">
<SpeedDial
direction="left"
style={{
position: "absolute",
bottom: 40,
right: 40
}}
>
{actions.map((action) => (
<SpeedDialAction
key={action.label}
direction="left"
setActiveAction={setActiveAction}
hovered={action.label === activeAction}
label={action.label}
icon={action.icon}
/>
))}
</SpeedDial>
</div>
);
}
Basically, the SpeedDial component accepts children, specifically an array that will create several SpeedDialAction components. When the SpeedDial button is hovered, the SpeedDialAction components become visible next to the main SpeedDial button (the blue button). When the mouse is moved out of the SpeedDial, they disappear.
SpeedDial.js
import React, { useState } from "react";
import Icon from "./Icon";
import "./SpeedDial.css";
export default function SpeedDial({ children, direction, style }) {
const [hovered, setHovered] = useState(false);
return (
<div
className={`speed-dial ${direction}`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => {
setHovered(false);
}}
onClick={() => {
setHovered(!hovered);
}}
style={style}
>
<button className="main-btn">
<Icon
name="plus"
style={{
transform: hovered ? "rotate(45deg" : "none",
transition: "transform ease .25s"
}}
/>
</button>
<div className={`action-wrapper ${direction}`}>{hovered && children}</div>
</div>
);
}
SpeedDial.defaultProps = {
direction: "right",
onClick: () => {}
};
Notice how in the action-wrapper that this is where we are conditionally rendering the SpeedDialAction components via the children prop. I am aware that conditionally rendering like this works pretty much like display: none, and won't allow me to animate it. That's why I hope you can help!
SpeedDial.css
.speed-dial {
display: inline-flex;
position: relative;
align-items: center;
width: auto;
}
.speed-dial.top {
flex-direction: column-reverse;
align-items: flex-start;
justify-content: center;
}
.speed-dial.bottom {
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.speed-dial.left {
flex-direction: row-reverse;
}
.speed-dial > .main-btn {
background: rgb(25, 118, 210);
height: 60px;
width: 60px;
border-radius: 50%;
color: white;
font-size: 18px;
cursor: pointer;
box-shadow: rgb(0 0 0 / 20%) 0px 3px 5px -1px,
rgb(0 0 0 / 14%) 0px 6px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 18px 0px;
}
.speed-dial > .action-wrapper {
display: flex;
}
.speed-dial > .action-wrapper.top {
flex-direction: column;
align-items: flex-start;
transform: translateX(7px);
}
.speed-dial > .action-wrapper.bottom {
flex-direction: column;
align-items: flex-start;
transform: translateX(7px);
}
SpeedDialAction.js
import React from "react";
import Icon from "./Icon";
import "./SpeedDialAction.css";
export default function SpeedDialAction({
icon,
label,
hovered,
setActiveAction,
direction,
onClick
}) {
return (
<div className="speed-dial-action" onClick={onClick}>
{hovered && <div className={`label ${direction}`}>{label}</div>}
<button
onMouseEnter={() => setActiveAction(label)}
onMouseLeave={() => setActiveAction("")}
className={direction}
>
<Icon name={icon} />
</button>
</div>
);
}
Nothing crazy going on here, I just added it for sake of thoroughness.
SpeedDialAction.css
.speed-dial-action {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.speed-dial-action button {
box-shadow: rgb(0 0 0 / 20%) 0px 3px 5px -1px,
rgb(0 0 0 / 14%) 0px 6px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 18px 0px;
background: white;
height: 45px;
width: 45px;
border-radius: 50%;
font-size: 20px;
cursor: pointer;
}
.speed-dial-action button:hover {
background: #d8d8d8;
}
.speed-dial-action button.left {
margin-right: 15px;
}
.speed-dial-action button.right {
margin-left: 15px;
}
.speed-dial-action button.bottom {
margin-top: 15px;
}
.speed-dial-action button.top {
margin-bottom: 15px;
}
.speed-dial-action .label {
background: #565656;
color: white;
padding: 5px 8px;
position: absolute;
border-radius: 4px;
text-transform: capitalize;
}
.speed-dial-action .label.left {
top: -40px;
}
.speed-dial-action .label.right {
top: -40px;
}
List item
I've created a React Iframe (using 'react-frame-component'), and some styled components. When I try to render the styled components inside the React Iframe, the styled components inherits the Parent's (App.css) style, and loses its unique style. Is it possible to maintain the styled component?
Here is my code:
Content.js
import React from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import App from "./App";
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<FrameContextConsumer>
{
({document, window}) => {
return <App document={document} window={window} isExt={true}/>
}
}
</FrameContextConsumer>
</Frame>
)
}
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Card from './components/Card/index';
class App extends Component {
render() {
return (
<>
<div className="App">
{this.props.isExt ?
<img src={chrome.runtime.getURL("static/media/logo.svg")} className="App-logo" alt="logo" />
:
<img src={logo} className="App-logo" alt="logo" />
}
<Card />
</div>
</>
);
}
}
export default App;
Style:
import React from "react";
import styled, { css } from "styled-components";
export const CardWrapper = styled.div`
overflow: hidden;
padding: 0 0 32px;
margin: 48px auto 0;
width: 300px;
font-family: Quicksand, arial, sans-serif;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05), 0 0px 40px rgba(0, 0, 0, 0.08);
border-radius: 5px;
`;
export const CardHeader = styled.header`
padding-top: 32px;
padding-bottom: 32px;
`;
export const CardHeading = styled.h1`
font-size: 24px;
font-weight: bold;
text-align: center;
`;
export const CardBody = styled.div`
padding-right: 32px;
padding-left: 32px;
`;
export const CardFieldset = styled.fieldset`
position: relative;
padding: 0;
margin: 0;
border: 0;
& + & {
margin-top: 24px;
}
&:nth-last-of-type(2) {
margin-top: 32px;
}
&:last-of-type {
text-align: center;
}
`;
export const CardInput = styled.input`
padding: 7px 0;
width: 100%;
font-family: inherit;
font-size: 14px;
border-top: 0;
border-right: 0;
border-bottom: 1px solid #ddd;
border-left: 0;
transition: border-bottom-color 0.25s ease-in;
&:focus {
border-bottom-color: #e5195f;
outline: 0;
}
`;
export const CardIcon = styled.span`
color: #666;
cursor: pointer;
opacity: .25;
transition: opacity .25s ease-in;
&:hover {
opacity: .95;
}
${props =>
props.big &&
css`
font-size: 26px;
`}
${props =>
props.eye &&
css`
position: absolute;
top: 8px;
right: 0;
`}
${props =>
props.small &&
css`
font-size: 14px;
`}
`;
export const CardOptionsNote = styled.small`
padding-top: 8px;
display: block;
width: 100%;
font-size: 12px;
text-align: center;
text-transform: uppercase;
`;
export const CardOptions = styled.ul`
padding: 0;
margin: 16px 0 8px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
width: 100%;
list-style-type: none;
`;
export const CardOptionsItem = styled.li`
&:nth-of-type(n + 2) {
margin-left: 16px;
}
`;
export const CardButton = styled.button`
display: block;
width: 100%;
padding: 12px 0;
font-family: inherit;
font-size: 14px;
font-weight: 700;
color: #fff;
background-color: #e5195f;
border: 0;
border-radius: 35px;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.08);
cursor: pointer;
transition: all 0.25s cubic-bezier(0.02, 0.01, 0.47, 1);
&:hover {
box-shadow: 0 15px 15px rgba(0, 0, 0, 0.16);
transform: translate(0, -5px);
}
`;
export const CardLink = styled.a`
display: inline-block;
font-size: 12px;
text-decoration: none;
color: #aaa;
border-bottom: 1px solid #ddd;
cursor: pointer;
transition: color 0.25s ease-in;
&:hover {
color: #777;
}
`;
Component:
import React from "react";
import {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardIcon,
CardFieldset,
CardInput,
CardOptionsItem,
CardOptions,
CardOptionsNote,
CardButton,
CardLink
} from './Card';
const Card = () => {
return (
<>
<CardWrapper>
<CardHeader>
<CardHeading>Sign in</CardHeading>
</CardHeader>
<CardBody>
<CardFieldset>
<CardInput placeholder="Username" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="E-mail" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="Password" type="password" required />
<CardIcon className="fa fa-eye" eye small />
</CardFieldset>
<CardFieldset>
<CardOptionsNote>Or sign up with</CardOptionsNote>
<CardOptions>
<CardOptionsItem>
<CardIcon className="fab fa-google" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-twitter" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-facebook" big />
</CardOptionsItem>
</CardOptions>
</CardFieldset>
<CardFieldset>
<CardButton type="button">Sign Up</CardButton>
</CardFieldset>
<CardFieldset>
<CardLink>I already have an account</CardLink>
</CardFieldset>
</CardBody>
</CardWrapper>
</>
);
};
export default Card;
Attempt at injecting stylesheet for CardWrapper:
/*global chrome*/
/* src/content.js */
import React from 'react';
import { useContext, FrameContext } from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
import App from "./App";
export const StyledFrame = (props) => {
const {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardFieldset,
CardInput,
CardIcon,
CardOptionsNote,
CardOptions,
CardOptionsItem,
CardButton,
CardLink,
} = props;
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<InjectFrameStyles>
{props.CardWrapper}
</InjectFrameStyles>
</Frame>
)
}
}
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.CardWrapper}</StyleSheetManager>;
}
const app = document.createElement('div');
app.id = "my-extension-root";
document.body.appendChild(app);
ReactDOM.render(<Main />, app);
app.style.display = "none";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clicked_browser_action") {
toggle();
}
}
);
function toggle(){
if(app.style.display === "none"){
app.style.display = "block";
}else{
app.style.display = "none";
}
}
I had the same problem. I ended up creating a helper component called StyledFrame.
What happens is that your styled-components css is being rendered in the parent frame. You need to intercept those styles, and render them inside your iframe instead. Thats what StyleSheetManager is for.
You can use StyledFrame just like you would the Frame component.
import React, { useContext } from 'react';
import Frame, { FrameContext } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
export const StyledFrame = (props) => {
const { style, children, ...otherProps } = props;
return (
<Frame
initialContent={
'<!DOCTYPE html><html><head></head><body><div class="frame-root"></div><div id="modal-root"></div></body></html>'
}
style={{ display: 'block', overflow: 'scroll', border: 0, ...style }}
{...otherProps}
>
<InjectFrameStyles>{props.children}</InjectFrameStyles>
</Frame>
);
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.children}</StyleSheetManager>;
};
How do I make a child component's border go on top (higher z-index) of its parent component's border? Z-index didn't work for me.
Wanted behavior: clicking on the tab enables green border-bottom to show that the tab is clicked. This green border overlaps on top of the default gray border that is set throughout all the tabs.
https://codesandbox.io/s/navbar-component-d5f1c?file=/Navbar.js
import React, { useState } from 'react';
import styled from "styled-components";
const Navbar = ({ value, children, tabFilter, contentFilter }) => {
const [activeTab, setActiveTab] = useState(value[0].title);
const onClickTabItem = tab => {
setActiveTab(tab);
}
return (
<React.Fragment>
<NavbarOutline id="main">
<ol>
{value.map(child => {
const { title } = child;
return <Tab activeTab={activeTab} key={title} title={title} handleClick={onClickTabItem} />;
})}
</ol>
</NavbarOutline>
<div>
{value.map(child => {
if (child.title !== activeTab) return undefined;
return <StyledTabs className="content">{child.title}</StyledTabs>
})}
</div>
</React.Fragment>
);
}
const Tab = props => {
const { activeTab, title, handleClick } = props;
let className = 'not-active';
const onClick = () => {
handleClick(title);
};
if (activeTab === title) {
className = 'active';
}
return (
<StyledTabs className={className} onClick={onClick}>
{title}
</StyledTabs>
);
};
// eslint-disable-next-line import/prefer-default-export
export const NavbarOutline = styled.div`
margin-left: 35px;
margin-right: 35px;
overflow-x: auto;
white-space: nowrap;
border-bottom: 2px solid #e3e3e3;
top: 0px;
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
&::-webkit-scrollbar {
display: none; /* Safari and Chrome */
}
`;
const StyledTabs = styled.button.attrs(props => ({
className: props.className
}))`
&.not-active {
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 20px;
padding: 16px 31px 16px 31px;
background: none;
border: none;
position: relative;
bottom: -29px;
}
&.active {
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 20px;
position: relative;
bottom: -29px;
z-index: 3;
background: none;
color: #2b8000;
border: none;
border-bottom: 3px solid #2b8000;
}
&.content {
background: none;
border: none;
}
`;
The parent div with id="main" has overflow-x: auto, which is causing your child tab element to disappear when it goes outside of it. It's probably what you want, but that's why you can't see the green border.
Open the devtools and untick overflow-x: auto to see for yourself.
I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.
I have a simple custom React carousel:
https://codesandbox.io/s/infallible-wood-740s2?fontsize=14
It goes through numbers from 1 to 100. I'm trying to have an active number centered with large font size, and its neighbors with smaller font size:
To progress through this carousel, you have to click on the carousel.
The issue is that progressing through the carousel numbers starts to misalign. I suppose that it is because of the wrong calculation for left property.
I've tried to tweak the formula, use React Transition Group, and find some existing package which could solve this issue, but I haven't succeeded. I would appreciate any help.
Component code:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import {
Wrapper,
NumbersWrapper,
NumbersScroller,
NumberText
} from "./Numbers.style";
const hundred = new Array(100)
.fill(0)
.map((k, v) => ({ key: v, label: v + 1 }));
class Numbers extends Component {
constructor(props) {
super(props);
this.state = {
activeNumber: 0
};
}
setActiveNumber(number) {
this.setState({
activeNumber: number
});
}
render() {
const { activeNumber } = this.state;
const intAciveNumber = Number(activeNumber);
return (
<Wrapper>
<NumbersWrapper
onClick={() => this.setActiveNumber(intAciveNumber + 1)}
>
<NumbersScroller
style={{
left: `${130 - intAciveNumber * 55}px`
}}
>
{hundred.map(({ key, label }) => {
const isNeighbor =
key + 1 === activeNumber || key - 1 === activeNumber;
const isActive = key === activeNumber;
return (
<NumberText
key={key}
isNeighbor={isNeighbor}
isActive={isActive}
>
{label}
</NumberText>
);
})}
</NumbersScroller>
</NumbersWrapper>
</Wrapper>
);
}
}
export default Numbers;
const rootElement = document.getElementById("root");
ReactDOM.render(<Numbers />, rootElement);
Numbers.style.js:
import styled from "styled-components";
export const Wrapper = styled.div`
height: 549px;
width: 612px;
border-radius: 28px;
background-color: #ffffff;
margin-top: 116px;
padding: 26px 0 0 0;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
left: 50%;
top: 0%;
transform: translate(-50%, -10%);
overflow: hidden;
`;
export const NumbersWrapper = styled.div`
white-space: nowrap;
width: 359.5px;
overflow: hidden;
`;
export const NumbersScroller = styled.div`
transition: all 150ms ease-in;
position: relative;
left: 130px;
`;
const numberTextStyle = props => {
if (props.isNeighbor) {
return `
height: 88px;
width: 53px;
opacity: 0.45;
color: #6C879C;
font-size: 80px;
font-weight: 300;
letter-spacing: -1.6px;
line-height: 88px;
text-align: center;
`;
}
if (props.isActive) {
return `
height: 156px;
color: #6C879C;
font-size: 150px;
font-weight: 300;
letter-spacing: -3px;
line-height: 156px;
text-align: center;
`;
}
return `
opacity: 0.2;
color: #6C879C;
font-size: 40px;
font-weight: 300;
letter-spacing: -0.8px;
line-height: 48px;
text-align: center;
`;
};
export const NumberText = styled.span`
font-family: Avenir;
margin: 0 15px;
user-select: none;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
${props => numberTextStyle(props)}
`;