diff options
Diffstat (limited to 'apps/web/src/media/use-file-upload.ts')
| -rw-r--r-- | apps/web/src/media/use-file-upload.ts | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/apps/web/src/media/use-file-upload.ts b/apps/web/src/media/use-file-upload.ts new file mode 100644 index 00000000..1ee30ad9 --- /dev/null +++ b/apps/web/src/media/use-file-upload.ts @@ -0,0 +1,101 @@ +import { useState, useRef } from "react"; +import { hasDragData } from "@/timeline/drag-data"; + +interface UseFileUploadOptions { + accept?: string; + multiple?: boolean; + onFilesSelected?: (files: File[]) => void; +} + +function containsFiles(dataTransfer: DataTransfer): boolean { + return !hasDragData({ dataTransfer }) && dataTransfer.types.includes("Files"); +} + +export function useFileUpload({ + accept, + multiple, + onFilesSelected, +}: UseFileUploadOptions = {}) { + const [isDragOver, setIsDragOver] = useState(false); + const dragCounterRef = useRef(0); + const inputRef = useRef<HTMLInputElement>(null); + + function openFilePicker() { + if (!inputRef.current) return; + + inputRef.current.accept = accept || "*"; + inputRef.current.multiple = multiple || false; + inputRef.current.click(); + } + + function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) { + const files = Array.from(event.target.files ?? []); + if (files.length > 0 && onFilesSelected) { + onFilesSelected(files); + } + + if (event.target) { + event.target.value = ""; + } + } + + function handleDragEnter(e: React.DragEvent) { + e.preventDefault(); + + if (!containsFiles(e.dataTransfer)) return; + + dragCounterRef.current += 1; + setIsDragOver(true); + } + + function handleDragOver(e: React.DragEvent) { + e.preventDefault(); + + if (!containsFiles(e.dataTransfer)) return; + } + + function handleDragLeave(e: React.DragEvent) { + e.preventDefault(); + + if (!containsFiles(e.dataTransfer)) return; + + dragCounterRef.current -= 1; + if (dragCounterRef.current === 0) { + setIsDragOver(false); + } + } + + function handleDrop(e: React.DragEvent) { + e.preventDefault(); + setIsDragOver(false); + dragCounterRef.current = 0; + + if (onFilesSelected && containsFiles(e.dataTransfer)) { + const files = Array.from(e.dataTransfer.files); + const shouldUseMultiple = multiple ?? false; + + if (shouldUseMultiple) { + onFilesSelected(files); + } else if (files.length > 0) { + onFilesSelected([files[0]]); + } + } + } + + return { + isDragOver, + openFilePicker, + fileInputProps: { + ref: inputRef, + type: "file", + style: { display: "none" }, + onChange: handleFileChange, + }, + dragProps: { + onDragEnter: handleDragEnter, + onDragOver: handleDragOver, + onDragLeave: handleDragLeave, + onDrop: handleDrop, + }, + }; +} |
