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

PropTypeDefaultDescription
targetReactElement-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.
isOpenboolean-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.
childrenReactNode-MenuItem rows and MenuDividers.
itemsMenuItemData[]-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.
marginThresholdkeyof Theme['space']2The least room between the list and the window's edge.
elevationPaperElevation (0-24)8Shadow depth of the surface.
borderRadiuskeyof Theme['radii']theme.defaults.surfaceRadius, else squareCorner radius of the list.
layerkeyof Theme['zOrder']'modal'Stacking layer.
emptyLabelReactNode-Shown as one disabled row when the menu has no rows; without it, the list stays empty.
colorkeyof Theme['palette']-Accent color for every row.
textColorkeyof Theme['text']-Base text color for every row.
sizekeyof Theme['sizes']-Density token for every row.
denseboolean-Compact vertical padding for every row.
aria-labelstring-Names the list.
classNamestring-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 marginThreshold inside 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 a paper slot of its own, over Menu's: theme.components.Menu.styleOverrides.paper styles every menu's surface, theme.components.ContextMenu.styleOverrides.paper only 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.