car_mms/app/components/ui/Button.tsx
2025-09-11 14:22:27 +03:00

92 lines
3.0 KiB
TypeScript

import { ReactNode, ButtonHTMLAttributes } from 'react';
import { getButtonClasses, defaultLayoutConfig, type LayoutConfig } from '~/lib/layout-utils';
interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
children: ReactNode;
className?: string;
config?: Partial<LayoutConfig>;
variant?: 'primary' | 'secondary' | 'danger' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
fullWidth?: boolean;
loading?: boolean;
icon?: ReactNode;
iconPosition?: 'start' | 'end';
}
export function Button({
children,
className = '',
config = {},
variant = 'primary',
size = 'md',
fullWidth = false,
loading = false,
icon,
iconPosition = 'start',
disabled,
...props
}: ButtonProps) {
const layoutConfig = { ...defaultLayoutConfig, ...config };
const baseClasses = getButtonClasses(variant as any, size);
const variantClasses = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500 disabled:bg-blue-300',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900 focus:ring-gray-500 disabled:bg-gray-100',
danger: 'bg-red-600 hover:bg-red-700 text-white focus:ring-red-500 disabled:bg-red-300',
outline: 'border border-gray-300 bg-white hover:bg-gray-50 text-gray-700 focus:ring-blue-500 disabled:bg-gray-50',
ghost: 'bg-transparent hover:bg-gray-100 text-gray-700 focus:ring-gray-500 disabled:text-gray-400',
};
const fullWidthClass = fullWidth ? 'w-full' : '';
const disabledClass = (disabled || loading) ? 'cursor-not-allowed opacity-50' : '';
const iconSpacing = layoutConfig.direction === 'rtl' ? 'space-x-reverse' : '';
const iconOrderClass = iconPosition === 'end' ? 'flex-row-reverse' : '';
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${fullWidthClass} ${disabledClass} ${iconSpacing} ${iconOrderClass} ${className}`}
disabled={disabled || loading}
dir={layoutConfig.direction}
{...props}
>
{loading && (
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-current"
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>
)}
{icon && iconPosition === 'start' && !loading && (
<span className={layoutConfig.direction === 'rtl' ? 'ml-2' : 'mr-2'}>
{icon}
</span>
)}
<span>{children}</span>
{icon && iconPosition === 'end' && !loading && (
<span className={layoutConfig.direction === 'rtl' ? 'mr-2' : 'ml-2'}>
{icon}
</span>
)}
</button>
);
}