refactor: refactor the button component using forwardRef (#4379)
Co-authored-by: KVOJJJin <jzongcode@gmail.com>
This commit is contained in:
@@ -1,50 +1,44 @@
|
||||
import type { FC, MouseEventHandler, PropsWithChildren } from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import React from 'react'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import classNames from 'classnames'
|
||||
import Spinner from '../spinner'
|
||||
|
||||
export type IButtonProps = PropsWithChildren<{
|
||||
type?: string
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
const buttonVariants = cva(
|
||||
'btn disabled:pointer-events-none',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary: 'btn-primary disabled:btn-primary-disabled',
|
||||
warning:
|
||||
'btn-warning disabled:btn-warning-disabled',
|
||||
default: 'btn-default disabled:btn-default-disabled',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ButtonProps = {
|
||||
loading?: boolean
|
||||
tabIndex?: number
|
||||
onClick?: MouseEventHandler<HTMLDivElement>
|
||||
}>
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
|
||||
|
||||
const Button: FC<IButtonProps> = ({
|
||||
type,
|
||||
disabled,
|
||||
children,
|
||||
className,
|
||||
onClick,
|
||||
loading = false,
|
||||
tabIndex,
|
||||
}) => {
|
||||
let typeClassNames = 'cursor-pointer'
|
||||
switch (type) {
|
||||
case 'primary':
|
||||
typeClassNames = (disabled || loading) ? 'btn-primary-disabled' : 'btn-primary'
|
||||
break
|
||||
case 'warning':
|
||||
typeClassNames = (disabled || loading) ? 'btn-warning-disabled' : 'btn-warning'
|
||||
break
|
||||
default:
|
||||
typeClassNames = disabled ? 'btn-default-disabled' : 'btn-default'
|
||||
break
|
||||
}
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, loading, children, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={classNames(buttonVariants({ variant, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />
|
||||
</button>
|
||||
)
|
||||
},
|
||||
)
|
||||
Button.displayName = 'Button'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('btn', typeClassNames, className)}
|
||||
tabIndex={tabIndex}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
>
|
||||
{children}
|
||||
{/* Spinner is hidden when loading is false */}
|
||||
<Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Button)
|
||||
export default Button
|
||||
export { Button, buttonVariants }
|
||||
|
||||
@@ -93,7 +93,7 @@ const ConfigPanel = () => {
|
||||
<Form />
|
||||
<div className={`pl-[136px] flex items-center ${isMobile && '!pl-0'}`}>
|
||||
<Button
|
||||
type='primary'
|
||||
variant='primary'
|
||||
className='mr-2 text-sm font-medium'
|
||||
onClick={() => {
|
||||
setCollapsed(true)
|
||||
@@ -118,7 +118,7 @@ const ConfigPanel = () => {
|
||||
<Form />
|
||||
<Button
|
||||
className={`px-4 py-0 h-9 ${inputsForms.length && !isMobile && 'ml-[136px]'}`}
|
||||
type='primary'
|
||||
variant='primary'
|
||||
onClick={handleStartChat}
|
||||
>
|
||||
<MessageDotsCircle className='mr-2 w-4 h-4 text-white' />
|
||||
|
||||
@@ -42,7 +42,7 @@ const ConfirmUI: FC<IConfirmUIProps> = ({
|
||||
</div>
|
||||
|
||||
<div className='flex gap-3 mt-4 ml-12'>
|
||||
<Button type='primary' onClick={onConfirm} className='flex items-center justify-center min-w-20 text-center text-white rounded-lg cursor-pointer h-9 '>{confirmText || t('common.operation.confirm')}</Button>
|
||||
<Button variant='primary' onClick={onConfirm} className='flex items-center justify-center min-w-20 text-center text-white rounded-lg cursor-pointer h-9 '>{confirmText || t('common.operation.confirm')}</Button>
|
||||
<Button onClick={onCancel} className='flex items-center justify-center min-w-20 text-center text-gray-500 border rounded-lg cursor-pointer h-9 border-color-gray-200'>{cancelText || t('common.operation.cancel')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,7 @@ const ConfirmCommon: FC<ConfirmCommonProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={() => {}} className='!w-[480px] !max-w-[480px] !p-0 !rounded-2xl' wrapperClassName={confirmWrapperClassName}>
|
||||
<Modal isShow={isShow} onClose={() => { }} className='!w-[480px] !max-w-[480px] !p-0 !rounded-2xl' wrapperClassName={confirmWrapperClassName}>
|
||||
<div className={cn(s[`wrapper-${type}`], 'relative p-8')}>
|
||||
<div className='flex items-center justify-center absolute top-4 right-4 w-8 h-8 cursor-pointer' onClick={onCancel}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
@@ -77,7 +77,7 @@ const ConfirmCommon: FC<ConfirmCommonProps> = ({
|
||||
)
|
||||
}
|
||||
<Button
|
||||
type='primary'
|
||||
variant='primary'
|
||||
className={confirmBtnClassName || ''}
|
||||
onClick={onConfirm}
|
||||
disabled={confirmDisabled}
|
||||
|
||||
@@ -189,14 +189,14 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||
</div>
|
||||
<Divider className='m-0' />
|
||||
<div className='w-full flex items-center justify-center p-3 gap-2'>
|
||||
<Button type="default" className='w-full' onClick={() => {
|
||||
<Button variant="default" className='w-full' onClick={() => {
|
||||
onClose && onClose()
|
||||
}}>
|
||||
{t('app.emoji.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={selectedEmoji === ''}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
className='w-full'
|
||||
onClick={() => {
|
||||
onSelect && onSelect(selectedEmoji, selectedBackground)
|
||||
|
||||
@@ -262,7 +262,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
||||
onClick={() => handleDataTypeChange(provider.key)}
|
||||
>
|
||||
<div className={`
|
||||
mr-2 w-4 h-4 rounded-full border
|
||||
mr-2 w-4 h-4 rounded-full border
|
||||
${localeData.type === provider.key ? 'border-[5px] border-primary-600' : 'border border-gray-300'}`} />
|
||||
{provider.name}
|
||||
</div>
|
||||
@@ -362,7 +362,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
variant='primary'
|
||||
className='text-sm font-medium'
|
||||
onClick={handleSave}
|
||||
disabled={localeData.type === 'openai_moderation' && !openaiProviderConfiged}
|
||||
|
||||
@@ -169,7 +169,7 @@ const OpeningStatement: FC<OpeningStatementProps> = ({
|
||||
isFocus ? (
|
||||
<div className='flex items-center space-x-1'>
|
||||
<div className='px-3 leading-[18px] text-xs font-medium text-gray-700 cursor-pointer' onClick={handleCancel}>{t('common.operation.cancel')}</div>
|
||||
<Button className='!h-8 !px-3 text-xs' onClick={handleConfirm} type="primary">{t('common.operation.save')}</Button>
|
||||
<Button className='!h-8 !px-3 text-xs' onClick={handleConfirm} variant="primary">{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
) : (
|
||||
<OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpener') as string} onClick={handleEdit} />
|
||||
|
||||
@@ -42,7 +42,7 @@ const ImageLinkInput: FC<ImageLinkInputProps> = ({
|
||||
placeholder={t('common.imageUploader.pasteImageLinkInputPlaceholder') || ''}
|
||||
/>
|
||||
<Button
|
||||
type='primary'
|
||||
variant='primary'
|
||||
className='!h-6 text-xs font-medium'
|
||||
disabled={!imageLink || disabled}
|
||||
onClick={handleClick}
|
||||
|
||||
@@ -50,7 +50,7 @@ const DeleteConfirmModal: FC<Props> = ({
|
||||
<div className='flex gap-2 justify-end'>
|
||||
<Button onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||
<Button
|
||||
type='warning'
|
||||
variant='warning'
|
||||
onClick={onRemove}
|
||||
className='border-red-700 border-[0.5px]'
|
||||
>
|
||||
|
||||
@@ -23,7 +23,7 @@ const TagRemoveModal = ({ show, tag, onConfirm, onClose }: TagRemoveModalProps)
|
||||
<Modal
|
||||
className={cn('p-8 max-w-[480px] w-[480px]', s.bg)}
|
||||
isShow={show}
|
||||
onClose={() => {}}
|
||||
onClose={() => { }}
|
||||
>
|
||||
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onClose}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
@@ -40,7 +40,7 @@ const TagRemoveModal = ({ show, tag, onConfirm, onClose }: TagRemoveModalProps)
|
||||
</div>
|
||||
<div className='pt-6 flex items-center justify-end'>
|
||||
<Button className='mr-2 text-gray-700 text-sm font-medium' onClick={onClose}>{t('common.operation.cancel')}</Button>
|
||||
<Button className='text-sm font-medium border-red-700 border-[0.5px]' type="warning" onClick={onConfirm}>{t('common.operation.delete')}</Button>
|
||||
<Button className='text-sm font-medium border-red-700 border-[0.5px]' variant="warning" onClick={onConfirm}>{t('common.operation.delete')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user