From ebb3cecd97ab1ed3a09bacb3afa3652c34bf376e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 10:06:20 +0000 Subject: [PATCH] refactor(web): share one PointerMenu for the space and section menus The space and section context menus repeated the same dropdown with an invisible trigger anchored at the pointer; only their label and items differ. Move-only: PointerMenu in pages/shell renders that dropdown and both menus pass their label, close handler and items. DOM, attributes and handlers are unchanged, and the labels stay in ShellPage, so the catalogs do not change. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --- apps/web/src/pages/Shell.tsx | 105 +++++++--------------- apps/web/src/pages/shell/pointer-menu.tsx | 41 +++++++++ 2 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 apps/web/src/pages/shell/pointer-menu.tsx diff --git a/apps/web/src/pages/Shell.tsx b/apps/web/src/pages/Shell.tsx index 2f9b136..e846e08 100644 --- a/apps/web/src/pages/Shell.tsx +++ b/apps/web/src/pages/Shell.tsx @@ -72,10 +72,7 @@ import { AvatarStyleProvider, BotAvatar, Button, - DropdownMenu, - DropdownMenuContent, DropdownMenuItem, - DropdownMenuTrigger, GroupAvatar, type GroupAvatarMember, InputGroup, @@ -214,6 +211,7 @@ import { } from "./shell/dialogs"; import { FullscreenComputerOverlay } from "./shell/fullscreen-computer-overlay"; import { MainConversationRow } from "./shell/main-conversation-row"; +import { PointerMenu } from "./shell/pointer-menu"; import { cleanRosterPreview, formatRosterTime, stripSpaceTitlePrefix } from "./shell/roster-format"; import { rosterRowClass } from "./shell/roster-row-style"; import { ProjectRosterRow, TeamRosterRow } from "./shell/roster-rows"; @@ -4552,43 +4550,23 @@ export function ShellPage() { ) : null} {spaceMenu ? ( - { - if (!open) closeSpaceMenu(); - }} + - {/* Invisible anchor at the pointer position, mirroring the bot menu. */} - - } - /> - { + const target = spaces.find((space) => space.id === spaceMenu.id); + if (target) setDeleteSpaceTarget(target); + setSpaceMenu(null); + }} > - { - const target = spaces.find((space) => space.id === spaceMenu.id); - if (target) setDeleteSpaceTarget(target); - setSpaceMenu(null); - }} - > - - {t`Delete space`} - - - + + {t`Delete space`} + + ) : null} {deleteTarget ? ( @@ -4679,43 +4657,24 @@ export function ShellPage() { ) : null} {sectionMenu ? ( - { - if (!open) closeSectionMenu(); - }} + - - } - /> - { + setRenameSectionTarget({ + section: sectionMenu.section, + spaceId: sectionMenu.spaceId, + }); + setSectionMenu(null); + }} > - { - setRenameSectionTarget({ - section: sectionMenu.section, - spaceId: sectionMenu.spaceId, - }); - setSectionMenu(null); - }} - > - - {t`Rename section`} - - - + + {t`Rename section`} + + ) : null} void; + children: ReactNode; +}) { + return ( + { + if (!open) onClose(); + }} + > + {/* Invisible anchor at the pointer position, mirroring the bot menu. */} + + } + /> + + {children} + + + ); +}