Skip to content

Commit 2208b95

Browse files
committed
refactor: add custom Steps component
fix #245
1 parent 12487f2 commit 2208b95

File tree

5 files changed

+215
-145
lines changed

5 files changed

+215
-145
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
@use "src/stylesheets/variables/spacings";
2+
@use "src/stylesheets/variables/typo";
3+
4+
.steps {
5+
position: relative;
6+
display: flex;
7+
width: 100%;
8+
&.vertical {
9+
flex-direction: column;
10+
}
11+
}
12+
13+
.stepItem {
14+
position: relative;
15+
display: flex;
16+
flex: 1 0 0;
17+
align-items: center;
18+
padding-bottom: spacings.$s-10;
19+
20+
.circleWrapper {
21+
display: flex;
22+
position: relative;
23+
24+
.circle {
25+
display: flex;
26+
justify-content: center;
27+
align-items: center;
28+
width: spacings.$s-10;
29+
height: spacings.$s-10;
30+
background-color: hsl(var(--english-breakfast-800));
31+
32+
.dot {
33+
width: spacings.$s-2;
34+
height: spacings.$s-2;
35+
background-color: hsl(var(--english-breakfast-800));
36+
border-radius: 50%;
37+
}
38+
svg {
39+
width: spacings.$s-6;
40+
height: spacings.$s-6;
41+
}
42+
/* svg {
43+
path {
44+
fill: hsl(var(--starbright-600));
45+
stroke: hsl(var(--starbright-600));
46+
}
47+
} */
48+
}
49+
}
50+
51+
.title {
52+
margin-top: spacings.$s-2;
53+
font-size: typo.$m;
54+
color: hsl(var(--english-breakfast-500));
55+
}
56+
57+
.title,
58+
.description {
59+
text-align: center;
60+
}
61+
}
62+
63+
/* .stepItem:not(:first-child) .title {
64+
margin-top: 0;
65+
} */
66+
67+
.stepItem:not(:last-child) .circleWrapper::after {
68+
content: "";
69+
position: absolute;
70+
background-color: hsl(var(--english-breakfast-500));
71+
z-index: -1;
72+
}
73+
74+
.stepItem {
75+
&.vertical {
76+
.circleWrapper {
77+
width: auto;
78+
padding-left: 0;
79+
border-color: transparent;
80+
}
81+
82+
&:not(:last-child) .circleWrapper::after {
83+
right: 50%;
84+
width: 1px;
85+
height: 250%;
86+
}
87+
88+
&:not(:first-child) .title {
89+
margin-top: 0;
90+
}
91+
92+
&.active .circleWrapper .circle .dot {
93+
width: spacings.$s-4;
94+
height: spacings.$s-4;
95+
}
96+
97+
&.completed .circleWrapper .circle {
98+
background-color: hsl(var(--background));
99+
}
100+
}
101+
102+
&.horizontal {
103+
flex-direction: column;
104+
.circleWrapper {
105+
width: 100%;
106+
padding-left: 40%;
107+
.circle {
108+
border: 1px solid hsl(var(--english-breakfast-500));
109+
border-radius: 50%;
110+
}
111+
}
112+
&:not(:last-child) .circleWrapper::after {
113+
top: 50%;
114+
width: 100%;
115+
height: 1px;
116+
}
117+
&.active .circleWrapper .circle {
118+
background-color: hsl(var(--english-breakfast-800));
119+
}
120+
&.awaiting .circleWrapper .circle {
121+
&.withBorder {
122+
border-color: hsl(var(--english-breakfast-500));
123+
}
124+
}
125+
}
126+
}
127+
128+
.stepItem {
129+
&.completed .circleWrapper::after,
130+
&.completed .circleWrapper .circle {
131+
background-color: hsl(var(--english-breakfast-800));
132+
}
133+
134+
&:not(.completed) .circleWrapper::after {
135+
background-color: hsl(var(--english-breakfast-500));
136+
}
137+
138+
&.active .circleWrapper .circle,
139+
&.awaiting .circleWrapper .circle {
140+
background-color: hsl(var(--background));
141+
}
142+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from "react";
2+
import StepItem, { Status } from "./stepItem";
3+
import * as styles from "./index.module.scss";
4+
5+
type StepsProps = {
6+
items: {
7+
title: string;
8+
description: string;
9+
icon?: React.ReactNode;
10+
}[];
11+
current: number;
12+
direction?: "vertical" | "horizontal";
13+
completed?: boolean;
14+
};
15+
16+
const Steps: React.FC<StepsProps> = ({ items, current, direction = "horizontal", completed }) => {
17+
const itemsWithStatus = items.map((item, index) => ({
18+
...item,
19+
status: completed ? "completed" : getStatus(index, current),
20+
}));
21+
22+
return (
23+
<div className={`${styles.steps} ${styles[direction] ?? ""}`}>
24+
{itemsWithStatus.map((item, index) => (
25+
<StepItem key={index} {...{ ...item, direction }} />
26+
))}
27+
</div>
28+
);
29+
};
30+
31+
export default Steps;
32+
33+
const getStatus = (index: number, current: number): Status => {
34+
return index < current ? "completed" : index === current ? "active" : "awaiting";
35+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
import * as styles from "./index.module.scss";
3+
4+
import CheckIcon from "jsx:/src/assets/checkMark.svg";
5+
6+
export type Status = "completed" | "active" | "awaiting";
7+
8+
type StepItemProps = {
9+
title: string;
10+
description: string;
11+
icon?: React.ReactNode;
12+
status?: Status;
13+
direction: "vertical" | "horizontal";
14+
};
15+
16+
export default function StepItem({ title, description, icon, status = "awaiting", direction }: StepItemProps) {
17+
icon = status === "completed" ? <CheckIcon /> : icon;
18+
return (
19+
<div className={`${styles.stepItem} ${styles[direction] ?? ""} ${styles[status] ?? ""} `}>
20+
<Circle {...{ icon, direction }} />
21+
<div>
22+
<div className={styles.title}>{title}</div>
23+
<div className={styles.description}>{description}</div>
24+
</div>
25+
</div>
26+
);
27+
}
28+
29+
function Circle({ icon, direction }: { direction: string; icon?: React.ReactNode }) {
30+
const isVertical = direction === "vertical";
31+
return (
32+
<div className={`${styles.circleWrapper} blink`}>
33+
<div className={`${styles.circle} ${icon && !isVertical ? styles.withBorder : ""}`}>
34+
{icon && !isVertical ? icon : <div className={styles.dot} />}
35+
</div>
36+
</div>
37+
);
38+
}

src/components/presentational/timeline/index.jsx

-21
This file was deleted.

src/components/presentational/timeline/index.module.scss

-124
This file was deleted.

0 commit comments

Comments
 (0)