133 lines
4.8 KiB
TypeScript
133 lines
4.8 KiB
TypeScript
import * as React from 'react'
|
|
import { Slot } from '@radix-ui/react-slot'
|
|
import { cva, type VariantProps } from 'class-variance-authority'
|
|
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const buttonVariants = cva(
|
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-semibold tracking-wide uppercase transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
{
|
|
variants: {
|
|
variant: {
|
|
// Primary: Neon Cyan glow
|
|
default:
|
|
'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:shadow-[0_0_20px_oklch(0.75_0.18_195_/_0.4)]',
|
|
|
|
// Secondary: Hot Magenta glow
|
|
secondary:
|
|
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/90 hover:shadow-[0_0_20px_oklch(0.65_0.25_330_/_0.4)]',
|
|
|
|
// Destructive: Error state
|
|
destructive:
|
|
'bg-destructive text-white shadow-sm hover:bg-destructive/90 hover:shadow-[0_0_15px_oklch(0.60_0.22_25_/_0.3)] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
|
|
|
|
// Outline: Bordered with hover fill
|
|
outline:
|
|
'border border-border bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground hover:border-primary dark:border-border dark:hover:border-primary',
|
|
|
|
// Ghost: Minimal, text only
|
|
ghost:
|
|
'text-muted-foreground hover:bg-accent hover:text-foreground',
|
|
|
|
// Link: Underlined text
|
|
link:
|
|
'text-primary underline-offset-4 hover:underline normal-case font-medium tracking-normal',
|
|
|
|
// Success: Lime green glow
|
|
success:
|
|
'bg-success text-success-foreground shadow-sm hover:bg-success/90 hover:shadow-[0_0_20px_oklch(0.72_0.19_145_/_0.4)]',
|
|
|
|
// Gaming: XP Gold style with depth
|
|
gaming:
|
|
'bg-gradient-to-b from-[#2a2a2a] to-[#1a1a1a] text-[oklch(0.88_0.16_85)] border-2 border-[oklch(0.88_0.16_85)] shadow-[inset_0_1px_0_rgba(255,255,255,0.1),inset_0_-2px_0_rgba(0,0,0,0.3),0_2px_8px_rgba(0,0,0,0.5)] hover:shadow-[inset_0_1px_0_rgba(255,255,255,0.1),inset_0_-2px_0_rgba(0,0,0,0.3),0_0_20px_oklch(0.88_0.16_85_/_0.4)] font-mono',
|
|
|
|
// Nature: Organic moss green
|
|
nature:
|
|
'bg-gradient-to-br from-[oklch(0.40_0.08_145)] to-[oklch(0.55_0.12_145)] text-white shadow-[0_4px_15px_oklch(0.40_0.08_145_/_0.4)] hover:shadow-[0_6px_25px_oklch(0.40_0.08_145_/_0.5)] hover:scale-[1.02] rounded-full normal-case',
|
|
|
|
// Hacker: Terminal style
|
|
hacker:
|
|
'bg-black text-[oklch(0.72_0.19_145)] border border-[oklch(0.72_0.19_145)] font-mono normal-case tracking-normal hover:bg-[oklch(0.72_0.19_145)] hover:text-black hover:shadow-[0_0_20px_oklch(0.72_0.19_145_/_0.4)]',
|
|
},
|
|
size: {
|
|
default: 'h-10 px-5 py-2',
|
|
sm: 'h-8 px-3 text-xs',
|
|
lg: 'h-12 px-8 text-base',
|
|
xl: 'h-14 px-10 text-lg',
|
|
icon: 'size-10',
|
|
'icon-sm': 'size-8',
|
|
'icon-lg': 'size-12',
|
|
},
|
|
shape: {
|
|
default: 'rounded-md',
|
|
pill: 'rounded-full',
|
|
manga: 'rounded-none [clip-path:polygon(0_0,calc(100%-10px)_0,100%_10px,100%_100%,10px_100%,0_calc(100%-10px))]',
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: 'default',
|
|
size: 'default',
|
|
shape: 'default',
|
|
},
|
|
},
|
|
)
|
|
|
|
interface ButtonProps
|
|
extends React.ComponentProps<'button'>,
|
|
VariantProps<typeof buttonVariants> {
|
|
asChild?: boolean
|
|
loading?: boolean
|
|
}
|
|
|
|
function Button({
|
|
className,
|
|
variant,
|
|
size,
|
|
shape,
|
|
asChild = false,
|
|
loading = false,
|
|
children,
|
|
disabled,
|
|
...props
|
|
}: ButtonProps) {
|
|
const Comp = asChild ? Slot : 'button'
|
|
|
|
return (
|
|
<Comp
|
|
data-slot="button"
|
|
className={cn(buttonVariants({ variant, size, shape, className }))}
|
|
disabled={disabled || loading}
|
|
{...props}
|
|
>
|
|
{loading ? (
|
|
<>
|
|
<svg
|
|
className="size-4 animate-spin"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<circle
|
|
className="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
strokeWidth="4"
|
|
/>
|
|
<path
|
|
className="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
/>
|
|
</svg>
|
|
<span>Loading...</span>
|
|
</>
|
|
) : (
|
|
children
|
|
)}
|
|
</Comp>
|
|
)
|
|
}
|
|
|
|
export { Button, buttonVariants }
|