Spaces:
Build error
Build error
| 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; |