public commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { type ChangeEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { type FileWithPath, fromEvent } from "./file-selector";
|
||||
|
||||
type DropzoneProps = {
|
||||
onDropped: (files: FileWithPath[]) => void;
|
||||
onOver?: (items: DataTransferItem[]) => void;
|
||||
};
|
||||
|
||||
const events = {
|
||||
enter: ["dragenter", "dragover", "dragstart"],
|
||||
leave: ["dragleave", "drop"],
|
||||
};
|
||||
const allEvents = [...events.enter, ...events.leave];
|
||||
|
||||
export function useDropzone({ onDropped, onOver }: DropzoneProps) {
|
||||
const [isOver, setIsOver] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const onOverCalled = useRef(false);
|
||||
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
const preventDefaults = useCallback((e: Event) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
const toggleHighlight = useCallback(async (e: Event) => {
|
||||
const _isOver = events.enter.includes(e.type);
|
||||
if (onOver && _isOver !== isOver && !onOverCalled.current) {
|
||||
onOver((await fromEvent(e)) as DataTransferItem[]);
|
||||
onOverCalled.current = true;
|
||||
}
|
||||
|
||||
setIsOver(_isOver);
|
||||
if (_isOver === false && onOverCalled.current) {
|
||||
onOverCalled.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
async (e: DragEvent) => {
|
||||
const files = await fromEvent(e);
|
||||
onDropped?.(files as any);
|
||||
onOverCalled.current = false;
|
||||
},
|
||||
[onDropped],
|
||||
);
|
||||
|
||||
const handleFileInputChange = useCallback(
|
||||
async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const files = await fromEvent(e);
|
||||
onDropped?.(files as any);
|
||||
},
|
||||
[onDropped],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const el: HTMLDivElement = ref.current!;
|
||||
|
||||
allEvents.forEach((eventName) => {
|
||||
el.addEventListener(eventName, preventDefaults);
|
||||
el.addEventListener(eventName, toggleHighlight);
|
||||
});
|
||||
|
||||
// Handle dropped files
|
||||
el.addEventListener("drop", handleDrop);
|
||||
|
||||
return () => {
|
||||
allEvents.forEach((eventName) => {
|
||||
el.removeEventListener(eventName, preventDefaults);
|
||||
el.removeEventListener(eventName, toggleHighlight);
|
||||
});
|
||||
el.removeEventListener("drop", handleDrop);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { ref, isOver, fromEvent, onDropped, handleFileInputChange };
|
||||
}
|
||||
|
||||
export type { FileWithPath };
|
||||
Reference in New Issue
Block a user