import * as React from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { cn } from "@/lib/utils" export type ProgressTone = "default" | "success" | "info" | "warning" | "danger" | "muted" export type ProgressSize = "sm" | "default" | "lg" export type ProgressProps = Omit, "children"> & { value?: number | null max?: number min?: number label?: React.ReactNode description?: React.ReactNode showValue?: boolean valueFormatter?: (value: number, percent: number) => React.ReactNode tone?: ProgressTone size?: ProgressSize indeterminate?: boolean trackClassName?: string indicatorClassName?: string } export type ProgressCardProps = React.ComponentProps & { title?: React.ReactNode description?: React.ReactNode value?: number | null max?: number min?: number tone?: ProgressTone size?: ProgressSize showValue?: boolean valueFormatter?: ProgressProps["valueFormatter"] footer?: React.ReactNode progressClassName?: string } const toneClassName: Record = { default: "bg-primary", success: "bg-emerald-500", info: "bg-blue-500", warning: "bg-amber-500", danger: "bg-destructive", muted: "bg-muted-foreground", } const sizeClassName: Record = { sm: "h-1.5", default: "h-2", lg: "h-3", } function clampValue(value: number, min: number, max: number) { return Math.min(Math.max(value, min), max) } function getProgressPercent(value: number | null | undefined, min: number, max: number) { if (value === null || value === undefined || !Number.isFinite(value)) return 0 if (max <= min) return 0 return ((clampValue(value, min, max) - min) / (max - min)) * 100 } function Progress({ value = 0, min = 0, max = 100, label, description, showValue = false, valueFormatter, tone = "default", size = "default", indeterminate = false, className, trackClassName, indicatorClassName, ...props }: ProgressProps) { const percent = getProgressPercent(value, min, max) const resolvedValue = value ?? min return (
{(label || description || showValue) && (
{label &&
{label}
} {description &&
{description}
}
{showValue && (
{valueFormatter ? valueFormatter(resolvedValue, percent) : `${Math.round(percent)}%`}
)}
)}
) } function ProgressCard({ title, description, value, min, max, tone, size, showValue = true, valueFormatter, footer, progressClassName, className, ...props }: ProgressCardProps) { return (
{title && {title}} {description && {description}}
{footer}
) } export { Progress, ProgressCard, getProgressPercent }