authorgravatar for jakeleventhal@me.comJake Leventhal <jakeleventhal@me.com> 2026-10-06 19:21:17-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-06 20:21:17-03:00
logd8d037eae1b77a77f372fd1afd2662a31ebe37c8
tree0fa7a62a2d7ee5aa3a64deaa0ace75d1f7432979
parent13aabf3d7d0ec13e902234e86d3df27149c0e4a9
signature Signed by PGP key B5690EEEBB952194

fix(web): open pull request row actions on right-click (#16612)


1 files changed, 29 insertions(+), 3 deletions(-)

apps/web/src/components/pullRequest/ThreadPullRequestsPanel.tsx+29-3
......@@ -11,7 +11,7 @@ import {
1111 MoreHorizontalIcon,
1212 PlusIcon,
1313} from "lucide-react";
14import { useCallback, useMemo } from "react";
14import { useCallback, useMemo, useState } from "react";
1515
1616import { writeTextToClipboard } from "~/hooks/useCopyToClipboard";
1717import { useOpenPrLink } from "~/lib/openPullRequestLink";
......@@ -84,6 +84,15 @@ function LinkRow({
8484 onSetWatching: ((link: ThreadPullRequestLink, watching: boolean) => void) | null;
8585}) {
8686 const openPrLink = useOpenPrLink(threadRef);
87 const [menuOpen, setMenuOpen] = useState(false);
88 const [menuPosition, setMenuPosition] = useState<{ x: number; y: number } | null>(null);
89 const menuAnchor = useMemo(
90 () =>
91 menuPosition
92 ? { getBoundingClientRect: () => new DOMRect(menuPosition.x, menuPosition.y, 0, 0) }
93 : undefined,
94 [menuPosition],
95 );
8796 const { link, depth, stack } = line;
8897 const snapshot = link.snapshot;
8998 const open = snapshot === null || snapshot.state === "open";
......@@ -91,6 +100,12 @@ function LinkRow({
91100 return (
92101 <div
93102 className={cn(PULL_REQUEST_ROW_CLASS, "relative hover:bg-accent/60")}
103 onContextMenu={(event) => {
104 event.preventDefault();
105 event.stopPropagation();
106 setMenuPosition({ x: event.clientX, y: event.clientY });
107 setMenuOpen(true);
108 }}
94109 // Each layer steps in under the one it targets. The step is capped: beyond a few layers
95110 // the indent only says "still in the stack", which the connector line already does, and
96111 // a sixteen-layer stack would otherwise stair-step off the right edge.
......@@ -221,7 +236,13 @@ function LinkRow({
221236 )}
222237 >
223238 <span aria-hidden className="absolute inset-0 bg-accent/60" />
224 <Menu>
239 <Menu
240 open={menuOpen}
241 onOpenChange={(open) => {
242 setMenuOpen(open);
243 if (!open) setMenuPosition(null);
244 }}
245 >
225246 <MenuTrigger
226247 render={
227248 <Button
......@@ -234,7 +255,12 @@ function LinkRow({
234255 </Button>
235256 }
236257 />
237 <MenuPopup align="end" side="bottom">
258 <MenuPopup
259 anchor={menuAnchor}
260 align={menuPosition ? "start" : "end"}
261 side="bottom"
262 sideOffset={menuPosition ? 0 : 4}
263 >
238264 <MenuItem onClick={() => void writeTextToClipboard(link.url, "link")}>
239265 <LinkIcon className="size-3.5" />
240266 Copy link