File size: 2,027 Bytes
8fc8501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useRef, useState, type DragEvent } from "react";
import { Upload } from "lucide-react";
import { ACCEPTED_DATA_EXTS, MAX_DATA_FILE_SIZE } from "../utils/data-files";

interface UploadZoneProps {
  onFiles: (files: FileList | File[]) => void | Promise<void>;
  disabled?: boolean;
  compact?: boolean;
}

const ACCEPT_ATTR = ACCEPTED_DATA_EXTS.map((e) => `.${e}`).join(",");
const MAX_MB = (MAX_DATA_FILE_SIZE / (1024 * 1024)).toFixed(0);

export function UploadZone({ onFiles, disabled, compact }: UploadZoneProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [isDragging, setIsDragging] = useState(false);

  const handleDrop = (e: DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(false);
    if (disabled) return;
    if (e.dataTransfer.files?.length) void onFiles(e.dataTransfer.files);
  };

  const handleDragOver = (e: DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    if (!disabled) setIsDragging(true);
  };

  const handleDragLeave = () => setIsDragging(false);

  return (
    <div
      className={`es-upload-zone ${isDragging ? "es-upload-zone--active" : ""} ${compact ? "es-upload-zone--compact" : ""}`}
      onDrop={handleDrop}
      onDragOver={handleDragOver}
      onDragLeave={handleDragLeave}
      onClick={() => !disabled && inputRef.current?.click()}
      role="button"
      tabIndex={disabled ? -1 : 0}
      aria-label="Upload data file"
    >
      <input
        ref={inputRef}
        type="file"
        accept={ACCEPT_ATTR}
        multiple
        onChange={(e) => {
          if (e.target.files?.length) void onFiles(e.target.files);
          e.target.value = "";
        }}
        style={{ display: "none" }}
      />
      <Upload size={compact ? 14 : 18} />
      {compact ? (
        <span>Add data file</span>
      ) : (
        <div className="es-upload-zone__text">
          <strong>Drop a data file</strong>
          <span>CSV, TSV, JSON, NDJSON, TXT - up to {MAX_MB} MB</span>
        </div>
      )}
    </div>
  );
}