flnxi-Wu/components/ui/Button.tsx
2025-11-22 17:56:24 +08:00

99 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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