All files / components/ui button.tsx

100% Statements 7/7
100% Branches 9/9
100% Functions 0/0
100% Lines 7/7

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 58 59 60 61 62 63 64 65 66 67 68 69 707x   7x 7x   7x                         7x                               53x                                                                   7x  
import { forwardRef } from "react";
import type { ButtonHTMLAttributes } from "react";
import { cn } from "@/utils/class-name";
import { ArrowPathIcon } from "@/components/icons/ArrowPath";
 
const BUTTON_VARIANT_CLASSES = {
  primary:
    "bg-violet-600 text-white hover:bg-violet-500 disabled:bg-violet-300 disabled:text-white light:bg-violet-600 light:hover:bg-violet-700 light:disabled:bg-violet-300",
  secondary:
    "bg-white/12 text-white/80 hover:bg-white/18 disabled:opacity-50 light:bg-slate-100 light:text-slate-700 light:hover:bg-slate-200 light:disabled:bg-slate-100 light:disabled:text-slate-400",
  outline:
    "border border-white/20 bg-transparent text-white/80 hover:bg-white/10 disabled:opacity-50 light:border-slate-300 light:bg-white light:text-slate-800 light:hover:bg-slate-50 light:disabled:border-slate-200 light:disabled:text-slate-400",
  ghost:
    "bg-transparent text-white/70 hover:bg-white/10 disabled:opacity-50 light:text-slate-700 light:hover:bg-slate-100 light:disabled:text-slate-400",
  danger:
    "bg-red-600 text-white hover:bg-red-500 disabled:bg-red-300 disabled:text-red-50",
} as const;
 
const BUTTON_SIZE_CLASSES = {
  sm: "h-8 px-3 text-xs",
  md: "h-10 px-4 text-sm",
  lg: "h-11 px-5 text-sm",
} as const;
 
type ButtonVariant = keyof typeof BUTTON_VARIANT_CLASSES;
type ButtonSize = keyof typeof BUTTON_SIZE_CLASSES;
 
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: ButtonVariant;
  size?: ButtonSize;
  fullWidth?: boolean;
  isLoading?: boolean;
};
 
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      variant = "primary",
      size = "md",
      fullWidth = false,
      isLoading = false,
      className,
      disabled,
      children,
      ...props
    },
    ref,
  ) => (
    <button
      ref={ref}
      disabled={disabled || isLoading}
      className={cn(
        "inline-flex items-center justify-center rounded-lg font-medium transition cursor-pointer disabled:cursor-not-allowed",
        BUTTON_VARIANT_CLASSES[variant],
        BUTTON_SIZE_CLASSES[size],
        fullWidth && "w-full",
        className,
      )}
      {...props}
    >
      {isLoading && (
        <ArrowPathIcon className="mr-2 size-4 animate-spin" />
      )}
      {children}
    </button>
  ),
);
 
Button.displayName = "Button";