Default, Hover, Focus and Locked
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
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>