Skip to content
File

Blob: src/client/components/ui/button.tsx

typescript58 lines
1import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
2import { Loader2 } from "lucide-react";
3 
4const variantClasses = {
5 primary: "bg-accent-600 text-white hover:bg-accent-500 active:scale-[0.98]",
6 secondary: "border border-zinc-700/60 bg-zinc-800/60 text-zinc-300 hover:bg-zinc-700/60 active:scale-[0.98]",
7 danger: "border border-red-500/20 bg-red-500/10 text-red-400 hover:bg-red-500/20 active:scale-[0.98]",
8 ghost: "text-zinc-400 hover:bg-zinc-800/70 hover:text-zinc-100 active:scale-[0.97]",
9} as const;
10 
11const sizeClasses = {
12 sm: "gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium",
13 md: "gap-2 rounded-lg px-5 py-2.5 text-sm font-semibold",
14} as const;
15 
16export type ButtonVariant = keyof typeof variantClasses;
17export type ButtonSize = keyof typeof sizeClasses;
18 
19const baseClasses =
20 "inline-flex items-center justify-center transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-canvas disabled:pointer-events-none disabled:opacity-50";
21 
22export function buttonClasses({
23 variant = "secondary",
24 size = "sm",
25 className,
26}: {
27 variant?: ButtonVariant;
28 size?: ButtonSize;
29 className?: string;
30} = {}) {
31 return [baseClasses, variantClasses[variant], sizeClasses[size], className].filter(Boolean).join(" ");
32}
33 
34interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
35 variant?: ButtonVariant;
36 size?: ButtonSize;
37 loading?: boolean;
38 icon?: ReactNode;
39}
40 
41export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
42 ({ variant = "secondary", size = "sm", loading, icon, children, className, disabled, ...rest }, ref) => {
43 return (
44 <button
45 ref={ref}
46 className={buttonClasses({ variant, size, className })}
47 disabled={disabled || loading}
48 {...rest}
49 >
50 {loading ? <Loader2 className={size === "sm" ? "h-3.5 w-3.5 animate-spin" : "h-4 w-4 animate-spin"} /> : icon}
51 {children}
52 </button>
53 );
54 },
55);
56 
57Button.displayName = "Button";