Spaces:
Running
Running
| import { useState, useCallback, useRef } from "react"; | |
| import GlassButton from "./GlassButton"; | |
| import GlassContainer from "./GlassContainer"; | |
| import { GLASS_EFFECTS } from "../constants"; | |
| interface ImageUploadProps { | |
| onImagesUploaded: (files: File[]) => void; | |
| isAnalyzing: boolean; | |
| } | |
| export default function ImageUpload({ onImagesUploaded, isAnalyzing }: ImageUploadProps) { | |
| const [dragActive, setDragActive] = useState(false); | |
| const fileInputRef = useRef<HTMLInputElement>(null); | |
| const handleFiles = useCallback( | |
| (files: FileList | null) => { | |
| if (!files) return; | |
| const imageFiles = Array.from(files).filter(file => | |
| file.type.startsWith("image/") | |
| ); | |
| if (imageFiles.length > 0) { | |
| onImagesUploaded(imageFiles); | |
| } | |
| }, | |
| [onImagesUploaded] | |
| ); | |
| const handleDrag = useCallback((e: React.DragEvent) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| }, []); | |
| const handleDragIn = useCallback((e: React.DragEvent) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| if (e.dataTransfer?.items && e.dataTransfer.items.length > 0) { | |
| setDragActive(true); | |
| } | |
| }, []); | |
| const handleDragOut = useCallback((e: React.DragEvent) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| setDragActive(false); | |
| }, []); | |
| const handleDrop = useCallback( | |
| (e: React.DragEvent) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| setDragActive(false); | |
| if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { | |
| handleFiles(e.dataTransfer.files); | |
| } | |
| }, | |
| [handleFiles] | |
| ); | |
| const handleFileInputChange = useCallback( | |
| (e: React.ChangeEvent<HTMLInputElement>) => { | |
| handleFiles(e.target.files); | |
| }, | |
| [handleFiles] | |
| ); | |
| const handleClick = useCallback(() => { | |
| if (!isAnalyzing) { | |
| fileInputRef.current?.click(); | |
| } | |
| }, [isAnalyzing]); | |
| return ( | |
| <div className="absolute inset-0 flex items-center justify-center"> | |
| <div | |
| className={`p-8 rounded-2xl border-2 border-dashed transition-all duration-300 cursor-pointer max-w-md mx-4 ${ | |
| dragActive ? "border-blue-400 scale-105" : "border-white/30" | |
| } ${isAnalyzing ? "opacity-50 pointer-events-none" : "hover:border-white/50"}`} | |
| onDragEnter={handleDragIn} | |
| onDragLeave={handleDragOut} | |
| onDragOver={handleDrag} | |
| onDrop={handleDrop} | |
| onClick={handleClick} | |
| > | |
| <GlassContainer | |
| bgColor={dragActive ? GLASS_EFFECTS.COLORS.BUTTON_BG : GLASS_EFFECTS.COLORS.DEFAULT_BG} | |
| className="rounded-2xl" | |
| > | |
| <div className="text-center text-white"> | |
| <div className="mb-4"> | |
| <svg | |
| className="mx-auto w-16 h-16 text-white/60" | |
| fill="none" | |
| stroke="currentColor" | |
| viewBox="0 0 24 24" | |
| > | |
| <path | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| strokeWidth={1.5} | |
| d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" | |
| /> | |
| </svg> | |
| </div> | |
| <h3 className="text-xl font-semibold mb-2">Upload Images</h3> | |
| <p className="text-white/80 mb-4"> | |
| Drag and drop images here, or click to select files | |
| </p> | |
| <p className="text-sm text-white/60 mb-6"> | |
| Supports JPG, PNG, GIF, WebP formats. Multiple files allowed. | |
| </p> | |
| <GlassButton | |
| onClick={handleClick} | |
| disabled={isAnalyzing} | |
| > | |
| {isAnalyzing ? "Analyzing..." : "Choose Files"} | |
| </GlassButton> | |
| </div> | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| multiple | |
| accept="image/*" | |
| onChange={handleFileInputChange} | |
| className="hidden" | |
| /> | |
| </GlassContainer> | |
| </div> | |
| </div> | |
| ); | |
| } |