Skip to content

Commit

Permalink
Merge pull request #638 from onewelcome/UCL-394
Browse files Browse the repository at this point in the history
UCL-394
  • Loading branch information
pawel-napieracz authored Nov 10, 2023
2 parents baa0a33 + 9bd24db commit 6ada19b
Show file tree
Hide file tree
Showing 10 changed files with 2,292 additions and 1,176 deletions.
2,602 changes: 1,427 additions & 1,175 deletions package-lock.json

Large diffs are not rendered by default.

231 changes: 231 additions & 0 deletions src/components/Stepper/Step.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* Copyright 2022 OneWelcome B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@use "../../mixins.module.scss";

@mixin stepState($status: "waiting") {
& .step-content .step {
color: var(--stepper-default-text-color);

@if $status == "current" {
background-color: var(--stepper-current-color);
border-color: var(--stepper-current-color);
} @else if $status == "done" {
background-color: var(--stepper-done-color);
border-color: var(--stepper-done-color);
} @else if $status == "error" {
background-color: var(--stepper-error-color);
border-color: var(--stepper-error-color);
} @else if $status == "waiting" {
color: var(--stepper-waiting-color);
}
}

&:not(:last-child)::after {
@if $status == "current" or $status == "error" {
border-color: var(--stepper-line-color);
} @else if $status == "done" {
border-color: var(--stepper-line-bold-color);
}
}

&:hover {
& .step-content .step {
@if $status == "current" {
border-color: var(--stepper-current-hover-color);
background-color: var(--stepper-current-hover-color);
} @else if $status == "done" {
border-color: var(--stepper-done-hover-color);
background-color: var(--stepper-done-hover-color);
} @else if $status == "error" {
border-color: var(--stepper-error-hover-color);
background-color: var(--stepper-error-hover-color);
} @else if $status == "waiting" {
border: 2px solid var(--stepper-waiting-hover-color);
color: var(--stepper-waiting-hover-color);
}
}
}

&:active {
& .step-content .step {
@if $status == "current" {
border-color: var(--stepper-current-active-color);
background-color: var(--stepper-current-active-color);
} @else if $status == "done" {
border-color: var(--stepper-done-active-color);
background-color: var(--stepper-done-active-color);
} @else if $status == "error" {
border-color: var(--stepper-error-active-color);
background-color: var(--stepper-error-active-color);
} @else if $status == "waiting" {
border: 2px solid var(--stepper-waiting-active-color);
color: var(--stepper-waiting-active-color);
}
}
}

&:disabled {
cursor: not-allowed;

& .step-content .step {
@if $status == "current" {
border-color: var(--stepper-current-disabled-color);
background-color: var(--stepper-current-disabled-color);
} @else if $status == "done" {
border-color: var(--stepper-done-disabled-color);
background-color: var(--stepper-done-disabled-color);
} @else if $status == "error" {
border-color: var(--stepper-error-disabled-color);
background-color: var(--stepper-error-disabled-color);
} @else if $status == "waiting" {
border: 2px solid var(--stepper-waiting-disabled-color);
color: var(--stepper-waiting-disabled-color);
}
}

& .step-content .label {
color: var(--stepper-label-disabled-color);

& .caption {
color: if(
$status == "error",
var(--stepper-caption-error-disabled-color),
var(--stepper-caption-disabled-color)
);
}
}
&:not(:last-child)::after {
border-color: if(
$status == "done",
var(--stepper-line-bold-disabled-color),
var(--stepper-line-disabled-color)
);
}
}
}

.step-wrapper {
display: flex;
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: transparent;
border: 0;
padding: 0;
margin: 0;
cursor: pointer;

@include mixins.focusVisibleOutline($outlineOffset: 0px);

.step-content {
display: flex;
justify-content: center;
align-items: center;
}

&:last-child {
flex-grow: 0;
flex-basis: fit-content;
}

&:not(:last-child)::after {
content: "";
margin: 0 0.5rem;
flex-grow: 1;
min-width: 0.5rem;
border-bottom: 2px solid var(--stepper-line-color);
}

&.vertical {
display: flex;
flex-direction: column;
flex-grow: 1;

&:last-child {
flex-grow: 0;
flex-basis: fit-content;
}

&:not(:last-child)::after {
content: "";
border-bottom: none;
margin: 0.5rem 0 0.5rem 1.6875rem;
border-left: 2px solid var(--stepper-line-color);
min-height: 0.5rem;
height: 100%;
width: 100%;
}
}

&.waiting {
@include stepState($status: "waiting");
}

&.current {
@include stepState($status: "current");

& .step-content .label {
font-weight: 700;
& .caption {
font-weight: 400;
}
}
}

&.done {
@include stepState($status: "done");
}

&.error {
@include stepState($status: "error");

& .step-content .label .caption {
color: var(--stepper-caption-error-color);
}
}
}

