import React, { useRef, useCallback, useState } from 'react'; import { FileReader } from '@kanaries/web-data-loader'; import { ArrowUpTrayIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'; import { observer } from 'mobx-react-lite'; import { useTranslation } from 'react-i18next'; import { IRow } from '../../interfaces'; import DropdownSelect from '../../components/dropdownSelect'; import Spinner from '../../components/spinner'; import { classNames } from '../../utils'; import { charsetOptions } from './config'; import { jsonReader } from './utils'; import { CommonStore } from '../../store/commonStore'; interface IFileUploadProps { commonStore: CommonStore; } const FileUpload: React.FC = ({ commonStore }) => { const fileRef = useRef(null); const dragCountRef = useRef(0); const [dragOver, setDragOver] = useState(false); const [parsingFile, setParsingFile] = useState(null); const [error, setError] = useState(null); const [encoding, setEncoding] = useState('utf-8'); const { t } = useTranslation('translation', { keyPrefix: 'DataSource.dialog.file' }); const loadFile = useCallback( (file: File) => { const name = file.name; const lowerName = name.toLowerCase(); const isJSON = lowerName.endsWith('.json'); const isCSV = lowerName.endsWith('.csv') || lowerName.endsWith('.tsv') || lowerName.endsWith('.txt'); if (!isJSON && !isCSV) { setError(t('unsupported', { file: name })); return; } setError(null); setParsingFile(name); const parsed: Promise = isJSON ? jsonReader(file) : (FileReader.csvReader({ file, config: { type: 'reservoirSampling', size: Infinity }, onLoading: () => {}, encoding, }) as Promise); parsed .then((data) => { if (!Array.isArray(data) || data.length === 0) { throw new Error('empty data'); } commonStore.updateTempDS(data as IRow[]); commonStore.updateTempName(name.replace(/\.(csv|tsv|txt|json)$/i, '')); }) .catch(() => { setError(t('read_error', { file: name })); }) .finally(() => { setParsingFile(null); }); }, [commonStore, encoding, t] ); const onDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); dragCountRef.current = 0; setDragOver(false); if (parsingFile) return; const file = e.dataTransfer.files?.[0]; if (file) { loadFile(file); } }, [loadFile, parsingFile] ); const openFilePicker = useCallback(() => { if (!parsingFile && fileRef.current) { fileRef.current.click(); } }, [parsingFile]); return (
{ const file = e.target.files?.[0]; if (file) { loadFile(file); } e.target.value = ''; }} accept=".csv,.tsv,.txt,.json" />
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePicker(); } }} onDragEnter={(e) => { e.preventDefault(); dragCountRef.current += 1; setDragOver(true); }} onDragOver={(e) => { e.preventDefault(); }} onDragLeave={(e) => { e.preventDefault(); dragCountRef.current -= 1; if (dragCountRef.current <= 0) { dragCountRef.current = 0; setDragOver(false); } }} onDrop={onDrop} > {parsingFile ? ( <>

{t('parsing', { file: parsingFile })}

) : ( <>

{t('drop_title')}

{t('drop_desc')}

)}
{error ? (

{error}

) : ( )}
); }; export default observer(FileUpload);