fix(base): replace Popover with Menu for view-tab context menu

The Rename / Delete-view popover (right-click on a view tab) wasn't
closing on outside click or Escape. The container was a <Popover>
with hand-rolled <UnstyledButton> menu items — closeOnClickOutside
and closeOnEscape on Mantine Popover only fire onClose when focus is
inside the dropdown, which never happens here because the popover
opens via a context-menu (focus stays on body) and there's no
trapFocus.

Switch to Mantine <Menu>, which is purpose-built for this pattern:
closeOnClickOutside / closeOnEscape work without focus being inside,
closeOnItemClick removes the manual setMenuOpened(false) wiring on
each item, and the keyboard arrow-key navigation is free.
This commit is contained in:
Philipinho
2026-04-28 18:41:59 +01:00
parent 4b75383460
commit 38f7ffefe0
@@ -6,9 +6,7 @@ import {
ActionIcon, ActionIcon,
Tooltip, Tooltip,
TextInput, TextInput,
Popover, Menu,
Stack,
Divider,
} from "@mantine/core"; } from "@mantine/core";
import { IconPlus, IconPencil, IconTrash, IconTable } from "@tabler/icons-react"; import { IconPlus, IconPencil, IconTrash, IconTable } from "@tabler/icons-react";
import { IBaseView } from "@/features/base/types/base.types"; import { IBaseView } from "@/features/base/types/base.types";
@@ -17,7 +15,6 @@ import {
useDeleteViewMutation, useDeleteViewMutation,
} from "@/features/base/queries/base-view-query"; } from "@/features/base/queries/base-view-query";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import cellClasses from "@/features/base/styles/cells.module.css";
type ViewTabsProps = { type ViewTabsProps = {
views: IBaseView[]; views: IBaseView[];
@@ -166,15 +163,19 @@ function ViewTab({
} }
return ( return (
<Popover <Menu
opened={menuOpened} opened={menuOpened}
onClose={() => setMenuOpened(false)} onChange={setMenuOpened}
position="bottom-start" position="bottom-start"
shadow="md" shadow="md"
width={180} width={180}
withinPortal withinPortal
// Default Menu behavior: closeOnClickOutside, closeOnEscape, and
// closeOnItemClick all true — replaces the manual setMenuOpened
// calls and gives the popover the same outside-click / Esc
// semantics every other Mantine menu in the app has.
> >
<Popover.Target> <Menu.Target>
<UnstyledButton <UnstyledButton
onClick={onClick} onClick={onClick}
onContextMenu={(e) => { onContextMenu={(e) => {
@@ -197,41 +198,27 @@ function ViewTab({
</Text> </Text>
</Group> </Group>
</UnstyledButton> </UnstyledButton>
</Popover.Target> </Menu.Target>
<Popover.Dropdown p={4}> <Menu.Dropdown>
<Stack gap={0}> <Menu.Item
<UnstyledButton leftSection={<IconPencil size={14} />}
className={cellClasses.menuItem} onClick={onRenameStart}
onClick={() => { >
setMenuOpened(false); {t("Rename")}
onRenameStart(); </Menu.Item>
}} {canDelete && (
> <>
<Group gap={8} wrap="nowrap"> <Menu.Divider />
<IconPencil size={14} /> <Menu.Item
<Text size="sm">{t("Rename")}</Text> leftSection={<IconTrash size={14} />}
</Group> onClick={onDelete}
</UnstyledButton> color="red"
{canDelete && ( >
<> {t("Delete view")}
<Divider my={4} /> </Menu.Item>
<UnstyledButton </>
className={cellClasses.menuItem} )}
onClick={() => { </Menu.Dropdown>
setMenuOpened(false); </Menu>
onDelete();
}}
style={{ color: "var(--mantine-color-red-6)" }}
>
<Group gap={8} wrap="nowrap">
<IconTrash size={14} />
<Text size="sm">{t("Delete view")}</Text>
</Group>
</UnstyledButton>
</>
)}
</Stack>
</Popover.Dropdown>
</Popover>
); );
} }