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>