0
# Context Menu System
1
2
Customizable right-click context menus with support for custom items, submenus, and conditional visibility. The context menu plugin provides an intuitive way to access tree operations and custom actions through right-click interactions.
3
4
## Capabilities
5
6
### Context Menu Configuration
7
8
Configuration options for the context menu plugin behavior.
9
10
```javascript { .api }
11
/**
12
* Context menu plugin configuration
13
*/
14
interface ContextMenuConfig {
15
/** Select node when showing context menu (default: true) */
16
select_node?: boolean;
17
/** Show menu at node position vs cursor position (default: true) */
18
show_at_node?: boolean;
19
/** Menu items configuration */
20
items?: object|function;
21
}
22
23
interface ContextMenuItem {
24
/** Display label */
25
label?: string;
26
/** Icon class */
27
icon?: string;
28
/** Action function */
29
action?: function;
30
/** Submenu items */
31
submenu?: {[key: string]: ContextMenuItem};
32
/** Separator before item */
33
separator_before?: boolean;
34
/** Separator after item */
35
separator_after?: boolean;
36
/** Conditional visibility */
37
visible?: boolean|function;
38
/** Conditional enablement */
39
disabled?: boolean|function;
40
/** Custom CSS class name */
41
_class?: string;
42
/** Shortcut key display */
43
shortcut?: string;
44
/** Shortcut key combination */
45
shortcut_label?: string;
46
}
47
48
// Usage in tree initialization
49
const config = {
50
"plugins": ["contextmenu"],
51
"contextmenu": {
52
"select_node": true,
53
"show_at_node": true,
54
"items": contextMenuBuilder
55
}
56
};
57
```
58
59
**Usage Examples:**
60
61
```javascript
62
// Initialize tree with context menu
63
$("#tree").jstree({
64
"core": {
65
"data": ["Item 1", "Item 2", "Folder"],
66
"check_callback": true
67
},
68
"plugins": ["contextmenu"],
69
"contextmenu": {
70
"items": function(node) {
71
return {
72
"create": {
73
"label": "Create",
74
"action": function(data) {
75
const inst = $.jstree.reference(data.reference);
76
const obj = inst.get_node(data.reference);
77
inst.create_node(obj, {}, "last", function(new_node) {
78
setTimeout(function() { inst.edit(new_node); }, 0);
79
});
80
}
81
}
82
};
83
}
84
}
85
});
86
87
// Get instance for context menu operations
88
const tree = $("#tree").jstree(true);
89
```
90
91
### Context Menu Methods
92
93
Methods for programmatically controlling context menus.
94
95
```javascript { .api }
96
/**
97
* Show context menu programmatically
98
* @param obj - Node to show menu for
99
* @param x - X coordinate (optional)
100
* @param y - Y coordinate (optional)
101
* @param e - Original event (optional)
102
*/
103
show_contextmenu(obj: string|object, x?: number, y?: number, e?: Event): void;
104
```
105
106
**Usage Examples:**
107
108
```javascript
109
// Show context menu programmatically
110
tree.show_contextmenu("node_1");
111
112
// Show at specific coordinates
113
tree.show_contextmenu("node_1", 100, 200);
114
115
// Show context menu on custom trigger
116
$("#custom-button").click(function() {
117
const selectedNodes = tree.get_selected();
118
if (selectedNodes.length > 0) {
119
tree.show_contextmenu(selectedNodes[0]);
120
}
121
});
122
```
123
124
### Static Menu Items
125
126
Configuration for static menu items that don't change based on context.
127
128
```javascript { .api }
129
/**
130
* Static menu items configuration
131
*/
132
interface StaticMenuItems {
133
[key: string]: ContextMenuItem;
134
}
135
```
136
137
**Usage Examples:**
138
139
```javascript
140
// Static menu items
141
$("#tree").jstree({
142
"plugins": ["contextmenu"],
143
"contextmenu": {
144
"items": {
145
"create": {
146
"label": "Create",
147
"icon": "fa fa-plus",
148
"action": function(data) {
149
const inst = $.jstree.reference(data.reference);
150
const obj = inst.get_node(data.reference);
151
inst.create_node(obj, {}, "last", function(new_node) {
152
inst.edit(new_node);
153
});
154
}
155
},
156
"rename": {
157
"label": "Rename",
158
"icon": "fa fa-edit",
159
"shortcut": "F2",
160
"action": function(data) {
161
const inst = $.jstree.reference(data.reference);
162
const obj = inst.get_node(data.reference);
163
inst.edit(obj);
164
}
165
},
166
"delete": {
167
"label": "Delete",
168
"icon": "fa fa-trash",
169
"shortcut": "Del",
170
"separator_before": true,
171
"action": function(data) {
172
const inst = $.jstree.reference(data.reference);
173
const obj = inst.get_node(data.reference);
174
if (confirm("Delete " + obj.text + "?")) {
175
inst.delete_node(obj);
176
}
177
}
178
}
179
}
180
}
181
});
182
```
183
184
### Dynamic Menu Items
185
186
Configuration for menu items that change based on the selected node or context.
187
188
```javascript { .api }
189
/**
190
* Dynamic menu items function
191
* @param node - Node that was right-clicked
192
* @returns Menu items object
193
*/
194
type DynamicMenuFunction = (node: object) => {[key: string]: ContextMenuItem};
195
```
196
197
**Usage Examples:**
198
199
```javascript
200
// Dynamic menu based on node type
201
$("#tree").jstree({
202
"plugins": ["contextmenu", "types"],
203
"contextmenu": {
204
"items": function(node) {
205
const items = {};
206
207
// Common items for all nodes
208
items.rename = {
209
"label": "Rename",
210
"action": function(data) {
211
const inst = $.jstree.reference(data.reference);
212
inst.edit(data.reference);
213
}
214
};
215
216
// Type-specific items
217
if (node.type === "folder") {
218
items.create_folder = {
219
"label": "New Folder",
220
"icon": "fa fa-folder",
221
"action": function(data) {
222
const inst = $.jstree.reference(data.reference);
223
inst.create_node(data.reference, {
224
"text": "New Folder",
225
"type": "folder"
226
}, "last", function(new_node) {
227
inst.edit(new_node);
228
});
229
}
230
};
231
232
items.create_file = {
233
"label": "New File",
234
"icon": "fa fa-file",
235
"action": function(data) {
236
const inst = $.jstree.reference(data.reference);
237
inst.create_node(data.reference, {
238
"text": "New File",
239
"type": "file"
240
}, "last", function(new_node) {
241
inst.edit(new_node);
242
});
243
}
244
};
245
}
246
247
// Permission-based items
248
if (hasDeletePermission(node)) {
249
items.delete = {
250
"label": "Delete",
251
"icon": "fa fa-trash",
252
"separator_before": true,
253
"action": function(data) {
254
if (confirm("Delete this item?")) {
255
const inst = $.jstree.reference(data.reference);
256
inst.delete_node(data.reference);
257
}
258
}
259
};
260
}
261
262
return items;
263
}
264
}
265
});
266
```
267
268
### Submenus
269
270
Configuration for nested submenu items.
271
272
```javascript { .api }
273
/**
274
* Submenu configuration
275
*/
276
interface SubmenuConfig {
277
submenu: {[key: string]: ContextMenuItem};
278
}
279
```
280
281
**Usage Examples:**
282
283
```javascript
284
// Context menu with submenus
285
$("#tree").jstree({
286
"plugins": ["contextmenu"],
287
"contextmenu": {
288
"items": {
289
"create": {
290
"label": "Create",
291
"icon": "fa fa-plus",
292
"submenu": {
293
"folder": {
294
"label": "Folder",
295
"icon": "fa fa-folder",
296
"action": function(data) {
297
const inst = $.jstree.reference(data.reference);
298
inst.create_node(data.reference, {
299
"text": "New Folder",
300
"type": "folder"
301
});
302
}
303
},
304
"file": {
305
"label": "File",
306
"icon": "fa fa-file",
307
"action": function(data) {
308
const inst = $.jstree.reference(data.reference);
309
inst.create_node(data.reference, {
310
"text": "New File",
311
"type": "file"
312
});
313
}
314
},
315
"separator": {
316
"separator_before": true
317
},
318
"template": {
319
"label": "From Template",
320
"icon": "fa fa-copy",
321
"submenu": {
322
"html": {
323
"label": "HTML File",
324
"action": function(data) {
325
createFromTemplate(data.reference, "html");
326
}
327
},
328
"css": {
329
"label": "CSS File",
330
"action": function(data) {
331
createFromTemplate(data.reference, "css");
332
}
333
}
334
}
335
}
336
}
337
},
338
"edit": {
339
"label": "Edit",
340
"submenu": {
341
"rename": {
342
"label": "Rename",
343
"action": function(data) {
344
const inst = $.jstree.reference(data.reference);
345
inst.edit(data.reference);
346
}
347
},
348
"properties": {
349
"label": "Properties",
350
"action": function(data) {
351
showProperties(data.reference);
352
}
353
}
354
}
355
}
356
}
357
}
358
});
359
```
360
361
### Conditional Menu Items
362
363
Configuration for menu items with conditional visibility and enablement.
364
365
```javascript { .api }
366
/**
367
* Conditional visibility function
368
* @param key - Menu item key
369
* @param opt - Menu options
370
* @returns True if item should be visible
371
*/
372
type VisibilityFunction = (key: string, opt: object) => boolean;
373
374
/**
375
* Conditional enablement function
376
* @param key - Menu item key
377
* @param opt - Menu options
378
* @returns True if item should be enabled
379
*/
380
type DisabledFunction = (key: string, opt: object) => boolean;
381
```
382
383
**Usage Examples:**
384
385
```javascript
386
// Conditional menu items
387
$("#tree").jstree({
388
"plugins": ["contextmenu"],
389
"contextmenu": {
390
"items": function(node) {
391
return {
392
"cut": {
393
"label": "Cut",
394
"action": function(data) {
395
const inst = $.jstree.reference(data.reference);
396
inst.cut(data.reference);
397
},
398
"visible": function(key, opt) {
399
// Only show if node is not root
400
return node.parent !== "#";
401
}
402
},
403
"copy": {
404
"label": "Copy",
405
"action": function(data) {
406
const inst = $.jstree.reference(data.reference);
407
inst.copy(data.reference);
408
}
409
},
410
"paste": {
411
"label": "Paste",
412
"action": function(data) {
413
const inst = $.jstree.reference(data.reference);
414
inst.paste(data.reference);
415
},
416
"visible": function(key, opt) {
417
const inst = $.jstree.reference(opt.$trigger);
418
return inst.can_paste();
419
},
420
"disabled": function(key, opt) {
421
const inst = $.jstree.reference(opt.$trigger);
422
return !inst.can_paste();
423
}
424
},
425
"delete": {
426
"label": "Delete",
427
"action": function(data) {
428
if (confirm("Delete this item?")) {
429
const inst = $.jstree.reference(data.reference);
430
inst.delete_node(data.reference);
431
}
432
},
433
"visible": function(key, opt) {
434
// Only show if user has delete permission
435
return hasPermission("delete", node);
436
}
437
}
438
};
439
}
440
}
441
});
442
```
443
444
### Context Menu Events
445
446
Events triggered during context menu operations.
447
448
```javascript { .api }
449
// Context menu specific events
450
"show_contextmenu.jstree": ContextMenuEvent;
451
"hide_contextmenu.jstree": ContextMenuEvent;
452
453
interface ContextMenuEvent {
454
node: object;
455
x: number;
456
y: number;
457
instance: jsTree;
458
}
459
```
460
461
**Usage Examples:**
462
463
```javascript
464
// Listen for context menu events
465
$("#tree").on("show_contextmenu.jstree", function(e, data) {
466
console.log("Context menu shown for:", data.node.text);
467
console.log("Position:", data.x, data.y);
468
});
469
470
$("#tree").on("hide_contextmenu.jstree", function(e, data) {
471
console.log("Context menu hidden");
472
});
473
474
// Custom context menu styling based on node
475
$("#tree").on("show_contextmenu.jstree", function(e, data) {
476
const menuContainer = $(".jstree-contextmenu");
477
478
// Add custom class based on node type
479
menuContainer.removeClass("folder-menu file-menu");
480
if (data.node.type === "folder") {
481
menuContainer.addClass("folder-menu");
482
} else if (data.node.type === "file") {
483
menuContainer.addClass("file-menu");
484
}
485
});
486
```
487
488
### Advanced Context Menu Patterns
489
490
Advanced configuration patterns for complex scenarios.
491
492
```javascript { .api }
493
/**
494
* Advanced context menu patterns
495
*/
496
interface AdvancedContextMenuConfig {
497
/** Multi-selection context menu */
498
multi_selection?: boolean;
499
/** Custom menu positioning */
500
position?: function;
501
/** Menu theme/styling */
502
theme?: string;
503
/** Animation settings */
504
animation?: object;
505
}
506
```
507
508
**Usage Examples:**
509
510
```javascript
511
// Multi-selection context menu
512
$("#tree").jstree({
513
"plugins": ["contextmenu"],
514
"contextmenu": {
515
"items": function(node) {
516
const inst = $.jstree.reference($("#tree"));
517
const selected = inst.get_selected();
518
const isMultiple = selected.length > 1;
519
520
const items = {};
521
522
if (isMultiple) {
523
items.multi_delete = {
524
"label": "Delete Selected (" + selected.length + ")",
525
"icon": "fa fa-trash",
526
"action": function(data) {
527
if (confirm("Delete " + selected.length + " items?")) {
528
inst.delete_node(selected);
529
}
530
}
531
};
532
533
items.multi_move = {
534
"label": "Move Selected",
535
"icon": "fa fa-arrows",
536
"action": function(data) {
537
// Show move dialog for multiple items
538
showMoveDialog(selected);
539
}
540
};
541
} else {
542
// Single item context menu
543
items.rename = {
544
"label": "Rename",
545
"action": function(data) {
546
inst.edit(data.reference);
547
}
548
};
549
}
550
551
return items;
552
}
553
}
554
});
555
556
// Integration with other plugins
557
$("#tree").jstree({
558
"plugins": ["contextmenu", "dnd", "clipboard"],
559
"contextmenu": {
560
"items": function(node) {
561
const tree = $.jstree.reference($("#tree"));
562
563
return {
564
"cut": {
565
"label": "Cut",
566
"action": function(data) {
567
tree.cut(data.reference);
568
}
569
},
570
"copy": {
571
"label": "Copy",
572
"action": function(data) {
573
tree.copy(data.reference);
574
}
575
},
576
"paste": {
577
"label": "Paste",
578
"action": function(data) {
579
tree.paste(data.reference);
580
},
581
"visible": function() {
582
return tree.can_paste();
583
}
584
}
585
};
586
}
587
}
588
});
589
```
590
591
## Types
592
593
```javascript { .api }
594
// Context menu data structures
595
interface ContextMenuData {
596
reference: jQuery;
597
element: jQuery;
598
position: {x: number, y: number};
599
node: object;
600
}
601
602
// Menu action function signature
603
type MenuActionFunction = (data: ContextMenuData) => void;
604
605
// Context menu settings
606
interface ContextMenuSettings {
607
select_node: boolean;
608
show_at_node: boolean;
609
items: object|DynamicMenuFunction;
610
}
611
```