Severian's picture
initial commit
a8b3f00
raw
history blame
4.93 kB
import type { FC } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiCloseLine,
RiLoader2Line,
} from '@remixicon/react'
import cn from '@/utils/classnames'
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import type { ImageFile } from '@/types/app'
import { TransferMethod } from '@/types/app'
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
type ImageListProps = {
list: ImageFile[]
readonly?: boolean
onRemove?: (imageFileId: string) => void
onReUpload?: (imageFileId: string) => void
onImageLinkLoadSuccess?: (imageFileId: string) => void
onImageLinkLoadError?: (imageFileId: string) => void
}
const ImageList: FC<ImageListProps> = ({
list,
readonly,
onRemove,
onReUpload,
onImageLinkLoadSuccess,
onImageLinkLoadError,
}) => {
const { t } = useTranslation()
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
const handleImageLinkLoadSuccess = (item: ImageFile) => {
if (
item.type === TransferMethod.remote_url
&& onImageLinkLoadSuccess
&& item.progress !== -1
)
onImageLinkLoadSuccess(item._id)
}
const handleImageLinkLoadError = (item: ImageFile) => {
if (item.type === TransferMethod.remote_url && onImageLinkLoadError)
onImageLinkLoadError(item._id)
}
return (
<div className="flex flex-wrap">
{list.map(item => (
<div
key={item._id}
className="group relative mr-1 border-[0.5px] border-black/5 rounded-lg"
>
{item.type === TransferMethod.local_file && item.progress !== 100 && (
<>
<div
className="absolute inset-0 flex items-center justify-center z-[1] bg-black/30"
style={{ left: item.progress > -1 ? `${item.progress}%` : 0 }}
>
{item.progress === -1 && (
<RefreshCcw01
className="w-5 h-5 text-white"
onClick={() => onReUpload && onReUpload(item._id)}
/>
)}
</div>
{item.progress > -1 && (
<span className="absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] text-sm text-white mix-blend-lighten z-[1]">
{item.progress}%
</span>
)}
</>
)}
{item.type === TransferMethod.remote_url && item.progress !== 100 && (
<div
className={`
absolute inset-0 flex items-center justify-center rounded-lg z-[1] border
${item.progress === -1
? 'bg-[#FEF0C7] border-[#DC6803]'
: 'bg-black/[0.16] border-transparent'
}
`}
>
{item.progress > -1 && (
<RiLoader2Line className="animate-spin w-5 h-5 text-white" />
)}
{item.progress === -1 && (
<Tooltip
popupContent={t('common.imageUploader.pasteImageLinkInvalid')}
>
<AlertTriangle className="w-4 h-4 text-[#DC6803]" />
</Tooltip>
)}
</div>
)}
<img
className="w-16 h-16 rounded-lg object-cover cursor-pointer border-[0.5px] border-black/5"
alt={item.file?.name}
onLoad={() => handleImageLinkLoadSuccess(item)}
onError={() => handleImageLinkLoadError(item)}
src={
item.type === TransferMethod.remote_url
? item.url
: item.base64Url
}
onClick={() =>
item.progress === 100
&& setImagePreviewUrl(
(item.type === TransferMethod.remote_url
? item.url
: item.base64Url) as string,
)
}
/>
{!readonly && (
<button
type="button"
className={cn(
'absolute z-10 -top-[9px] -right-[9px] items-center justify-center w-[18px] h-[18px]',
'bg-white hover:bg-gray-50 border-[0.5px] border-black/2 rounded-2xl shadow-lg',
item.progress === -1 ? 'flex' : 'hidden group-hover:flex',
)}
onClick={() => onRemove && onRemove(item._id)}
>
<RiCloseLine className="w-3 h-3 text-gray-500" />
</button>
)}
</div>
))}
{imagePreviewUrl && (
<ImagePreview
url={imagePreviewUrl}
onCancel={() => setImagePreviewUrl('')}
title=''
/>
)}
</div>
)
}
export default ImageList