diff --git a/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx b/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx index e7088fbc8b..48069020d9 100644 --- a/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx +++ b/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx @@ -725,9 +725,9 @@ export function CommentFileLinks({ context }: PluginCommentAnnotationProps) { // --------------------------------------------------------------------------- /** - * Per-comment context menu item that appears in the comment "more" (⋮) menu. - * Extracts file paths from the comment body and, if any are found, renders - * a button to open the first file in the project Files tab. + * Per-comment context menu item that appears in the comment header area. + * Renders a "Files" trigger button that opens a dropdown listing all + * file paths extracted from the comment body. * * Respects the `commentAnnotationMode` instance config — hidden when mode * is `"annotation"` or `"none"`. @@ -742,6 +742,27 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) companyId: context.companyId, }); + const [open, setOpen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!open) return; + function handleClickOutside(e: globalThis.MouseEvent) { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setOpen(false); + } + } + function handleKeyDown(e: globalThis.KeyboardEvent) { + if (e.key === "Escape") setOpen(false); + } + document.addEventListener("mousedown", handleClickOutside); + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + document.removeEventListener("keydown", handleKeyDown); + }; + }, [open]); + if (mode === "annotation" || mode === "none") return null; if (!data?.links?.length) return null; @@ -749,25 +770,54 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) const projectId = context.projectId; return ( -
-
- Files -
- {data.links.map((link) => { - const href = buildFileBrowserHref(prefix, projectId, link); - const fileName = link.split("/").pop() ?? link; - return ( - navigateToFileBrowser(href, e)} - className="flex w-full items-center gap-2 rounded px-2 py-1 text-xs text-foreground hover:bg-accent transition-colors" - title={`Open ${link} in file browser`} - > - {fileName} - - ); - })} +
+ + + {open && ( +
+
+ Referenced files +
+ {data.links.map((link) => { + const href = buildFileBrowserHref(prefix, projectId, link); + const fileName = link.split("/").pop() ?? link; + return ( + { navigateToFileBrowser(href, e); setOpen(false); }} + title={link} + className="flex items-center gap-2 px-2 py-1.5 rounded-md text-xs text-foreground no-underline transition-colors hover:bg-accent" + > + + {fileName} + {fileName !== link && ( + + {link} + + )} + + ); + })} +
+ )}
); }