Files
RasadDam_Frontend/src/components/FIleUploader/FileUploader.tsx
2026-01-19 13:08:58 +03:30

123 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { TrashIcon } from "@heroicons/react/24/outline";
import { useState, useRef, ChangeEvent } from "react";
interface FileUploaderProps {
onFileSelected: (base64: string) => void;
error?: string;
defaultValue?: string;
title?: string;
}
export default function FileUploader({
onFileSelected,
error,
defaultValue,
title = "سند",
}: FileUploaderProps) {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
setSelectedFile(file);
const base64 = await convertToBase64(file);
onFileSelected(base64);
};
const convertToBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
};
const handleRemoveFile = () => {
setSelectedFile(null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
onFileSelected("");
};
const handleButtonClick = () => {
fileInputRef.current?.click();
};
const formatFileSize = (size: number) => {
const sizeInMB = size / (1024 * 1024);
return sizeInMB.toFixed(2) + " MB";
};
const getDefaultDocumentName = () => {
const name = defaultValue?.split("/")[defaultValue?.split("/")?.length - 1];
if (name) {
return name;
} else {
return null;
}
};
return (
<>
<div className="flex items-center w-full gap-2 border border-gray1-200 dark:border-dark-400 rounded-lg">
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
className="hidden"
accept="*/*"
/>
<button
type="button"
onClick={handleButtonClick}
className={`flex justify-center cursor-pointer items-center px-4 py-2 rounded-lg transition-colors w-full ${
selectedFile
? "border-l border-gray1-200 dark:border-dark-400 bg-gray-50 dark:bg-dark-600 dark:text-dark-100 hover:bg-gray-100 dark:hover:bg-dark-600 text-gray-700"
: "bg-white1-200 dark:bg-dark-500 dark:text-dark-100 text-gray-700 hover:bg-white1-300 dark:hover:bg-dark-600"
}`}
>
{selectedFile || getDefaultDocumentName() ? (
<div className="flex items-center gap-2">
<div className="flex justify-center">
<span className=" text-[10px] w-10 bg-info-500 rounded-2xl text-white">
{title}
</span>
</div>
{selectedFile ? (
<span className="">
{selectedFile.name?.slice(0, 15)}
{selectedFile.name?.length > 15 ? "..." : ""} (
{formatFileSize(selectedFile.size)})
</span>
) : (
<span>{getDefaultDocumentName()}</span>
)}
</div>
) : (
<span>انتخاب {title}</span>
)}
</button>
{selectedFile && (
<button
type="button"
onClick={handleRemoveFile}
className="p-2 text-red-400 rounded-lg transition-colors"
aria-label="Remove file"
>
<TrashIcon className="h-5 w-5" />
</button>
)}
</div>
{error && (
<p className="text-xs text-red-500 dark:text-red-400">{error}</p>
)}
</>
);
}