|
import type { FC } from 'react' |
|
import { |
|
Fragment, |
|
useEffect, |
|
useState, |
|
} from 'react' |
|
import { useTranslation } from 'react-i18next' |
|
import Uploader from './uploader' |
|
import ImageLinkInput from './image-link-input' |
|
import ImageList from './image-list' |
|
import { useImageFiles } from './hooks' |
|
import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images' |
|
import { Link03 } from '@/app/components/base/icons/src/vender/line/general' |
|
import { |
|
PortalToFollowElem, |
|
PortalToFollowElemContent, |
|
PortalToFollowElemTrigger, |
|
} from '@/app/components/base/portal-to-follow-elem' |
|
import type { ImageFile, VisionSettings } from '@/types/app' |
|
import { TransferMethod } from '@/types/app' |
|
|
|
type PasteImageLinkButtonProps = { |
|
onUpload: (imageFile: ImageFile) => void |
|
disabled?: boolean |
|
} |
|
const PasteImageLinkButton: FC<PasteImageLinkButtonProps> = ({ |
|
onUpload, |
|
disabled, |
|
}) => { |
|
const { t } = useTranslation() |
|
const [open, setOpen] = useState(false) |
|
|
|
const handleUpload = (imageFile: ImageFile) => { |
|
setOpen(false) |
|
onUpload(imageFile) |
|
} |
|
|
|
const handleToggle = () => { |
|
if (disabled) |
|
return |
|
|
|
setOpen(v => !v) |
|
} |
|
|
|
return ( |
|
<PortalToFollowElem |
|
open={open} |
|
onOpenChange={setOpen} |
|
placement='top-start' |
|
> |
|
<PortalToFollowElemTrigger onClick={handleToggle}> |
|
<div className={` |
|
relative flex items-center justify-center px-3 h-8 bg-gray-100 hover:bg-gray-200 text-xs text-gray-500 rounded-lg |
|
${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} |
|
`}> |
|
<Link03 className='mr-2 w-4 h-4' /> |
|
{t('common.imageUploader.pasteImageLink')} |
|
</div> |
|
</PortalToFollowElemTrigger> |
|
<PortalToFollowElemContent className='z-10'> |
|
<div className='p-2 w-[320px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg'> |
|
<ImageLinkInput onUpload={handleUpload} /> |
|
</div> |
|
</PortalToFollowElemContent> |
|
</PortalToFollowElem> |
|
) |
|
} |
|
|
|
type TextGenerationImageUploaderProps = { |
|
settings: VisionSettings |
|
onFilesChange: (files: ImageFile[]) => void |
|
} |
|
const TextGenerationImageUploader: FC<TextGenerationImageUploaderProps> = ({ |
|
settings, |
|
onFilesChange, |
|
}) => { |
|
const { t } = useTranslation() |
|
|
|
const { |
|
files, |
|
onUpload, |
|
onRemove, |
|
onImageLinkLoadError, |
|
onImageLinkLoadSuccess, |
|
onReUpload, |
|
} = useImageFiles() |
|
|
|
useEffect(() => { |
|
onFilesChange(files) |
|
}, [files]) |
|
|
|
const localUpload = ( |
|
<Uploader |
|
onUpload={onUpload} |
|
disabled={files.length >= settings.number_limits} |
|
limit={+settings.image_file_size_limit!} |
|
> |
|
{ |
|
hovering => ( |
|
<div className={` |
|
flex items-center justify-center px-3 h-8 bg-gray-100 |
|
text-xs text-gray-500 rounded-lg cursor-pointer |
|
${hovering && 'bg-gray-200'} |
|
`}> |
|
<ImagePlus className='mr-2 w-4 h-4' /> |
|
{t('common.imageUploader.uploadFromComputer')} |
|
</div> |
|
) |
|
} |
|
</Uploader> |
|
) |
|
|
|
const urlUpload = ( |
|
<PasteImageLinkButton |
|
onUpload={onUpload} |
|
disabled={files.length >= settings.number_limits} |
|
/> |
|
) |
|
|
|
return ( |
|
<div> |
|
<div className='mb-1'> |
|
<ImageList |
|
list={files} |
|
onRemove={onRemove} |
|
onReUpload={onReUpload} |
|
onImageLinkLoadError={onImageLinkLoadError} |
|
onImageLinkLoadSuccess={onImageLinkLoadSuccess} |
|
/> |
|
</div> |
|
<div className={`grid gap-1 ${settings.transfer_methods.length === 2 ? 'grid-cols-2' : 'grid-cols-1'}`}> |
|
{ |
|
settings.transfer_methods.map((method) => { |
|
if (method === TransferMethod.local_file) |
|
return <Fragment key={TransferMethod.local_file}>{localUpload}</Fragment> |
|
|
|
if (method === TransferMethod.remote_url) |
|
return <Fragment key={TransferMethod.remote_url}>{urlUpload}</Fragment> |
|
|
|
return null |
|
}) |
|
} |
|
</div> |
|
</div> |
|
) |
|
} |
|
|
|
export default TextGenerationImageUploader |
|
|