/ src / components / ui / button.tsx
button.tsx
  1  import { Slot } from "@radix-ui/react-slot";
  2  import { cva, type VariantProps } from "class-variance-authority";
  3  import { cn, focusRing } from "../../lib/utils";
  4  import * as React from "react";
  5  
  6  const buttonVariants = cva(
  7    "ml-1px inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
  8    {
  9      compoundVariants: [
 10        {
 11          variant: "default",
 12          color: "muted",
 13          className: "text-foreground hover:text-background",
 14        },
 15        {
 16          variant: "outline",
 17          color: "muted",
 18          className: "text-gray-500",
 19        },
 20        {
 21          variant: ["outline", "default"],
 22          size: "xl",
 23          className: "border-3",
 24        },
 25        {
 26          variant: "filled",
 27          color: "primary",
 28          className: "bg-primary",
 29        },
 30        {
 31          variant: "filled",
 32          color: "secondary",
 33          className: "bg-secondary",
 34        },
 35        {
 36          variant: "filled",
 37          color: "error",
 38          className: "bg-error",
 39        },
 40        {
 41          variant: "filled",
 42          color: "success",
 43          className: "bg-success",
 44        },
 45        {
 46          variant: "filled",
 47          color: "warning",
 48          className: "bg-warning",
 49        },
 50        {
 51          variant: "filled",
 52          color: "info",
 53          className: "bg-info",
 54        },
 55        {
 56          asIconButton: true,
 57          size: "xs",
 58          className: "w-5",
 59        },
 60        {
 61          asIconButton: true,
 62          size: "sm",
 63          className: "w-6",
 64        },
 65        {
 66          asIconButton: true,
 67          size: "md",
 68          className: "w-8",
 69        },
 70        {
 71          asIconButton: true,
 72          size: "lg",
 73          className: "w-10",
 74        },
 75        {
 76          asIconButton: true,
 77          size: "xl",
 78          className: "w-12",
 79        },
 80        {
 81          variant: "ghost",
 82          color: ["primary", "secondary", "error", "success", "warning", "info"],
 83          className: "bg-transparent",
 84        },
 85      ],
 86      variants: {
 87        color: {
 88          primary:
 89            "bg-primary/75 border-primary text-primary hover:bg-primary/50 focus:ring-primary",
 90          secondary:
 91            "bg-secondary/75 border-secondary text-secondary hover:bg-secondary/50 focus:ring-secondary",
 92          error:
 93            "bg-error/75 border-error text-error hover:bg-error/50 focus:ring-error",
 94          success:
 95            "bg-success/75 border-success text-success hover:bg-success/50 focus:ring-success",
 96          warning:
 97            "bg-warning/75 border-warning text-warning hover:bg-warning/50 focus:ring-warning",
 98          info: "bg-info/75 border-info text-info hover:bg-info/50 focus:ring-info",
 99          muted: "border-gray-500 hover:bg-gray-400/50",
100        },
101        variant: {
102          default: "border-2 shadow text-foreground hover:border-foreground",
103          filled:
104            "shadow text-foreground hover:bg-foreground hover:text-background",
105          outline:
106            "border-2 shadow bg-transparent hover:text-foreground hover:border-foreground",
107          ghost: "shadow hover:bg-accent hover:text-accent-foreground",
108          link: "bg-transparent hover:bg-transparent underline focus:ring-offset-0",
109        },
110        size: {
111          xs: "h-5 px-2 text-xs",
112          sm: "h-6 px-3 text-sm",
113          md: "h-8 px-4 text-base",
114          lg: "h-10 px-8 text-lg",
115          xl: "h-12 px-10 text-xl",
116        },
117        round: {
118          none: "rounded-none",
119          both: "rounded-full",
120          left: "rounded-l-full",
121          right: "rounded-r-full",
122          t: "rounded-t",
123          b: "rounded-b",
124          tl: "rounded-tl",
125          tr: "rounded-tr",
126          bl: "rounded-bl",
127          br: "rounded-br",
128        },
129        asIconButton: {
130          true: "p-0",
131        },
132        noShadow: {
133          true: "shadow-none",
134        },
135      },
136      defaultVariants: {
137        variant: "default",
138        color: "primary",
139        round: "both",
140        size: "md",
141      },
142    },
143  );
144  
145  export interface ButtonProps
146    extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "color">,
147      VariantProps<typeof buttonVariants> {
148    asChild?: boolean;
149    height?: string;
150    asIconButton?: boolean;
151    noShadow?: boolean;
152    isLoading?: boolean;
153  }
154  
155  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
156    (
157      {
158        className,
159        children,
160        color,
161        variant,
162        round,
163        size,
164        asIconButton,
165        height,
166        noShadow,
167        isLoading,
168        asChild = false,
169        ...props
170      },
171      ref,
172    ) => {
173      const Comp = asChild ? Slot : "button";
174  
175      return (
176        <Comp
177          className={cn(
178            focusRing,
179            buttonVariants({
180              variant,
181              color,
182              size,
183              round,
184              asIconButton,
185              noShadow,
186            }),
187            className,
188            size && size !== "xs" && `rounded-${round}-${size}`,
189            height,
190            isLoading && "animate-border-pulse",
191          )}
192          ref={ref}
193          disabled={isLoading}
194          {...props}
195        >
196          {children}
197        </Comp>
198      );
199    },
200  );
201  Button.displayName = "Button";
202  
203  export { Button, buttonVariants };