Skip to content

Instantly share code, notes, and snippets.

@rebolyte
Created October 7, 2020 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rebolyte/2091c9a835d9dd55748ec8ca8dfd37f1 to your computer and use it in GitHub Desktop.
Save rebolyte/2091c9a835d9dd55748ec8ca8dfd37f1 to your computer and use it in GitHub Desktop.
import React, { useState, FunctionComponent, createRef, DragEvent, ChangeEvent } from 'react';
import classNames from 'classnames';
import { useOnMount, isIE } from '@utilities';
export interface Props {
onFileAdded?: (value: File) => any;
disabled?: boolean;
classes?: string;
accepts?: string;
}
const DropZone: FunctionComponent<Props> = ({
onFileAdded = () => {},
disabled = false,
classes,
accepts = '*'
}: Props) => {
const [highlight, setHighlight] = useState(false);
const [file, setFile] = useState<File | undefined>();
const fileInputRef = createRef<HTMLInputElement>();
const overrideEventDefaults = (evt: Event | React.DragEvent<HTMLDivElement>) => {
evt.preventDefault();
evt.stopPropagation();
};
useOnMount(() => {
window.addEventListener('dragover', overrideEventDefaults);
window.addEventListener('drop', overrideEventDefaults);
return () => {
window.removeEventListener('dragover', overrideEventDefaults);
window.removeEventListener('drop', overrideEventDefaults);
};
});
const openFileDialog = () => {
if (!disabled) {
fileInputRef.current && fileInputRef.current.click();
}
};
const handleFileAdded = (evt: ChangeEvent<HTMLInputElement>) => {
if (!disabled) {
const files = evt.target.files || [];
onFileAdded(Array.from(files)[0]);
setFile(files[0]);
}
};
const onDragOver = (evt: DragEvent<HTMLDivElement>) => {
overrideEventDefaults(evt);
setHighlight(true);
};
const onDragLeave = () => {
setHighlight(false);
};
const onDrop = (evt: DragEvent<HTMLDivElement>) => {
overrideEventDefaults(evt);
if (!disabled) {
onFileAdded(Array.from(evt.dataTransfer.files)[0]);
setFile(evt.dataTransfer.files[0]);
setHighlight(false);
}
};
const containerClasses = classNames(
'border',
'bg-white',
'rounded',
'shadow-inner',
'p-4',
'select-none',
'cursor-pointer',
{
'bg-blue-100': highlight,
'cursor-pointer': !disabled,
'cursor-not-allowed opacity-75': disabled
},
classes
);
return (
<div
className={containerClasses}
onDragOver={onDragOver}
onDragLeave={onDragLeave}
onDrop={onDrop}
onClick={openFileDialog}
>
<input
ref={fileInputRef}
type="file"
onChange={handleFileAdded}
accept={accepts}
className="hidden"
/>
{file ? (
<div>{file.name}</div>
) : (
<span>{isIE ? 'Click here to select file' : 'Drop file or click here'}</span>
)}
</div>
);
};
export default DropZone;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment