Skip to content

ContextMenu

Molecule

Context menus display a list of context specific actions. They are commonly opened using a right click but could also be opened from buttons, for example a “More” Icon Button.

Properties

ts
export type ContextMenuOpenDirection = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
export type ContextMenuPosition = { x: number; y: number; };

export interface ContextMenuOption {
  id: number;
  name: string;
  action: string;
  icon?: IconName;
  locked?: boolean;
}

export interface ContextMenuProps {
  options: ContextMenuOption[];
  position: ContextMenuPosition;
  openDirection?: ContextMenuOpenDirection;
}

ModelValues

For more information on ModelValues please have a look to the Vue's Data Binding section.

ts
const isOpen = defineModel<boolean>('isOpen', {
  required: true,
  default: false,
});

Playground

Right click on an element of the list to open the Context Menu
or click the button to open the Menu on left click

  • Example 1

  • Example 2

  • Example 3



        
          
< ContextMenu
  isOpen
  options = "[ { "id": 1, "name": "Delete", "action": "Delete", "icon": "close" }, { "id": 2, "name": "Open on New Tab", "action": "Open on New Tab", "icon": "error" }, { "id": 3, "name": "Duplicate", "action": "Duplicate", "icon": "plus", "locked": true } ]"
  openDirection
/>

Anatomy

  • Container: the menu that appears, usually when a user right clicks.
  • Menu Item: Subcomponent, clickable item with an action.
  • Text: describes the action. It must be concise yet inform users on what will happen next. It may use words of phrases. Recommended maximum character count is 32 and a good rule of thumb is not to exceed two words. The first letter of the first word and proper names should be capitalized.
  • Icon : to communicate the action visually and to help draw attention. Icons should clearly communicate their meaning and try to decrease the cognitive load for user.
States

Default, Hover, Focus and Locked

Variants
Opening direction

Can be opened in four directions: top right, top left, bottom right, bottom left.


Basic Example with Event Handlers

vue
<script setup lang="ts">
import { ContextMenu, IconButton, useContextMenu, type ContextMenuOpenDirection } from '@pohlcon/design-system';
import type { IconName } from '@pohlcon/design-system';
import { useId } from 'vue';

const {
  isOpen: contextMenuOpen,
  coordinates: contextMenuPosition,
  open: openContextMenu,
  close: closeContextMenu,
  isTriggerPressed,
  lastOpenDirection
} = useContextMenu();

const openDirection: ContextMenuOpenDirection = 'top-left';

const menuOptions = [
  { id: 1, name: 'Delete', action: 'delete', icon: 'close' as IconName },
  { id: 2, name: 'Open on New Tab', action: 'open-new-tab', icon: 'error' as IconName },
  { id: 3, name: 'Duplicate', action: 'duplicate', icon: 'plus' as IconName, locked: true },
];

const handleContextMenuSelection = (selection: { action: string; id: number | string }) => {
  closeContextMenu();
  // Handle the selected action here
};

const items = [
  { id: '1', name: 'Item 1' },
  { id: '2', name: 'Item 2' },
  { id: '3', name: 'Item 3' },
];

const buttonPressedId = useId(); // or any string | number
</script>

<template>
  <div>
    <ContextMenu 
      v-model:isOpen="contextMenuOpen" 
      :options="menuOptions" 
      :position="contextMenuPosition"
      :openDirection="lastOpenDirection"
      @context-menu-selected="handleContextMenuSelection"
      @close="closeContextMenu"
    />
    <ul>
      <li 
        v-for="item in items" 
        :key="item.id" 
        @contextmenu.prevent="openContextMenu($event, item.id, openDirection)"
      >
        {{ item.name }}
        <IconButton
          icon="menu"
          :pressed="isTriggerPressed(buttonPressedId)"
          @click.stop="openContextMenu($event, buttonPressedId, openDirection)"
        />
      </li>
    </ul>
  </div>
</template>