| "use client"; |
|
|
| import { useRef } from "react"; |
|
|
| |
| const ACCEPTED_EXTENSIONS = ".fasta,.fa,.pdb,.cif"; |
|
|
| function formatSize(bytes: number): string { |
| if (bytes < 1024) return `${bytes} B`; |
| if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; |
| return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; |
| } |
|
|
| interface FileUploadProps { |
| file: File | null; |
| onFileChange: (file: File | null) => void; |
| disabled?: boolean; |
| } |
|
|
| |
| |
| |
| |
| export default function FileUpload({ file, onFileChange, disabled }: FileUploadProps) { |
| const inputRef = useRef<HTMLInputElement>(null); |
|
|
| const handleClick = () => { |
| if (disabled) return; |
| inputRef.current?.click(); |
| }; |
|
|
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
| const selected = e.target.files?.[0] ?? null; |
| onFileChange(selected); |
| |
| if (inputRef.current) inputRef.current.value = ""; |
| }; |
|
|
| return ( |
| <div className="flex items-center gap-1.5"> |
| <input |
| ref={inputRef} |
| type="file" |
| accept={ACCEPTED_EXTENSIONS} |
| className="hidden" |
| onChange={handleChange} |
| /> |
| |
| {/* Paperclip button */} |
| <button |
| onClick={handleClick} |
| disabled={disabled} |
| title="Upload PDB/FASTA file (.fasta, .fa, .pdb, .cif)" |
| className={`p-2.5 rounded-xl transition-colors ${ |
| file |
| ? "text-accent bg-accent/10" |
| : "text-muted-fg hover:text-foreground hover:bg-muted" |
| } disabled:opacity-30`} |
| > |
| <svg |
| className="w-4 h-4" |
| viewBox="0 0 24 24" |
| fill="none" |
| stroke="currentColor" |
| strokeWidth={2} |
| strokeLinecap="round" |
| strokeLinejoin="round" |
| > |
| <path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" /> |
| </svg> |
| </button> |
| |
| {/* File chip */} |
| {file && ( |
| <span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-accent/10 text-accent text-[11px] font-medium max-w-[200px]"> |
| <span className="truncate">{file.name}</span> |
| <span className="text-accent/60 shrink-0">({formatSize(file.size)})</span> |
| <button |
| onClick={(e) => { |
| e.stopPropagation(); |
| onFileChange(null); |
| }} |
| className="ml-0.5 hover:text-red-500 transition-colors shrink-0" |
| title="Remove file" |
| > |
| <svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}> |
| <line x1="18" y1="6" x2="6" y2="18" /> |
| <line x1="6" y1="6" x2="18" y2="18" /> |
| </svg> |
| </button> |
| </span> |
| )} |
| </div> |
| ); |
| } |
|
|