"use client"; import React from "react"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/design-system/lib/utils"; /** * Progress 进度条组件 * * Design System 中的进度条组件,用于显示任务完成进度。 * * @example * ```tsx * // 默认进度条 * * * // 不同尺寸 * * * * // 不同变体 * * * * * // 无标签 * * * // 自定义颜色 * * ``` */ /** * Progress 变体样式 */ const progressVariants = cva( // 基础样式 "overflow-hidden rounded-full bg-gray-200 transition-all duration-250", { variants: { size: { sm: "h-1.5", md: "h-2", lg: "h-3", }, variant: { default: "", success: "", warning: "", error: "", }, }, defaultVariants: { size: "md", variant: "default", }, } ); export type ProgressSize = VariantProps["size"]; export type ProgressVariant = VariantProps["variant"]; export interface ProgressProps extends React.HTMLAttributes, VariantProps { // 进度值(0-100) value: number; // 是否显示百分比标签 showLabel?: boolean; // 自定义标签 label?: string; // 是否显示动画 animated?: boolean; // 自定义颜色(覆盖 variant) color?: string; } /** * Progress 进度条组件 */ export function Progress({ value = 0, size = "md", variant = "default", showLabel = true, label, animated = true, color, className, ...props }: ProgressProps) { // 确保值在 0-100 之间 const clampedValue = Math.min(100, Math.max(0, value)); // 计算颜色 const getColor = () => { if (color) return color; const colors = { default: "bg-primary-500", success: "bg-success-500", warning: "bg-warning-500", error: "bg-error-500", }; const actualVariant = variant ?? "default"; return colors[actualVariant]; }; // 格式化标签 const formatLabel = () => { if (label !== undefined) return label; return `${Math.round(clampedValue)}%`; }; return ( {showLabel && ( {formatLabel()} )} ); } /** * CircularProgress - 环形进度条 */ export interface CircularProgressProps extends React.SVGProps { value: number; size?: number; strokeWidth?: number; variant?: ProgressVariant; showLabel?: boolean; label?: string; } export function CircularProgress({ value = 0, size = 120, strokeWidth = 8, variant = "default", showLabel = true, label, className, ...props }: CircularProgressProps) { const clampedValue = Math.min(100, Math.max(0, value)); const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const offset = circumference - (clampedValue / 100) * circumference; const colors = { default: "#35786f", success: "#22c55e", warning: "#f59e0b", error: "#ef4444", }; const strokeColor = colors[variant ?? "default"]; return ( {/* 背景圆 */} {/* 进度圆 */} {showLabel && ( {label !== undefined ? label : `${Math.round(clampedValue)}%`} )} ); }