Oct 23, 2024
Dropzone used to drag & drop files for your website
This dropzone uses native HTML ondragenter, ondrag, ondrop event listeners to provide drag & drop functionality with zero dependencies.
It also uses a file callback function to allow it to have as much flexibility as possible. So you can use the files in any way you want.
In the example I show that this dropzone can be used to easily add/remove files to a list. We'll add that feature later. Make sure you import this into a navigatable page from NextJS
1"use client"
2
3export default function ClientSideWrapper() {
4 return(
5 <div>
6 <Dropzone />
7 </div>
8 )
9}
10
I'm using tailwind to style it, but you can use whatever styling method you want.
1"use client"
2import React from "react";
3
4type DropzoneProps = {
5 acceptedFiletypes: string;
6 fileCallback(files: File[]): void;
7 multiple?: boolean;
8 name?: string;
9}
10
11export default function Dropzone() {
12 return(
13 <div
14 className='flex flex-col justify-center items-center border-dashed border-2 border-[#ccc]'
15 >
16 <input
17 id={`fileSelect${name}`}
18 type="file"
19 className='border-0 clip-[rect(0,0,0,0)] h-[1px] overflow-hidden p-0 absolute whitespace-nowrap w-[1px] hidden'
20 />
21 <label htmlFor={`fileSelect${name}`} className="mt-[1rem] bg-blue-800 rounded !text-inherit cursor-pointer
22 inline-block px-1 py-3 text-center select-none hover:text-[#fff] focus:text-[#fff] focus:outlline-dotted focus:outline-[5px] focus:outline-white">
23 Select {name}
24 </label>
25
26 <h3 className='my-[1em] text-white'>
27 or drag & drop your {name} here
28 </h3>
29 </div>
30 )
31}
This is only the Dropzone's return value
1<div
2 className='flex flex-col justify-center items-center border-dashed border-2 border-[#ccc]'
3 onDragEnter={(e) => handleDragEnter(e)}
4 onDragOver={(e) => handleDragOver(e)}
5 onDragLeave={(e) => handleDragLeave(e)}
6 onDrop={(e) => handleDrop(e)}
7>
8 <input
9 id={`fileSelect${name}`}
10 type="file"
11 accept={acceptedFileTypes}
12 className='border-0 clip-[rect(0,0,0,0)] h-[1px] overflow-hidden p-0 absolute whitespace-nowrap w-[1px] hidden'
13 onChange={(e) => handleFileSelect(e)}
14 multiple={multiple}
15 />
16 <label htmlFor={`fileSelect${name}`} className="mt-[1rem] bg-blue-800 rounded !text-inherit cursor-pointer inline-block px-1 py-3 text-center select-none
17 hover:text-[#fff] focus:text-[#fff] focus:outlline-dotted focus:outline-[5px] focus:outline-white">Select {name}</label>
18
19 <h3 className='my-[1em] text-white'>
20 or drag & drop your {name} here
21 </h3>
22</div>
ondragenter, ondragover, and ondragleave are just to stop refreshing (preventDefault) and highlighting (stopPropagation)
We also make sure the drop effect is set to copy in the ondragenter function
1// onDragEnter stops refreshing & highlighting & makes sure the dropEffect is "copy"
2const handleDragEnter = (e: MouseEvent<HTMLDivElement>) => {
3 e.preventDefault();
4 e.stopPropagation();
5
6 e.dataTransfer.dropEffect = "copy";
7};
8
9// onDragOver stops refreshing & highlighting
10const handleDragOver = (e: MouseEvent<HTMLDivElement>) => {
11 e.preventDefault();
12 e.stopPropagation();
13};
14
15// onDragLeave stops refreshing & highlighting
16const handleDragLeave = (e: MouseEvent<HTMLDivElement>) => {
17 e.preventDefault();
18 e.stopPropagation();
19};
1// onDrop adds files to fileList
2const handleDrop = async (e: MouseEvent<HTMLDivElement>) => {
3 e.preventDefault();
4 e.stopPropagation();
5 // Additional info on these next few lines in the next paragraph
6 let isDropzoneFile: string = e.dataTransfer.getData("isDropzoneFile")
7 if (isDropzoneFile.includes("false")) {
8 return
9 }
10
11 // get files from event on the dataTransfer object as an array
12 let files = [...e.dataTransfer.files];
13 // ensure a file or files are dropped
14 if (files && files.length > 0) {
15 fileCallback(files);
16 }
17};
18
19// handle file selection via input element
20const handleFileSelect = async (e: ChangeEventHandler<HTMLInputElement>) => {
21 // get files from event on the input element as an array
22 let files = [...e.target.files];
23 // ensure a file or files are dropped
24 if (files && files.length > 0) {
25 fileCallback(files);
26 }
27};
Notice: The lines below allows you to not drop certain elements. So if you have a drag & sort component on the same page. These lines will help stop the sorting elements from getting dropped into the dropzone. Just set the dataTransfer variable `isDropzoneFile` to false on the sorting elements.
1let isDropzoneFile: string = e.dataTransfer.getData("isDropzoneFile")
2if (isDropzoneFile.includes("false")) {
3 return
4}
1"use client"
2export default function ClientSideWrapper() {
3 const fileFunction = (files: File[]): void => {
4 // Do whatever you want with the dropped files here
5 }
6
7 return(
8 <div>
9 <DropZone name="Media" acceptedFileTypes="image/png,image/jpeg,video/*" fileCallback={fileFunction} multiple/>
10 </div>
11 )
12}