212 lines
5.6 KiB
TypeScript
212 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import Image from "next/image";
|
|
import {
|
|
X,
|
|
CirclePlus,
|
|
FileText,
|
|
FileSpreadsheet,
|
|
FileType,
|
|
FileImage,
|
|
} from "lucide-react";
|
|
|
|
import clsx from "clsx";
|
|
import { PropsUploadMultipleFile, UploadFileItem } from "./interface";
|
|
|
|
export default function UploadMultipleFile({
|
|
images = [],
|
|
setImages,
|
|
isDisableDelete = false,
|
|
}: PropsUploadMultipleFile) {
|
|
/* =========================
|
|
CHECK IMAGE FILE
|
|
========================= */
|
|
const isImageFile = (item: UploadFileItem) => {
|
|
const type = item?.fileType || "";
|
|
|
|
return (
|
|
type.startsWith("image/") ||
|
|
/\.(jpg|jpeg|png|gif|webp)$/i.test(item?.path || "")
|
|
);
|
|
};
|
|
|
|
/* =========================
|
|
GET FILE ICON
|
|
========================= */
|
|
const renderFileIcon = (item: UploadFileItem) => {
|
|
const fileName = item?.fileName || item?.path?.split("/").pop() || "";
|
|
|
|
const ext = fileName.split(".").pop()?.toLowerCase();
|
|
|
|
switch (ext) {
|
|
case "jpg":
|
|
case "jpeg":
|
|
case "png":
|
|
case "gif":
|
|
case "webp":
|
|
return <FileImage size={28} />;
|
|
|
|
case "pdf":
|
|
return <FileText size={28} />;
|
|
|
|
case "xls":
|
|
case "xlsx":
|
|
return <FileSpreadsheet size={28} />;
|
|
|
|
case "doc":
|
|
case "docx":
|
|
return <FileType size={28} />;
|
|
|
|
case "ppt":
|
|
case "pptx":
|
|
return <FileType size={28} />;
|
|
|
|
case "txt":
|
|
return <FileText size={28} />;
|
|
|
|
default:
|
|
return <FileText size={28} />;
|
|
}
|
|
};
|
|
|
|
/* =========================
|
|
UPLOAD FILES
|
|
========================= */
|
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = event.target.files;
|
|
|
|
if (!files) return;
|
|
|
|
const newFiles: UploadFileItem[] = Array.from(files).map((file) => ({
|
|
file,
|
|
url: URL.createObjectURL(file),
|
|
fileName: file.name,
|
|
fileType: file.type,
|
|
}));
|
|
|
|
setImages((prev) => [...prev, ...newFiles]);
|
|
};
|
|
|
|
/* =========================
|
|
DELETE FILE
|
|
========================= */
|
|
const handleDelete = (index: number) => {
|
|
setImages((prev) => {
|
|
const target = prev[index];
|
|
|
|
if (target?.url) {
|
|
URL.revokeObjectURL(target.url);
|
|
}
|
|
|
|
return prev.filter((_, i) => i !== index);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{/* FILE LIST */}
|
|
{images?.length > 0 && (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{images.map((item, index) => {
|
|
const fileUrl =
|
|
item?.url ||
|
|
(item?.path
|
|
? `${process.env.NEXT_PUBLIC_IMAGE}${item.path}`
|
|
: "");
|
|
|
|
return (
|
|
<div
|
|
key={index}
|
|
className="relative h-[72px] w-[72px] overflow-hidden rounded-md border border-[#99a2b34d]"
|
|
>
|
|
{/* IMAGE */}
|
|
{isImageFile(item) ? (
|
|
<a
|
|
href={fileUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="block h-full w-full"
|
|
>
|
|
<Image
|
|
src={fileUrl}
|
|
alt="file"
|
|
fill
|
|
className="object-cover"
|
|
/>
|
|
</a>
|
|
) : (
|
|
<a
|
|
href={`${process.env.NEXT_PUBLIC_IMAGE}/${fileUrl}`}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="flex h-full w-full flex-col items-center justify-center gap-1 bg-gray-50 p-1"
|
|
>
|
|
{renderFileIcon(item)}
|
|
|
|
<span className="line-clamp-2 text-center text-[9px]">
|
|
{item?.fileName || item?.path?.split("/").pop()}
|
|
</span>
|
|
</a>
|
|
)}
|
|
|
|
{/* DELETE */}
|
|
{isDisableDelete && !item?.file && item?.img ? null : (
|
|
<button
|
|
type="button"
|
|
onClick={() => handleDelete(index)}
|
|
className="absolute right-[2px] top-[2px] flex h-[18px] w-[18px] items-center justify-center rounded-sm bg-white transition active:scale-90"
|
|
>
|
|
<X size={14} color="#8496AC" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
|
|
{/* UPLOAD BUTTON */}
|
|
<div className="flex items-center gap-2">
|
|
<label
|
|
className={clsx(
|
|
"flex h-[72px] w-[72px] cursor-pointer items-center justify-center rounded-md border bg-white",
|
|
"border-[#99a2b34d] transition hover:border-gray-400",
|
|
)}
|
|
>
|
|
<CirclePlus color="rgba(198, 201, 206, 1)" />
|
|
|
|
<input
|
|
hidden
|
|
multiple
|
|
type="file"
|
|
accept="
|
|
image/*,
|
|
.pdf,
|
|
.doc,
|
|
.docx,
|
|
.xls,
|
|
.xlsx,
|
|
.ppt,
|
|
.pptx,
|
|
.txt
|
|
"
|
|
onClick={(e) => {
|
|
(e.target as HTMLInputElement).value = "";
|
|
}}
|
|
onChange={handleFileChange}
|
|
/>
|
|
</label>
|
|
|
|
<div className="flex flex-col">
|
|
<p className="text-[14px] font-medium text-[#2f3643]">Upload file</p>
|
|
|
|
<p className="text-[12px] font-medium text-[#99a2b3]">
|
|
JPG, PNG, PDF, Word, Excel, PPT (≤ 50MB)
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|