| 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 |
|
|