pelagia-portal/App/components/po/file-uploader.tsx
2026-05-18 23:18:58 +05:30

78 lines
2.8 KiB
TypeScript

"use client";
import { useRef, useState } from "react";
interface Props {
onChange: (files: File[]) => void;
files: File[];
disabled?: boolean;
}
export function FileUploader({ onChange, files, disabled }: Props) {
const inputRef = useRef<HTMLInputElement>(null);
const [dragOver, setDragOver] = useState(false);
function addFiles(incoming: FileList | null) {
if (!incoming) return;
const next = [...files];
for (const file of Array.from(incoming)) {
if (!next.some((f) => f.name === file.name && f.size === file.size)) {
next.push(file);
}
}
onChange(next);
}
function remove(index: number) {
onChange(files.filter((_, i) => i !== index));
}
return (
<div className="space-y-3">
<div
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
onDragLeave={() => setDragOver(false)}
onDrop={(e) => { e.preventDefault(); setDragOver(false); addFiles(e.dataTransfer.files); }}
onClick={() => !disabled && inputRef.current?.click()}
className={`flex cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed px-6 py-8 text-sm transition-colors ${
dragOver ? "border-primary-400 bg-primary-50" : "border-neutral-300 hover:border-neutral-400"
} ${disabled ? "cursor-not-allowed opacity-60" : ""}`}
>
<span className="font-medium text-neutral-700">Drop files here or click to browse</span>
<span className="mt-1 text-xs text-neutral-400">PDF, images up to 10 MB each</span>
<input
ref={inputRef}
type="file"
multiple
className="hidden"
disabled={disabled}
onChange={(e) => addFiles(e.target.files)}
accept=".pdf,.png,.jpg,.jpeg,.gif,.webp"
/>
</div>
{files.length > 0 && (
<ul className="space-y-1.5">
{files.map((file, i) => (
<li key={i} className="flex items-center justify-between rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2 text-sm">
<span className="font-medium text-neutral-800 truncate max-w-xs">{file.name}</span>
<div className="flex items-center gap-3 ml-3 shrink-0">
<span className="text-xs text-neutral-400">{(file.size / 1024).toFixed(0)} KB</span>
{!disabled && (
<button
type="button"
onClick={(e) => { e.stopPropagation(); remove(i); }}
className="text-neutral-400 hover:text-danger-700 transition-colors"
aria-label="Remove file"
>
</button>
)}
</div>
</li>
))}
</ul>
)}
</div>
);
}