DocDoeAI / src /components /clean /shared /CleanButton.tsx
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
2.73 kB
"use client";
import { forwardRef } from "react";
import { cn } from "@/lib/utils";
// =============================================================================
// CleanButton — Black primary, gray secondary, ghost
// =============================================================================
export type CleanButtonVariant = "primary" | "secondary" | "ghost" | "outline";
export type CleanButtonSize = "sm" | "md" | "lg";
export interface CleanButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: CleanButtonVariant;
size?: CleanButtonSize;
fullWidth?: boolean;
icon?: React.ReactNode;
iconRight?: React.ReactNode;
}
const variantClasses: Record<CleanButtonVariant, string> = {
primary:
"bg-[#111217] text-[#fbfbfc] hover:bg-[#0b0d12] active:bg-[#1a1c24] border border-transparent shadow-[0_1px_2px_rgba(15,17,23,0.06)]",
secondary:
"bg-[#F0F1F4] text-[#3A3F4C] hover:bg-[#E4E6EC] active:bg-[#D4D7E0] border border-[#E4E6EC]",
ghost:
"bg-transparent text-[#5C6170] hover:bg-[#F3F4F7] hover:text-[#111217] active:bg-[#EBECF0] border border-transparent",
outline:
"bg-white text-[#3A3F4C] hover:bg-[#F7F8FA] active:bg-[#F0F1F4] border border-[#E4E6EC]",
};
const sizeClasses: Record<CleanButtonSize, string> = {
sm: "h-8 px-3 text-[12px] font-medium gap-1.5 rounded-lg",
md: "h-10 px-4 text-[13px] font-semibold gap-2 rounded-lg",
lg: "h-11 px-5 text-[14px] font-semibold gap-2 rounded-xl",
};
export const CleanButton = forwardRef<HTMLButtonElement, CleanButtonProps>(
(
{
variant = "primary",
size = "md",
fullWidth = false,
icon,
iconRight,
className,
children,
disabled,
...props
},
ref,
) => {
return (
<button
ref={ref}
type="button"
disabled={disabled}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap select-none",
"transition-[transform,background-color,border-color,box-shadow,color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)]",
"active:scale-[0.97]",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#111217]/25 focus-visible:ring-offset-2",
"disabled:opacity-50 disabled:pointer-events-none disabled:active:scale-100",
variantClasses[variant],
sizeClasses[size],
fullWidth && "w-full",
className,
)}
{...props}
>
{icon && <span className="shrink-0 [&>svg]:size-4">{icon}</span>}
{children}
{iconRight && <span className="shrink-0 [&>svg]:size-4">{iconRight}</span>}
</button>
);
},
);
CleanButton.displayName = "CleanButton";