Skip to content
File

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

typescript60 lines
1import { type ButtonHTMLAttributes, type ReactNode, type Ref } from "react";
2import {
3 BUTTON_BASE_CLASSES,
4 BUTTON_DISABLED_CLASSES,
5 BUTTON_ICON_ONLY_SIZE_CLASSES,
6 BUTTON_SIZE_CLASSES,
7 BUTTON_VARIANT_CLASSES,
8 type ButtonSize,
9 type ButtonVariant,
10} from "./button-classes";
11 
12interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
13 ref?: Ref<HTMLButtonElement>;
14 variant?: ButtonVariant;
15 size?: ButtonSize;
16 loading?: boolean;
17 icon?: ReactNode;
18 iconOnly?: boolean;
19}
20 
21export function Button({
22 ref,
23 variant = "secondary",
24 size = "md",
25 loading = false,
26 icon,
27 iconOnly = false,
28 children,
29 disabled,
30 className,
31 ...rest
32}: ButtonProps) {
33 return (
34 <button
35 ref={ref}
36 type="button"
37 disabled={disabled || loading}
38 className={[
39 BUTTON_BASE_CLASSES,
40 BUTTON_DISABLED_CLASSES,
41 iconOnly ? BUTTON_ICON_ONLY_SIZE_CLASSES[size] : BUTTON_SIZE_CLASSES[size],
42 BUTTON_VARIANT_CLASSES[variant],
43 className,
44 ]
45 .filter(Boolean)
46 .join(" ")}
47 {...rest}
48 >
49 {loading
50 ? (icon ?? <span className="h-4 w-4 animate-spin rounded-full border-2 border-current/30 border-t-current" />)
51 : icon}
52 {variant === "subtle" ? (
53 <span className="opacity-60 transition-opacity group-hover:opacity-100">{children}</span>
54 ) : (
55 children
56 )}
57 </button>
58 );
59}