drag and drop visual

This commit is contained in:
starmorph
2025-05-20 13:22:46 -07:00
parent 06e0de6f85
commit d322066d6a
2 changed files with 53 additions and 1 deletions

View File

@@ -133,6 +133,7 @@ export function Thread() {
dropRef, dropRef,
removeBlock, removeBlock,
resetBlocks, resetBlocks,
dragOver,
} = useFileUpload(); } = useFileUpload();
const [firstTokenReceived, setFirstTokenReceived] = useState(false); const [firstTokenReceived, setFirstTokenReceived] = useState(false);
const isLargeScreen = useMediaQuery("(min-width: 1024px)"); const isLargeScreen = useMediaQuery("(min-width: 1024px)");
@@ -442,8 +443,18 @@ export function Thread() {
<div <div
ref={dropRef} ref={dropRef}
className="bg-muted relative z-10 mx-auto mb-8 w-full max-w-3xl rounded-2xl border shadow-xs" className={cn(
"bg-muted relative z-10 mx-auto mb-8 w-full max-w-3xl rounded-2xl border shadow-xs transition-all",
dragOver ? "border-primary border-2 border-dotted" : "",
)}
> >
{dragOver && (
<div className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-2xl bg-black/40">
<span className="text-lg font-semibold text-white">
Drop file here
</span>
</div>
)}
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="mx-auto grid max-w-3xl grid-rows-[1fr_auto] gap-2" className="mx-auto grid max-w-3xl grid-rows-[1fr_auto] gap-2"

View File

@@ -21,6 +21,8 @@ export function useFileUpload({
const [contentBlocks, setContentBlocks] = const [contentBlocks, setContentBlocks] =
useState<Base64ContentBlock[]>(initialBlocks); useState<Base64ContentBlock[]>(initialBlocks);
const dropRef = useRef<HTMLDivElement>(null); const dropRef = useRef<HTMLDivElement>(null);
const [dragOver, setDragOver] = useState(false);
const dragCounter = useRef(0);
const isDuplicate = (file: File, blocks: Base64ContentBlock[]) => { const isDuplicate = (file: File, blocks: Base64ContentBlock[]) => {
if (file.type === "application/pdf") { if (file.type === "application/pdf") {
@@ -81,14 +83,45 @@ export function useFileUpload({
useEffect(() => { useEffect(() => {
if (!dropRef.current) return; if (!dropRef.current) return;
// Global drag events with counter for robust dragOver state
const handleWindowDragEnter = (e: DragEvent) => {
if (e.dataTransfer?.types?.includes("Files")) {
dragCounter.current++;
setDragOver(true);
}
};
const handleWindowDragLeave = (e: DragEvent) => {
if (e.dataTransfer?.types?.includes("Files")) {
dragCounter.current--;
if (dragCounter.current <= 0) {
setDragOver(false);
dragCounter.current = 0;
}
}
};
const handleWindowDrop = (e: DragEvent) => {
dragCounter.current = 0;
setDragOver(false);
};
const handleWindowDragEnd = (e: DragEvent) => {
dragCounter.current = 0;
setDragOver(false);
};
window.addEventListener("dragenter", handleWindowDragEnter);
window.addEventListener("dragleave", handleWindowDragLeave);
window.addEventListener("drop", handleWindowDrop);
window.addEventListener("dragend", handleWindowDragEnd);
const handleDragOver = (e: DragEvent) => { const handleDragOver = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setDragOver(true);
}; };
const handleDrop = async (e: DragEvent) => { const handleDrop = async (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setDragOver(false);
if (!e.dataTransfer) return; if (!e.dataTransfer) return;
@@ -126,11 +159,13 @@ export function useFileUpload({
const handleDragEnter = (e: DragEvent) => { const handleDragEnter = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setDragOver(true);
}; };
const handleDragLeave = (e: DragEvent) => { const handleDragLeave = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setDragOver(false);
}; };
const element = dropRef.current; const element = dropRef.current;
@@ -144,6 +179,11 @@ export function useFileUpload({
element.removeEventListener("drop", handleDrop); element.removeEventListener("drop", handleDrop);
element.removeEventListener("dragenter", handleDragEnter); element.removeEventListener("dragenter", handleDragEnter);
element.removeEventListener("dragleave", handleDragLeave); element.removeEventListener("dragleave", handleDragLeave);
window.removeEventListener("dragenter", handleWindowDragEnter);
window.removeEventListener("dragleave", handleWindowDragLeave);
window.removeEventListener("drop", handleWindowDrop);
window.removeEventListener("dragend", handleWindowDragEnd);
dragCounter.current = 0;
}; };
}, [contentBlocks]); }, [contentBlocks]);
@@ -160,5 +200,6 @@ export function useFileUpload({
dropRef, dropRef,
removeBlock, removeBlock,
resetBlocks, resetBlocks,
dragOver,
}; };
} }