60 lines
3.0 KiB
TypeScript
60 lines
3.0 KiB
TypeScript
import * as React from 'react';
|
|
import { Slot } from '@radix-ui/react-slot';
|
|
import { clsx } from 'clsx';
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive' | 'outline' | 'subtle' | 'link';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
loading?: boolean;
|
|
asChild?: boolean;
|
|
}
|
|
|
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
(
|
|
{ variant = 'primary', size = 'md', loading, asChild, className, children, disabled, ...props },
|
|
ref
|
|
) => {
|
|
const Comp = asChild ? Slot : 'button';
|
|
|
|
const baseStyles =
|
|
'inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-lg font-semibold tracking-normal transition duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--bl-focus-ring,var(--bl-accent,#5A8CFF))] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bl-bg-canvas,#0b0f17)] disabled:pointer-events-none disabled:opacity-50';
|
|
|
|
const variants: Record<string, string> = {
|
|
primary:
|
|
'border border-transparent bg-[var(--bl-accent,#5A8CFF)] text-[var(--bl-accent-foreground,var(--bl-bg-canvas,#0b0f17))] shadow-sm shadow-black/10 hover:brightness-105 active:brightness-95',
|
|
secondary:
|
|
'border border-[var(--bl-border,#2a2a4a)] bg-[var(--bl-surface-card,#1a1a2e)] text-[var(--bl-text-primary,#fff)] shadow-sm shadow-black/5 hover:border-[var(--bl-border-strong,var(--bl-border,#2a2a4a))] hover:bg-[var(--bl-surface-highlight,var(--bl-surface-muted,#252540))]',
|
|
ghost:
|
|
'border border-transparent text-[var(--bl-text-secondary,#a0a0b0)] hover:bg-[var(--bl-surface-muted,#252540)] hover:text-[var(--bl-text-primary,#fff)]',
|
|
destructive:
|
|
'border border-transparent bg-[var(--bl-danger)] text-[var(--bl-danger-foreground,#fff)] shadow-sm shadow-black/10 hover:brightness-105 active:brightness-95',
|
|
outline:
|
|
'border border-[var(--bl-border,#2a2a4a)] bg-transparent text-[var(--bl-text-primary,#fff)] hover:border-[var(--bl-accent,#5A8CFF)] hover:bg-[var(--bl-accent-muted,var(--bl-surface-muted,#252540))]',
|
|
subtle:
|
|
'border border-transparent bg-[var(--bl-surface-muted,#252540)] text-[var(--bl-text-primary,#fff)] hover:bg-[var(--bl-surface-highlight,var(--bl-surface-card,#1a1a2e))]',
|
|
link: 'h-auto rounded-md border border-transparent p-0 text-[var(--bl-accent,#5A8CFF)] underline-offset-4 hover:underline',
|
|
};
|
|
|
|
const sizes: Record<string, string> = {
|
|
sm: 'h-8 px-3 text-xs gap-1.5',
|
|
md: 'h-10 px-4 text-sm gap-2',
|
|
lg: 'h-11 px-5 text-sm gap-2.5',
|
|
};
|
|
|
|
return (
|
|
<Comp
|
|
ref={ref}
|
|
className={clsx(baseStyles, variants[variant], sizes[size], className)}
|
|
disabled={disabled || loading}
|
|
{...props}
|
|
>
|
|
{loading && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
{children}
|
|
</Comp>
|
|
);
|
|
}
|
|
);
|
|
|
|
Button.displayName = 'Button';
|