返回 AiToEarn
button.tsx
根目录 / project / aitoearn-web / src / components / ui / button.tsx
1 import type { VariantProps } from 'class-variance-authority'
2 import { Slot } from '@radix-ui/react-slot'
3 import { cva } from 'class-variance-authority'
4 import * as React from 'react'
5
6 import { cn } from '@/utils/className'
7
8 const buttonVariants = cva(
9 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 cursor-pointer',
10 {
11 variants: {
12 variant: {
13 default:
14 'rounded-md bg-gradient-back text-gradient-foreground shadow-sm shadow-primary/20 hover:shadow-md hover:shadow-primary/25 active:scale-[0.98]',
15 destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
16 outline:
17 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
18 secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
19 ghost: 'hover:bg-accent hover:text-accent-foreground',
20 link: 'text-primary underline-offset-4 hover:underline',
21 },
22 size: {
23 default: 'h-9 px-4 py-2',
24 sm: 'h-8 rounded-md px-3 text-xs',
25 lg: 'h-10 rounded-md px-8',
26 icon: 'h-9 w-9',
27 },
28 },
29 defaultVariants: {
30 variant: 'default',
31 size: 'default',
32 },
33 },
34 )
35
36 export interface ButtonProps
37 extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
38 asChild?: boolean
39 loading?: boolean
40 }
41
42 const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43 ({ className, variant, size, asChild = false, loading, ...props }, ref) => {
44 const Comp = asChild ? Slot : 'button'
45 const isDisabled = props.disabled || loading
46
47 // asChild 模式下,Slot 需要单个子元素,不能渲染 loading spinner
48 if (asChild) {
49 return (
50 <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
51 )
52 }
53
54 return (
55 <Comp
56 className={cn(buttonVariants({ variant, size, className }))}
57 ref={ref}
58 disabled={isDisabled}
59 {...props}
60 >
61 {loading && (
62 <svg className="w-4 h-4 mr-2 animate-spin" viewBox="0 0 24 24" aria-hidden>
63 <circle
64 cx="12"
65 cy="12"
66 r="10"
67 stroke="currentColor"
68 strokeWidth="4"
69 strokeOpacity="0.2"
70 fill="none"
71 />
72 <path
73 d="M22 12a10 10 0 0 1-10 10"
74 stroke="currentColor"
75 strokeWidth="4"
76 strokeLinecap="round"
77 fill="none"
78 />
79 </svg>
80 )}
81 {props.children}
82 </Comp>
83 )
84 },
85 )
86 Button.displayName = 'Button'
87
88 export { Button, buttonVariants }
89
89 lines Plain Text