drag and drop visual
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user