feat: add a right-click menu to unibox conversation rows (open in new tab, read state, snooze, labels, archive or move to inbox, delete) that acts on the whole selection from a ticked row, closes the open conversation when it files or marks it unread, and share useAnchoredMenu and the label panel with the scope rail and reader

This commit is contained in:
Matthew Meszaros
2026-09-30 20:39:10 -07:00
parent 41a388e80a
commit a4b85ebcf9
8 changed files with 481 additions and 108 deletions
+31
View File
@@ -0,0 +1,31 @@
// A PopoverMenu that opens under its own trigger on a click and at the pointer
// on a right-click. Spread `menuProps` onto the PopoverMenu and put
// `onContextMenu` on the element the right-click belongs to.
import React from "react";
export type AnchorPoint = { x: number; y: number };
export function useAnchoredMenu() {
const [open, setOpen] = React.useState(false);
const [point, setPoint] = React.useState<AnchorPoint | null>(null);
const onContextMenu = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
// A keyboard context-menu key reports 0,0: open under the element instead.
const r = e.currentTarget.getBoundingClientRect();
setPoint(e.clientX || e.clientY ? { x: e.clientX, y: e.clientY } : { x: r.left + 12, y: r.bottom });
setOpen(true);
};
const menuProps = {
open,
anchorPoint: point,
onOpenChange: (o: boolean) => {
if (o) setPoint(null);
setOpen(o);
},
};
// `fromPointer` tells a right-click from the trigger, for menus that act on
// more than their own row when right-clicked.
return { open, fromPointer: open && point !== null, onContextMenu, menuProps };
}