File size: 2,092 Bytes
2bc6d22 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import React, { useCallback } from 'react';
import { Upload } from 'lucide-react';
import type { ImageFile } from '../types';
interface DropZoneProps {
onFilesDrop: (files: ImageFile[]) => void;
}
export function DropZone({ onFilesDrop }: DropZoneProps) {
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files)
.filter(file => file.type.startsWith('image/') || file.name.toLowerCase().endsWith('jxl'))
.map(file => ({
id: crypto.randomUUID(),
file,
status: 'pending' as const,
originalSize: file.size,
}));
onFilesDrop(files);
}, [onFilesDrop]);
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
}, []);
const handleFileInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || [])
.filter(file => file.type.startsWith('image/') || file.name.toLowerCase().endsWith('jxl'))
.map(file => ({
id: crypto.randomUUID(),
file,
status: 'pending' as const,
originalSize: file.size,
}));
onFilesDrop(files);
e.target.value = '';
}, [onFilesDrop]);
return (
<div
className="border-2 border-dashed border-gray-300 rounded-lg p-12 text-center hover:border-blue-500 transition-colors"
onDrop={handleDrop}
onDragOver={handleDragOver}
>
<input
type="file"
id="fileInput"
className="hidden"
multiple
accept="image/*,.jxl"
onChange={handleFileInput}
/>
<label
htmlFor="fileInput"
className="cursor-pointer flex flex-col items-center gap-4"
>
<Upload className="w-12 h-12 text-gray-400" />
<div>
<p className="text-lg font-medium text-gray-700">
Drop images here or click to upload
</p>
<p className="text-sm text-gray-500">
Supports JPEG, PNG, WebP, AVIF, and JXL
</p>
</div>
</label>
</div>
);
} |