Skip to content

Commit

Permalink
Merge pull request #223 from Peersyst/rc/feat/circular-progress
Browse files Browse the repository at this point in the history
[RC] Feat: add `CircularProgress` component
  • Loading branch information
AgustinMJ authored May 8, 2024
2 parents 878a49b + df0c5e1 commit 98816be
Show file tree
Hide file tree
Showing 11 changed files with 997 additions and 2,005 deletions.
4 changes: 3 additions & 1 deletion packages/genesys/packages/react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@peersyst/react-components",
"author": "Peersyst",
"version": "3.9.28",
"version": "3.9.29",
"license": "MIT",
"main": "./src/index.tsx",
"engines": {
Expand Down Expand Up @@ -58,6 +58,7 @@
"react-transition-group": "^4.4.2"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-interactions": "^6.5.16",
Expand All @@ -73,6 +74,7 @@
"@types/styled-components": "^5.1.18",
"babel-loader": "^8.3.0",
"react": "18.2.0",
"react-docgen-typescript-plugin": "^1.0.6",
"react-dom": "18.2.0",
"styled-components": "^5.3.3"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* Greatly inspired by MUI's `CircularProgress` */

import styled, { css, keyframes } from "styled-components";
import { CircularProgressProps } from "./CircularProgress.types";

const circularRotateKeyframe = keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
`;

const circularDashKeyframe = keyframes`
0% {
stroke-dasharray: 1px, 200px;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -15px;
}
100% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -125px;
}
`;

function getCircularProgressRootStyles(value: CircularProgressProps["value"]) {
if (!value) {
return css`
animation: ${circularRotateKeyframe} 1.4s linear infinite;
`;
}
}

function getCircularProgressCircleStyles(value: CircularProgressProps["value"]) {
if (value) {
return css`
transition: stroke-dashoffset 0.3s cubic-bezier(0.4, 0, 0.2, 1);
`;
} else {
return css`
animation: ${circularDashKeyframe} 1.4s ease-in-out infinite;
stroke-dasharray: 80px, 200px;
stroke-dashoffset: 0px;
`;
}
}

export const CircularProgressRoot = styled.span<CircularProgressProps>(
({ value }) => css`
display: inline-block;
${getCircularProgressRootStyles(value)};
`,
);

export const CircularProgressSvg = styled.svg(
() => css`
display: block;
`,
);

export const CircularProgressCircle = styled.circle<CircularProgressProps>(
({ value }) => css`
stroke: currentColor;
stroke-linecap: round;
${getCircularProgressCircleStyles(value)};
`,
);

export const CircularProgressContent = styled.div(
() => css`
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
`,
);

export const CircularProgressWrapper = styled.div(
() => css`
position: relative;
display: inline-flex;
`,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* Greatly inspired by MUI's `CircularProgress` */

import { CircularProgressProps } from "./CircularProgress.types";
import {
CircularProgressCircle,
CircularProgressContent,
CircularProgressRoot,
CircularProgressSvg,
CircularProgressWrapper,
} from "./CircularProgress.styles";
import { cx } from "@peersyst/react-utils";
import { useColor, useMergeDefaultProps } from "@peersyst/react-components-core";
import { CSSProperties } from "react";

const SIZE = 44;

export default function CircularProgress(props: CircularProgressProps): JSX.Element {
const {
value = 0,
className,
style,
color: colorProp = "primary",
size = 40,
thickness = 4,
children,
} = useMergeDefaultProps("CircularProgress", props);

const color = useColor(colorProp);

const circleStyle: CSSProperties = {};
const rootStyle: CSSProperties = {};

if (value) {
const circumference = 2 * Math.PI * ((SIZE - thickness) / 2);
circleStyle.strokeDasharray = circumference.toFixed(3);
circleStyle.strokeDashoffset = `${(((100 - value) / 100) * circumference).toFixed(3)}px`;
rootStyle.transform = "rotate(-90deg)";
}

return (
<CircularProgressWrapper>
<CircularProgressRoot
className={cx("CircularProgress", className)}
style={{ width: size, height: size, color, ...rootStyle, ...style }}
role="progressbar"
value={value}
>
<CircularProgressSvg
className="CircularProgressSvg"
viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}
>
<CircularProgressCircle
className="CircularProgressCircle"
style={circleStyle}
cx={SIZE}
cy={SIZE}
r={(SIZE - thickness) / 2}
fill="none"
strokeWidth={thickness}
value={value}
/>
</CircularProgressSvg>
</CircularProgressRoot>
{children && (
<CircularProgressContent className="CircularProgressContent">
{children}
</CircularProgressContent>
)}
</CircularProgressWrapper>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ThemeColor } from "@peersyst/react-components-core";
import { CSSProperties, ReactNode } from "react";

export interface CircularProgressProps {
/**
* Progress indicator. Must be between 0 and 100
*/
value?: number;
/**
* CircularProgress className
*/
className?: string;
/**
* CircularProgress style
*/
style?: CSSProperties;
/**
* CircularProgress color
*/
color?: ThemeColor;
/**
* CircularProgress size
*/
size?: number;
/**
* CircularProgress thickness
*/
thickness?: number;
/**
* CircularProgress children
*/
children?: ReactNode;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as CircularProgress } from "./CircularProgress";
export * from "./CircularProgress.styles";
export * from "./CircularProgress.types";
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ const componentsConfig: ComponentsConfig = {
Label: FormControlLabel,
},
},
CircularProgress: {
defaultProps: {},
},
};

export default componentsConfig;
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { ColorInputProps } from "../ColorInput";
import { SelectorGroupProps, SelectorProps } from "../SelectorGroup";
import { DialogProps } from "../Dialog";
import { CodeFieldProps } from "../CodeField";
import { CircularProgressProps } from "../CircularProgress";

/**
* No config for:
Expand Down Expand Up @@ -148,6 +149,7 @@ export interface ToggleButtonConfig extends ComponentConfig<ToggleButtonProps> {
export interface ToolbarConfig extends ComponentConfig<ToolbarConfig> {}
export interface TypographyConfig extends ComponentConfig<TypographyProps> {}
export interface UploadConfig extends ComponentConfig<UploadProps> {}
export interface CircularProgressConfig extends ComponentConfig<CircularProgressProps> {}

export interface ComponentsConfig {
Alert: AlertConfig;
Expand Down Expand Up @@ -220,4 +222,5 @@ export interface ComponentsConfig {
Toolbar: ToolbarConfig;
Typography: TypographyConfig;
Upload: UploadConfig;
CircularProgress: CircularProgressConfig;
}
1 change: 1 addition & 0 deletions packages/genesys/packages/react-components/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ export * from "./Upload";
export * from "./utils";
export * from "./DeprecatedSlider";
export * from "./DepcrecatedRangeSlider";
export * from "./CircularProgress";
3 changes: 3 additions & 0 deletions packages/genesys/packages/react-components/storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ module.exports = {
"@storybook/addon-interactions",
],
framework: "@storybook/react",
typescript: {
reactDocgen: "react-docgen-typescript-plugin",
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { CircularProgress } from "../../src";

export default {
title: "CircularProgress",
component: CircularProgress,
} as ComponentMeta<typeof CircularProgress>;

const Template: ComponentStory<typeof CircularProgress> = (args) => <CircularProgress {...args} />;

export const Indeterminate = Template.bind({});
Indeterminate.args = {};

export const Determinate = Template.bind({});
Determinate.args = {
value: 50,
};
Loading

0 comments on commit 98816be

Please sign in to comment.