aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/loaders/progress-bar/progress-bar.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-09-29 15:49:14 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:40 +0100
commit9128c224c65f8f2a172b22a443ccb4573c7acd90 (patch)
treebc435554174a5ed4c3f8808190cb94016f8d28f0 /src/components/atoms/loaders/progress-bar/progress-bar.tsx
parent81b1e0e05919eb368a66aef47adcf7738af76f29 (diff)
refactor(components): rewrite ProgressBar component
* Avoid browser vendors by adding an extra div * Add a loading state * Add an option to center the progress bar (no longer the default) * Remove min since it is always 0
Diffstat (limited to 'src/components/atoms/loaders/progress-bar/progress-bar.tsx')
-rw-r--r--src/components/atoms/loaders/progress-bar/progress-bar.tsx104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/components/atoms/loaders/progress-bar/progress-bar.tsx b/src/components/atoms/loaders/progress-bar/progress-bar.tsx
new file mode 100644
index 0000000..d885260
--- /dev/null
+++ b/src/components/atoms/loaders/progress-bar/progress-bar.tsx
@@ -0,0 +1,104 @@
+import {
+ useId,
+ type CSSProperties,
+ type HTMLAttributes,
+ type ForwardRefRenderFunction,
+ forwardRef,
+} from 'react';
+import { Label } from '../../forms';
+import styles from './progress-bar.module.scss';
+
+export type ProgressBarProps = Omit<
+ HTMLAttributes<HTMLDivElement>,
+ 'children'
+> & {
+ /**
+ * Current value.
+ */
+ current: number;
+ /**
+ * Should the progress bar be centered inside its parent?
+ *
+ * @default false
+ */
+ isCentered?: boolean;
+ /**
+ * Should the progress bar indicate a loading state?
+ *
+ * @default false
+ */
+ isLoading?: boolean;
+ /**
+ * The progress bar label.
+ */
+ label: string;
+ /**
+ * Maximal value.
+ */
+ max: number;
+};
+
+const ProgressBarWithRef: ForwardRefRenderFunction<
+ HTMLDivElement,
+ ProgressBarProps
+> = (
+ {
+ className = '',
+ current,
+ isCentered = false,
+ isLoading = false,
+ label,
+ max,
+ ...props
+ },
+ ref
+) => {
+ const wrapperClass = [
+ styles.wrapper,
+ styles[isCentered ? 'wrapper--centered' : ''],
+ className,
+ ].join(' ');
+ const progressClass = `${styles.progress} ${
+ styles[isLoading ? 'progress--loading' : '']
+ }`;
+ const progressBarId = useId();
+ const progressValueFallback = `${current}/${max}`;
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers -- Percent
+ const progressPercent = `${((max - current) / max) * 100}%`;
+
+ return (
+ <div {...props} className={wrapperClass} ref={ref}>
+ <Label
+ className={styles.label}
+ htmlFor={progressBarId}
+ // eslint-disable-next-line react/jsx-no-literals -- Size allowed
+ size="md"
+ >
+ {label}
+ </Label>
+ <div
+ className={progressClass}
+ style={{ '--currentProgress': `-${progressPercent}` } as CSSProperties}
+ >
+ <progress
+ aria-valuemin={0}
+ aria-valuemax={max}
+ aria-valuenow={current}
+ id={progressBarId}
+ max={max}
+ value={isLoading ? undefined : current}
+ >
+ {progressValueFallback}
+ </progress>
+ <div aria-hidden className={styles.progress__bar} />
+ </div>
+ </div>
+ );
+};
+
+/**
+ * ProgressBar component
+ *
+ * Render a progress bar.
+ */
+export const ProgressBar = forwardRef(ProgressBarWithRef);