aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/loaders/progress-bar/progress-bar.tsx
blob: d8852608bb56b80f2e1a2c2328e59d524a310a4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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);