.step {
flex-shrink: 0;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
width: 1.75rem;
height: 1.75rem;
border: 2px solid var(--stepper-waiting-color);
border-radius: 50%;
font-size: var(--font-size-form-label);
font-family: var(--font-family);
font-weight: 700;
}

.label {
flex-shrink: 0;
position: relative;
margin-left: 0.5rem;
color: var(--stepper-label-color);
}

.caption {
position: absolute;
top: 1.25rem;
left: 0;
color: var(--stepper-caption-color);
font-size: 0.75rem;
}
112 changes: 112 additions & 0 deletions src/components/Stepper/Step.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2022 OneWelcome B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, {
ComponentPropsWithRef,
ForwardRefRenderFunction,
useLayoutEffect,
useRef,
useState
} from "react";
import { Icon, Icons } from "../Icon/Icon";
import classes from "./Step.module.scss";

export type StepStatus = "waiting" | "current" | "done" | "error";

export interface Props extends ComponentPropsWithRef<"button"> {
status: StepStatus;
label: string;
caption?: string;
index?: number;
lastStep?: boolean;
disabled?: boolean;
direction?: "horizontal" | "vertical";
}

const getStepContent = (index: number, status: StepStatus) => {
switch (status) {
case "waiting":
return String(index + 1);
case "current":
return String(index + 1);
case "done":
return <Icon icon={Icons.Checkmark} />;
case "error":
return "!";
}
};

export const StepComponent: ForwardRefRenderFunction<HTMLButtonElement, Props> = (
{ label, caption, status, index, direction, disabled, lastStep, ...rest }: Props,
ref
) => {
const stepIndex = index ?? 0;
const additionalClasses = [classes[status]];
const [stepContentHeight, setStepContentHeight] = useState<number>(28);
const [stepContentWidth, setStepContentWidth] = useState<number>(28);
direction === "vertical" && additionalClasses.push(classes["vertical"]);

const captionRef = useRef<HTMLSpanElement>(null);
const labelRef = useRef<HTMLSpanElement>(null);
const stepContentRef = useRef<HTMLDivElement>(null);

useLayoutEffect(() => {
if (captionRef.current && stepContentRef.current && labelRef.current && lastStep) {
if (direction == "vertical") {
const capionHeight = captionRef.current.getBoundingClientRect().height;
const stepContentHeight = stepContentRef.current.getBoundingClientRect().height;
setStepContentHeight(capionHeight + stepContentHeight);
}

if (direction == "horizontal") {
const captionWidth = captionRef.current.getBoundingClientRect().width;
const labelWidth = labelRef.current.getBoundingClientRect().width;
setStepContentWidth(captionWidth > labelWidth ? captionWidth + 36 : labelWidth + 36);
}
}
}, []);

return (
<button
{...rest}
ref={ref}
disabled={disabled}
aria-current={status === "current" ? "step" : undefined}
className={`${classes["step-wrapper"]} ${additionalClasses.join(" ")}`}
>
<div
style={{
height: lastStep && direction == "vertical" ? `${stepContentHeight}px` : "auto",
width: lastStep && direction == "horizontal" ? `${stepContentWidth}px` : "auto"
}}
>
<div ref={stepContentRef} className={classes["step-content"]}>
<div aria-hidden className={classes["step"]}>
{getStepContent(stepIndex, status)}
</div>
<span ref={labelRef} className={classes["label"]}>
{label}
<span ref={captionRef} className={classes["caption"]}>
{caption}
</span>
</span>
</div>
</div>
</button>
);
};

export const Step = React.forwardRef(StepComponent);
13 changes: 13 additions & 0 deletions src/components/Stepper/Stepper.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.stepper {
width: 100%;
display: flex;
justify-content: center;
align-items: center;

&.vertical {
flex-direction: column;
justify-content: flex-start;
height: 100%;
width: auto;
}
}
Loading

0 comments on commit 6ada19b

Please sign in to comment.