Skip to content

Instantly share code, notes, and snippets.

@valoricDe
Created September 10, 2020 19:52
Show Gist options
  • Save valoricDe/712155a659d50b48f41b0d79a22d043e to your computer and use it in GitHub Desktop.
Save valoricDe/712155a659d50b48f41b0d79a22d043e to your computer and use it in GitHub Desktop.
import React, { useCallback, useEffect, useState } from 'react'
import CircularProgress from 'material-ui/CircularProgress'
import { useDropzone } from 'react-dropzone'
import cx from '../../helpers/classname'
import './drop-zone.scss'
const useProgress = (initial = { current: 0, total: 1 }) => {
const [{ current, total }, setProgress] = useState(initial)
return [
{ current, total, percent: (current / total) * 100, reset: () => setProgress(initial) },
setProgress,
]
}
export function readFile(mode, file, map) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onerror = (err) => {
reject(err)
}
const dataMapper =
map || (({ target: { result } }) => (mode === 'text' ? result : result.split(',')[1]))
reader.onload = (event) => {
resolve({ data: dataMapper(event), mime: file.type })
}
if (mode === 'text') {
reader.readAsText(file)
} else {
reader.readAsDataURL(file)
}
})
}
const cssPrefix = 'DropZone'
export default function DropZone({
uploadHandler,
accept = '*/*',
children,
renderPreview = ({ name }) => `${name} is being uploaded`,
renderMedia = () => null,
mode = 'binary',
noClick,
setDropZone = () => {},
}) {
const [{ percent, reset: resetProgress }, setProgress] = useProgress()
const [error, setError] = useState()
const [uploadingFile, setUploadingFile] = useState()
const onDrop = useCallback(
async ([firstAcceptedFile]) => {
if (!firstAcceptedFile) {
setError('FileType not supported')
return
}
if (error) {
setError()
}
setUploadingFile(firstAcceptedFile)
try {
const data = await readFile(mode, firstAcceptedFile)
uploadHandler(data, setProgress)
} catch (err) {
setError(err)
} finally {
resetProgress()
setUploadingFile()
}
},
[mode]
)
const { getRootProps, getInputProps, open } = useDropzone({
accept,
onDrop,
multiple: false,
noClick,
})
useEffect(() => {
setDropZone({ open })
}, [open])
return (
<div className={cx.modifiers(cssPrefix, { uploading: !!uploadingFile })}>
<div
{...getRootProps({
className: cx.element(cssPrefix, 'ReactDropZone'),
})}
>
<input {...getInputProps()} />
{uploadingFile ? (
renderPreview(uploadingFile)
) : (
<>
{renderMedia()}
{children}
</>
)}
</div>
{uploadingFile && (
<div className={cx.element(cssPrefix, 'Progress')}>
<CircularProgress mode="determinate" value={percent} />
</div>
)}
</div>
)
}
export function ImageDropZone({ imgPath = null, ...props }) {
return (
<DropZone
accept="image/jpeg, image/png, image/svg+xml"
renderPreview={({ preview = null }) =>
preview && (
<img className={cx.element(cssPrefix, 'Image')} alt="Preview loading" src={preview} />
)
}
renderMedia={() =>
imgPath && <img className={cx.element(cssPrefix, 'Image')} alt={imgPath} src={imgPath} />
}
{...props}
/>
)
}
export function CSVDropZone({ csv = null, onChange, ...props }) {
return (
<DropZone
accept="text/csv, application/vnd.ms-excel, text/plain, text/comma-separated-values, " // ", " => win does not set mime type at all
renderPreview={({ preview = null, ...rest }) =>
console.log(preview, rest) || (preview && <div>{preview}</div>)
}
renderMedia={() => (
<textarea style={{ width: '100%', minHeight: '200px' }} onChange={onChange} value={csv} />
)}
mode="text"
noClick
{...props}
/>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment