From 086db34c12e9a742320f2e74a38623c724c751f2 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 25 Jul 2026 09:13:54 +0330 Subject: [PATCH] preview image --- .../task-detail/attachment/AttachmentItem.tsx | 123 +++++++++++++----- .../attachment/isImageAttachment.ts | 10 ++ 2 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 src/pages/taskmanager/components/task-detail/attachment/isImageAttachment.ts diff --git a/src/pages/taskmanager/components/task-detail/attachment/AttachmentItem.tsx b/src/pages/taskmanager/components/task-detail/attachment/AttachmentItem.tsx index 2de91b1..9a17f6e 100644 --- a/src/pages/taskmanager/components/task-detail/attachment/AttachmentItem.tsx +++ b/src/pages/taskmanager/components/task-detail/attachment/AttachmentItem.tsx @@ -1,7 +1,9 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; -import { Document, Link2, More } from "iconsax-react"; -import { type FC } from "react"; +import { CloseCircle, Document, Link2, More } from "iconsax-react"; +import { useEffect, useState, type FC } from "react"; +import { createPortal } from "react-dom"; import { timeAgo } from "../../../../../config/func"; +import { isImageAttachment } from "./isImageAttachment"; type Props = { title: string; @@ -13,41 +15,102 @@ type Props = { }; const AttachmentItem: FC = ({ title, createdAt, type, url, onEdit, onDelete }) => { + const [isPreviewOpen, setIsPreviewOpen] = useState(false); + const isImage = type === "file" && isImageAttachment(url); + + useEffect(() => { + if (!isPreviewOpen) return; + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") setIsPreviewOpen(false); + }; + + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [isPreviewOpen]); + return ( -
-
-
- {type === "file" ? : } + <> +
+
+ {isImage && url ? ( + + ) : ( +
+ {type === "file" ? : } +
+ )} + +
+ {isImage && url ? ( + + ) : url ? ( + + {title} + + ) : ( +
{title}
+ )} + {createdAt &&
{timeAgo(createdAt)}
} +
-
- {url ? ( - - {title} - - ) : ( -
{title}
- )} - {createdAt &&
{timeAgo(createdAt)}
} -
+ + + + + + + + + + +
- - - - + {isPreviewOpen && + url && + createPortal( +
+ +
+ - - - -
+ {title} +
+
, + document.body, + )} + ); }; diff --git a/src/pages/taskmanager/components/task-detail/attachment/isImageAttachment.ts b/src/pages/taskmanager/components/task-detail/attachment/isImageAttachment.ts new file mode 100644 index 0000000..0cc0d87 --- /dev/null +++ b/src/pages/taskmanager/components/task-detail/attachment/isImageAttachment.ts @@ -0,0 +1,10 @@ +const IMAGE_EXTENSIONS = new Set(["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg", "avif"]); + +export const isImageAttachment = (url?: string): boolean => { + if (!url) return false; + + const path = url.split("?")[0].split("#")[0]; + const extension = path.split(".").pop()?.toLowerCase(); + + return !!extension && IMAGE_EXTENSIONS.has(extension); +};