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

Progress Bar Component #99

Merged
merged 17 commits into from
Nov 30, 2023
Merged
52 changes: 52 additions & 0 deletions src/components/presentational/ProgressBar/ProgressBar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.mdhui-progress-bar {
display: block;
width: 100%;
position: relative;
--icon_size: 24px;
--icon_border_width: 2px;
}

.mdhui-progress-bar.mdhui-progress-bar-default-margin {
margin: 16px;
width:calc(100% - 32px)
}

.mdhui-progress-bar .mdhui-progress-bar-background {
border-radius: 500px;
border-width: var(--icon_border_width) ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh...did you just add a border to this? I didn't think there was a border before

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the border width is added to the width so it ends up overflowing 100% width

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would probably drop the border because it makes it look a bit awkward when stuff overlaps it:
Screenshot 2023-11-30 at 2 04 36 PM

border-style: none;
background: #fff;
overflow: hidden;
width: 100%;
position: relative;
height:calc( var(--icon_size) + 4px);
z-index:1;
}

.mdhui-progress-bar .mdhui-progress-bar-background .mdhui-progress-bar-fill {
position: absolute;
min-height: 1px;
height: calc( var(--icon_size) + 4px);
top:0;
border-radius: var(--icon_size);
z-index:1
}

.mdhui-progress-bar .mdhui-progress-steps {
top:0;
width: 100%;
position: absolute;
z-index: 2;
display:inline-block;
}

.mdhui-progress-bar .step-icon {
position: absolute;
transform: translate(-100%, 0);
}

.mdhui-progress-bar .step-icon * {
display: flex;
align-items: center;
justify-content: center;
}
111 changes: 111 additions & 0 deletions src/components/presentational/ProgressBar/ProgressBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React from "react";
import ProgressBar, { ProgressBarProps } from "./ProgressBar";
import { Meta, StoryFn } from "@storybook/react";
import Layout from "../Layout";
import { faStar } from "@fortawesome/free-regular-svg-icons";
import { faStar as faSolidStar } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import ProgressBarStep from "./ProgressBarStep";

export default {
title: "Presentational/ProgressBar",
component: ProgressBar,
parameters: {
layout: 'auto',
},
argTypes: {
backgroundColor: { control: 'object', description: "The background color of the progress bar" },
fillColor: { control: 'object', description: "The fill color of the progress bar" },
fillPercent: { control: 'number', description: "The percent of the progress bar that is filled" },
steps: { control: 'object', description: "An array of steps to display on the progress bar. A step specifies the position on the progress bar as a percent and a React element to display at that position." },
},
args: {
defaultMargin: true
}

} as Meta<typeof ProgressBar>;

const Template: StoryFn<typeof ProgressBar> = (args: ProgressBarProps) => <Layout colorScheme="auto"><div style={{padding: "35px 0"}}><ProgressBar {...args}/></div></Layout>;

export const Default = Template.bind({});
Default.args = {
fillPercent: 35,
}

export const GradientFill75TargetStep = Template.bind({});
GradientFill75TargetStep.args = {
fillPercent: 75,
backgroundColor: { lightMode: "#d3d3d3", darkMode: "#c2c2c2" },
fillColor: "linear-gradient(90deg, #6cb144, #adc247)",
steps: [{
percent: 100,
icon:
<ProgressBarStep borderColor="rgba(148, 148, 148, 1)" backgroundColor="rgba(148, 148, 148, 1)" height="24px">
<FontAwesomeIcon icon={faStar} size={"1x"} style={{ color: "#fcfcfc", marginTop: "-3px" }} />
</ProgressBarStep>
}],

} as ProgressBarProps;

export const GradientFill100TargetStep = Template.bind({});
GradientFill100TargetStep.args = {
fillPercent: 100,
fillColor: "linear-gradient(90deg, #6cb144, #adc247)",
steps: [{
percent: 100,
icon:
<ProgressBarStep borderColor="gold" backgroundColor="#EA6B54" height="24px">
<FontAwesomeIcon icon={faStar} size={"1x"} style={{ color: "gold", marginTop: "-3px" }} />
</ProgressBarStep>
}],
} as ProgressBarProps;

export const GradientFillIconSteps = Template.bind({});
GradientFillIconSteps.args = {
fillPercent: 50,
backgroundColor: "#d3d3d3",
fillColor: "linear-gradient(270deg, #F0CA68 0%, #E5917F 100%)",
steps: [...Array(4)].map((_e, i) => {
let value = (i + 1) * 25;
let icon = (value <= 50) ?
<ProgressBarStep borderColor="gold" backgroundColor="#EA6B54" height="16px">
<FontAwesomeIcon icon={faSolidStar} size={"1x"} style={{ color: "white", marginTop: "-3px" }} />
</ProgressBarStep> :
<ProgressBarStep borderColor="rgba(148, 148, 148, 1)" backgroundColor="rgba(148, 148, 148, 1)" height="16px">
<FontAwesomeIcon icon={faSolidStar} size={"1x"} style={{ color: "white", marginTop: "-3px" }} />
</ProgressBarStep>
return {
percent: Math.trunc(value),
icon: icon
}
})
} as ProgressBarProps;

