2025-12-06 12:46:40 +09:00
|
|
|
"use client"
|
2025-03-27 08:02:03 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
import { X } from "lucide-react"
|
|
|
|
|
import Image from "next/image"
|
|
|
|
|
import React, { useEffect, useState } from "react"
|
2025-03-27 08:02:03 +00:00
|
|
|
|
|
|
|
|
interface FilePreviewListProps {
|
2025-12-06 12:46:40 +09:00
|
|
|
files: File[]
|
|
|
|
|
onRemoveFile: (fileToRemove: File) => void
|
2025-03-27 08:02:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FilePreviewList({ files, onRemoveFile }: FilePreviewListProps) {
|
2025-12-06 12:46:40 +09:00
|
|
|
const [selectedImage, setSelectedImage] = useState<string | null>(null)
|
2025-11-10 09:17:11 +09:00
|
|
|
|
2025-03-27 08:02:03 +00:00
|
|
|
// Cleanup object URLs on unmount
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const objectUrls = files
|
|
|
|
|
.filter((file) => file.type.startsWith("image/"))
|
2025-12-06 12:46:40 +09:00
|
|
|
.map((file) => URL.createObjectURL(file))
|
2025-03-27 08:02:03 +00:00
|
|
|
|
|
|
|
|
return () => {
|
2025-12-06 12:46:40 +09:00
|
|
|
objectUrls.forEach(URL.revokeObjectURL)
|
|
|
|
|
}
|
|
|
|
|
}, [files])
|
2025-03-27 08:02:03 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
if (files.length === 0) return null
|
2025-03-27 08:02:03 +00:00
|
|
|
|
|
|
|
|
return (
|
2025-11-10 09:17:11 +09:00
|
|
|
<>
|
|
|
|
|
<div className="flex flex-wrap gap-2 mt-2 p-2 bg-muted/50 rounded-md">
|
|
|
|
|
{files.map((file, index) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
const imageUrl = file.type.startsWith("image/")
|
|
|
|
|
? URL.createObjectURL(file)
|
|
|
|
|
: null
|
2025-11-10 09:17:11 +09:00
|
|
|
return (
|
|
|
|
|
<div key={file.name + index} className="relative group">
|
|
|
|
|
<div
|
|
|
|
|
className="w-20 h-20 border rounded-md overflow-hidden bg-muted cursor-pointer"
|
2025-12-06 12:46:40 +09:00
|
|
|
onClick={() =>
|
|
|
|
|
imageUrl && setSelectedImage(imageUrl)
|
|
|
|
|
}
|
2025-11-10 09:17:11 +09:00
|
|
|
>
|
|
|
|
|
{file.type.startsWith("image/") ? (
|
|
|
|
|
<Image
|
|
|
|
|
src={imageUrl!}
|
|
|
|
|
alt={file.name}
|
|
|
|
|
width={80}
|
|
|
|
|
height={80}
|
|
|
|
|
className="object-cover w-full h-full"
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex items-center justify-center h-full text-xs text-center p-1">
|
|
|
|
|
{file.name}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-03-27 08:02:03 +00:00
|
|
|
</div>
|
2025-11-10 09:17:11 +09:00
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => onRemoveFile(file)}
|
|
|
|
|
className="absolute -top-2 -right-2 bg-destructive rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
|
|
|
aria-label="Remove file"
|
|
|
|
|
>
|
|
|
|
|
<X className="h-3 w-3" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-11-10 09:17:11 +09:00
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Image Modal/Lightbox */}
|
|
|
|
|
{selectedImage && (
|
|
|
|
|
<div
|
|
|
|
|
className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4"
|
|
|
|
|
onClick={() => setSelectedImage(null)}
|
|
|
|
|
>
|
2025-03-27 08:02:03 +00:00
|
|
|
<button
|
2025-11-10 10:28:37 +09:00
|
|
|
className="absolute top-4 right-4 z-10 bg-white rounded-full p-2 hover:bg-gray-200 transition-colors"
|
2025-11-10 09:17:11 +09:00
|
|
|
onClick={() => setSelectedImage(null)}
|
|
|
|
|
aria-label="Close"
|
2025-03-27 08:02:03 +00:00
|
|
|
>
|
2025-11-10 09:17:11 +09:00
|
|
|
<X className="h-6 w-6" />
|
2025-03-27 08:02:03 +00:00
|
|
|
</button>
|
2025-11-10 10:28:37 +09:00
|
|
|
<div className="relative w-auto h-auto max-w-[90vw] max-h-[90vh]">
|
2025-11-10 09:17:11 +09:00
|
|
|
<Image
|
|
|
|
|
src={selectedImage}
|
2025-11-16 08:36:13 +09:00
|
|
|
alt="Full size preview of uploaded diagram or image"
|
2025-11-10 10:28:37 +09:00
|
|
|
width={1200}
|
|
|
|
|
height={900}
|
|
|
|
|
className="object-contain max-w-full max-h-[90vh] w-auto h-auto"
|
2025-11-10 09:17:11 +09:00
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2025-03-27 08:02:03 +00:00
|
|
|
</div>
|
2025-11-10 09:17:11 +09:00
|
|
|
)}
|
|
|
|
|
</>
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-03-27 08:02:03 +00:00
|
|
|
}
|