Files
userbase/app/src/ui/modules/media/components/dropzone/use-dropzone.ts
T
dswbx d182640981 Add Media.Dropzone and Media.Preview as isolated elements
Introduce `Media.*` to modularize and customize file upload handling. Refactor media-related components to improve usability, add max item limits, overwrite support, and event callbacks.
2025-01-09 15:43:43 +01:00

83 lines
2.4 KiB
TypeScript

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;
onLeave?: () => void;
};
const events = {
enter: ["dragenter", "dragover", "dragstart"],
leave: ["dragleave", "drop"]
};
const allEvents = [...events.enter, ...events.leave];
export function useDropzone({ onDropped, onOver, onLeave }: 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;
onLeave?.();
}
}, []);
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 };