2025-02-23 04:50:10 +08:00
|
|
|
import * as React from 'react';
|
|
|
|
import { Slot } from '@radix-ui/react-slot';
|
|
|
|
import { cva, type VariantProps } from 'class-variance-authority';
|
2025-02-23 05:01:55 +08:00
|
|
|
import { cn } from '@/lib/classMerge';
|
2025-02-23 05:58:58 +08:00
|
|
|
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
2025-02-23 05:01:55 +08:00
|
|
|
|
|
|
|
const buttonVariants = cva(
|
2025-02-23 05:58:58 +08:00
|
|
|
'inline-flex items-center justify-center gap-1.5 shadow-btn rounded transition-all duration-200 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none cursor-pointer disabled:cursor-not-allowed data-[loading=true]:cursor-progress px-2.5 py-1.5',
|
2025-02-23 05:01:55 +08:00
|
|
|
{
|
|
|
|
variants: {
|
2025-02-23 05:58:58 +08:00
|
|
|
buttonType: {
|
|
|
|
default:
|
2025-02-23 06:20:50 +08:00
|
|
|
'bg-white border hover:bg-item-hover disabled:bg-disabled disabled:text-light-gray active:bg-item-active data-[selected=true]:bg-nav-background',
|
2025-02-23 06:05:27 +08:00
|
|
|
black: 'bg-black text-white hover:bg-black-hover disabled:bg-black-hover',
|
2025-02-23 06:27:32 +08:00
|
|
|
primary:
|
|
|
|
'bg-primary text-white hover:bg-primary-light active:bg-primary-dark data-[selected=true]:bg-primary-dark',
|
|
|
|
ghost:
|
|
|
|
'bg-transparent text-dark-gray shadow-none hover:bg-item-hover disabled:bg-transparent disabled:text-light-gray active:bg-item-active data-[selected=true]:bg-nav-background'
|
2025-02-23 05:01:55 +08:00
|
|
|
},
|
|
|
|
size: {
|
|
|
|
default: 'h-6',
|
2025-02-23 06:27:32 +08:00
|
|
|
tall: 'h-7'
|
2025-02-23 05:01:55 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
defaultVariants: {
|
2025-02-23 05:58:58 +08:00
|
|
|
buttonType: 'default',
|
2025-02-23 05:01:55 +08:00
|
|
|
size: 'default'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
export interface ButtonProps
|
2025-02-23 05:58:58 +08:00
|
|
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'prefix'>,
|
2025-02-23 05:01:55 +08:00
|
|
|
VariantProps<typeof buttonVariants> {
|
|
|
|
asChild?: boolean;
|
2025-02-23 05:58:58 +08:00
|
|
|
prefix?: React.ReactNode;
|
|
|
|
suffix?: React.ReactNode;
|
|
|
|
loading?: boolean;
|
|
|
|
selected?: boolean;
|
2025-02-23 05:01:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const AppButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
2025-02-23 05:58:58 +08:00
|
|
|
(
|
|
|
|
{
|
2025-02-23 06:20:50 +08:00
|
|
|
className = '',
|
2025-02-23 06:05:27 +08:00
|
|
|
buttonType = 'default',
|
2025-02-23 06:20:50 +08:00
|
|
|
size = 'default',
|
2025-02-23 05:58:58 +08:00
|
|
|
asChild = false,
|
|
|
|
prefix,
|
|
|
|
suffix,
|
|
|
|
children,
|
|
|
|
loading = false,
|
|
|
|
selected = false,
|
2025-02-23 06:20:50 +08:00
|
|
|
disabled = false,
|
2025-02-23 05:58:58 +08:00
|
|
|
...props
|
|
|
|
},
|
|
|
|
ref
|
|
|
|
) => {
|
2025-02-23 05:01:55 +08:00
|
|
|
const Comp = asChild ? Slot : 'button';
|
|
|
|
return (
|
2025-02-23 05:58:58 +08:00
|
|
|
<Comp
|
|
|
|
className={cn(buttonVariants({ buttonType, size, className }))}
|
|
|
|
ref={ref}
|
2025-02-23 06:20:50 +08:00
|
|
|
disabled={disabled}
|
2025-02-23 05:58:58 +08:00
|
|
|
data-loading={loading}
|
|
|
|
data-selected={selected}
|
|
|
|
{...props}>
|
2025-02-23 06:05:27 +08:00
|
|
|
{loading ? <LoadingIcon buttonType={buttonType} /> : prefix && <span>{prefix}</span>}
|
2025-02-23 05:58:58 +08:00
|
|
|
{children}
|
|
|
|
{suffix && <span>{suffix}</span>}
|
|
|
|
</Comp>
|
2025-02-23 05:01:55 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
AppButton.displayName = 'AppButton';
|
|
|
|
|
2025-02-23 06:05:27 +08:00
|
|
|
const LoadingIcon: React.FC<{
|
2025-02-23 06:27:32 +08:00
|
|
|
buttonType: 'default' | 'black' | 'primary' | 'ghost' | null;
|
2025-02-23 06:05:27 +08:00
|
|
|
}> = ({ buttonType = 'default' }) => {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className={cn(
|
|
|
|
'animate-in fade-in text-black duration-400 dark:text-white',
|
|
|
|
buttonType === 'black' && 'dark'
|
|
|
|
)}>
|
|
|
|
<CircleSpinnerLoader
|
|
|
|
size={9}
|
2025-02-23 06:27:32 +08:00
|
|
|
fill={
|
|
|
|
buttonType === 'black'
|
|
|
|
? 'var(--color-white)'
|
|
|
|
: buttonType === 'primary'
|
|
|
|
? 'var(--color-primary)'
|
|
|
|
: 'var(--color-primary)'
|
|
|
|
}
|
2025-02-23 06:05:27 +08:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2025-02-23 05:01:55 +08:00
|
|
|
export { AppButton, buttonVariants };
|