export const LabelSteps = Template.bind({});
LabelSteps.args = {
fillPercent: 100 * 75 / 175,
fillColor: "rgb(34, 115, 209, 0.5)",
backgroundColor: { lightMode: "#d3d3d3", darkMode: "#c2c2c2" },
steps: [...Array(7)].map((_e, i) => {
let amount = (i + 1) * 25;
let icon = (amount == 75) ?
<ProgressBarStep borderColor="white" backgroundColor="rgb(34, 115, 209)" height="14px">
<span style={{ fontSize: "0.8em" }}>75</span>
</ProgressBarStep> :
((amount < 75) ?
<ProgressBarStep borderColor="white" backgroundColor="rgb(34, 115, 209)" height="14px">
<FontAwesomeIcon icon={faSolidStar} size={"1x"} style={{ color: "white", marginTop: "-3px" }} />
</ProgressBarStep> :
<ProgressBarStep borderColor="rgba(148, 148, 148, 1)" backgroundColor="rgba(148, 148, 148, 1)" height="14px">
<FontAwesomeIcon icon={faStar} size={"1x"} style={{ color: "rgb(34, 115, 209)", marginTop: "-3px" }} />
</ProgressBarStep>
);
return {
percent: Math.trunc(100 * amount / 175),
icon: icon
}
})
} as ProgressBarProps



52 changes: 52 additions & 0 deletions src/components/presentational/ProgressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ColorDefinition, resolveColor } from "../../../helpers/colors";
import "./ProgressBar.css";
import React, { ReactElement, useContext, useEffect } from "react";
import { LayoutContext } from '../Layout';

export interface ProgressBarProps {
backgroundColor?: ColorDefinition;
borderColor?: ColorDefinition;
fillColor?: ColorDefinition;
fillPercent: number;
chrisnowak marked this conversation as resolved.
Show resolved Hide resolved
steps?:
{
percent: number;
icon: ReactElement;
}[]
,
innerRef?: React.Ref<HTMLDivElement>;
defaultMargin?: boolean
}

ProgressBar.defaultProps = {
chrisnowak marked this conversation as resolved.
Show resolved Hide resolved
backgroundColor: "var(--mdhui-background-color-0)",
fillColor: "var(--mdhui-color-primary)",
fillPercent: 0
}

export default function ProgressBar(props: ProgressBarProps) {
let layoutContext = useContext(LayoutContext);
let classes = ["mdhui-progress-bar"];
if (props.defaultMargin) {
classes.push("mdhui-progress-bar-default-margin");
}

return (
<div className={classes.join(" ")} ref={props.innerRef}>
<div className="mdhui-progress-bar-background" style={{ background: resolveColor(layoutContext?.colorScheme, props.backgroundColor) }}>
<div className="mdhui-progress-bar-fill" style={{ width: Math.trunc(props.fillPercent) + "%", background: resolveColor(layoutContext?.colorScheme, props.fillColor) }} />
</div>
<div className="mdhui-progress-steps">
{props.steps &&
<>
{props.steps.map((step, i) =>
<div key={`${i}-step`} className="step-icon" style={{ left: Math.trunc(step.percent) + "%" }}>
{step.icon}
</div>
)}
</>
}
</div>
</div>
);
}
20 changes: 20 additions & 0 deletions src/components/presentational/ProgressBar/ProgressBarStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { useContext } from "react";
import { ColorDefinition, resolveColor } from "../../../helpers/colors";
import { LayoutContext } from "../Layout";

export interface ProgressBarStepProps {
borderColor?: ColorDefinition;
backgroundColor?: ColorDefinition;
children?: React.ReactNode;
height: string;
}

export default function ProgressBarStep(props: ProgressBarStepProps) {
let layoutContext = useContext(LayoutContext);
let calcTopMargin = "calc( var(--icon_size) / 2 - "+props.height+" / 2 - 4px)";
return (
<span style={{ border: "2px solid", borderColor: resolveColor(layoutContext?.colorScheme,props.borderColor), borderRadius: props.height, height: props.height, width: props.height, marginTop: calcTopMargin, backgroundColor: resolveColor(layoutContext?.colorScheme, props.backgroundColor), padding: "4px 4px" }}>
{props.children}
</span>
)
}
2 changes: 2 additions & 0 deletions src/components/presentational/ProgressBar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './ProgressBar'
export { default as ProgressBarStep } from './ProgressBarStep'
4 changes: 3 additions & 1 deletion src/components/presentational/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export { default as Layout, LayoutContext } from "./Layout"
export { default as LoadingIndicator } from "./LoadingIndicator"
export { default as NavigationBar } from "./NavigationBar"
export { default as NotesInput } from "./NotesInput"
export { default as ProgressBar } from "./ProgressBar"
export { default as ProgressBarStep } from "./ProgressBar/ProgressBarStep"
export { default as ProgressRing } from "./ProgressRing"
export { default as Section } from "./Section"
export { default as SegmentedControl } from "./SegmentedControl"
export { default as ShinyOverlay } from "./ShinyOverlay"
Expand All @@ -28,4 +31,3 @@ export { default as TrackerItem } from "./TrackerItem"
export { default as UnstyledButton } from "./UnstyledButton"
export { default as ViewHeader } from "./ViewHeader"
export { default as WeekCalendar } from "./WeekCalendar"
export { default as ProgressRing } from "./ProgressRing"