This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
import type { Meta, StoryObj } from '@storybook/nextjs'
import ImageGallery from '.'
const IMAGE_SOURCES = [
'data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'600\' height=\'400\'><rect width=\'600\' height=\'400\' fill=\'%23E0EAFF\'/><text x=\'50%\' y=\'50%\' dominant-baseline=\'middle\' text-anchor=\'middle\' font-family=\'sans-serif\' font-size=\'48\' fill=\'%23455675\'>Dataset</text></svg>',
'data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'600\' height=\'400\'><rect width=\'600\' height=\'400\' fill=\'%23FEF7C3\'/><text x=\'50%\' y=\'50%\' dominant-baseline=\'middle\' text-anchor=\'middle\' font-family=\'sans-serif\' font-size=\'48\' fill=\'%237A5B00\'>Playground</text></svg>',
'data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'600\' height=\'400\'><rect width=\'600\' height=\'400\' fill=\'%23D5F5F6\'/><text x=\'50%\' y=\'50%\' dominant-baseline=\'middle\' text-anchor=\'middle\' font-family=\'sans-serif\' font-size=\'48\' fill=\'%23045C63\'>Workflow</text></svg>',
'data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'600\' height=\'400\'><rect width=\'600\' height=\'400\' fill=\'%23FCE7F6\'/><text x=\'50%\' y=\'50%\' dominant-baseline=\'middle\' text-anchor=\'middle\' font-family=\'sans-serif\' font-size=\'48\' fill=\'%238E2F63\'>Prompts</text></svg>',
]
const meta = {
title: 'Base/Data Display/ImageGallery',
component: ImageGallery,
parameters: {
docs: {
description: {
component: 'Responsive thumbnail grid with lightbox preview for larger imagery.',
},
source: {
language: 'tsx',
code: `
<ImageGallery srcs={[
'data:image/svg+xml;utf8,<svg ... fill=%23E0EAFF ...>',
'data:image/svg+xml;utf8,<svg ... fill=%23FEF7C3 ...>',
]} />
`.trim(),
},
},
},
tags: ['autodocs'],
args: {
srcs: IMAGE_SOURCES,
},
} satisfies Meta<typeof ImageGallery>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {}

View File

@@ -0,0 +1,84 @@
'use client'
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
import cn from '@/utils/classnames'
import type { FC } from 'react'
import React, { useState } from 'react'
import s from './style.module.css'
type Props = {
srcs: string[]
}
const getWidthStyle = (imgNum: number) => {
if (imgNum === 1) {
return {
maxWidth: '100%',
}
}
if (imgNum === 2 || imgNum === 4) {
return {
width: 'calc(50% - 4px)',
}
}
return {
width: 'calc(33.3333% - 5.3333px)',
}
}
const ImageGallery: FC<Props> = ({
srcs,
}) => {
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
const imgNum = srcs.length
const imgStyle = getWidthStyle(imgNum)
return (
<div className={cn(s[`img-${imgNum}`], 'flex flex-wrap')}>
{srcs.map((src, index) => (
!src ? null : <img
key={index}
className={s.item}
style={imgStyle}
src={src}
alt=''
onClick={() => setImagePreviewUrl(src)}
onError={e => e.currentTarget.remove()}
/>
))}
{
imagePreviewUrl && (
<ImagePreview
url={imagePreviewUrl}
onCancel={() => setImagePreviewUrl('')}
title={''}
/>
)
}
</div>
)
}
export default React.memo(ImageGallery)
export const ImageGalleryTest = () => {
const imgGallerySrcs = (() => {
const srcs = []
for (let i = 0; i < 6; i++)
// srcs.push('https://placekitten.com/640/360')
// srcs.push('https://placekitten.com/360/640')
srcs.push('https://placekitten.com/360/360')
return srcs
})()
return (
<div className='space-y-2'>
{imgGallerySrcs.map((_, index) => (
<div key={index} className='rounded-lg bg-[#D1E9FF80] p-4 pb-2'>
<ImageGallery srcs={imgGallerySrcs.slice(0, index + 1)} />
</div>
))}
</div>
)
}

View File

@@ -0,0 +1,22 @@
.item {
max-height: 200px;
margin-right: 8px;
margin-bottom: 8px;
object-fit: contain;
object-position: center;
border-radius: 8px;
cursor: pointer;
}
.item:nth-child(3n) {
margin-right: 0;
}
.img-2 .item:nth-child(2n),
.img-4 .item:nth-child(2n) {
margin-right: 0;
}
.img-4 .item:nth-child(3n) {
margin-right: 8px;
}