ContextMenu
A MenuList opened by a right-click over its target, at the pointer - or, from the keyboard, by the context-menu key or Shift+F10 at the focused element.
A MenuList opened by a right-click over its target, at the pointer - or,
from the keyboard, by the context-menu key or Shift+F10 at the focused element. It is placed and
closes as Menu does, and gives focus back to where it was. The browser's own
context menu is held back only when this one opens.
import { ContextMenu } from '@soroush.tech/design-system/Menu'
import { MenuItem } from '@soroush.tech/design-system/MenuItem'
;<ContextMenu target={<Canvas />} aria-label="Canvas" onSelect={run}>
<MenuItem value="paste" shortcut="Control+V">
Paste
</MenuItem>
<MenuItem value="select-all">Select all</MenuItem>
</ContextMenu>
Rows can come as data instead, with items - the same entries
MenuItems renders:
<ContextMenu
target={<Canvas />}
items={[
{ value: 'paste', label: 'Paste', shortcut: 'Control+V' },
{ type: 'divider' },
{ label: 'Align', submenu: [{ value: 'left', label: 'Left' }] },
]}
/>
Demos
For usage examples, visit the component demo page: Menu
Import
import { ContextMenu } from '@soroush.tech/design-system/Menu'Props
| Prop | Type | Default | Description |
|---|---|---|---|
target | ReactElement | - | The element whose right-click opens the menu. Its own onContextMenu / onKeyDown still run. Optional when the app opens the menu with isOpen and anchorPosition. |
isOpen | boolean | - | Whether it is open, when the app decides: it opens at anchorPosition, asks to close through onClose, and a right-click on target is the app's. |
anchorPosition | { top, left } | - | With isOpen: a point in the window (client coordinates, as Popover's) to open at. A changed point moves the open list. Without one it stays closed. |
children | ReactNode | - | MenuItem rows and MenuDividers. |
items | MenuItemData[] | - | The rows as data, in place of children. |
onClose | (event, reason: MenuCloseReason) => void | - | Fired when the menu closes: 'escapeKey', 'select' or 'blur'. |
onSelect | (value: string | number) => void | - | Fired with a row's value when a row that has one is chosen. |
marginThreshold | keyof Theme['space'] | 2 | The least room between the list and the window's edge. |
elevation | PaperElevation (0-24) | 8 | Shadow depth of the surface. |
borderRadius | keyof Theme['radii'] | theme.defaults.surfaceRadius, else square | Corner radius of the list. |
layer | keyof Theme['zOrder'] | 'modal' | Stacking layer. |
emptyLabel | ReactNode | - | Shown as one disabled row when the menu has no rows; without it, the list stays empty. |
color | keyof Theme['palette'] | - | Accent color for every row. |
textColor | keyof Theme['text'] | - | Base text color for every row. |
size | keyof Theme['sizes'] | - | Density token for every row. |
dense | boolean | - | Compact vertical padding for every row. |
aria-label | string | - | Names the list. |
className | string | - | Class on the menu's root. |
slotProps | { list? } | - | Props for a part: list is the role="menu" list - and every submenu's - for the app's data-*, class and aria-*; an onKeyDown runs before the list's own. Its role, id and name are the menu's. |
Notes
- Placement. Its top-left corner at the pointer (top-right, right to left), moved above or to
the other side where the window has no room, and kept
marginThresholdinside it. From the keyboard it opens at the focused element's bottom start corner. - Layout. The root is
display: contents, so wrapping a target changes nothing about how it lays out. Its surface has apaperslot of its own, overMenu's:theme.components.Menu.styleOverrides.paperstyles every menu's surface,theme.components.ContextMenu.styleOverrides.paperonly this one's. - Submenus, the keys inside the list and focus following the pointer are the same as in a
Menu.
Source code
If this page does not answer your question, have a look at the implementation of the component for more detail.