import { useState } from 'react' import * as m from '../paraglide/messages' type FileSelectProps = { onSelection: (file: File) => void } export default function FileSelect(props: FileSelectProps) { const { onSelection } = props const [dragHover, setDragHover] = useState(false) const [uploadElemId] = useState(`file-upload-${Math.random().toString()}`) function onFileSelected(file: File) { if (!file) { return } // Skip non-image files const isImage = file.type.match('image.*') if (!isImage) { return } try { // Check if file is larger than 10mb if (file.size > 10 * 1024 * 1024) { throw new Error('file too large') } onSelection(file) } catch (e) { // eslint-disable-next-line alert(`error: ${(e as any).message}`) } } async function getFile(entry: any): Promise { return new Promise(resolve => { entry.file((file: File) => resolve(file)) }) } /* eslint-disable no-await-in-loop */ // Drop handler function to get all files async function getAllFileEntries(items: DataTransferItemList) { const fileEntries: Array = [] // Use BFS to traverse entire directory/file structure const queue = [] // Unfortunately items is not iterable i.e. no forEach for (let i = 0; i < items.length; i += 1) { queue.push(items[i].webkitGetAsEntry()) } while (queue.length > 0) { const entry = queue.shift() if (entry?.isFile) { // Only append images const file = await getFile(entry) fileEntries.push(file) } else if (entry?.isDirectory) { queue.push( ...(await readAllDirectoryEntries((entry as any).createReader())) ) } } return fileEntries } // Get all the entries (files or sub-directories) in a directory // by calling readEntries until it returns empty array async function readAllDirectoryEntries(directoryReader: any) { const entries = [] let readEntries = await readEntriesPromise(directoryReader) while (readEntries.length > 0) { entries.push(...readEntries) readEntries = await readEntriesPromise(directoryReader) } return entries } /* eslint-enable no-await-in-loop */ // Wrap readEntries in a promise to make working with readEntries easier // readEntries will return only some of the entries in a directory // e.g. Chrome returns at most 100 entries at a time async function readEntriesPromise(directoryReader: any): Promise { return new Promise((resolve, reject) => { directoryReader.readEntries(resolve, reject) }) } async function handleDrop(ev: React.DragEvent) { ev.preventDefault() const items = await getAllFileEntries(ev.dataTransfer.items) setDragHover(false) onFileSelected(items[0]) } return ( ) }