1
7import * as React from "react";
8import * as Slot from "@radix-ui/react-slot";
9import { clsx } from "clsx";
10
11export type ButtonVariant = "primary" | "secondary" | "ghost" | "destructive" | "danger";
12export type ButtonSize = "sm" | "md" | "lg";
13
14const VARIANT: Record<ButtonVariant, string> = {
15 primary: "bg-primary text-primary-foreground hover:opacity-90",
16 secondary: "bg-secondary text-foreground hover:bg-accent",
17 ghost: "bg-transparent text-foreground hover:bg-accent",
18 destructive: "bg-destructive text-destructive-foreground hover:opacity-90",
19 danger: "bg-transparent text-destructive hover:bg-destructive/10",
20};
21
22const SIZE: Record<ButtonSize, string> = {
23 sm: "h-7 px-2.5 text-xs",
24 md: "h-9 px-3.5 text-sm",
25 lg: "h-11 px-5 text-base",
26};
27
28export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
29 variant?: ButtonVariant;
30 size?: ButtonSize;
31
32 asChild?: boolean;
33}
34
35export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
36 (
37 { variant = "primary", size = "md", asChild = false, className, type = "button", ...props },
38 ref,
39 ) => {
40 const classes = clsx(
41 "inline-flex items-center justify-center gap-2 rounded-md font-medium",
42 "transition-colors select-none cursor-pointer",
43 "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
44 "disabled:pointer-events-none disabled:opacity-50",
45 VARIANT[variant],
46 SIZE[size],
47 className,
48 );
49 if (asChild) {
50 return (
51 <Slot.Root className={classes} ref={ref} {...props}>
52 {}
53 </Slot.Root>
54 );
55 }
56 return <button ref={ref} type={type} className={classes} {...props} />;
57 },
58);
59Button.displayName = "Button";
60