0
# Vue3 Context Menu
1
2
Vue3 Context Menu is a comprehensive context menu component library for Vue 3 applications that enables developers to create customizable right-click menus with rich functionality. It offers both functional and component-based APIs for displaying context menus, supports nested submenus and separators, provides multiple built-in themes with both light and dark variants, and includes keyboard navigation support.
3
4
## Package Information
5
6
- **Package Name**: @imengyu/vue3-context-menu
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @imengyu/vue3-context-menu`
10
11
## Core Imports
12
13
```typescript
14
import ContextMenu from '@imengyu/vue3-context-menu';
15
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css';
16
17
// For functional API
18
import ContextMenu from '@imengyu/vue3-context-menu';
19
20
// For component imports
21
import {
22
ContextMenu as ContextMenuComponent,
23
ContextMenuItem,
24
ContextMenuSeparator,
25
ContextMenuGroup,
26
MenuBar
27
} from '@imengyu/vue3-context-menu';
28
```
29
30
For CommonJS:
31
32
```javascript
33
const ContextMenu = require('@imengyu/vue3-context-menu');
34
require('@imengyu/vue3-context-menu/lib/vue3-context-menu.css');
35
```
36
37
## Basic Usage
38
39
### Installation and Setup
40
41
```typescript
42
import { createApp } from 'vue';
43
import ContextMenu from '@imengyu/vue3-context-menu';
44
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css';
45
46
const app = createApp(App);
47
app.use(ContextMenu);
48
```
49
50
### Quick Example - Functional API
51
52
```typescript
53
import ContextMenu from '@imengyu/vue3-context-menu';
54
55
function onContextMenu(e: MouseEvent) {
56
e.preventDefault();
57
58
ContextMenu.showContextMenu({
59
x: e.x,
60
y: e.y,
61
items: [
62
{
63
label: "Copy",
64
icon: "copy-icon",
65
onClick: () => console.log("Copy clicked")
66
},
67
{
68
label: "Edit",
69
children: [
70
{ label: "Cut", onClick: () => console.log("Cut clicked") },
71
{ label: "Paste", onClick: () => console.log("Paste clicked") },
72
]
73
},
74
{ divided: true, label: "Delete", onClick: () => console.log("Delete clicked") }
75
]
76
});
77
}
78
```
79
80
### Quick Example - Component API
81
82
```vue
83
<template>
84
<div @contextmenu="onRightClick">
85
<context-menu v-model:show="show" :options="menuOptions">
86
<context-menu-item label="Copy" icon="copy-icon" @click="handleCopy" />
87
<context-menu-separator />
88
<context-menu-group label="Edit">
89
<context-menu-item label="Cut" @click="handleCut" />
90
<context-menu-item label="Paste" @click="handlePaste" />
91
</context-menu-group>
92
</context-menu>
93
</div>
94
</template>
95
96
<script setup>
97
import { ref } from 'vue';
98
99
const show = ref(false);
100
const menuOptions = ref({
101
x: 0,
102
y: 0,
103
zIndex: 1000
104
});
105
106
function onRightClick(e) {
107
e.preventDefault();
108
menuOptions.value.x = e.x;
109
menuOptions.value.y = e.y;
110
show.value = true;
111
}
112
</script>
113
```
114
115
## Architecture
116
117
Vue3 Context Menu is built around several key components:
118
119
- **Functional API**: Global methods for programmatic menu display (`showContextMenu`, `closeContextMenu`)
120
- **Component API**: Vue components for declarative menu construction (`ContextMenu`, `ContextMenuItem`, etc.)
121
- **Theme System**: Built-in themes (default, flat, win10, mac) with light/dark variants
122
- **Position System**: Automatic position adjustment to prevent menu overflow
123
- **Keyboard Navigation**: Full keyboard control support matching Windows menu behavior
124
- **Menu Bar**: Traditional horizontal menu bar component for desktop-style applications
125
126
## Capabilities
127
128
### Functional API
129
130
Core programmatic interface for displaying context menus with full configuration control. Perfect for dynamic menus and event-driven scenarios.
131
132
```typescript { .api }
133
interface ContextMenuGlobal {
134
showContextMenu(options: MenuOptions, customSlots?: Record<string, Slot>): ContextMenuInstance;
135
closeContextMenu(): void;
136
isAnyContextMenuOpen(): boolean;
137
transformMenuPosition(element: HTMLElement, x: number, y: number, container?: HTMLElement): { x: number, y: number };
138
}
139
140
interface MenuOptions {
141
/** Menu items array */
142
items?: MenuItem[];
143
/** Menu display X position */
144
x: number;
145
/** Menu display Y position */
146
y: number;
147
/** X-coordinate offset of submenu and parent menu */
148
xOffset?: number;
149
/** Y-coordinate offset of submenu and parent menu */
150
yOffset?: number;
151
/** Theme name (default, flat, win10, mac, with optional 'dark' suffix) */
152
theme?: string;
153
/** Menu pop-up direction relative to coordinates */
154
direction?: MenuPopDirection;
155
/** Z-index for menu display */
156
zIndex?: number;
157
/** Menu zoom level */
158
zoom?: number;
159
/** Custom CSS class for menu */
160
customClass?: string;
161
/** Enable mouse scroll wheel in menu area */
162
mouseScroll?: boolean;
163
/** Provide space placeholder for up/down buttons */
164
updownButtonSpaceholder?: boolean;
165
/** Element class name to ignore clicks */
166
ignoreClickClassName?: string;
167
/** Close menu when clicking outside */
168
clickCloseOnOutside?: boolean;
169
/** Element class name that closes menu when clicked */
170
clickCloseClassName?: string;
171
/** Custom icon library font class name */
172
iconFontClass?: string;
173
/** Vue Transition props for menu show/hide */
174
menuTransitionProps?: TransitionProps;
175
/** Reserve fixed-width icon area for items without icon */
176
preserveIconWidth?: boolean;
177
/** Enable keyboard control */
178
keyboardControl?: boolean;
179
/** Maximum width of menu (pixels) */
180
maxWidth?: number;
181
/** Maximum height of menu (pixels) */
182
maxHeight?: number;
183
/** Minimum width of menu (pixels) */
184
minWidth?: number;
185
/** Close when user scrolls */
186
closeWhenScroll?: boolean;
187
/** Padding for submenu position adjustment */
188
adjustPadding?: { x: number, y: number } | number;
189
/** Automatically adjust position to prevent overflow */
190
adjustPosition?: boolean;
191
/** Container element for menu mounting */
192
getContainer?: HTMLElement | (() => HTMLElement);
193
/** Event when menu is closing */
194
onClose?: (lastClickItem: MenuItem | undefined) => void;
195
/** Event when clicking outside (when clickCloseOnOutside is false) */
196
onClickOnOutside?: (e: MouseEvent) => void;
197
/** Event for MenuBar left focus move */
198
onKeyFocusMoveLeft?: () => void;
199
/** Event for MenuBar right focus move */
200
onKeyFocusMoveRight?: () => void;
201
}
202
```
203
204
[Functional API](./functional-api.md)
205
206
### Component API
207
208
Vue components for declarative menu construction with full template integration. Ideal for static menus and component-based architectures.
209
210
```typescript { .api }
211
// Main context menu component
212
declare component ContextMenu {
213
props: {
214
options: MenuOptions;
215
show: boolean;
216
};
217
events: {
218
'update:show': (show: boolean) => void;
219
'close': () => void;
220
};
221
}
222
223
// Menu item component
224
declare component ContextMenuItem {
225
props: {
226
label?: string;
227
icon?: string;
228
disabled?: boolean;
229
hidden?: boolean;
230
checked?: boolean;
231
shortcut?: string;
232
};
233
events: {
234
'click': (e: MouseEvent | KeyboardEvent) => void;
235
};
236
}
237
238
// Menu separator component
239
declare component ContextMenuSeparator {
240
// No props
241
}
242
243
// Menu group component for nested submenus
244
declare component ContextMenuGroup {
245
props: {
246
label: string;
247
};
248
// Contains slot for child menu items
249
}
250
```
251
252
[Component API](./component-api.md)
253
254
### Menu Bar
255
256
Traditional horizontal menu bar component for desktop-style applications with dropdown menus.
257
258
```typescript { .api }
259
declare component MenuBar {
260
props: {
261
options: MenuBarOptions;
262
};
263
}
264
265
interface MenuBarOptions extends Omit<MenuOptions, 'x'|'y'|'getContainer'> {
266
mini?: boolean;
267
barPopDirection?: MenuPopDirection;
268
}
269
```
270
271
[Menu Bar](./menu-bar.md)
272
273
### Themes and Styling
274
275
Built-in theme system with light and dark variants for different UI styles.
276
277
```typescript { .api }
278
type ThemeNames =
279
| 'default'
280
| 'default dark'
281
| 'flat'
282
| 'flat dark'
283
| 'win10'
284
| 'win10 dark'
285
| 'mac'
286
| 'mac dark';
287
```
288
289
[Themes and Styling](./themes.md)
290
291
## Types
292
293
### Vue Types
294
295
```typescript { .api }
296
// Vue framework types used throughout the API
297
interface VNode {
298
// Vue virtual node for custom renders
299
}
300
301
interface ComputedRef<T> {
302
// Vue computed reference
303
readonly value: T;
304
}
305
306
interface Slot {
307
// Vue slot function
308
(...args: any[]): VNode[];
309
}
310
```
311
312
### Core Types
313
314
```typescript { .api }
315
type MenuPopDirection = 'br'|'b'|'bl'|'tr'|'t'|'tl'|'l'|'r';
316
317
interface MenuItem {
318
/** Menu item label */
319
label?: string | VNode | ((label: string) => VNode);
320
/** Menu item icon */
321
icon?: string | VNode | ((icon: string) => VNode);
322
/** Custom icon font class name */
323
iconFontClass?: string;
324
/** Reserve fixed-width icon area */
325
preserveIconWidth?: boolean;
326
/** SVG symbol icon reference */
327
svgIcon?: string;
328
/** SVG element properties */
329
svgProps?: SVGAttributes;
330
/** Disable menu item */
331
disabled?: boolean | ComputedRef<boolean>;
332
/** Hide menu item */
333
hidden?: boolean | ComputedRef<boolean>;
334
/** Show check mark */
335
checked?: boolean | ComputedRef<boolean>;
336
/** Shortcut key display text */
337
shortcut?: string;
338
/** Submenu popup direction */
339
direction?: MenuPopDirection;
340
/** Adjust submenu position */
341
adjustSubMenuPosition?: boolean;
342
/** Allow click when has children */
343
clickableWhenHasChildren?: boolean;
344
/** Close menu on click */
345
clickClose?: boolean;
346
/** Separator display option */
347
divided?: boolean | 'up' | 'down' | 'self';
348
/** Custom CSS class */
349
customClass?: string;
350
/** Maximum height in pixels */
351
maxHeight?: number;
352
/** Maximum width in pixels */
353
maxWidth?: number | string;
354
/** Minimum width in pixels */
355
minWidth?: number | string;
356
/** Click event handler */
357
onClick?: (e?: MouseEvent | KeyboardEvent) => void;
358
/** Submenu close event */
359
onSubMenuClose?: (itemInstance?: MenuItemContext) => void;
360
/** Submenu open event */
361
onSubMenuOpen?: (itemInstance?: MenuItemContext) => void;
362
/** Custom render function */
363
customRender?: VNode | ((item: MenuItem) => VNode);
364
/** Child menu items */
365
children?: MenuItem[];
366
}
367
368
interface ContextMenuInstance {
369
/** Close the menu */
370
closeMenu(fromItem?: MenuItem | undefined): void;
371
/** Check if menu is closed */
372
isClosed(): boolean;
373
/** Get root menu instance */
374
getMenuRef(): ContextSubMenuInstance | undefined;
375
/** Get menu dimensions */
376
getMenuDimensions(): { width: number, height: number };
377
}
378
379
interface ContextSubMenuInstance {
380
/** Get submenu root element */
381
getSubmenuRoot(): HTMLElement | undefined;
382
/** Get menu container element */
383
getMenu(): HTMLElement | undefined;
384
/** Get child menu item by index */
385
getChildItem(index: number): MenuItemContext | undefined;
386
/** Get menu dimensions */
387
getMenuDimensions(): { width: number, height: number };
388
/** Get current scroll value */
389
getScrollValue(): number;
390
/** Set scroll value */
391
setScrollValue(v: number): void;
392
/** Get scroll height */
393
getScrollHeight(): number;
394
/** Force adjust position */
395
adjustPosition(): void;
396
/** Get maximum height */
397
getMaxHeight(): number;
398
/** Get current position */
399
getPosition(): { x: number, y: number };
400
/** Set position */
401
setPosition(x: number, y: number): void;
402
}
403
404
interface MenuItemContext {
405
/** Get submenu instance */
406
getSubMenuInstance(): ContextSubMenuInstance | undefined;
407
/** Show submenu */
408
showSubMenu(): boolean;
409
/** Hide submenu */
410
hideSubMenu(): void;
411
/** Get HTML element */
412
getElement(): HTMLElement | undefined;
413
/** Check if disabled or hidden */
414
isDisabledOrHidden(): boolean;
415
/** Focus item */
416
focus(): void;
417
/** Blur item */
418
blur(): void;
419
/** Click item */
420
click(e: MouseEvent | KeyboardEvent): void;
421
}
422
423
interface SVGAttributes {
424
[key: string]: any;
425
}
426
427
interface TransitionProps {
428
name?: string;
429
mode?: 'in-out' | 'out-in' | 'default';
430
appear?: boolean;
431
css?: boolean;
432
type?: 'transition' | 'animation';
433
duration?: number | { enter: number; leave: number };
434
enterFromClass?: string;
435
enterActiveClass?: string;
436
enterToClass?: string;
437
appearFromClass?: string;
438
appearActiveClass?: string;
439
appearToClass?: string;
440
leaveFromClass?: string;
441
leaveActiveClass?: string;
442
leaveToClass?: string;
443
}
444
```