Skip to content

Commit

Permalink
feat(progress): ✨ add circular progress example
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy committed Sep 3, 2020
1 parent 07a536a commit d35e519
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 116 deletions.
31 changes: 26 additions & 5 deletions src/progress/Progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,48 @@
* We improved the Progress Component [Progress](https://github.com/chakra-ui/chakra-ui/tree/develop/packages/progress)
* to work with Reakit System
*/
import { isFunction } from "@chakra-ui/utils";
import { BoxHTMLProps, BoxOptions, useBox } from "reakit";
import { createComponent, createHook } from "reakit-system";
import { createHook, createComponent } from "reakit-system";

import { PROGRESS_KEYS } from "./__keys";
import { ProgressStateReturn } from "./ProgressState";

export type ProgressOptions = BoxOptions & ProgressStateReturn;
export type ProgressOptions = BoxOptions &
ProgressStateReturn & {
/**
* Function that returns the `aria-valuetext` for screen readers.
* It's mostly used to generate a more human-readable
* representation of the value for assistive technologies
*/
getAriaValueText?(value: number, percent: number): string;
};

export type ProgressHTMLProps = BoxHTMLProps;

export type ProgressProps = ProgressOptions & ProgressHTMLProps;

export const useProgress = createHook<ProgressOptions, ProgressHTMLProps>({
const useProgress = createHook<ProgressOptions, ProgressHTMLProps>({
name: "Progress",
compose: useBox,
keys: PROGRESS_KEYS,

useProps(options, { style: htmlStyle, ...htmlProps }) {
useProps(options, { "aria-valuetext": ariaValueText, ...htmlProps }) {
const { isIndeterminate, value, max, min, percent } = options;

return {
...htmlProps,
style: { ...htmlStyle, overflow: "hidden", position: "relative" },
role: "progressbar",
"data-indeterminate": isIndeterminate ? "" : undefined,
"aria-valuemax": max,
"aria-valuemin": min,
"aria-valuenow": isIndeterminate ? undefined : value,
"aria-valuetext":
value == null || percent == null
? undefined
: isFunction(options.getAriaValueText)
? options.getAriaValueText(value, percent)
: ariaValueText,
};
},
});
Expand Down
65 changes: 0 additions & 65 deletions src/progress/ProgressBar.ts

This file was deleted.

9 changes: 0 additions & 9 deletions src/progress/ProgressState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@ export interface UseProgressProps {
max?: number;
}

/**
* Progress (Linear)
*
* Progress is used to display the progress status for a task that takes a long
* time or consists of several steps.
*
* It includes accessible attributes to help assistive technologies understand
* and speak the progress values.
*/
export const useProgressState = (props: UseProgressProps = {}) => {
const { value, min = 0, max = 100 } = props;

Expand Down
3 changes: 1 addition & 2 deletions src/progress/__keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ const PROGRESS_STATE_KEYS = [
"percent",
] as const;

export const PROGRESS_KEYS = PROGRESS_STATE_KEYS;
export const PROGRESS_BAR_KEYS = [...PROGRESS_STATE_KEYS, "getAriaValueText"];
export const PROGRESS_KEYS = [...PROGRESS_STATE_KEYS, "getAriaValueText"];
3 changes: 1 addition & 2 deletions src/progress/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./ProgressState";
export * from "./Progress";
export * from "./ProgressBar";
export * from "./ProgressState";
161 changes: 161 additions & 0 deletions src/progress/stories/CircularProgress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import React from "react";
import { Meta } from "@storybook/react";
import { css, keyframes } from "emotion";
import { isUndefined } from "@chakra-ui/utils";

import { Progress } from "../Progress";
import { useProgressState } from "../ProgressState";

export default {
title: "Component/Progress/Circular",
} as Meta;

const spin = keyframes({
"0%": {
strokeDasharray: "1, 400",
strokeDashoffset: "0",
},
"50%": {
strokeDasharray: "400, 400",
strokeDashoffset: "-100",
},
"100%": {
strokeDasharray: "400, 400",
strokeDashoffset: "-260",
},
});

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

const CircularProgress = (props: any) => {
const {
size = "48px",
value,
capIsRound,
thickness = "10px",
color = "#0078d4",
trackColor = "#edebe9",
label = false,
} = props;

const progress = useProgressState({ value });

const determinant = progress.isIndeterminate
? undefined
: (progress.percent ?? 0) * 2.64;

const strokeDasharray = isUndefined(determinant)
? undefined
: `${determinant} ${264 - determinant}`;

const indicatorStyles = progress.isIndeterminate
? css({
animation: `${spin} 1.5s linear infinite`,
})
: css({
strokeDashoffset: 66,
strokeDasharray,
transition: `stroke-dasharray 0.6s ease 0s, stroke 0.6s ease`,
});

const rootStyles = css({
display: "inline-block",
position: "relative",
verticalAlign: "middle",
});

const svgStyles = css({
width: size,
height: size,
animation: progress.isIndeterminate
? `${rotate} 2s linear infinite`
: undefined,
});

const labelStyles = css({
fontSize: "14px",
top: "50%",
left: "50%",
width: "100%",
textAlign: "center",
position: "absolute",
transform: "translate(-50%, -50%)",
});

return (
<Progress {...progress} className={rootStyles}>
<svg viewBox="0 0 100 100" className={svgStyles}>
<circle
cx={50}
cy={50}
r={42}
fill="transparent"
stroke={trackColor}
strokeWidth={thickness}
/>
<circle
cx={50}
cy={50}
r={42}
fill="transparent"
stroke={color}
strokeWidth={thickness}
strokeLinecap={capIsRound ? "round" : undefined}
className={indicatorStyles}
/>
</svg>
{label && <div className={labelStyles}>{`${progress.value}%`}</div>}
</Progress>
);
};

export const Default = () => {
const [value, setValue] = React.useState(0);

React.useEffect(() => {
const clearId = setInterval(() => {
setValue(prevValue => prevValue + 5);
}, 500);

if (value === 100) {
clearInterval(clearId);
}

return () => {
clearInterval(clearId);
};
}, [value]);

return <CircularProgress value={value} />;
};

export const WithLabel = () => {
const [value, setValue] = React.useState(0);

React.useEffect(() => {
const clearId = setInterval(() => {
setValue(prevValue => prevValue + 5);
}, 500);

if (value === 100) {
clearInterval(clearId);
}

return () => {
clearInterval(clearId);
};
}, [value]);

return <CircularProgress value={value} label />;
};

export const IsIndeterminate = () => {
return <CircularProgress />;
};
Loading

0 comments on commit d35e519

Please sign in to comment.