| 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> | |
| ); | |
| } | |