File size: 4,905 Bytes
19e25f3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
import { LucideIcon } from 'lucide-react';
import * as React from 'react';
import { IconType } from 'react-icons';
import { ImSpinner2 } from 'react-icons/im';
import { cn } from '@/lib/utils';
const ButtonVariant = ['primary', 'outline', 'ghost', 'light', 'dark'] as const;
const ButtonSize = ['sm', 'base'] as const;
type ButtonProps = {
isLoading?: boolean;
isDarkBg?: boolean;
variant?: (typeof ButtonVariant)[number];
size?: (typeof ButtonSize)[number];
leftIcon?: IconType | LucideIcon;
rightIcon?: IconType | LucideIcon;
classNames?: {
leftIcon?: string;
rightIcon?: string;
};
} & React.ComponentPropsWithRef<'button'>;
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
{
children,
className,
disabled: buttonDisabled,
isLoading,
variant = 'primary',
size = 'base',
isDarkBg = false,
leftIcon: LeftIcon,
rightIcon: RightIcon,
classNames,
...rest
},
ref
) => {
const disabled = isLoading || buttonDisabled;
return (
<button
ref={ref}
type='button'
disabled={disabled}
className={cn(
'inline-flex items-center rounded font-medium',
'focus-visible:ring-primary-500 focus:outline-none focus-visible:ring',
'shadow-sm',
'transition-colors duration-75',
//#region //*=========== Size ===========
[
size === 'base' && ['px-3 py-1.5', 'text-sm md:text-base'],
size === 'sm' && ['px-2 py-1', 'text-xs md:text-sm'],
],
//#endregion //*======== Size ===========
//#region //*=========== Variants ===========
[
variant === 'primary' && [
'bg-primary-500 text-white',
'border-primary-600 border',
'hover:bg-primary-600 hover:text-white',
'active:bg-primary-700',
'disabled:bg-primary-700',
],
variant === 'outline' && [
'text-primary-500',
'border-primary-500 border',
'hover:bg-primary-50 active:bg-primary-100 disabled:bg-primary-100',
isDarkBg &&
'hover:bg-gray-900 active:bg-gray-800 disabled:bg-gray-800',
],
variant === 'ghost' && [
'text-primary-500',
'shadow-none',
'hover:bg-primary-50 active:bg-primary-100 disabled:bg-primary-100',
isDarkBg &&
'hover:bg-gray-900 active:bg-gray-800 disabled:bg-gray-800',
],
variant === 'light' && [
'bg-white text-gray-700',
'border border-gray-300',
'hover:text-dark hover:bg-gray-100',
'active:bg-white/80 disabled:bg-gray-200',
],
variant === 'dark' && [
'bg-gray-900 text-white',
'border border-gray-600',
'hover:bg-gray-800 active:bg-gray-700 disabled:bg-gray-700',
],
],
//#endregion //*======== Variants ===========
'disabled:cursor-not-allowed',
isLoading &&
'relative text-transparent transition-none hover:text-transparent disabled:cursor-wait',
className
)}
{...rest}
>
{isLoading && (
<div
className={cn(
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
{
'text-white': ['primary', 'dark'].includes(variant),
'text-black': ['light'].includes(variant),
'text-primary-500': ['outline', 'ghost'].includes(variant),
}
)}
>
<ImSpinner2 className='animate-spin' />
</div>
)}
{LeftIcon && (
<div
className={cn([
size === 'base' && 'mr-1',
size === 'sm' && 'mr-1.5',
])}
>
<LeftIcon
size='1em'
className={cn(
[
size === 'base' && 'md:text-md text-md',
size === 'sm' && 'md:text-md text-sm',
],
classNames?.leftIcon
)}
/>
</div>
)}
{children}
{RightIcon && (
<div
className={cn([
size === 'base' && 'ml-1',
size === 'sm' && 'ml-1.5',
])}
>
<RightIcon
size='1em'
className={cn(
[
size === 'base' && 'text-md md:text-md',
size === 'sm' && 'md:text-md text-sm',
],
classNames?.rightIcon
)}
/>
</div>
)}
</button>
);
}
);
export default Button;
|