NotebookDragDropUpload.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import React, { useCallback, useState } from 'react';
  2. import { Upload as UploadIcon, FileText, Image as ImageIcon, Folder, FileUp, ShieldCheck } from 'lucide-react';
  3. import { useLanguage } from '../contexts/LanguageContext';
  4. import { GROUP_ALLOWED_EXTENSIONS, IMAGE_MIME_TYPES } from '../constants/fileSupport';
  5. import { motion, AnimatePresence } from 'framer-motion';
  6. interface NotebookDragDropUploadProps {
  7. onFilesSelected: (files: FileList) => void;
  8. isAdmin: boolean;
  9. globalMode?: boolean;
  10. children?: React.ReactNode;
  11. }
  12. export const NotebookDragDropUpload: React.FC<NotebookDragDropUploadProps> = ({ onFilesSelected, isAdmin, globalMode = false, children }) => {
  13. const { t } = useLanguage();
  14. const [isDragging, setIsDragging] = useState(false);
  15. const handleDragEnter = useCallback((e: React.DragEvent) => {
  16. e.preventDefault();
  17. e.stopPropagation();
  18. if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
  19. setIsDragging(true);
  20. }
  21. }, []);
  22. const handleDragLeave = useCallback((e: React.DragEvent) => {
  23. e.preventDefault();
  24. e.stopPropagation();
  25. setIsDragging(false);
  26. }, []);
  27. const handleDragOver = useCallback((e: React.DragEvent) => {
  28. e.preventDefault();
  29. e.stopPropagation();
  30. e.dataTransfer.dropEffect = 'copy';
  31. }, []);
  32. const handleDrop = useCallback((e: React.DragEvent) => {
  33. e.preventDefault();
  34. e.stopPropagation();
  35. setIsDragging(false);
  36. if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
  37. onFilesSelected(e.dataTransfer.files);
  38. e.dataTransfer.clearData();
  39. }
  40. }, [onFilesSelected]);
  41. const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
  42. if (e.target.files && e.target.files.length > 0) {
  43. onFilesSelected(e.target.files);
  44. e.target.value = '';
  45. }
  46. };
  47. if (!isAdmin) return <>{children}</>;
  48. return (
  49. <div className="relative h-full flex flex-col">
  50. <AnimatePresence>
  51. {isDragging && (
  52. <motion.div
  53. initial={{ opacity: 0 }}
  54. animate={{ opacity: 1 }}
  55. exit={{ opacity: 0 }}
  56. className="absolute inset-0 z-[100] bg-blue-600/10 backdrop-blur-sm flex items-center justify-center p-8 pointer-events-none"
  57. >
  58. <motion.div
  59. initial={{ scale: 0.9 }}
  60. animate={{ scale: 1 }}
  61. className="bg-white rounded-[2rem] p-10 text-center shadow-2xl border border-blue-100 flex flex-col items-center gap-6"
  62. >
  63. <div className="w-16 h-16 bg-blue-600 text-white rounded-2xl flex items-center justify-center animate-bounce">
  64. <FileUp size={32} />
  65. </div>
  66. <div className="space-y-1">
  67. <h3 className="text-xl font-bold text-slate-900">Ingest into Group</h3>
  68. <p className="text-slate-500 font-medium text-sm">Release to start processing</p>
  69. </div>
  70. </motion.div>
  71. </motion.div>
  72. )}
  73. </AnimatePresence>
  74. <div
  75. className="flex-1 flex flex-col"
  76. onDragEnter={handleDragEnter}
  77. onDragOver={handleDragOver}
  78. onDragLeave={handleDragLeave}
  79. onDrop={handleDrop}
  80. >
  81. {children}
  82. </div>
  83. <input
  84. type="file"
  85. multiple
  86. onChange={handleFileInput}
  87. className="hidden"
  88. id="notebook-file-upload-input"
  89. accept={GROUP_ALLOWED_EXTENSIONS.map(ext => `.${ext}`).join(',') + ',' + IMAGE_MIME_TYPES.join(',')}
  90. />
  91. </div>
  92. );
  93. };