diff --git a/frontend/src/Components/CircularProgressBar.js b/frontend/src/Components/CircularProgressBar.js
deleted file mode 100644
index b6b841c71..000000000
--- a/frontend/src/Components/CircularProgressBar.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
-import styles from './CircularProgressBar.css';
-
-class CircularProgressBar extends Component {
-
- //
- // Lifecycle
-
- constructor(props, context) {
- super(props, context);
-
- this.state = {
- progress: 0
- };
- }
-
- componentDidMount() {
- this._progressStep();
- }
-
- componentDidUpdate(prevProps) {
- const progress = this.props.progress;
-
- if (prevProps.progress !== progress) {
- this._cancelProgressStep();
- this._progressStep();
- }
- }
-
- componentWillUnmount() {
- this._cancelProgressStep();
- }
-
- //
- // Control
-
- _progressStep() {
- this.requestAnimationFrame = window.requestAnimationFrame(() => {
- this.setState({
- progress: this.state.progress + 1
- }, () => {
- if (this.state.progress < this.props.progress) {
- this._progressStep();
- }
- });
- });
- }
-
- _cancelProgressStep() {
- if (this.requestAnimationFrame) {
- window.cancelAnimationFrame(this.requestAnimationFrame);
- }
- }
-
- //
- // Render
-
- render() {
- const {
- className,
- containerClassName,
- size,
- strokeWidth,
- strokeColor,
- showProgressText
- } = this.props;
-
- const progress = this.state.progress;
-
- const center = size / 2;
- const radius = center - strokeWidth;
- const circumference = Math.PI * (radius * 2);
- const sizeInPixels = `${size}px`;
- const strokeDashoffset = ((100 - progress) / 100) * circumference;
- const progressText = `${Math.round(progress)}%`;
-
- return (
-
-
-
- {
- showProgressText &&
-
- {progressText}
-
- }
-
- );
- }
-}
-
-CircularProgressBar.propTypes = {
- className: PropTypes.string,
- containerClassName: PropTypes.string,
- size: PropTypes.number,
- progress: PropTypes.number.isRequired,
- strokeWidth: PropTypes.number,
- strokeColor: PropTypes.string,
- showProgressText: PropTypes.bool
-};
-
-CircularProgressBar.defaultProps = {
- className: styles.circularProgressBar,
- containerClassName: styles.circularProgressBarContainer,
- size: 60,
- strokeWidth: 5,
- strokeColor: '#ffc230',
- showProgressText: false
-};
-
-export default CircularProgressBar;
diff --git a/frontend/src/Components/CircularProgressBar.tsx b/frontend/src/Components/CircularProgressBar.tsx
new file mode 100644
index 000000000..bad48f83e
--- /dev/null
+++ b/frontend/src/Components/CircularProgressBar.tsx
@@ -0,0 +1,99 @@
+import React, { useCallback, useEffect, useState } from 'react';
+import styles from './CircularProgressBar.css';
+
+interface CircularProgressBarProps {
+ className?: string;
+ containerClassName?: string;
+ size?: number;
+ progress: number;
+ strokeWidth?: number;
+ strokeColor?: string;
+ showProgressText?: boolean;
+}
+
+function CircularProgressBar({
+ className = styles.circularProgressBar,
+ containerClassName = styles.circularProgressBarContainer,
+ size = 60,
+ strokeWidth = 5,
+ strokeColor = '#ffc230',
+ showProgressText = false,
+ progress,
+}: CircularProgressBarProps) {
+ const [currentProgress, setCurrentProgress] = useState(0);
+ const raf = React.useRef(0);
+ const center = size / 2;
+ const radius = center - strokeWidth;
+ const circumference = Math.PI * (radius * 2);
+ const sizeInPixels = `${size}px`;
+ const strokeDashoffset = ((100 - currentProgress) / 100) * circumference;
+ const progressText = `${Math.round(currentProgress)}%`;
+
+ const handleAnimation = useCallback(
+ (p: number) => {
+ setCurrentProgress((prevProgress) => {
+ if (prevProgress < p) {
+ return prevProgress + Math.min(1, p - prevProgress);
+ }
+
+ return prevProgress;
+ });
+ },
+ [setCurrentProgress]
+ );
+
+ useEffect(() => {
+ if (progress > currentProgress) {
+ cancelAnimationFrame(raf.current);
+
+ raf.current = requestAnimationFrame(() => handleAnimation(progress));
+ }
+ }, [progress, currentProgress, handleAnimation]);
+
+ useEffect(
+ () => {
+ return () => cancelAnimationFrame(raf.current);
+ },
+ // We only want to run this effect once
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ return (
+
+
+
+ {showProgressText && (
+
{progressText}
+ )}
+
+ );
+}
+
+export default CircularProgressBar;
diff --git a/frontend/src/Components/Icon.tsx b/frontend/src/Components/Icon.tsx
index 86ff57a30..ea5279840 100644
--- a/frontend/src/Components/Icon.tsx
+++ b/frontend/src/Components/Icon.tsx
@@ -5,6 +5,7 @@ import {
import classNames from 'classnames';
import React, { ComponentProps } from 'react';
import { kinds } from 'Helpers/Props';
+import { Kind } from 'Helpers/Props/kinds';
import styles from './Icon.css';
export interface IconProps
@@ -14,7 +15,7 @@ export interface IconProps
> {
containerClassName?: ComponentProps<'span'>['className'];
name: FontAwesomeIconProps['icon'];
- kind?: Extract<(typeof kinds.all)[number], keyof typeof styles>;
+ kind?: Extract;
size?: number;
isSpinning?: FontAwesomeIconProps['spin'];
title?: string | (() => string);
diff --git a/frontend/src/Components/Label.tsx b/frontend/src/Components/Label.tsx
index 411cefddf..9ab360f42 100644
--- a/frontend/src/Components/Label.tsx
+++ b/frontend/src/Components/Label.tsx
@@ -1,11 +1,13 @@
import classNames from 'classnames';
import React, { ComponentProps, ReactNode } from 'react';
import { kinds, sizes } from 'Helpers/Props';
+import { Kind } from 'Helpers/Props/kinds';
+import { Size } from 'Helpers/Props/sizes';
import styles from './Label.css';
export interface LabelProps extends ComponentProps<'span'> {
- kind?: Extract<(typeof kinds.all)[number], keyof typeof styles>;
- size?: Extract<(typeof sizes.all)[number], keyof typeof styles>;
+ kind?: Extract;
+ size?: Extract;
outline?: boolean;
children: ReactNode;
}
diff --git a/frontend/src/Components/Link/Button.tsx b/frontend/src/Components/Link/Button.tsx
index c512b3a90..cf2293f59 100644
--- a/frontend/src/Components/Link/Button.tsx
+++ b/frontend/src/Components/Link/Button.tsx
@@ -1,6 +1,8 @@
import classNames from 'classnames';
import React from 'react';
import { align, kinds, sizes } from 'Helpers/Props';
+import { Kind } from 'Helpers/Props/kinds';
+import { Size } from 'Helpers/Props/sizes';
import Link, { LinkProps } from './Link';
import styles from './Button.css';
@@ -9,8 +11,8 @@ export interface ButtonProps extends Omit {
(typeof align.all)[number],
keyof typeof styles
>;
- kind?: Extract<(typeof kinds.all)[number], keyof typeof styles>;
- size?: Extract<(typeof sizes.all)[number], keyof typeof styles>;
+ kind?: Extract;
+ size?: Extract;
children: Required;
}
diff --git a/frontend/src/Components/ProgressBar.js b/frontend/src/Components/ProgressBar.js
deleted file mode 100644
index 171b4c0fa..000000000
--- a/frontend/src/Components/ProgressBar.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import React from 'react';
-import { ColorImpairedConsumer } from 'App/ColorImpairedContext';
-import { kinds, sizes } from 'Helpers/Props';
-import translate from 'Utilities/String/translate';
-import styles from './ProgressBar.css';
-
-function ProgressBar(props) {
- const {
- className,
- containerClassName,
- title,
- progress,
- precision,
- showText,
- text,
- kind,
- size,
- width
- } = props;
-
- const progressPercent = `${progress.toFixed(precision)}%`;
- const progressText = text || progressPercent;
- const actualWidth = width ? `${width}px` : '100%';
-
- return (
-
- {(enableColorImpairedMode) => {
- return (
-
- {
- showText && width ?
-
:
- null
- }
-
-
-
- {
- showText ?
-
:
- null
- }
-
- );
- }}
-
- );
-}
-
-ProgressBar.propTypes = {
- className: PropTypes.string,
- containerClassName: PropTypes.string,
- title: PropTypes.string,
- progress: PropTypes.number.isRequired,
- precision: PropTypes.number.isRequired,
- showText: PropTypes.bool.isRequired,
- text: PropTypes.string,
- kind: PropTypes.oneOf(kinds.all).isRequired,
- size: PropTypes.oneOf(sizes.all).isRequired,
- width: PropTypes.number
-};
-
-ProgressBar.defaultProps = {
- className: styles.progressBar,
- containerClassName: styles.container,
- precision: 1,
- showText: false,
- kind: kinds.PRIMARY,
- size: sizes.MEDIUM
-};
-
-export default ProgressBar;
diff --git a/frontend/src/Components/ProgressBar.tsx b/frontend/src/Components/ProgressBar.tsx
new file mode 100644
index 000000000..07b20d8a4
--- /dev/null
+++ b/frontend/src/Components/ProgressBar.tsx
@@ -0,0 +1,94 @@
+import classNames from 'classnames';
+import React from 'react';
+import { ColorImpairedConsumer } from 'App/ColorImpairedContext';
+import { Kind } from 'Helpers/Props/kinds';
+import { Size } from 'Helpers/Props/sizes';
+import translate from 'Utilities/String/translate';
+import styles from './ProgressBar.css';
+
+interface ProgressBarProps {
+ className?: string;
+ containerClassName?: string;
+ title?: string;
+ progress: number;
+ precision?: number;
+ showText?: boolean;
+ text?: string;
+ kind?: Extract;
+ size?: Extract;
+ width?: number;
+}
+
+function ProgressBar({
+ className = styles.progressBar,
+ containerClassName = styles.container,
+ title,
+ progress,
+ precision = 1,
+ showText = false,
+ text,
+ kind = 'primary',
+ size = 'medium',
+ width,
+}: ProgressBarProps) {
+ const progressPercent = `${progress.toFixed(precision)}%`;
+ const progressText = text || progressPercent;
+ const actualWidth = width ? `${width}px` : '100%';
+
+ return (
+
+ {(enableColorImpairedMode) => {
+ return (
+
+ {showText && width ? (
+
+ ) : null}
+
+
+
+ {showText ? (
+
+ ) : null}
+
+ );
+ }}
+
+ );
+}
+
+export default ProgressBar;
diff --git a/frontend/src/Helpers/Props/kinds.ts b/frontend/src/Helpers/Props/kinds.ts
index 9f205dd91..fc94defbb 100644
--- a/frontend/src/Helpers/Props/kinds.ts
+++ b/frontend/src/Helpers/Props/kinds.ts
@@ -25,3 +25,15 @@ export const all = [
WARNING,
QUEUE,
] as const;
+
+export type Kind =
+ | 'danger'
+ | 'default'
+ | 'disabled'
+ | 'info'
+ | 'inverse'
+ | 'pink'
+ | 'primary'
+ | 'purple'
+ | 'success'
+ | 'warning';
diff --git a/frontend/src/Helpers/Props/sizes.ts b/frontend/src/Helpers/Props/sizes.ts
index 809f0397a..526f49f8c 100644
--- a/frontend/src/Helpers/Props/sizes.ts
+++ b/frontend/src/Helpers/Props/sizes.ts
@@ -13,3 +13,11 @@ export const all = [
EXTRA_LARGE,
EXTRA_EXTRA_LARGE,
] as const;
+
+export type Size =
+ | 'extraSmall'
+ | 'small'
+ | 'medium'
+ | 'large'
+ | 'extraLarge'
+ | 'extraExtraLarge';
diff --git a/frontend/src/System/Status/DiskSpace/DiskSpace.tsx b/frontend/src/System/Status/DiskSpace/DiskSpace.tsx
index 2174e5b1e..f4f8e84d6 100644
--- a/frontend/src/System/Status/DiskSpace/DiskSpace.tsx
+++ b/frontend/src/System/Status/DiskSpace/DiskSpace.tsx
@@ -11,6 +11,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableRow from 'Components/Table/TableRow';
import { kinds, sizes } from 'Helpers/Props';
+import { Kind } from 'Helpers/Props/kinds';
import { fetchDiskSpace } from 'Store/Actions/systemActions';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
@@ -67,7 +68,7 @@ function DiskSpace() {
const { freeSpace, totalSpace } = item;
const diskUsage = 100 - (freeSpace / totalSpace) * 100;
- let diskUsageKind: (typeof kinds.all)[number] = kinds.PRIMARY;
+ let diskUsageKind: Kind = kinds.PRIMARY;
if (diskUsage > 90) {
diskUsageKind = kinds.DANGER;