mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 19:19:03 +01:00 
			
		
		
		
	context menu refactoring
This commit is contained in:
		
							parent
							
								
									3e22804a76
								
							
						
					
					
						commit
						c7b5784123
					
				
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "trilium", |   "name": "trilium", | ||||||
|   "version": "0.31.3", |   "version": "0.31.4", | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  | |||||||
							
								
								
									
										85
									
								
								src/public/javascripts/services/clipboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/public/javascripts/services/clipboard.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | import treeUtils from "./tree_utils.js"; | ||||||
|  | import treeChangesService from "./branches.js"; | ||||||
|  | import cloningService from "./cloning.js"; | ||||||
|  | import infoService from "./info.js"; | ||||||
|  | 
 | ||||||
|  | let clipboardIds = []; | ||||||
|  | let clipboardMode = null; | ||||||
|  | 
 | ||||||
|  | async function pasteAfter(node) { | ||||||
|  |     if (clipboardMode === 'cut') { | ||||||
|  |         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||||
|  | 
 | ||||||
|  |         await treeChangesService.moveAfterNode(nodes, node); | ||||||
|  | 
 | ||||||
|  |         clipboardIds = []; | ||||||
|  |         clipboardMode = null; | ||||||
|  |     } | ||||||
|  |     else if (clipboardMode === 'copy') { | ||||||
|  |         for (const noteId of clipboardIds) { | ||||||
|  |             await cloningService.cloneNoteAfter(noteId, node.data.branchId); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places
 | ||||||
|  |     } | ||||||
|  |     else if (clipboardIds.length === 0) { | ||||||
|  |         // just do nothing
 | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         infoService.throwError("Unrecognized clipboard mode=" + clipboardMode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function pasteInto(node) { | ||||||
|  |     if (clipboardMode === 'cut') { | ||||||
|  |         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||||
|  | 
 | ||||||
|  |         await treeChangesService.moveToNode(nodes, node); | ||||||
|  | 
 | ||||||
|  |         await node.setExpanded(true); | ||||||
|  | 
 | ||||||
|  |         clipboardIds = []; | ||||||
|  |         clipboardMode = null; | ||||||
|  |     } | ||||||
|  |     else if (clipboardMode === 'copy') { | ||||||
|  |         for (const noteId of clipboardIds) { | ||||||
|  |             await cloningService.cloneNoteTo(noteId, node.data.noteId); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         await node.setExpanded(true); | ||||||
|  | 
 | ||||||
|  |         // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places
 | ||||||
|  |     } | ||||||
|  |     else if (clipboardIds.length === 0) { | ||||||
|  |         // just do nothing
 | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         infoService.throwError("Unrecognized clipboard mode=" + mode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function copy(nodes) { | ||||||
|  |     clipboardIds = nodes.map(node => node.data.noteId); | ||||||
|  |     clipboardMode = 'copy'; | ||||||
|  | 
 | ||||||
|  |     infoService.showMessage("Note(s) have been copied into clipboard."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function cut(nodes) { | ||||||
|  |     clipboardIds = nodes.map(node => node.key); | ||||||
|  |     clipboardMode = 'cut'; | ||||||
|  | 
 | ||||||
|  |     infoService.showMessage("Note(s) have been cut into clipboard."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function isEmpty() { | ||||||
|  |     return clipboardIds.length === 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |     pasteAfter, | ||||||
|  |     pasteInto, | ||||||
|  |     cut, | ||||||
|  |     copy, | ||||||
|  |     isEmpty | ||||||
|  | } | ||||||
| @ -2,7 +2,7 @@ const $contextMenuContainer = $("#context-menu-container"); | |||||||
| 
 | 
 | ||||||
| let dateContextMenuOpenedMs = 0; | let dateContextMenuOpenedMs = 0; | ||||||
| 
 | 
 | ||||||
| function initContextMenu(event, contextMenuItems, selectContextMenuItem) { | async function initContextMenu(event, contextMenu) { | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|     $contextMenuContainer.empty(); |     $contextMenuContainer.empty(); | ||||||
| @ -34,7 +34,7 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) { | |||||||
| 
 | 
 | ||||||
|                         e.originalTarget = event.target; |                         e.originalTarget = event.target; | ||||||
| 
 | 
 | ||||||
|                         selectContextMenuItem(e, cmd); |                         contextMenu.selectContextMenuItem(e, cmd); | ||||||
| 
 | 
 | ||||||
|                         // it's important to stop the propagation especially for sub-menus, otherwise the event
 |                         // it's important to stop the propagation especially for sub-menus, otherwise the event
 | ||||||
|                         // might be handled again by top-level menu
 |                         // might be handled again by top-level menu
 | ||||||
| @ -61,7 +61,7 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     addItems($contextMenuContainer, contextMenuItems); |     addItems($contextMenuContainer, await contextMenu.getContextMenuItems()); | ||||||
| 
 | 
 | ||||||
|     // code below tries to detect when dropdown would overflow from page
 |     // 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
 |     // in such case we'll position it above click coordinates so it will fit into client
 | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import Branch from '../entities/branch.js'; | |||||||
| import NoteShort from '../entities/note_short.js'; | import NoteShort from '../entities/note_short.js'; | ||||||
| import hoistedNoteService from '../services/hoisted_note.js'; | import hoistedNoteService from '../services/hoisted_note.js'; | ||||||
| import confirmDialog from "../dialogs/confirm.js"; | import confirmDialog from "../dialogs/confirm.js"; | ||||||
|  | import TreeContextMenu from "./tree_context_menu.js"; | ||||||
| 
 | 
 | ||||||
| const $tree = $("#tree"); | const $tree = $("#tree"); | ||||||
| const $createTopLevelNoteButton = $("#create-top-level-note-button"); | const $createTopLevelNoteButton = $("#create-top-level-note-button"); | ||||||
| @ -485,9 +486,15 @@ function initFancyTree(tree) { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $tree.on('contextmenu', '.fancytree-node', function(e) { |     $tree.on('contextmenu', '.fancytree-node', function(e) { | ||||||
|         treeContextMenuService.getContextMenuItems(e).then(([node, contextMenuItems]) => { |         const node = $.ui.fancytree.getNode(e); | ||||||
|             contextMenuWidget.initContextMenu(e, contextMenuItems, treeContextMenuService.selectContextMenuItem); | 
 | ||||||
|         }); |         // right click resets selection to just this node
 | ||||||
|  |         // this is important when e.g. you right click on a note while having different note active
 | ||||||
|  |         // and then click on delete - obviously you want to delete only that one right-clicked
 | ||||||
|  |         node.setSelected(true); | ||||||
|  |         clearSelectedNodes(); | ||||||
|  | 
 | ||||||
|  |         contextMenuWidget.initContextMenu(e, new TreeContextMenu(node)); | ||||||
| 
 | 
 | ||||||
|         return false; // blocks default browser right click menu
 |         return false; // blocks default browser right click menu
 | ||||||
|     }); |     }); | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| import treeService from './tree.js'; | import treeService from './tree.js'; | ||||||
| import cloningService from './cloning.js'; |  | ||||||
| import messagingService from './messaging.js'; | import messagingService from './messaging.js'; | ||||||
| import protectedSessionService from './protected_session.js'; | import protectedSessionService from './protected_session.js'; | ||||||
| import treeChangesService from './branches.js'; | import treeChangesService from './branches.js'; | ||||||
| @ -7,235 +6,147 @@ import treeUtils from './tree_utils.js'; | |||||||
| import branchPrefixDialog from '../dialogs/branch_prefix.js'; | import branchPrefixDialog from '../dialogs/branch_prefix.js'; | ||||||
| import exportDialog from '../dialogs/export.js'; | import exportDialog from '../dialogs/export.js'; | ||||||
| import importDialog from '../dialogs/import.js'; | import importDialog from '../dialogs/import.js'; | ||||||
| import infoService from "./info.js"; |  | ||||||
| import treeCache from "./tree_cache.js"; | import treeCache from "./tree_cache.js"; | ||||||
| import syncService from "./sync.js"; | import syncService from "./sync.js"; | ||||||
| import hoistedNoteService from './hoisted_note.js'; | import hoistedNoteService from './hoisted_note.js'; | ||||||
| import noteDetailService from './note_detail.js'; | import noteDetailService from './note_detail.js'; | ||||||
|  | import clipboard from './clipboard.js'; | ||||||
| 
 | 
 | ||||||
| let clipboardIds = []; | class TreeContextMenu { | ||||||
| let clipboardMode = null; |     constructor(node) { | ||||||
| 
 |         this.node = node; | ||||||
| async function pasteAfter(node) { |  | ||||||
|     if (clipboardMode === 'cut') { |  | ||||||
|         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); |  | ||||||
| 
 |  | ||||||
|         await treeChangesService.moveAfterNode(nodes, node); |  | ||||||
| 
 |  | ||||||
|         clipboardIds = []; |  | ||||||
|         clipboardMode = null; |  | ||||||
|     } |     } | ||||||
|     else if (clipboardMode === 'copy') { | 
 | ||||||
|         for (const noteId of clipboardIds) { |     getNoteTypeItems(baseCmd) { | ||||||
|             await cloningService.cloneNoteAfter(noteId, node.data.branchId); |         return [ | ||||||
|  |             { title: "Text", cmd: baseCmd + "_text", uiIcon: "file" }, | ||||||
|  |             { title: "Code", cmd: baseCmd + "_code", uiIcon: "terminal" }, | ||||||
|  |             { title: "Saved search", cmd: baseCmd + "_search", uiIcon: "search-folder" }, | ||||||
|  |             { title: "Relation Map", cmd: baseCmd + "_relation-map", uiIcon: "map" }, | ||||||
|  |             { title: "Render HTML note", cmd: baseCmd + "_render", uiIcon: "play" } | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async getContextMenuItems() { | ||||||
|  |         const branch = await treeCache.getBranch(this.node.data.branchId); | ||||||
|  |         const note = await treeCache.getNote(this.node.data.noteId); | ||||||
|  |         const parentNote = await treeCache.getNote(branch.parentNoteId); | ||||||
|  |         const isNotRoot = note.noteId !== 'root'; | ||||||
|  |         const isHoisted = note.noteId === await hoistedNoteService.getHoistedNoteId(); | ||||||
|  | 
 | ||||||
|  |         const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNote.type !== 'search'; | ||||||
|  |         const insertChildNoteEnabled = note.type !== 'search'; | ||||||
|  | 
 | ||||||
|  |         return [ | ||||||
|  |             { title: "Open in new tab", cmd: "openInTab", uiIcon: "empty" }, | ||||||
|  |             { title: "Insert note after <kbd>Ctrl+O</kbd>", cmd: "insertNoteAfter", uiIcon: "plus", | ||||||
|  |                 items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null, | ||||||
|  |                 enabled: insertNoteAfterEnabled }, | ||||||
|  |             { title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus", | ||||||
|  |                 items: insertChildNoteEnabled ? this.getNoteTypeItems("insertChildNote") : null, | ||||||
|  |                 enabled: insertChildNoteEnabled }, | ||||||
|  |             { title: "Delete <kbd>Delete</kbd>", cmd: "delete", uiIcon: "trash", | ||||||
|  |                 enabled: isNotRoot && !isHoisted && parentNote.type !== 'search' }, | ||||||
|  |             { title: "----" }, | ||||||
|  |             isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty" }, | ||||||
|  |             !isHoisted || !isNotRoot ? null : { title: "Unhoist note <kbd>Ctrl-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up" }, | ||||||
|  |             { title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "empty", | ||||||
|  |                 enabled: isNotRoot && parentNote.type !== 'search'}, | ||||||
|  |             { title: "----" }, | ||||||
|  |             { title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check" }, | ||||||
|  |             { title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield-close" }, | ||||||
|  |             { title: "----" }, | ||||||
|  |             { title: "Copy / clone <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "files", | ||||||
|  |                 enabled: isNotRoot }, | ||||||
|  |             { title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "scissors", | ||||||
|  |                 enabled: isNotRoot }, | ||||||
|  |             { title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "clipboard", | ||||||
|  |                 enabled: !clipboard.isEmpty() && note.type !== 'search' }, | ||||||
|  |             { title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard", | ||||||
|  |                 enabled: !clipboard.isEmpty() && isNotRoot && parentNote.type !== 'search' }, | ||||||
|  |             { title: "----" }, | ||||||
|  |             { title: "Export", cmd: "export", uiIcon: "empty", | ||||||
|  |                 enabled: note.type !== 'search' }, | ||||||
|  |             { title: "Import into note", cmd: "importIntoNote", uiIcon: "empty", | ||||||
|  |                 enabled: note.type !== 'search' }, | ||||||
|  |             { title: "----" }, | ||||||
|  |             { title: "Collapse subtree <kbd>Alt+-</kbd>", cmd: "collapseSubtree", uiIcon: "align-justify" }, | ||||||
|  |             { title: "Force note sync", cmd: "forceNoteSync", uiIcon: "refresh" }, | ||||||
|  |             { title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "empty" } | ||||||
|  |         ].filter(row => row !== null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async selectContextMenuItem(event, cmd) { | ||||||
|  |         if (cmd === 'openInTab') { | ||||||
|  |             noteDetailService.openInTab(this.node.data.noteId); | ||||||
|         } |         } | ||||||
|  |         else if (cmd.startsWith("insertNoteAfter")) { | ||||||
|  |             const parentNoteId = this.node.data.parentNoteId; | ||||||
|  |             const isProtected = await treeUtils.getParentProtectedStatus(this.node); | ||||||
|  |             const type = cmd.split("_")[1]; | ||||||
| 
 | 
 | ||||||
|         // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places
 |             treeService.createNote(this.node, parentNoteId, 'after', { | ||||||
|     } |                 type: type, | ||||||
|     else if (clipboardIds.length === 0) { |                 isProtected: isProtected | ||||||
|         // just do nothing
 |             }); | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         infoService.throwError("Unrecognized clipboard mode=" + clipboardMode); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function pasteInto(node) { |  | ||||||
|     if (clipboardMode === 'cut') { |  | ||||||
|         const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); |  | ||||||
| 
 |  | ||||||
|         await treeChangesService.moveToNode(nodes, node); |  | ||||||
| 
 |  | ||||||
|         await node.setExpanded(true); |  | ||||||
| 
 |  | ||||||
|         clipboardIds = []; |  | ||||||
|         clipboardMode = null; |  | ||||||
|     } |  | ||||||
|     else if (clipboardMode === 'copy') { |  | ||||||
|         for (const noteId of clipboardIds) { |  | ||||||
|             await cloningService.cloneNoteTo(noteId, node.data.noteId); |  | ||||||
|         } |         } | ||||||
|  |         else if (cmd.startsWith("insertChildNote")) { | ||||||
|  |             const type = cmd.split("_")[1]; | ||||||
| 
 | 
 | ||||||
|         await node.setExpanded(true); |             treeService.createNote(this.node, this.node.data.noteId, 'into', { | ||||||
| 
 |                 type: type, | ||||||
|         // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places
 |                 isProtected: this.node.data.isProtected | ||||||
|     } |             }); | ||||||
|     else if (clipboardIds.length === 0) { |         } | ||||||
|         // just do nothing
 |         else if (cmd === "editBranchPrefix") { | ||||||
|     } |             branchPrefixDialog.showDialog(this.node); | ||||||
|     else { |         } | ||||||
|         infoService.throwError("Unrecognized clipboard mode=" + mode); |         else if (cmd === "protectSubtree") { | ||||||
|  |             protectedSessionService.protectSubtree(this.node.data.noteId, true); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "unprotectSubtree") { | ||||||
|  |             protectedSessionService.protectSubtree(this.node.data.noteId, false); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "copy") { | ||||||
|  |             clipboard.copy(treeService.getSelectedNodes()); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "cut") { | ||||||
|  |             clipboard.cut(treeService.getSelectedNodes()); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "pasteAfter") { | ||||||
|  |             clipboard.pasteAfter(this.node); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "pasteInto") { | ||||||
|  |             clipboard.pasteInto(this.node); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "delete") { | ||||||
|  |             treeChangesService.deleteNodes(treeService.getSelectedNodes(true)); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "export") { | ||||||
|  |             exportDialog.showDialog("subtree"); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "importIntoNote") { | ||||||
|  |             importDialog.showDialog(); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "collapseSubtree") { | ||||||
|  |             treeService.collapseTree(this.node); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "forceNoteSync") { | ||||||
|  |             syncService.forceNoteSync(this.node.data.noteId); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "sortAlphabetically") { | ||||||
|  |             treeService.sortAlphabetically(this.node.data.noteId); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "hoist") { | ||||||
|  |             hoistedNoteService.setHoistedNoteId(this.node.data.noteId); | ||||||
|  |         } | ||||||
|  |         else if (cmd === "unhoist") { | ||||||
|  |             hoistedNoteService.unhoist(); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             messagingService.logError("Unknown command: " + cmd); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function copy(nodes) { | export default TreeContextMenu; | ||||||
|     clipboardIds = nodes.map(node => node.data.noteId); |  | ||||||
|     clipboardMode = 'copy'; |  | ||||||
| 
 |  | ||||||
|     infoService.showMessage("Note(s) have been copied into clipboard."); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function cut(nodes) { |  | ||||||
|     clipboardIds = nodes.map(node => node.key); |  | ||||||
|     clipboardMode = 'cut'; |  | ||||||
| 
 |  | ||||||
|     infoService.showMessage("Note(s) have been cut into clipboard."); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function getNoteTypeItems(baseCmd) { |  | ||||||
|     return [ |  | ||||||
|         { title: "Text", cmd: baseCmd + "_text", uiIcon: "file" }, |  | ||||||
|         { title: "Code", cmd: baseCmd + "_code", uiIcon: "terminal" }, |  | ||||||
|         { title: "Saved search", cmd: baseCmd + "_search", uiIcon: "search-folder" }, |  | ||||||
|         { title: "Relation Map", cmd: baseCmd + "_relation-map", uiIcon: "map" }, |  | ||||||
|         { title: "Render HTML note", cmd: baseCmd + "_render", uiIcon: "play" } |  | ||||||
|     ]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function getTopLevelItems(event) { |  | ||||||
|     const node = $.ui.fancytree.getNode(event); |  | ||||||
|     const branch = await treeCache.getBranch(node.data.branchId); |  | ||||||
|     const note = await treeCache.getNote(node.data.noteId); |  | ||||||
|     const parentNote = await treeCache.getNote(branch.parentNoteId); |  | ||||||
|     const isNotRoot = note.noteId !== 'root'; |  | ||||||
|     const isHoisted = note.noteId === await hoistedNoteService.getHoistedNoteId(); |  | ||||||
| 
 |  | ||||||
|     const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNote.type !== 'search'; |  | ||||||
|     const insertChildNoteEnabled = note.type !== 'search'; |  | ||||||
| 
 |  | ||||||
|     return [ |  | ||||||
|         { title: "Open in new tab", cmd: "openInTab", uiIcon: "empty" }, |  | ||||||
|         { title: "Insert note after <kbd>Ctrl+O</kbd>", cmd: "insertNoteAfter", uiIcon: "plus", |  | ||||||
|             items: insertNoteAfterEnabled ? getNoteTypeItems("insertNoteAfter") : null, |  | ||||||
|             enabled: insertNoteAfterEnabled }, |  | ||||||
|         { title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus", |  | ||||||
|             items: insertChildNoteEnabled ? getNoteTypeItems("insertChildNote") : null, |  | ||||||
|             enabled: insertChildNoteEnabled }, |  | ||||||
|         { title: "Delete <kbd>Delete</kbd>", cmd: "delete", uiIcon: "trash", |  | ||||||
|             enabled: isNotRoot && !isHoisted && parentNote.type !== 'search' }, |  | ||||||
|         { title: "----" }, |  | ||||||
|         isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty" }, |  | ||||||
|         !isHoisted || !isNotRoot ? null : { title: "Unhoist note <kbd>Ctrl-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up" }, |  | ||||||
|         { title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "empty", |  | ||||||
|             enabled: isNotRoot && parentNote.type !== 'search'}, |  | ||||||
|         { title: "----" }, |  | ||||||
|         { title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check" }, |  | ||||||
|         { title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield-close" }, |  | ||||||
|         { title: "----" }, |  | ||||||
|         { title: "Copy / clone <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "files", |  | ||||||
|             enabled: isNotRoot }, |  | ||||||
|         { title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "scissors", |  | ||||||
|             enabled: isNotRoot }, |  | ||||||
|         { title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "clipboard", |  | ||||||
|             enabled: clipboardIds.length > 0 && note.type !== 'search' }, |  | ||||||
|         { title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard", |  | ||||||
|             enabled: clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search' }, |  | ||||||
|         { title: "----" }, |  | ||||||
|         { title: "Export", cmd: "export", uiIcon: "empty", |  | ||||||
|             enabled: note.type !== 'search' }, |  | ||||||
|         { title: "Import into note", cmd: "importIntoNote", uiIcon: "empty", |  | ||||||
|             enabled: note.type !== 'search' }, |  | ||||||
|         { title: "----" }, |  | ||||||
|         { title: "Collapse subtree <kbd>Alt+-</kbd>", cmd: "collapseSubtree", uiIcon: "align-justify" }, |  | ||||||
|         { title: "Force note sync", cmd: "forceNoteSync", uiIcon: "refresh" }, |  | ||||||
|         { title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "empty" } |  | ||||||
|     ].filter(row => row !== null); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function getContextMenuItems(event) { |  | ||||||
|     const items = await getTopLevelItems(event); |  | ||||||
| 
 |  | ||||||
|     const node = $.ui.fancytree.getNode(event); |  | ||||||
| 
 |  | ||||||
|     // right click resets selection to just this node
 |  | ||||||
|     // this is important when e.g. you right click on a note while having different note active
 |  | ||||||
|     // and then click on delete - obviously you want to delete only that one right-clicked
 |  | ||||||
|     node.setSelected(true); |  | ||||||
|     treeService.clearSelectedNodes(); |  | ||||||
| 
 |  | ||||||
|     return [node, items]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function selectContextMenuItem(event, cmd) { |  | ||||||
|     // context menu is always triggered on current node
 |  | ||||||
|     const node = treeService.getActiveNode(); |  | ||||||
| 
 |  | ||||||
|     if (cmd === 'openInTab') { |  | ||||||
|         noteDetailService.openInTab(node.data.noteId); |  | ||||||
|     } |  | ||||||
|     else if (cmd.startsWith("insertNoteAfter")) { |  | ||||||
|         const parentNoteId = node.data.parentNoteId; |  | ||||||
|         const isProtected = await treeUtils.getParentProtectedStatus(node); |  | ||||||
|         const type = cmd.split("_")[1]; |  | ||||||
| 
 |  | ||||||
|         treeService.createNote(node, parentNoteId, 'after', { |  | ||||||
|             type: type, |  | ||||||
|             isProtected: isProtected |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     else if (cmd.startsWith("insertChildNote")) { |  | ||||||
|         const type = cmd.split("_")[1]; |  | ||||||
| 
 |  | ||||||
|         treeService.createNote(node, node.data.noteId, 'into', { |  | ||||||
|             type: type, |  | ||||||
|             isProtected: node.data.isProtected |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "editBranchPrefix") { |  | ||||||
|         branchPrefixDialog.showDialog(node); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "protectSubtree") { |  | ||||||
|         protectedSessionService.protectSubtree(node.data.noteId, true); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "unprotectSubtree") { |  | ||||||
|         protectedSessionService.protectSubtree(node.data.noteId, false); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "copy") { |  | ||||||
|         copy(treeService.getSelectedNodes()); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "cut") { |  | ||||||
|         cut(treeService.getSelectedNodes()); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "pasteAfter") { |  | ||||||
|         pasteAfter(node); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "pasteInto") { |  | ||||||
|         pasteInto(node); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "delete") { |  | ||||||
|         treeChangesService.deleteNodes(treeService.getSelectedNodes(true)); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "export") { |  | ||||||
|         exportDialog.showDialog("subtree"); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "importIntoNote") { |  | ||||||
|         importDialog.showDialog(); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "collapseSubtree") { |  | ||||||
|         treeService.collapseTree(node); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "forceNoteSync") { |  | ||||||
|         syncService.forceNoteSync(node.data.noteId); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "sortAlphabetically") { |  | ||||||
|         treeService.sortAlphabetically(node.data.noteId); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "hoist") { |  | ||||||
|         hoistedNoteService.setHoistedNoteId(node.data.noteId); |  | ||||||
|     } |  | ||||||
|     else if (cmd === "unhoist") { |  | ||||||
|         hoistedNoteService.unhoist(); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         messagingService.logError("Unknown command: " + cmd); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default { |  | ||||||
|     pasteAfter, |  | ||||||
|     pasteInto, |  | ||||||
|     cut, |  | ||||||
|     copy, |  | ||||||
|     getContextMenuItems, |  | ||||||
|     selectContextMenuItem |  | ||||||
| }; |  | ||||||
| @ -1,10 +1,9 @@ | |||||||
| import noteDetailService from "./note_detail.js"; | import noteDetailService from "./note_detail.js"; | ||||||
| import utils from "./utils.js"; |  | ||||||
| import treeChangesService from "./branches.js"; | import treeChangesService from "./branches.js"; | ||||||
| import contextMenuService from "./tree_context_menu.js"; |  | ||||||
| import treeService from "./tree.js"; | import treeService from "./tree.js"; | ||||||
| import editBranchPrefixDialog from "../dialogs/branch_prefix.js"; | import editBranchPrefixDialog from "../dialogs/branch_prefix.js"; | ||||||
| import hoistedNoteService from "./hoisted_note.js"; | import hoistedNoteService from "./hoisted_note.js"; | ||||||
|  | import clipboard from "./clipboard.js"; | ||||||
| 
 | 
 | ||||||
| const keyBindings = { | const keyBindings = { | ||||||
|     "del": node => { |     "del": node => { | ||||||
| @ -90,17 +89,17 @@ const keyBindings = { | |||||||
|         return false; |         return false; | ||||||
|     }, |     }, | ||||||
|     "ctrl+c": () => { |     "ctrl+c": () => { | ||||||
|         contextMenuService.copy(treeService.getSelectedNodes()); |         clipboard.copy(treeService.getSelectedNodes()); | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|     }, |     }, | ||||||
|     "ctrl+x": () => { |     "ctrl+x": () => { | ||||||
|         contextMenuService.cut(treeService.getSelectedNodes()); |         clipboard.cut(treeService.getSelectedNodes()); | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|     }, |     }, | ||||||
|     "ctrl+v": node => { |     "ctrl+v": node => { | ||||||
|         contextMenuService.pasteInto(node); |         clipboard.pasteInto(node); | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|     }, |     }, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam