Spaces:
Configuration error
Configuration error
File size: 3,875 Bytes
74aacd5 |
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
import React, { useState } from 'react'
import useResolution from '../../hooks/useResolution'
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()}`)
const resolution = useResolution()
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 20mb
if (file.size > 20 * 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<File> {
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<File> = []
// 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<any> {
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 (
<label htmlFor={uploadElemId} className="file-select-label">
<div
className={[
'file-select-container',
dragHover ? 'file-select-label-hover' : '',
].join(' ')}
onDrop={handleDrop}
onDragOver={ev => {
ev.stopPropagation()
ev.preventDefault()
setDragHover(true)
}}
onDragLeave={() => setDragHover(false)}
>
<input
id={uploadElemId}
name={uploadElemId}
type="file"
onChange={ev => {
const file = ev.currentTarget.files?.[0]
if (file) {
onFileSelected(file)
}
}}
accept="image/png, image/jpeg"
/>
<p className="file-select-message">
{resolution === 'desktop'
? 'Click here or drag an image file'
: 'Tap here to load your picture'}
</p>
</div>
</label>
)
}
|