Skip to content
File

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

typescript76 lines
1import type { ButtonHTMLAttributes, ReactNode } from "react";
2import { Link, type LinkProps } from "react-router-dom";
3 
4type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
5type ButtonSize = "sm" | "md";
6 
7interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
8 variant?: ButtonVariant;
9 size?: ButtonSize;
10 loading?: boolean;
11 icon?: ReactNode;
12}
13 
14const VARIANT_CLASSES: Record<ButtonVariant, string> = {
15 primary: "bg-accent-600 text-white hover:bg-accent-500 active:scale-[0.98] transition-transform",
16 secondary:
17 "border border-zinc-700/60 bg-zinc-800/60 text-zinc-300 hover:bg-zinc-700/60 hover:text-zinc-100 active:scale-[0.98] transition-transform",
18 danger:
19 "border border-red-500/20 bg-red-500/10 text-red-400 hover:bg-red-500/20 hover:text-red-300 active:scale-[0.98] transition-transform",
20 ghost: "text-zinc-400 hover:bg-zinc-800/70 hover:text-zinc-100 active:scale-[0.97] transition-transform",
21};
22 
23const SIZE_CLASSES: Record<ButtonSize, string> = {
24 sm: "rounded-lg px-3 py-1.5 text-xs",
25 md: "rounded-xl px-4 py-2.5 text-sm",
26};
27 
28export const Button = ({
29 variant = "secondary",
30 size = "md",
31 loading = false,
32 icon,
33 children,
34 disabled,
35 className,
36 ...rest
37}: ButtonProps) => (
38 <button
39 type="button"
40 disabled={disabled || loading}
41 className={[
42 "inline-flex items-center justify-center gap-2 font-medium disabled:cursor-not-allowed disabled:opacity-50",
43 SIZE_CLASSES[size],
44 VARIANT_CLASSES[variant],
45 className,
46 ]
47 .filter(Boolean)
48 .join(" ")}
49 {...rest}
50 >
51 {icon}
52 {loading ? "Loading..." : children}
53 </button>
54);
55 
56interface ButtonLinkProps extends LinkProps {
57 variant?: ButtonVariant;
58 size?: ButtonSize;
59}
60 
61export const ButtonLink = ({ variant = "secondary", size = "md", className, children, ...rest }: ButtonLinkProps) => (
62 <Link
63 className={[
64 "inline-flex items-center justify-center gap-2 font-medium",
65 SIZE_CLASSES[size],
66 VARIANT_CLASSES[variant],
67 className,
68 ]
69 .filter(Boolean)
70 .join(" ")}
71 {...rest}
72 >
73 {children}
74 </Link>
75);