All files / src/components/ui Button.tsx

100% Statements 5/5
100% Branches 7/7
100% Functions 1/1
100% Lines 5/5

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                      3x               3x           3x   96x                                                   3x      
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';
 
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
 
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  loading?: boolean;
  children: ReactNode;
  size?: 'sm' | 'md' | 'lg';
}
 
const variantClasses: Record<ButtonVariant, string> = {
  primary: 'bg-accent text-white hover:bg-accent-hover shadow-button hover:shadow-button-hover hover:-translate-y-0.5',
  secondary: 'bg-surface-secondary text-text-primary hover:bg-gray-200',
  ghost: 'bg-transparent text-text-secondary hover:text-text-primary hover:bg-gray-100',
  danger: 'bg-red text-white hover:bg-red-hover',
  link: 'bg-transparent text-accent hover:underline p-0 shadow-none',
};
 
const sizeClasses: Record<string, string> = {
  sm: 'px-3 py-1.5 text-xs',
  md: 'px-5 py-2 text-sm',
  lg: 'px-7 py-2.5 text-base',
};
 
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant = 'primary', size = 'md', loading, disabled, className = '', children, ...props }, ref) => {
    return (
      <button
        ref={ref}
        disabled={disabled || loading}
        className={`
          inline-flex items-center justify-center gap-1.5
          font-medium rounded-button transition-button
          disabled:opacity-50 disabled:cursor-not-allowed
          ${variantClasses[variant]}
          ${sizeClasses[size]}
          ${className}
        `}
        {...props}
      >
        {loading && (
          <svg className="animate-spin h-4 w-4" viewBox="0 0 24 24" fill="none">
            <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
            <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
          </svg>
        )}
        {children}
      </button>
    );
  }
);
 
Button.displayName = 'Button';
export { Button };
export type { ButtonProps };