"use client" import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const badgeVariants = cva( "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-all duration-200 ease-out focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { default: "border-transparent bg-primary text-primary-foreground shadow-sm hover:shadow-md hover:bg-primary/80", secondary: "border-transparent bg-surface text-secondary-foreground hover:bg-surface-hover", destructive: "border-transparent bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/80", outline: "text-foreground border-border hover:bg-surface hover:border-border-strong", success: "border-transparent bg-success text-success-foreground shadow-sm hover:bg-success/80", warning: "border-transparent bg-warning text-warning-foreground shadow-sm hover:bg-warning/80", info: "border-transparent bg-primary/20 text-primary border-primary/30 hover:bg-primary/30", accent: "border-transparent bg-accent text-accent-foreground shadow-sm hover:bg-accent/80", glass: "glass text-foreground border-border/20 hover:bg-surface/50 hover:border-border/40", gradient: "border-transparent bg-gradient-to-r from-primary to-accent text-white shadow-lg hover:shadow-xl hover:shadow-primary/25", neon: "border-transparent bg-primary/10 text-primary border-primary/20 shadow-lg shadow-primary/20 hover:bg-primary/20 hover:border-primary/30 hover:shadow-primary/30", pulse: "border-transparent bg-primary text-primary-foreground shadow-lg shadow-primary/25 animate-pulse hover:animate-none", dot: "border-transparent bg-primary text-primary-foreground w-2 h-2 p-0 rounded-full", premium: "border-transparent bg-gradient-to-r from-primary via-accent to-primary text-white shadow-lg hover:shadow-xl hover:shadow-primary/25 relative overflow-hidden", }, size: { default: "px-2.5 py-0.5 text-xs", sm: "px-2 py-0.5 text-xs", lg: "px-3 py-1 text-sm", xl: "px-4 py-1.5 text-base", icon: "w-6 h-6 p-0 rounded-lg flex items-center justify-center", dot: "w-2 h-2 p-0 rounded-full", }, interactive: { true: "cursor-pointer hover:scale-105 active:scale-95", false: "", }, }, defaultVariants: { variant: "default", size: "default", interactive: false, }, } ) export interface BadgeProps extends React.HTMLAttributes, VariantProps { icon?: React.ReactNode removable?: boolean onRemove?: () => void pulse?: boolean count?: number maxCount?: number } const Badge = React.forwardRef( ({ className, variant, size, interactive = false, icon, removable = false, onRemove, pulse = false, count, maxCount = 99, children, ...props }, ref) => { const [visible, setVisible] = React.useState(true) const [removing, setRemoving] = React.useState(false) const handleRemove = () => { setRemoving(true) setTimeout(() => { setVisible(false) onRemove?.() }, 200) } const displayCount = count && count > maxCount ? `${maxCount}+` : count if (!visible) return null return (
{/* Gradient Shine Effect */} {(variant === "premium" || variant === "gradient") && ( )} {/* Icon */} {icon && ( {icon} )} {/* Content */} {children} {displayCount && ( {displayCount} )} {/* Remove Button */} {removable && ( )} {/* Pulse Ring for Neon Variant */} {variant === "neon" && ( <> )}
) } ) Badge.displayName = "Badge" // Status Badge Component export const StatusBadge = React.forwardRef< HTMLDivElement, Omit & { status: "online" | "offline" | "busy" | "away" | "success" | "error" | "warning" showLabel?: boolean } >(({ className, status, showLabel = true, children, ...props }, ref) => { const statusConfig = { online: { variant: "success" as const, label: "Online", icon: "●" }, offline: { variant: "secondary" as const, label: "Offline", icon: "○" }, busy: { variant: "destructive" as const, label: "Busy", icon: "◐" }, away: { variant: "warning" as const, label: "Away", icon: "◐" }, success: { variant: "success" as const, label: "Success", icon: "✓" }, error: { variant: "destructive" as const, label: "Error", icon: "✕" }, warning: { variant: "warning" as const, label: "Warning", icon: "!" }, } const config = statusConfig[status] return ( {config.icon} {status === "online" && ( )} {showLabel && ( {children || config.label} )} ) }) StatusBadge.displayName = "StatusBadge" // Counter Badge Component export const CounterBadge = React.forwardRef< HTMLDivElement, Omit & { value: number showZero?: boolean position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" } >(({ className, value, showZero = false, position = "top-right", size = "icon", ...props }, ref) => { if (value === 0 && !showZero) return null const positionClasses = { "top-right": "-top-2 -right-2", "top-left": "-top-2 -left-2", "bottom-right": "-bottom-2 -right-2", "bottom-left": "-bottom-2 -left-2", } return ( {value > 99 ? "99+" : value} ) }) CounterBadge.displayName = "CounterBadge" // Progress Badge Component export const ProgressBadge = React.forwardRef< HTMLDivElement, Omit & { value: number max?: number showPercentage?: boolean } >(({ className, value, max = 100, showPercentage = true, ...props }, ref) => { const percentage = Math.min(100, Math.max(0, (value / max) * 100)) const displayValue = showPercentage ? `${Math.round(percentage)}%` : `${value}/${max}` return ( {/* Progress Background */} {/* Text */} {displayValue} ) }) ProgressBadge.displayName = "ProgressBadge" export { Badge, badgeVariants }