| 
									
										
										
										
											2019-11-22 22:35:59 +01:00
										 |  |  | import keyboardActionService from './keyboard_actions.js'; | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  | class ContextMenu { | 
					
						
							|  |  |  |     constructor() { | 
					
						
							|  |  |  |         this.$widget = $("#context-menu-container"); | 
					
						
							|  |  |  |         this.dateContextMenuOpenedMs = 0; | 
					
						
							| 
									
										
										
										
											2019-03-28 20:54:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         $(document).on('click', () => this.hide()); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |     async show(options) { | 
					
						
							|  |  |  |         this.options = options; | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         this.$widget.empty(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.addItems(this.$widget, options.items); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         keyboardActionService.updateDisplayedShortcuts(this.$widget); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.positionMenu(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.dateContextMenuOpenedMs = Date.now(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     positionMenu() { | 
					
						
							|  |  |  |         // code below tries to detect when dropdown would overflow from page
 | 
					
						
							|  |  |  |         // in such case we'll position it above click coordinates so it will fit into client
 | 
					
						
							| 
									
										
										
										
											2021-07-22 19:37:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const CONTEXT_MENU_PADDING = 5; // How many pixels to pad context menu from edge of screen
 | 
					
						
							|  |  |  |         const CONTEXT_MENU_OFFSET = 10; // How many pixels to offset context menu by relative to cursor
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         const clientHeight = document.documentElement.clientHeight; | 
					
						
							| 
									
										
										
										
											2021-07-22 19:37:53 +02:00
										 |  |  |         const clientWidth = document.documentElement.clientWidth; | 
					
						
							|  |  |  |         const contextMenuHeight = this.$widget.outerHeight(); | 
					
						
							|  |  |  |         const contextMenuWidth = this.$widget.outerWidth(); | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  |         let top, left; | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 19:37:53 +02:00
										 |  |  |         if (this.options.y + contextMenuHeight - CONTEXT_MENU_OFFSET > clientHeight - CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |             // Overflow: bottom
 | 
					
						
							|  |  |  |             top = clientHeight - contextMenuHeight - CONTEXT_MENU_PADDING; | 
					
						
							|  |  |  |         } else if (this.options.y - CONTEXT_MENU_OFFSET < CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |             // Overflow: top
 | 
					
						
							|  |  |  |             top = CONTEXT_MENU_PADDING; | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2021-07-22 19:37:53 +02:00
										 |  |  |             top = this.options.y - CONTEXT_MENU_OFFSET; | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  |         if (this.options.orientation === 'left') { | 
					
						
							| 
									
										
										
										
											2021-07-22 19:37:53 +02:00
										 |  |  |             if (this.options.x + CONTEXT_MENU_OFFSET > clientWidth - CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |                 // Overflow: right
 | 
					
						
							|  |  |  |                 left = clientWidth - contextMenuWidth - CONTEXT_MENU_OFFSET; | 
					
						
							|  |  |  |             } else if (this.options.x - contextMenuWidth + CONTEXT_MENU_OFFSET < CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |                 // Overflow: left
 | 
					
						
							|  |  |  |                 left = CONTEXT_MENU_PADDING; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 left = this.options.x - contextMenuWidth + CONTEXT_MENU_OFFSET; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (this.options.x + contextMenuWidth - CONTEXT_MENU_OFFSET > clientWidth - CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |                 // Overflow: right
 | 
					
						
							|  |  |  |                 left = clientWidth - contextMenuWidth - CONTEXT_MENU_PADDING; | 
					
						
							|  |  |  |             } else if (this.options.x - CONTEXT_MENU_OFFSET < CONTEXT_MENU_PADDING) { | 
					
						
							|  |  |  |                 // Overflow: left
 | 
					
						
							|  |  |  |                 left = CONTEXT_MENU_PADDING; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 left = this.options.x - CONTEXT_MENU_OFFSET; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         this.$widget.css({ | 
					
						
							|  |  |  |             display: "block", | 
					
						
							|  |  |  |             top: top, | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  |             left: left | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |         }).addClass("show"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addItems($parent, items) { | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |         for (const item of items) { | 
					
						
							|  |  |  |             if (item.title === '----') { | 
					
						
							|  |  |  |                 $parent.append($("<div>").addClass("dropdown-divider")); | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                 const $icon = $("<span>"); | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                 if (item.uiIcon) { | 
					
						
							| 
									
										
										
										
											2022-05-31 22:45:57 +02:00
										 |  |  |                     $icon.addClass(item.uiIcon); | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     $icon.append(" "); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 21:11:20 +02:00
										 |  |  |                 const $link = $("<span>") | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                     .append($icon) | 
					
						
							|  |  |  |                     .append("   ") // some space between icon and text
 | 
					
						
							|  |  |  |                     .append(item.title); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 21:11:20 +02:00
										 |  |  |                 const $item = $("<li>") | 
					
						
							|  |  |  |                     .addClass("dropdown-item") | 
					
						
							|  |  |  |                     .append($link) | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |                     // important to use mousedown instead of click since the former does not change focus
 | 
					
						
							|  |  |  |                     // (especially important for focused text for spell check)
 | 
					
						
							| 
									
										
										
										
											2021-10-30 15:36:50 +02:00
										 |  |  |                     .on('mousedown', e => { | 
					
						
							| 
									
										
										
										
											2020-02-28 22:07:08 +01:00
										 |  |  |                         e.stopPropagation(); | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |                         this.hide(); | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |                         if (item.handler) { | 
					
						
							|  |  |  |                             item.handler(item, e); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         this.options.selectMenuItemHandler(item, e); | 
					
						
							| 
									
										
										
										
											2019-05-03 21:50:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 21:11:20 +02:00
										 |  |  |                         // it's important to stop the propagation especially for sub-menus, otherwise the event
 | 
					
						
							|  |  |  |                         // might be handled again by top-level menu
 | 
					
						
							|  |  |  |                         return false; | 
					
						
							|  |  |  |                     }); | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-01 21:11:20 +02:00
										 |  |  |                 if (item.enabled !== undefined && !item.enabled) { | 
					
						
							| 
									
										
										
										
											2019-04-16 21:40:04 +02:00
										 |  |  |                     $item.addClass("disabled"); | 
					
						
							| 
									
										
										
										
											2019-04-01 21:11:20 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-11-09 22:18:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                 if (item.items) { | 
					
						
							|  |  |  |                     $item.addClass("dropdown-submenu"); | 
					
						
							|  |  |  |                     $link.addClass("dropdown-toggle"); | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  |                     const $subMenu = $("<ul>").addClass("dropdown-menu"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |                     this.addItems($subMenu, item.items); | 
					
						
							| 
									
										
										
										
											2019-03-17 11:38:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     $item.append($subMenu); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 $parent.append($item); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  |     hide() { | 
					
						
							|  |  |  |         // this date checking comes from change in FF66 - https://github.com/zadam/trilium/issues/468
 | 
					
						
							|  |  |  |         // "contextmenu" event also triggers "click" event which depending on the timing can close just opened context menu
 | 
					
						
							|  |  |  |         // we might filter out right clicks, but then it's better if even right clicks close the context menu
 | 
					
						
							|  |  |  |         if (Date.now() - this.dateContextMenuOpenedMs > 300) { | 
					
						
							|  |  |  |             this.$widget.hide(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 11:28:30 +01:00
										 |  |  | const contextMenu = new ContextMenu(); | 
					
						
							| 
									
										
										
										
											2018-11-09 17:11:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-14 23:29:37 +02:00
										 |  |  | export default contextMenu; |