Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Stepper #42

Merged
merged 1 commit into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export default {
};

export const Default: React.FC = () => {
return <Spinner iconsWidth="50px" />;
return <Spinner size={50} />;
};
90 changes: 90 additions & 0 deletions packages/pancake-uikit/src/components/Stepper/Step.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from "react";
import styled from "styled-components";
import Flex from "../Box/Flex";
import Box from "../Box/Box";
import { StatusProps, StepProps } from "./types";

const getStepNumberFontColor = ({ theme, status }: StatusProps) => {
if (status === "past") {
return theme.colors.success;
}
if (status === "current") {
return theme.colors.invertedContrast;
}
return theme.colors.textDisabled;
};

const Connector = styled.div<StatusProps>`
position: absolute;
width: 4px;
height: 100%;
top: 50%;
left: calc(50% - 2px);
z-index: -1;
background-color: ${({ theme, status }) => theme.colors[status === "past" ? "success" : "textDisabled"]};
`;

const ChildrenWrapper = styled(Box)<{ isVisible: boolean }>`
${({ theme }) => theme.mediaQueries.md} {
visibility: ${({ isVisible }) => (isVisible ? "visible" : "hidden")};
}
`;

const ChildrenLeftWrapper = styled(ChildrenWrapper)`
display: none;
${({ theme }) => theme.mediaQueries.md} {
display: block;
margin-right: 16px;
}
`;

const ChildrenRightWrapper = styled(ChildrenWrapper)`
margin-left: 8px;
${({ theme }) => theme.mediaQueries.md} {
margin-left: 16px;
}
`;

const Wrapper = styled.div`
position: relative;
display: flex;
align-items: center;
`;

export const StepNumber = styled.div<StatusProps>`
box-shadow: 0px 1px 4px rgba(25, 19, 38, 0.15);
background-color: ${({ theme, status }) => theme.colors[status === "current" ? "secondary" : "invertedContrast"]};
border: 2px solid ${({ theme, status }) => (status === "past" ? theme.colors.success : "transparent")};
border-radius: ${({ theme }) => theme.radii.circle};
color: ${getStepNumberFontColor};
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
font-size: 32px;
width: 48px;
height: 48px;
${({ theme }) => theme.mediaQueries.md} {
font-size: 40px;
width: 80px;
height: 80px;
}
`;

/**
* ChildrenLeftWrapper and ChildrenRightWrapper are used on the non mobile version, to force the alternate layout.
* One of the child is hidden based on the step number.
*/
export const Step: React.FC<StepProps> = ({ index, status, numberOfSteps = 0, children }) => {
const isIndexPair = index % 2 === 0;
return (
<Flex mb={index < numberOfSteps - 1 ? "16px" : 0}>
<ChildrenLeftWrapper isVisible={!isIndexPair}>{children}</ChildrenLeftWrapper>
<Wrapper>
<StepNumber status={status}>{index + 1}</StepNumber>
{index < numberOfSteps - 1 && <Connector status={status} />}
</Wrapper>
<ChildrenRightWrapper isVisible={isIndexPair}>{children}</ChildrenRightWrapper>
</Flex>
);
};
25 changes: 25 additions & 0 deletions packages/pancake-uikit/src/components/Stepper/Stepper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import styled from "styled-components";
import { ThemedProps } from "./types";

const StepperWrapper = styled.div<ThemedProps>`
display: flex;
flex-direction: column;
width: fit-content;
`;

const Stepper: React.FC = ({ children }) => {
const numberOfSteps = React.Children.count(children);
return (
<StepperWrapper>
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { numberOfSteps });
}
return child;
})}
</StepperWrapper>
);
};

export default Stepper;
91 changes: 91 additions & 0 deletions packages/pancake-uikit/src/components/Stepper/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from "react";
import styled from "styled-components";
import Stepper from "./Stepper";
import { Step, StepNumber } from "./Step";
import { Status } from "./types";
import Card from "../Card/Card";
import CardBody from "../Card/CardBody";

export default {
title: "Components/Stepper",
component: Stepper,
argTypes: {},
};

const mock =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at, placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed, finibus neque. Phasellus consequat at lorem a venenatis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at, placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed, finibus neque. Phasellus consequat at lorem a venenatis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at, placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed, finibus neque. Phasellus consequat at lorem a venenatis.";
const steps = [mock, mock, mock, mock];
const status: Status[] = ["past", "current", "future", "future"];

const Row = styled.div`
display: flex;
margin-bottom: 32px;
`;

export const Default: React.FC = () => {
return (
<Stepper>
{steps.map((step, index) => (
<Step key={step} index={index} status={status[index]}>
<Card>
<CardBody>{step}</CardBody>
</Card>
</Step>
))}
</Stepper>
);
};

export const Components: React.FC = () => {
return (
<div>
<Row>
<StepNumber status="past">1</StepNumber>
<StepNumber status="current">1</StepNumber>
<StepNumber status="future">1</StepNumber>
</Row>
<Row>
<Step index={0} status="past">
<Card>
<CardBody>
<h2>Step 0</h2>
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at,
placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed,
finibus neque. Phasellus consequat at lorem a venenatis.
</div>
</CardBody>
</Card>
</Step>
</Row>
<Row>
<Step index={1} status="current">
<Card>
<CardBody>
<h2>Step 1</h2>
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at,
placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed,
finibus neque. Phasellus consequat at lorem a venenatis.
</div>
</CardBody>
</Card>
</Step>
</Row>
<Row>
<Step index={2} status="future">
<Card>
<CardBody>
<h2>Step 2</h2>
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae nisl imperdiet, vestibulum lacus at,
placerat nisi. Vestibulum quis scelerisque purus. Curabitur non magna tincidunt, fermentum neque sed,
finibus neque. Phasellus consequat at lorem a venenatis.
</div>
</CardBody>
</Card>
</Step>
</Row>
</div>
);
};
3 changes: 3 additions & 0 deletions packages/pancake-uikit/src/components/Stepper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Stepper } from "./Stepper";
export { Step } from "./Step";
export type { Status as StepStatus, StepProps } from "./types";
18 changes: 18 additions & 0 deletions packages/pancake-uikit/src/components/Stepper/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DefaultTheme } from "styled-components";

export interface ThemedProps {
theme: DefaultTheme;
}

export type Status = "past" | "current" | "future";

export interface StatusProps extends ThemedProps {
theme: DefaultTheme;
status: Status;
}

export interface StepProps {
index: number;
status: Status;
numberOfSteps?: number;
}