import type { HTMLAttributes } from "react"; import { cx } from "../../utils/cx.utils"; import { Label } from "../label/label.component"; import { PatternOverlay } from "../pattern-overlay/pattern-overlay.component"; import "./progress-bar.component.css"; function clampPercent(value: number): number { return Math.min(100, Math.max(0, value)); } /** Props accepted by {@link ProgressBar}, {@link ProgressBar.Naked}, and {@link ProgressBar.Diagonal}. */ export interface ProgressBarProps extends HTMLAttributes { /** Progress value, clamped to `0`-`100`. */ value: number; /** Text shown below the bar as `label · value%`. Omit to render the bar alone. */ label?: string; } interface TrackProps extends HTMLAttributes { value: number; diagonal?: boolean; } function Track({ className, value, diagonal, ...rest }: TrackProps) { const percent = clampPercent(value); return (
{diagonal && }
); } interface BaseProgressBarProps extends ProgressBarProps { diagonal?: boolean; } function BaseProgressBar({ className, value, label, diagonal, ...rest }: BaseProgressBarProps) { const percent = clampPercent(value); return (
{label && ( )}
); } /** * Bordered, full-width progress bar filled solid black up to `value` * percent. Shows an optional {@link Label} below the bar reading * `label · value%`. * * @example * ```tsx * * ``` */ export function ProgressBar(props: ProgressBarProps) { return ; } /** Props accepted by {@link ProgressBar.Naked}. */ export type ProgressBarNakedProps = Omit; /** * {@link ProgressBar} without a label, ever - just the bar. * * @example * ```tsx * * ``` */ function ProgressBarNaked({ className, value, ...rest }: ProgressBarNakedProps) { return ; } /** * {@link ProgressBar} filled with diagonal black strokes instead of a solid * fill. Accepts the same props as {@link ProgressBar}. * * @example * ```tsx * * ``` */ function ProgressBarDiagonal(props: ProgressBarProps) { return ; } /** Props accepted by {@link ProgressBar.Stepper}. */ export interface ProgressBarStepperProps extends HTMLAttributes { /** Total number of steps. */ steps: number; /** Number of steps filled. `0` fills none, `>= steps` fills all. */ currentStep: number; /** Text shown below the bar as `label · currentStep / steps` ("-" when currentStep is 0, capped at steps). */ label?: string; } /** * {@link ProgressBar} variant made of discrete, evenly-sized steps instead * of a continuous fill. Shows an optional {@link Label} below the bar * reading `label · currentStep / steps` ("-" when currentStep is 0, capped * at steps). * * @example * ```tsx * * ``` */ function ProgressBarStepper({ className, steps, currentStep, label, ...rest }: ProgressBarStepperProps) { const filled = Math.min(Math.max(currentStep, 0), steps); return (
{Array.from({ length: steps }, (_, index) => (
))}
{label && ( )}
); } ProgressBar.Naked = ProgressBarNaked; ProgressBar.Diagonal = ProgressBarDiagonal; ProgressBar.Stepper = ProgressBarStepper;