anycoder-44574058 / components /LocalDropZone.js
mexicanamerican's picture
Upload components/LocalDropZone.js with huggingface_hub
f62c9f6 verified
import React, { useState, useCallback } from 'react';
import PokemonCard from './PokemonCard';
const LocalDropZone = () => {
const [dragActive, setDragActive] = useState(false);
const [cards, setCards] = useState([]);
const handleDrag = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
}, []);
const handleDragEnter = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setDragActive(true);
}, []);
const handleDragLeave = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
}, []);
const handleDragOver = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
e.dataTransfer.dropEffect = 'copy';
}, []);
const handleDrop = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
const files = e.dataTransfer.files;
if (files && files.length > 0) {
Array.from(files).forEach(file => {
if (file.type.startsWith('image/')) {
const imageUrl = URL.createObjectURL(file);
const newCard = {
id: Math.random().toString(36).substr(2, 9),
name: file.name.replace(/\.[^/.]+$/, ""),
image: imageUrl,
type: 'custom',
size: (file.size / 1024).toFixed(2) + ' KB'
};
setCards(prevCards => [...prevCards, newCard]);
}
});
}
}, []);
const handleFileSelect = (e) => {
const files = e.target.files;
if (files && files.length > 0) {
Array.from(files).forEach(file => {
if (file.type.startsWith('image/')) {
const imageUrl = URL.createObjectURL(file);
const newCard = {
id: Math.random().toString(36).substr(2, 9),
name: file.name.replace(/\.[^/.]+$/, ""),
image: imageUrl,
type: 'custom',
size: (file.size / 1024).toFixed(2) + ' KB'
};
setCards(prevCards => [...prevCards, newCard]);
}
});
}
};
const handleRemoveCard = (id) => {
setCards(prevCards => prevCards.filter(card => card.id !== id));
};
return (
<div className="w-full max-w-4xl mx-auto p-6">
<h1 className="text-4xl font-extrabold text-gray-800 mb-2 text-center">Pokemon Card Drop Monitor</h1>
<p className="text-gray-500 mb-8 text-center">Drag and drop images to create custom Pokemon cards</p>
<div className={`relative border-4 border-dashed rounded-2xl p-12 text-center transition-all duration-300 ease-in-out mb-8
${dragActive ? 'border-blue-500 bg-blue-50' : 'border-gray-300 bg-gray-50 hover:border-blue-400'}`}>
<input
type="file"
multiple
accept="image/*"
onChange={handleFileSelect}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
id="fileInput"
/>
<div className="pointer-events-none">
<svg
className="mx-auto h-16 w-16 text-gray-400 mb-4"
stroke="currentColor"
fill="none"
viewBox="0 0 48 48"
aria-hidden="true"
>
<path
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<p className="text-lg text-gray-600 font-medium">
{dragActive ? 'Drop images here!' : 'Drag & Drop images here, or click to upload'}
</p>
<p className="text-sm text-gray-400 mt-2">Supports PNG, JPG, GIF, WEBP</p>
</div>
</div>
{cards.length === 0 && (
<div className="text-center text-gray-400 py-8">
<p>No cards added yet. Start dropping images above!</p>
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{cards.map(card => (
<div key={card.id} className="relative group">
<PokemonCard pokemon={card} />
<button
onClick={() => handleRemoveCard(card.id)}
className="absolute top-2 right-2 bg-red-500 hover:bg-red-600 text-white rounded-full p-1.5 shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200"
aria-label="Remove card"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
))}
</div>
</div>
);
};
export default LocalDropZone;