useFileDrop
hook hookupload
Drag&Drop-Hook für Datei-Uploads: liefert ein dragOver-Flag und Event-Handler zum Spread auf einen Container. MIME-Filter via accept; verlässt-Container-Erkennung gegen Flackern bei Kind-Elementen.
Keine interaktive Live-Demo — siehe Code & Beispiele unten.
Barrierefreiheit
- Reines Verhalten — die a11y des Drop-Bereichs bestimmt der Konsument.
Beispiele
Eigener Drop-Bereich
const { dragOver, dropHandlers } = useFileDrop({ accept: ".sng", onFiles });
<div {...dropHandlers} data-over={dragOver}>…</div> Quelldateien
hooks/useFileDrop.ts ts
import { useCallback, useState, type DragEvent } from 'react';
interface Options {
/** MIME-Filter wie bei `<input accept>` — z.B. `'image/*,video/*'`. Dateien,
* die nicht matchen, werden vor `onFiles` herausgefiltert. */
accept?: string | undefined;
/** Erhält die gefilterten Dateien sowie das Drop-Event (für Koordinaten o.ä.). */
onFiles: (files: File[], e: DragEvent<HTMLElement>) => void;
/** Wenn `true`, ignoriert der Drop-Handler komplett (z.B. während Uploads). */
disabled?: boolean | undefined;
}
interface DropHandlers {
onDragOver: (e: DragEvent<HTMLElement>) => void;
onDragEnter: (e: DragEvent<HTMLElement>) => void;
onDragLeave: (e: DragEvent<HTMLElement>) => void;
onDrop: (e: DragEvent<HTMLElement>) => void;
}
/** Drag&Drop-Hook für Datei-Uploads. Liefert einen `dragOver`-Flag und
* Event-Handler, die per Spread an ein Container-Element gebunden werden. */
export function useFileDrop({ accept, onFiles, disabled }: Options): {
dragOver: boolean;
dropHandlers: DropHandlers;
} {
const [dragOver, setDragOver] = useState(false);
const matches = useCallback(
(file: File) => {
if (!accept) return true;
const patterns = accept.split(',').map((s) => s.trim());
return patterns.some((p) => {
if (p.endsWith('/*')) return file.type.startsWith(p.slice(0, -1));
return file.type === p;
});
},
[accept],
);
const dropHandlers: DropHandlers = {
onDragOver: (e) => {
if (disabled) return;
if (!Array.from(e.dataTransfer.types).includes('Files')) return;
e.preventDefault();
setDragOver(true);
},
onDragEnter: (e) => {
if (disabled) return;
if (!Array.from(e.dataTransfer.types).includes('Files')) return;
e.preventDefault();
setDragOver(true);
},
onDragLeave: (e) => {
// Nur ausschalten wenn wir den Container wirklich verlassen
// (nicht beim Übergang auf Kind-Elemente).
if (e.currentTarget.contains(e.relatedTarget as Node | null)) return;
setDragOver(false);
},
onDrop: (e) => {
if (disabled) return;
setDragOver(false);
if (!e.dataTransfer.files || e.dataTransfer.files.length === 0) return;
e.preventDefault();
const files = Array.from(e.dataTransfer.files).filter(matches);
if (files.length > 0) onFiles(files, e);
},
};
return { dragOver, dropHandlers };
}
Holen
# MCP (Claude Code / Cursor)
get_component({ name: "use-file-drop" })
# Registry direkt
https://www.ekklesi.tools/design/registry/components/use-file-drop.json