From 8b0c699d7690b0094e5ed11943cc94c4e264f42a Mon Sep 17 00:00:00 2001 From: rajun Date: Tue, 21 Jul 2026 18:14:29 +0530 Subject: [PATCH] feat: create FileOrUrlField component --- app/components/FileOrUrlField.tsx | 120 ++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 app/components/FileOrUrlField.tsx diff --git a/app/components/FileOrUrlField.tsx b/app/components/FileOrUrlField.tsx new file mode 100644 index 0000000..baedabe --- /dev/null +++ b/app/components/FileOrUrlField.tsx @@ -0,0 +1,120 @@ +import { Upload, Link, X } from 'lucide-react'; + +export type InputMode = 'url' | 'file'; + +export interface FileOrUrlFieldProps { + label: string; + required?: boolean; + mode: InputMode; + onModeChange: (m: InputMode) => void; + accept: string; + file: File | null; + onFileChange: (f: File | null) => void; + inputRef: React.RefObject; + url: string; + onUrlChange: (u: string) => void; + urlPlaceholder: string; +} + +export function FileOrUrlField({ + label, required, mode, onModeChange, + accept, file, onFileChange, inputRef, + url, onUrlChange, urlPlaceholder, +}: FileOrUrlFieldProps) { + return ( +
+ {/* Label + toggle */} +
+ +
+ + +
+
+ + {mode === 'file' ? ( +
inputRef.current?.click()} + className={`flex cursor-pointer items-center gap-3 rounded-xl border-2 border-dashed px-4 py-4 transition-all duration-200 ${ + file + ? 'border-orange-500/50 bg-[#161616]' + : 'border-neutral-800 hover:border-neutral-700 bg-[#161616]/40 hover:bg-[#161616]/70' + }`} + > + onFileChange(e.target.files?.[0] ?? null)} + /> + {file ? ( + <> +
+
+ +
+
+ {file.name} + + {(file.size / 1024 / 1024).toFixed(2)} MB + +
+
+ + + ) : ( +
+ + + Choose a file ({accept}) + +
+ )} +
+ ) : ( + onUrlChange(e.target.value)} + /> + )} +
+ ); +}