79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
|
|
import React, { ButtonHTMLAttributes, ReactNode } from 'react';
|
||
|
|
|
||
|
|
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||
|
|
children?: ReactNode; // Make children optional for icon-only buttons
|
||
|
|
loading?: boolean;
|
||
|
|
disabled?: boolean;
|
||
|
|
endIcon?: ReactNode;
|
||
|
|
size?: 'small' | 'medium' | 'large'; // Add size prop
|
||
|
|
iconOnly?: boolean; // Add iconOnly prop
|
||
|
|
}
|
||
|
|
|
||
|
|
const Button: React.FC<ButtonProps> = ({
|
||
|
|
children,
|
||
|
|
loading = false,
|
||
|
|
disabled = false,
|
||
|
|
endIcon,
|
||
|
|
size = 'medium', // Default size to medium
|
||
|
|
iconOnly = false, // Default iconOnly to false
|
||
|
|
className,
|
||
|
|
...props
|
||
|
|
}) => {
|
||
|
|
const baseStyles =
|
||
|
|
'inline-flex items-center justify-center border border-transparent font-medium rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 ';
|
||
|
|
const enabledStyles =
|
||
|
|
'text-white bg-primary-light hover:bg-indigo-700 active:bg-indigo-800 focus:ring-indigo-500';
|
||
|
|
const disabledStyles = 'text-gray-400 bg-gray-200 cursor-not-allowed';
|
||
|
|
const loadingStyles = 'text-gray-400 bg-gray-200 cursor-wait';
|
||
|
|
|
||
|
|
const sizeStyles = {
|
||
|
|
small: iconOnly ? 'p-2 text-sm' : 'px-3 py-1.5 text-sm',
|
||
|
|
medium: iconOnly ? 'p-2.5 text-base' : 'px-4 py-2 text-base',
|
||
|
|
large: iconOnly ? 'p-3 text-lg' : 'px-6 py-3 text-lg',
|
||
|
|
};
|
||
|
|
|
||
|
|
const currentStyles = disabled
|
||
|
|
? disabledStyles
|
||
|
|
: loading
|
||
|
|
? loadingStyles
|
||
|
|
: enabledStyles;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
className={`${baseStyles} ${sizeStyles[size]} ${currentStyles} ${className || 'bg-gray-500 '}`}
|
||
|
|
disabled={disabled || loading}
|
||
|
|
{...props}
|
||
|
|
>
|
||
|
|
{loading ? (
|
||
|
|
<svg
|
||
|
|
className="animate-spin -ml-1 mr-3 h-5 w-5 text-primary"
|
||
|
|
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"
|
||
|
|
></circle>
|
||
|
|
<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.938l2-2.647z"
|
||
|
|
></path>
|
||
|
|
</svg>
|
||
|
|
) : null}
|
||
|
|
{children && !iconOnly ? children : null}
|
||
|
|
{endIcon && !loading && !iconOnly ? (
|
||
|
|
<span className="ml-2">{endIcon}</span>
|
||
|
|
) : null}
|
||
|
|
{iconOnly && endIcon && !loading ? endIcon : null}
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default Button;
|