feat: multimodal support (image) (#27793)
Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
88
web/app/components/datasets/common/image-list/index.tsx
Normal file
88
web/app/components/datasets/common/image-list/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import type { FileEntity } from '@/app/components/base/file-thumb'
|
||||
import FileThumb from '@/app/components/base/file-thumb'
|
||||
import cn from '@/utils/classnames'
|
||||
import More from './more'
|
||||
import type { ImageInfo } from '../image-previewer'
|
||||
import ImagePreviewer from '../image-previewer'
|
||||
|
||||
type Image = {
|
||||
name: string
|
||||
mimeType: string
|
||||
sourceUrl: string
|
||||
size: number
|
||||
extension: string
|
||||
}
|
||||
|
||||
type ImageListProps = {
|
||||
images: Image[]
|
||||
size: 'sm' | 'md'
|
||||
limit?: number
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ImageList = ({
|
||||
images,
|
||||
size,
|
||||
limit = 9,
|
||||
className,
|
||||
}: ImageListProps) => {
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [previewIndex, setPreviewIndex] = useState(0)
|
||||
const [previewImages, setPreviewImages] = useState<ImageInfo[]>([])
|
||||
|
||||
const limitedImages = useMemo(() => {
|
||||
return showMore ? images : images.slice(0, limit)
|
||||
}, [images, limit, showMore])
|
||||
|
||||
const handleShowMore = useCallback(() => {
|
||||
setShowMore(true)
|
||||
}, [])
|
||||
|
||||
const handleImageClick = useCallback((file: FileEntity) => {
|
||||
const index = limitedImages.findIndex(image => image.sourceUrl === file.sourceUrl)
|
||||
if (index === -1) return
|
||||
setPreviewIndex(index)
|
||||
setPreviewImages(limitedImages.map(image => ({
|
||||
url: image.sourceUrl,
|
||||
name: image.name,
|
||||
size: image.size,
|
||||
})))
|
||||
}, [limitedImages])
|
||||
|
||||
const handleClosePreview = useCallback(() => {
|
||||
setPreviewImages([])
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cn('flex flex-wrap gap-1', className)}>
|
||||
{
|
||||
limitedImages.map(image => (
|
||||
<FileThumb
|
||||
key={image.sourceUrl}
|
||||
file={image}
|
||||
size={size}
|
||||
onClick={handleImageClick}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{images.length > limit && !showMore && (
|
||||
<More
|
||||
count={images.length - limitedImages.length}
|
||||
onClick={handleShowMore}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{previewImages.length > 0 && (
|
||||
<ImagePreviewer
|
||||
images={previewImages}
|
||||
initialIndex={previewIndex}
|
||||
onClose={handleClosePreview}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ImageList
|
||||
39
web/app/components/datasets/common/image-list/more.tsx
Normal file
39
web/app/components/datasets/common/image-list/more.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
type MoreProps = {
|
||||
count: number
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const More = ({ count, onClick }: MoreProps) => {
|
||||
const formatNumber = (num: number) => {
|
||||
if (num === 0)
|
||||
return '0'
|
||||
if (num < 1000)
|
||||
return num.toString()
|
||||
if (num < 1000000)
|
||||
return `${(num / 1000).toFixed(1)}k`
|
||||
return `${(num / 1000000).toFixed(1)}M`
|
||||
}
|
||||
|
||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
onClick?.()
|
||||
}, [onClick])
|
||||
|
||||
return (
|
||||
<div className='relative size-8 cursor-pointer p-[0.5px]' onClick={handleClick}>
|
||||
<div className='relative z-10 size-full rounded-md border-[1.5px] border-components-panel-bg bg-divider-regular'>
|
||||
<div className='flex size-full items-center justify-center'>
|
||||
<span className='system-xs-regular text-text-tertiary'>
|
||||
{`+${formatNumber(count)}`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='absolute -right-0.5 top-1 z-0 h-6 w-1 rounded-r-md bg-divider-regular' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(More)
|
||||
Reference in New Issue
Block a user