From 2208b951ac0ca3b9c13fa0f4fb6d507ee6678943 Mon Sep 17 00:00:00 2001 From: gratestas Date: Thu, 3 Aug 2023 17:35:01 +0300 Subject: [PATCH] refactor: add custom Steps component fix #245 --- .../presentational/steps/index.module.scss | 142 ++++++++++++++++++ src/components/presentational/steps/index.tsx | 35 +++++ .../presentational/steps/stepItem.tsx | 38 +++++ .../presentational/timeline/index.jsx | 21 --- .../presentational/timeline/index.module.scss | 124 --------------- 5 files changed, 215 insertions(+), 145 deletions(-) create mode 100644 src/components/presentational/steps/index.module.scss create mode 100644 src/components/presentational/steps/index.tsx create mode 100644 src/components/presentational/steps/stepItem.tsx delete mode 100644 src/components/presentational/timeline/index.jsx delete mode 100644 src/components/presentational/timeline/index.module.scss diff --git a/src/components/presentational/steps/index.module.scss b/src/components/presentational/steps/index.module.scss new file mode 100644 index 00000000..d34b2905 --- /dev/null +++ b/src/components/presentational/steps/index.module.scss @@ -0,0 +1,142 @@ +@use "src/stylesheets/variables/spacings"; +@use "src/stylesheets/variables/typo"; + +.steps { + position: relative; + display: flex; + width: 100%; + &.vertical { + flex-direction: column; + } +} + +.stepItem { + position: relative; + display: flex; + flex: 1 0 0; + align-items: center; + padding-bottom: spacings.$s-10; + + .circleWrapper { + display: flex; + position: relative; + + .circle { + display: flex; + justify-content: center; + align-items: center; + width: spacings.$s-10; + height: spacings.$s-10; + background-color: hsl(var(--english-breakfast-800)); + + .dot { + width: spacings.$s-2; + height: spacings.$s-2; + background-color: hsl(var(--english-breakfast-800)); + border-radius: 50%; + } + svg { + width: spacings.$s-6; + height: spacings.$s-6; + } + /* svg { + path { + fill: hsl(var(--starbright-600)); + stroke: hsl(var(--starbright-600)); + } + } */ + } + } + + .title { + margin-top: spacings.$s-2; + font-size: typo.$m; + color: hsl(var(--english-breakfast-500)); + } + + .title, + .description { + text-align: center; + } +} + +/* .stepItem:not(:first-child) .title { + margin-top: 0; +} */ + +.stepItem:not(:last-child) .circleWrapper::after { + content: ""; + position: absolute; + background-color: hsl(var(--english-breakfast-500)); + z-index: -1; +} + +.stepItem { + &.vertical { + .circleWrapper { + width: auto; + padding-left: 0; + border-color: transparent; + } + + &:not(:last-child) .circleWrapper::after { + right: 50%; + width: 1px; + height: 250%; + } + + &:not(:first-child) .title { + margin-top: 0; + } + + &.active .circleWrapper .circle .dot { + width: spacings.$s-4; + height: spacings.$s-4; + } + + &.completed .circleWrapper .circle { + background-color: hsl(var(--background)); + } + } + + &.horizontal { + flex-direction: column; + .circleWrapper { + width: 100%; + padding-left: 40%; + .circle { + border: 1px solid hsl(var(--english-breakfast-500)); + border-radius: 50%; + } + } + &:not(:last-child) .circleWrapper::after { + top: 50%; + width: 100%; + height: 1px; + } + &.active .circleWrapper .circle { + background-color: hsl(var(--english-breakfast-800)); + } + &.awaiting .circleWrapper .circle { + &.withBorder { + border-color: hsl(var(--english-breakfast-500)); + } + } + } +} + +.stepItem { + &.completed .circleWrapper::after, + &.completed .circleWrapper .circle { + background-color: hsl(var(--english-breakfast-800)); + } + + &:not(.completed) .circleWrapper::after { + background-color: hsl(var(--english-breakfast-500)); + } + + &.active .circleWrapper .circle, + &.awaiting .circleWrapper .circle { + background-color: hsl(var(--background)); + } +} diff --git a/src/components/presentational/steps/index.tsx b/src/components/presentational/steps/index.tsx new file mode 100644 index 00000000..a1045e33 --- /dev/null +++ b/src/components/presentational/steps/index.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import StepItem, { Status } from "./stepItem"; +import * as styles from "./index.module.scss"; + +type StepsProps = { + items: { + title: string; + description: string; + icon?: React.ReactNode; + }[]; + current: number; + direction?: "vertical" | "horizontal"; + completed?: boolean; +}; + +const Steps: React.FC = ({ items, current, direction = "horizontal", completed }) => { + const itemsWithStatus = items.map((item, index) => ({ + ...item, + status: completed ? "completed" : getStatus(index, current), + })); + + return ( +
+ {itemsWithStatus.map((item, index) => ( + + ))} +
+ ); +}; + +export default Steps; + +const getStatus = (index: number, current: number): Status => { + return index < current ? "completed" : index === current ? "active" : "awaiting"; +}; diff --git a/src/components/presentational/steps/stepItem.tsx b/src/components/presentational/steps/stepItem.tsx new file mode 100644 index 00000000..41e16333 --- /dev/null +++ b/src/components/presentational/steps/stepItem.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import * as styles from "./index.module.scss"; + +import CheckIcon from "jsx:/src/assets/checkMark.svg"; + +export type Status = "completed" | "active" | "awaiting"; + +type StepItemProps = { + title: string; + description: string; + icon?: React.ReactNode; + status?: Status; + direction: "vertical" | "horizontal"; +}; + +export default function StepItem({ title, description, icon, status = "awaiting", direction }: StepItemProps) { + icon = status === "completed" ? : icon; + return ( +
+ +
+
{title}
+
{description}
+
+
+ ); +} + +function Circle({ icon, direction }: { direction: string; icon?: React.ReactNode }) { + const isVertical = direction === "vertical"; + return ( +
+
+ {icon && !isVertical ? icon :
} +
+
+ ); +} diff --git a/src/components/presentational/timeline/index.jsx b/src/components/presentational/timeline/index.jsx deleted file mode 100644 index 2ee2fc5b..00000000 --- a/src/components/presentational/timeline/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as styles from "./index.module.scss"; -import { Steps } from "antd"; - -export default function Timeline(props) { - const { items, current, completed } = props; - return ( -
- - {items.map((item, _index) => ( - - ))} - -
- ); -} diff --git a/src/components/presentational/timeline/index.module.scss b/src/components/presentational/timeline/index.module.scss deleted file mode 100644 index 1e31bdfe..00000000 --- a/src/components/presentational/timeline/index.module.scss +++ /dev/null @@ -1,124 +0,0 @@ -@use "src/stylesheets/variables/spacings"; -@use "src/stylesheets/variables/typo"; - -.timelineSteps { - margin-top: spacings.$s-7; - margin-bottom: spacings.$s-18; -} - -:global(.ant-steps-item-title) { - font-size: typo.$m; -} - -:global(.ant-steps-item-description) { - font-size: typo.$s; - margin-top: spacings.$s-2; -} - -:global(.ant-steps-item-tail) { - top: spacings.$s-4; -} -:global(.ant-steps-label-vertical .ant-steps-item-tail) { - margin-left: 3.5rem; - padding: spacings.$s-1 spacings.$s-7; -} - -.timelineSteps :global { - .ant-steps-item-wait > .ant-steps-item-container { - .ant-steps-item-icon { - border: 2px solid hsl(var(--english-breakfast-400)); - } - svg { - [class$="cls-1"], - [class$="cls-2"] { - stroke: hsl(var(--english-breakfast-500)) !important; - } - [class$="cls-3"] { - fill: hsl(var(--english-breakfast-500)) !important; - } - } - .ant-steps-item-title { - color: hsl(var(--english-breakfast-500)); - } - } - - .ant-steps-item-finish > .ant-steps-item-container { - .ant-steps-item-icon { - border: 2px solid hsl(var(--english-breakfast-400)); - } - .ant-steps-item-title { - color: hsl(var(--english-breakfast-500)); - } - } - - .ant-steps-item-process > .ant-steps-item-container { - .ant-steps-item-icon { - background: hsl(var(--english-breakfast-800)); - border-color: hsl(var(--english-breakfast-800)); - .ant-steps-icon { - svg { - [class$="cls-1"], - [class$="cls-2"] { - stroke: hsl(var(--starbright-600)) !important; - } - stroke: hsl(var(--starbright-600)) !important; - } - } - } - - .ant-steps-item-tail:after { - background-color: hsl(var(--english-breakfast-500)); - } - - .ant-steps-item-title { - color: hsl(var(--english-breakfast-800)); - } - } - - .ant-steps:not(.ant-steps-vertical) .ant-steps-item-custom .ant-steps-item-icon { - display: flex; - justify-content: center; - align-items: center; - width: spacings.$s-10; - height: spacings.$s-10; - - .ant-steps-icon { - width: spacings.$s-6; - height: spacings.$s-6; - } - } - - .ant-steps-vertical > .ant-steps-item .ant-steps-item-icon { - display: flex; - justify-content: center; - align-items: center; - width: spacings.$s-10; - height: spacings.$s-10; - border: 1px solid hsl(var(--english-breakfast-800)); - } - - .ant-steps-label-vertical .ant-steps-item-icon { - margin-left: 2rem; - } - - .ant-steps-item-wait > .ant-steps-item-container { - .ant-steps-item-tail:after { - background-color: hsl(var(--english-breakfast-500)); - } - } - - .ant-steps-item-finish > .ant-steps-item-container { - .ant-steps-item-tail:after { - background-color: hsl(var(--english-breakfast-500)); - } - } - - .ant-steps-vertical > .ant-steps-item > .ant-steps-item-container > .ant-steps-item-tail { - left: spacings.$s-5; - top: spacings.$s-4; - padding: 1.65rem 0 1px; - } - .ant-steps-vertical > .ant-steps-item { - margin-bottom: spacings.$s-4; - } -}