import React, { useCallback, useState, useRef, useEffect } from 'react'; import { motion } from 'motion/react'; import { Upload, FileText, AlertCircle } from 'lucide-react'; import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'; interface FileUploadProps { onFileLoaded: (content: string, fileName: string) => void; } export const FileUpload: React.FC = ({ onFileLoaded }) => { const [dragActive, setDragActive] = useState(false); const [error, setError] = useState(null); const [isFocused, setIsFocused] = useState(false); const inputRef = useRef(null); const dropzoneRef = useRef(null); const dragCounter = useRef(0); const handleFile = (file: File) => { setError(null); if (!file.name.endsWith('.md') && !file.name.endsWith('.txt') && !file.name.endsWith('.markdown')) { setError('Please upload a Markdown (.md) or Text (.txt) file.'); return; } const reader = new FileReader(); reader.onload = (e) => { const text = e.target?.result; if (typeof text === 'string') { // Extract filename without extension const fileName = file.name.replace(/\.[^/.]+$/, ''); onFileLoaded(text, fileName); } }; reader.onerror = () => setError('Error reading file.'); reader.readAsText(file); }; const handleDrag = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.type === 'dragenter') { dragCounter.current += 1; setDragActive(true); } else if (e.type === 'dragleave') { dragCounter.current -= 1; if (dragCounter.current === 0) { setDragActive(false); } } else if (e.type === 'dragover') { // Keep drag active during dragover setDragActive(true); } }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); dragCounter.current = 0; setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFile(e.dataTransfer.files[0]); } }, []); const handleChange = (e: React.ChangeEvent) => { e.preventDefault(); if (e.target.files && e.target.files[0]) { handleFile(e.target.files[0]); } }; const openFilePicker = useCallback(() => { inputRef.current?.click(); }, []); // Keyboard navigation for the dropzone useKeyboardNavigation({ onEnter: () => { if (isFocused) { openFilePicker(); } }, onSpace: () => { if (isFocused) { openFilePicker(); } }, }, [isFocused]); // Clear error after 5 seconds useEffect(() => { if (error) { const timer = setTimeout(() => setError(null), 5000); return () => clearTimeout(timer); } }, [error]); return ( setIsFocused(true)} onBlur={() => setIsFocused(false)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePicker(); } }} whileHover={{ scale: 1.02, borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.05)' }} whileTap={{ scale: 0.98 }} transition={{ type: "spring", stiffness: 300, damping: 20 }} > Click to upload {' '} or drag and drop Markdown or Plain Text files Press Enter to browse {/* Animated border gradient on hover */} {error && ( {error} )} ); };