113 lines
2.8 KiB
TypeScript
113 lines
2.8 KiB
TypeScript
import React from 'react';
|
|
|
|
interface ButtonProps {
|
|
children: React.ReactNode;
|
|
onClick?: () => void;
|
|
variant?: 'primary' | 'secondary' | 'danger';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
loading?: boolean;
|
|
disabled?: boolean;
|
|
type?: 'button' | 'submit' | 'reset';
|
|
fullWidth?: boolean;
|
|
}
|
|
|
|
export function Button({
|
|
children,
|
|
onClick,
|
|
variant = 'primary',
|
|
size = 'md',
|
|
loading = false,
|
|
disabled = false,
|
|
type = 'button',
|
|
fullWidth = false,
|
|
}: ButtonProps) {
|
|
const variantStyles = {
|
|
primary: {
|
|
background: 'var(--color-accent-primary)',
|
|
color: 'white',
|
|
hover: 'var(--color-accent-hover)',
|
|
},
|
|
secondary: {
|
|
background: 'var(--color-background-tertiary)',
|
|
color: 'var(--color-text-primary)',
|
|
hover: 'var(--color-border-primary)',
|
|
},
|
|
danger: {
|
|
background: 'var(--color-error)',
|
|
color: 'white',
|
|
hover: '#dc2626',
|
|
},
|
|
};
|
|
|
|
const sizeStyles = {
|
|
sm: {
|
|
padding: 'var(--spacing-2) var(--spacing-3)',
|
|
fontSize: 'var(--font-size-sm)',
|
|
},
|
|
md: {
|
|
padding: 'var(--spacing-3) var(--spacing-5)',
|
|
fontSize: 'var(--font-size-base)',
|
|
},
|
|
lg: {
|
|
padding: 'var(--spacing-4) var(--spacing-6)',
|
|
fontSize: 'var(--font-size-lg)',
|
|
},
|
|
};
|
|
|
|
const style = variantStyles[variant];
|
|
const sizing = sizeStyles[size];
|
|
|
|
return (
|
|
<button
|
|
type={type}
|
|
onClick={onClick}
|
|
disabled={disabled || loading}
|
|
style={{
|
|
...sizing,
|
|
background: style.background,
|
|
color: style.color,
|
|
border: 'none',
|
|
borderRadius: 'var(--border-radius-md)',
|
|
fontWeight: 600,
|
|
cursor: disabled || loading ? 'not-allowed' : 'pointer',
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 'var(--spacing-2)',
|
|
transition: 'all 0.2s',
|
|
width: fullWidth ? '100%' : 'auto',
|
|
opacity: disabled || loading ? 0.6 : 1,
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
if (!disabled && !loading) {
|
|
e.currentTarget.style.background = style.hover;
|
|
e.currentTarget.style.transform = 'translateY(-1px)';
|
|
e.currentTarget.style.boxShadow = 'var(--shadow-md)';
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (!disabled && !loading) {
|
|
e.currentTarget.style.background = style.background;
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = 'none';
|
|
}
|
|
}}
|
|
>
|
|
{loading && (
|
|
<span
|
|
className="animate-spin"
|
|
style={{
|
|
display: 'inline-block',
|
|
width: '1em',
|
|
height: '1em',
|
|
border: '2px solid currentColor',
|
|
borderTopColor: 'transparent',
|
|
borderRadius: '50%',
|
|
}}
|
|
/>
|
|
)}
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|