File size: 2,669 Bytes
0842d68 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react";
import { LucideIcon } from "lucide-react";
import cn from "../../utils/classnames.ts";
interface ButtonProps extends DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> {
variant?:
| "primary"
| "outlined"
| "pill"
| "pill-selected"
| "text"
| "ghost";
icon?: LucideIcon;
iconPosition?: "left" | "right";
children?: ReactNode;
}
export default function Button({
variant = "primary",
icon: Icon,
iconPosition = "left",
children,
className = "",
...props
}: ButtonProps) {
const baseClasses =
"inline-flex items-center justify-center font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2";
const variantClasses = {
primary:
"bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 rounded-md shadow-sm hover:shadow focus:ring-primary/50",
outlined:
"bg-transparent text-primary hover:bg-muted active:bg-primary-50 border border-border rounded-[48px] focus:ring-primary/30",
pill: "bg-transparent text-primary hover:bg-primary-50 active:bg-muted border border-border rounded-[48px] focus:ring-primary/30",
"pill-selected":
"bg-primary-100 text-primary-800 hover:bg-primary-200 active:bg-primary-200 border border-primary-100 rounded-[48px] shadow-sm focus:ring-primary-800/30",
text: "bg-transparent text-primary hover:bg-muted active:bg-primary-50 rounded-md focus:ring-primary/30",
ghost:
"bg-transparent text-muted-foreground hover:bg-muted hover:text-primary active:bg-primary-50 rounded-md focus:ring-primary/30",
};
const sizeClasses = {
primary: "[padding:0.625em_1.5em] [gap:0.5em]",
outlined: "[padding:0.5em_0.75em] [gap:0.5em]",
pill: "[padding:0.5em_0.75em] [gap:0.5em]",
"pill-selected": "[padding:0.5em_0.75em] [gap:0.5em]",
text: "[padding:0.375em_0.5em] [gap:0.5em]",
ghost: "[padding:0.5em_0.75em] [gap:0.5em]",
};
const disabledClasses =
"disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-current disabled:hover:shadow-none";
return (
<button
className={cn(
"cursor-pointer",
baseClasses,
variantClasses[variant],
sizeClasses[variant],
disabledClasses,
className
)}
{...props}
>
{Icon && iconPosition === "left" && (
<Icon className="[width:1.25em] [height:1.25em]" />
)}
{children}
{Icon && iconPosition === "right" && (
<Icon className="[width:1.25em] [height:1.25em]" />
)}
</button>
);
}
|