|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useRef, useState } from "react";
|
|
|
|
|
|
|
|
|
|
type UploadAreaProps = {
|
|
|
|
|
onFilesSelected?: (files: File[]) => void;
|
|
|
|
|
className?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function UploadArea({
|
|
|
|
|
onFilesSelected,
|
|
|
|
|
className,
|
|
|
|
|
}: UploadAreaProps) {
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleClick = () => fileInputRef.current?.click();
|
|
|
|
|
|
|
|
|
|
const handleDragOver: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setIsDragging(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragLeave: React.DragEventHandler<HTMLDivElement> = () => {
|
|
|
|
|
setIsDragging(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDrop: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setIsDragging(false);
|
|
|
|
|
if (e.dataTransfer?.files?.length) {
|
|
|
|
|
onFilesSelected?.(Array.from(e.dataTransfer.files));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
|
|
|
if (e.target.files?.length) {
|
|
|
|
|
onFilesSelected?.(Array.from(e.target.files));
|
|
|
|
|
// reset input to allow re-selecting the same file
|
|
|
|
|
e.currentTarget.value = "";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
role="button"
|
|
|
|
|
aria-label="Upload files"
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
onDragOver={handleDragOver}
|
|
|
|
|
onDragLeave={handleDragLeave}
|
|
|
|
|
onDrop={handleDrop}
|
|
|
|
|
className={`rounded-xl border-2 border-dashed bg-white text-gray-700 cursor-pointer transition-colors ${
|
|
|
|
|
isDragging
|
|
|
|
|
? "border-gray-400 bg-gray-50"
|
|
|
|
|
: "border-gray-300 hover:border-gray-400"
|
|
|
|
|
} ${
|
|
|
|
|
// striped background (grey interrupted)
|
|
|
|
|
"[background-image:repeating-linear-gradient(135deg,rgba(0,0,0,0.06)_0_10px,transparent_10px_20px)]"
|
|
|
|
|
} p-10 min-h-56 text-center select-none ${className ?? ""}`}
|
|
|
|
|
>
|
|
|
|
|
<div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-lg border border-gray-300 bg-white/60">
|
|
|
|
|
<svg
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
className="h-6 w-6 text-gray-700"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth="2"
|
|
|
|
|
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-lg font-medium text-gray-700">Upload files here</div>
|
|
|
|
|
<div className="mt-1 text-sm text-gray-500">or click to browse files</div>
|
|
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
ref={fileInputRef}
|
|
|
|
|
type="file"
|
|
|
|
|
multiple
|
|
|
|
|
accept=".blend,.zip,.jpg,.jpeg,.png,.svg"
|
|
|
|
|
className="hidden"
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|