Skip to content

ContextMenu

Right-click activated menu with items, submenus, checkbox and radio items.

NameCategoryDescriptionTypeDefault
maxHeight? StyleMax menu height, scrollable when exceedednumber | string
hoverColor? StyleCustom hover background color for itemsstring
ContextMenuItemdivided? StyleShow divider abovebooleanfalse
disabled? StateWhether to disable context menubooleanfalse
ContextMenuItemdisabled? StateWhether disabledbooleanfalse
ContextMenuCheckboxItemdisabled? StateWhether disabledbooleanfalse
ContextMenuRadioItemdisabled? StateWhether disabledbooleanfalse
ContextMenuSubdisabled? StateWhether disabledbooleanfalse
hideOnClick? BehaviorWhether to close after item clickbooleantrue
lockScroll? BehaviorWhether to lock page scroll when menu is openbooleanfalse
↑ / ↓? BehaviorMove focus between items (skip disabled)
Enter / Space? BehaviorSelect the focused item
? BehaviorOpen submenu
? BehaviorClose submenu, return to parent
Escape? BehaviorClose menu
Home / End? BehaviorJump to first/last enabled item
items? ContentMenu items (shorthand)ContextMenuItemDef[]
ContextMenuItemcommand? ContentCommand identifierstring | number
ContextMenuItemlabel? ContentDisplay textstring | VNode
ContextMenuItemicon? ContentIcon namestring
ContextMenuItemshortcut? ContentShortcut hint, e.g. ["Ctrl", "C"]string[]
ContextMenuCheckboxItemmodelValue / v-model? ContentWhether checkedbooleanfalse
ContextMenuCheckboxItemlabel? ContentDisplay textstring | VNode
ContextMenuRadioGroupmodelValue / v-model? ContentCurrently selected valuestring
ContextMenuRadioItemvalue? ContentOption value (required)string
ContextMenuRadioItemlabel? ContentDisplay textstring | VNode
ContextMenuSublabel? ContentSubmenu trigger textstring | VNode
ContextMenuSubicon? ContentIcon namestring
ContextMenuSubitems? ContentSubmenu items (shorthand)ContextMenuItemDef[]
command? EventTriggered when item is clicked(command: string | number) => void
visible-change? EventTriggered when menu shows/hides(visible: boolean) => void
default? SlotRight-click trigger area
content? SlotMenu content, composed with sub-components
open? ExposeProgrammatically open menu(event: MouseEvent) => void
close? ExposeClose menu() => void

Basic Usage

Right-click the trigger area to open the context menu. Use the items prop for quick setup.

<template>
  <px-context-menu :items="items" @command="handleCommand">
    <div class="demo-area">
      Right-click here
    </div>
  </px-context-menu>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';

const items = [
  { label: 'Cut', command: 'cut' },
  { label: 'Copy', command: 'copy' },
  { label: 'Paste', command: 'paste' },
  { label: 'Delete', command: 'delete', divided: true },
];

const handleCommand = (command: string) => {
  PxMessage.info(`Command: ${command}`);
};
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>

Slot-Based Composition

Use sub-components in the content slot for full control: labels, separators, icons, and keyboard shortcuts.

<template>
  <px-context-menu @command="handleCommand">
    <div class="demo-area">
      Right-click for slot-based menu
    </div>
    <template #content>
      <px-context-menu-label>Edit</px-context-menu-label>
      <px-context-menu-item label="Undo" command="undo" :shortcut="['Ctrl', 'Z']" />
      <px-context-menu-item label="Redo" command="redo" :shortcut="['Ctrl', 'Y']" />
      <px-context-menu-separator />
      <px-context-menu-item label="Cut" command="cut" icon="cut" :shortcut="['Ctrl', 'X']" />
      <px-context-menu-item label="Copy" command="copy" icon="clipboard" :shortcut="['Ctrl', 'C']" />
      <px-context-menu-item label="Paste" command="paste" :shortcut="['Ctrl', 'V']" />
    </template>
  </px-context-menu>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';

const handleCommand = (command: string) => {
  PxMessage.info(`Command: ${command}`);
};
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>

Nest menus with PxContextMenuSub. Submenus open on hover with a slight delay.

<template>
  <px-context-menu @command="handleCommand">
    <div class="demo-area">
      Right-click for submenu
    </div>
    <template #content>
      <px-context-menu-item label="Back" command="back" />
      <px-context-menu-item label="Forward" command="forward" />
      <px-context-menu-item label="Reload" command="reload" :shortcut="['Ctrl', 'R']" />
      <px-context-menu-separator />
      <px-context-menu-sub label="More Tools">
        <px-context-menu-item label="Save Page As..." command="save" :shortcut="['Ctrl', 'S']" />
        <px-context-menu-item label="Create Shortcut" command="shortcut" />
        <px-context-menu-item label="Developer Tools" command="devtools" :shortcut="['F12']" />
      </px-context-menu-sub>
    </template>
  </px-context-menu>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';

const handleCommand = (command: string) => {
  PxMessage.info(`Command: ${command}`);
};
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>

Disabled Items

Set disabled on individual items to prevent interaction.

<template>
  <px-context-menu @command="handleCommand">
    <div class="demo-area">
      Right-click — some items disabled
    </div>
    <template #content>
      <px-context-menu-item label="Cut" command="cut" />
      <px-context-menu-item label="Copy" command="copy" />
      <px-context-menu-item label="Paste" command="paste" disabled />
      <px-context-menu-item label="Delete" command="delete" disabled />
    </template>
  </px-context-menu>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';

const handleCommand = (command: string) => {
  PxMessage.info(`Command: ${command}`);
};
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>

Checkbox Items

Use PxContextMenuCheckboxItem with v-model for toggle items. Set hide-on-click to false to keep the menu open.

<template>
  <px-context-menu :hide-on-click="false">
    <div class="demo-area">
      Right-click for checkbox items
    </div>
    <template #content>
      <px-context-menu-label>View</px-context-menu-label>
      <px-context-menu-checkbox-item v-model="showToolbar" label="Toolbar" />
      <px-context-menu-checkbox-item v-model="showStatusBar" label="Status Bar" />
      <px-context-menu-checkbox-item v-model="showMinimap" label="Minimap" />
    </template>
  </px-context-menu>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const showToolbar = ref(true);
const showStatusBar = ref(true);
const showMinimap = ref(false);
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>

Radio Items

Wrap PxContextMenuRadioItem in a PxContextMenuRadioGroup with v-model for exclusive selection.

<template>
  <px-context-menu :hide-on-click="false">
    <div class="demo-area">
      Right-click for radio items
    </div>
    <template #content>
      <px-context-menu-label>Theme</px-context-menu-label>
      <px-context-menu-radio-group v-model="theme">
        <px-context-menu-radio-item label="Light" value="light" />
        <px-context-menu-radio-item label="Dark" value="dark" />
        <px-context-menu-radio-item label="System" value="system" />
      </px-context-menu-radio-group>
    </template>
  </px-context-menu>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const theme = ref('light');
</script>

<style scoped>
.demo-area {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 120px;
  border: 2px dashed var(--px-border-color);
  font-family: var(--px-font-family);
  color: var(--px-text-color-secondary);
}
</style>