99 lines
2.3 KiB
TypeScript
99 lines
2.3 KiB
TypeScript
/**
|
||
* Button 组件
|
||
*
|
||
* 统一的按钮组件,遵循三层架构设计
|
||
* 提供多种变体(primary、secondary、ghost、danger、success)
|
||
* 支持多种尺寸(sm、md、lg)、加载状态、全宽等
|
||
* 使用 forwardRef 支持 ref 传递
|
||
*
|
||
* @created 2025-11-22
|
||
* @component
|
||
*/
|
||
|
||
'use client'
|
||
|
||
import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react'
|
||
import { useThemeStyles, cn } from '@/lib/ui-styles'
|
||
|
||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||
children: ReactNode
|
||
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'success'
|
||
size?: 'sm' | 'md' | 'lg'
|
||
fullWidth?: boolean
|
||
loading?: boolean
|
||
}
|
||
|
||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||
(
|
||
{
|
||
children,
|
||
variant = 'primary',
|
||
size = 'md',
|
||
fullWidth = false,
|
||
loading = false,
|
||
disabled,
|
||
className = '',
|
||
...props
|
||
},
|
||
ref
|
||
) => {
|
||
const styles = useThemeStyles()
|
||
|
||
const sizeClasses = {
|
||
sm: styles.buttonModifiers.compactXs,
|
||
md: '',
|
||
lg: 'px-8 py-4 text-base',
|
||
}
|
||
|
||
const variantClass = styles.buttonStyles[variant]
|
||
|
||
return (
|
||
<button
|
||
ref={ref}
|
||
disabled={disabled || loading}
|
||
className={cn(
|
||
variantClass,
|
||
sizeClasses[size],
|
||
fullWidth && styles.buttonModifiers.block,
|
||
loading && 'opacity-75 cursor-wait',
|
||
className
|
||
)}
|
||
{...props}
|
||
>
|
||
{loading ? (
|
||
<span className="flex items-center gap-2">
|
||
<svg
|
||
className="animate-spin h-4 w-4"
|
||
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>加载中...</span>
|
||
</span>
|
||
) : (
|
||
children
|
||
)}
|
||
</button>
|
||
)
|
||
}
|
||
)
|
||
|
||
Button.displayName = 'Button'
|
||
|
||
export default Button
|
||
|