refactor: Encapsulate image display related components (#7211)

This commit is contained in:
Wang Jiyuan 2025-06-16 00:05:25 +08:00 committed by GitHub
parent c7843ca288
commit 05727c637f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 130 additions and 101 deletions

View File

@ -1,15 +1,12 @@
import FileManager from '@renderer/services/FileManager'
import { FileType, FileTypes } from '@renderer/types'
import { formatFileSize } from '@renderer/utils'
import { Col, Image, Row, Spin } from 'antd'
import { t } from 'i18next'
import VirtualList from 'rc-virtual-list'
import React, { memo } from 'react'
import styled from 'styled-components'
import FileItem from './FileItem'
import ImageList from './ImageList'
interface FileItemProps {
interface FileListProps {
id: FileTypes | 'all' | string
list: {
key: FileTypes | 'all' | string
@ -24,37 +21,9 @@ interface FileItemProps {
files?: FileType[]
}
const FileList: React.FC<FileItemProps> = ({ id, list, files }) => {
const FileList: React.FC<FileListProps> = ({ id, list, files }) => {
if (id === FileTypes.IMAGE && files?.length && files?.length > 0) {
return (
<div style={{ padding: 16, overflowY: 'auto' }}>
<Image.PreviewGroup>
<Row gutter={[16, 16]}>
{files?.map((file) => (
<Col key={file.id} xs={24} sm={12} md={8} lg={4} xl={3}>
<ImageWrapper>
<LoadingWrapper>
<Spin />
</LoadingWrapper>
<Image
src={FileManager.getFileUrl(file)}
style={{ height: '100%', objectFit: 'cover', cursor: 'pointer' }}
preview={{ mask: false }}
onLoad={(e) => {
const img = e.target as HTMLImageElement
img.parentElement?.classList.add('loaded')
}}
/>
<ImageInfo>
<div>{formatFileSize(file.size)}</div>
</ImageInfo>
</ImageWrapper>
</Col>
))}
</Row>
</Image.PreviewGroup>
</div>
)
return <ImageList files={files}></ImageList>
}
return (
@ -93,70 +62,4 @@ const FileList: React.FC<FileItemProps> = ({ id, list, files }) => {
)
}
const ImageWrapper = styled.div`
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 8px;
background-color: var(--color-background-soft);
display: flex;
align-items: center;
justify-content: center;
border: 0.5px solid var(--color-border);
.ant-image {
height: 100%;
width: 100%;
opacity: 0;
transition:
opacity 0.3s ease,
transform 0.3s ease;
&.loaded {
opacity: 1;
}
}
&:hover {
.ant-image.loaded {
transform: scale(1.05);
}
div:last-child {
opacity: 1;
}
}
`
const LoadingWrapper = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-soft);
`
const ImageInfo = styled.div`
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 5px 8px;
opacity: 0;
transition: opacity 0.3s ease;
font-size: 12px;
> div:first-child {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`
export default memo(FileList)

View File

@ -0,0 +1,105 @@
import FileManager from '@renderer/services/FileManager'
import { FileType } from '@renderer/types'
import { formatFileSize } from '@renderer/utils'
import { Col, Image, Spin } from 'antd'
import { memo, useState } from 'react'
import styled from 'styled-components'
interface ImageItemProps {
file: FileType
}
const ImageItem: React.FC<ImageItemProps> = ({ file }) => {
const [loading, setLoading] = useState(true)
return (
<Col xs={24} sm={12} md={8} lg={4} xl={3}>
<ImageWrapper>
{loading && (
<LoadingWrapper>
<Spin />
</LoadingWrapper>
)}
<Image
src={FileManager.getFileUrl(file)}
style={{ height: '100%', objectFit: 'cover', cursor: 'pointer' }}
preview={{ mask: false }}
onLoad={(e) => {
const img = e.target as HTMLImageElement
img.parentElement?.classList.add('loaded')
setLoading(false)
}}
/>
<ImageInfo>
<div>{formatFileSize(file.size)}</div>
</ImageInfo>
</ImageWrapper>
</Col>
)
}
const ImageWrapper = styled.div`
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 8px;
background-color: var(--color-background-soft);
display: flex;
align-items: center;
justify-content: center;
border: 0.5px solid var(--color-border);
.ant-image {
height: 100%;
width: 100%;
opacity: 0;
transition:
opacity 0.3s ease,
transform 0.3s ease;
&.loaded {
opacity: 1;
}
}
&:hover {
.ant-image.loaded {
transform: scale(1.05);
}
div:last-child {
opacity: 1;
}
}
`
const LoadingWrapper = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-soft);
`
const ImageInfo = styled.div`
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 5px 8px;
opacity: 0;
transition: opacity 0.3s ease;
font-size: 12px;
> div:first-child {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`
export default memo(ImageItem)

View File

@ -0,0 +1,21 @@
import { FileType } from '@renderer/types'
import { Image, Row } from 'antd'
import { memo } from 'react'
import ImageItem from './ImageItem'
interface ImageListProps {
files?: FileType[]
}
const ImageList: React.FC<ImageListProps> = ({ files }) => {
return (
<div style={{ padding: 16, overflowY: 'auto' }}>
<Image.PreviewGroup>
<Row gutter={[16, 16]}>{files?.map((file) => <ImageItem key={file.id} file={file}></ImageItem>)}</Row>
</Image.PreviewGroup>
</div>
)
}
export default memo(ImageList)