mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	select template when creating note, closes #2813
This commit is contained in:
		
							
								
								
									
										94
									
								
								src/public/app/dialogs/note_type_chooser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/public/app/dialogs/note_type_chooser.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| import noteTypesService from "../services/note_types.js"; | ||||
|  | ||||
| const $dialog = $("#note-type-chooser-dialog"); | ||||
| const $noteTypeDropdown = $("#note-type-dropdown"); | ||||
| const $noteTypeDropdownTrigger = $("#note-type-dropdown-trigger"); | ||||
| $noteTypeDropdownTrigger.dropdown(); | ||||
|  | ||||
| let resolve; | ||||
| let $originalFocused; // element focused before the dialog was opened, so we can return to it afterwards | ||||
| let $originalDialog; | ||||
|  | ||||
| export async function chooseNoteType() { | ||||
|     $originalFocused = $(':focus'); | ||||
|  | ||||
|     const noteTypes = await noteTypesService.getNoteTypeItems(); | ||||
|  | ||||
|     $noteTypeDropdown.empty(); | ||||
|  | ||||
|     for (const noteType of noteTypes) { | ||||
|         if (noteType.title === '----') { | ||||
|             $noteTypeDropdown.append($('<h6 class="dropdown-header">').append("Templates:")); | ||||
|         } | ||||
|         else { | ||||
|             $noteTypeDropdown.append( | ||||
|                 $('<a class="dropdown-item" tabindex="0">') | ||||
|                     .attr("data-note-type", noteType.type) | ||||
|                     .attr("data-template-note-id", noteType.templateNoteId) | ||||
|                     .append($("<span>").addClass(noteType.uiIcon)) | ||||
|                     .append(" " + noteType.title) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     $noteTypeDropdownTrigger.dropdown('show'); | ||||
|  | ||||
|     $originalDialog = glob.activeDialog; | ||||
|     glob.activeDialog = $dialog; | ||||
|     $dialog.modal(); | ||||
|  | ||||
|     $noteTypeDropdown.find(".dropdown-item:first").focus(); | ||||
|  | ||||
|     return new Promise((res, rej) => { resolve = res; }); | ||||
| } | ||||
|  | ||||
| $dialog.on("hidden.bs.modal", () => { | ||||
|     if (resolve) { | ||||
|         resolve({success: false}); | ||||
|     } | ||||
|  | ||||
|     if ($originalFocused) { | ||||
|         $originalFocused.trigger('focus'); | ||||
|         $originalFocused = null; | ||||
|     } | ||||
|  | ||||
|     glob.activeDialog = $originalDialog; | ||||
| }); | ||||
|  | ||||
| function doResolve(e) { | ||||
|     const $item = $(e.target).closest(".dropdown-item"); | ||||
|     const noteType = $item.attr("data-note-type"); | ||||
|     const templateNoteId = $item.attr("data-template-note-id"); | ||||
|  | ||||
|     resolve({ | ||||
|         success: true, | ||||
|         noteType, | ||||
|         templateNoteId | ||||
|     }); | ||||
|     resolve = null; | ||||
|  | ||||
|     $dialog.modal("hide"); | ||||
| } | ||||
|  | ||||
| $noteTypeDropdown.on('click', '.dropdown-item', e => doResolve(e)); | ||||
|  | ||||
| $noteTypeDropdown.on('focus', '.dropdown-item', e => { | ||||
|     $noteTypeDropdown.find('.dropdown-item').each((i, el) => { | ||||
|         $(el).toggleClass('active', el === e.target); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| $noteTypeDropdown.on('keydown', '.dropdown-item', e => { | ||||
|     if (e.key === 'Enter') { | ||||
|         doResolve(e); | ||||
|         e.preventDefault(); | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $noteTypeDropdown.parent().on('hide.bs.dropdown', e => { | ||||
|     // prevent closing dropdown by clicking outside | ||||
|     if (e.clickEvent) { | ||||
|         e.preventDefault(); | ||||
|     } | ||||
| }); | ||||
| @@ -170,9 +170,18 @@ function initNoteAutocomplete($el, options) { | ||||
|         } | ||||
|  | ||||
|         if (suggestion.action === 'create-note') { | ||||
|             const noteTypeChooserDialog = await import('../dialogs/note_type_chooser.js'); | ||||
|             const {success, noteType, templateNoteId} = await noteTypeChooserDialog.chooseNoteType(); | ||||
|  | ||||
|             if (!success) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const {note} = await noteCreateService.createNote(suggestion.parentNoteId, { | ||||
|                 title: suggestion.noteTitle, | ||||
|                 activate: false | ||||
|                 activate: false, | ||||
|                 type: noteType, | ||||
|                 templateNoteId: templateNoteId | ||||
|             }); | ||||
|  | ||||
|             suggestion.notePath = treeService.getSomeNotePath(note); | ||||
|   | ||||
| @@ -75,6 +75,20 @@ async function createNote(parentNotePath, options = {}) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| async function createNoteWithTypePrompt(parentNotePath, options = {}) { | ||||
|     const noteTypeChooserDialog = await import('../dialogs/note_type_chooser.js'); | ||||
|     const {success, noteType, templateNoteId} = await noteTypeChooserDialog.chooseNoteType(); | ||||
|  | ||||
|     if (!success) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     options.type = noteType; | ||||
|     options.templateNoteId = templateNoteId; | ||||
|  | ||||
|     return await createNote(parentNotePath, options); | ||||
| } | ||||
|  | ||||
| /* If first element is heading, parse it out and use it as a new heading. */ | ||||
| function parseSelectedHtml(selectedHtml) { | ||||
|     const dom = $.parseHTML(selectedHtml); | ||||
| @@ -106,5 +120,6 @@ async function duplicateSubtree(noteId, parentNotePath) { | ||||
|  | ||||
| export default { | ||||
|     createNote, | ||||
|     createNoteWithTypePrompt, | ||||
|     duplicateSubtree | ||||
| }; | ||||
|   | ||||
							
								
								
									
										40
									
								
								src/public/app/services/note_types.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/public/app/services/note_types.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| import server from "./server.js"; | ||||
| import froca from "./froca.js"; | ||||
|  | ||||
| async function getNoteTypeItems(command) { | ||||
|     const items = [ | ||||
|         { title: "Text", command: command, type: "text", uiIcon: "bx bx-note" }, | ||||
|         { title: "Code", command: command, type: "code", uiIcon: "bx bx-code" }, | ||||
|         { title: "Saved Search", command: command, type: "search", uiIcon: "bx bx-file-find" }, | ||||
|         { title: "Relation Map", command: command, type: "relation-map", uiIcon: "bx bx-map-alt" }, | ||||
|         { title: "Note Map", command: command, type: "note-map", uiIcon: "bx bx-map-alt" }, | ||||
|         { title: "Render Note", command: command, type: "render", uiIcon: "bx bx-extension" }, | ||||
|         { title: "Book", command: command, type: "book", uiIcon: "bx bx-book" }, | ||||
|         { title: "Mermaid Diagram", command: command, type: "mermaid", uiIcon: "bx bx-selection" }, | ||||
|         { title: "Canvas", command: command, type: "canvas", uiIcon: "bx bx-pen" }, | ||||
|         { title: "Web View", command: command, type: "iframe", uiIcon: "bx bx-globe-alt" }, | ||||
|     ]; | ||||
|  | ||||
|     const templateNoteIds = await server.get("search-templates"); | ||||
|     const templateNotes = await froca.getNotes(templateNoteIds); | ||||
|  | ||||
|     if (items.length > 0) { | ||||
|         items.push({ title: "----" }); | ||||
|  | ||||
|         for (const templateNote of templateNotes) { | ||||
|             items.push({ | ||||
|                 title: templateNote.title, | ||||
|                 uiIcon: templateNote.getIcon(), | ||||
|                 command: command, | ||||
|                 type: templateNote.type, | ||||
|                 templateNoteId: templateNote.noteId | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return items; | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     getNoteTypeItems | ||||
| } | ||||
| @@ -4,7 +4,7 @@ import clipboard from './clipboard.js'; | ||||
| import noteCreateService from "./note_create.js"; | ||||
| import contextMenu from "./context_menu.js"; | ||||
| import appContext from "./app_context.js"; | ||||
| import server from "./server.js"; | ||||
| import noteTypesService from "./note_types.js"; | ||||
|  | ||||
| class TreeContextMenu { | ||||
|     /** | ||||
| @@ -25,40 +25,6 @@ class TreeContextMenu { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     async getNoteTypeItems(command) { | ||||
|         const items = [ | ||||
|             { title: "Text", command: command, type: "text", uiIcon: "bx bx-note" }, | ||||
|             { title: "Code", command: command, type: "code", uiIcon: "bx bx-code" }, | ||||
|             { title: "Saved Search", command: command, type: "search", uiIcon: "bx bx-file-find" }, | ||||
|             { title: "Relation Map", command: command, type: "relation-map", uiIcon: "bx bx-map-alt" }, | ||||
|             { title: "Note Map", command: command, type: "note-map", uiIcon: "bx bx-map-alt" }, | ||||
|             { title: "Render Note", command: command, type: "render", uiIcon: "bx bx-extension" }, | ||||
|             { title: "Book", command: command, type: "book", uiIcon: "bx bx-book" }, | ||||
|             { title: "Mermaid Diagram", command: command, type: "mermaid", uiIcon: "bx bx-selection" }, | ||||
|             { title: "Canvas", command: command, type: "canvas", uiIcon: "bx bx-pen" }, | ||||
|             { title: "Web View", command: command, type: "iframe", uiIcon: "bx bx-globe-alt" }, | ||||
|         ]; | ||||
|  | ||||
|         const templateNoteIds = await server.get("search-templates"); | ||||
|         const templateNotes = await froca.getNotes(templateNoteIds); | ||||
|  | ||||
|         if (items.length > 0) { | ||||
|             items.push({ title: "----" }); | ||||
|  | ||||
|             for (const templateNote of templateNotes) { | ||||
|                 items.push({ | ||||
|                     title: templateNote.title, | ||||
|                     uiIcon: templateNote.getIcon(), | ||||
|                     command: command, | ||||
|                     type: templateNote.type, | ||||
|                     templateNoteId: templateNote.noteId | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return items; | ||||
|     } | ||||
|  | ||||
|     async getMenuItems() { | ||||
|         const note = await froca.getNote(this.node.data.noteId); | ||||
|         const branch = froca.getBranch(this.node.data.branchId); | ||||
| @@ -81,10 +47,10 @@ class TreeContextMenu { | ||||
|             { title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "bx bx-empty", enabled: noSelectedNotes }, | ||||
|             { title: 'Open in a new split', command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes }, | ||||
|             { title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "bx bx-plus", | ||||
|                 items: insertNoteAfterEnabled ? await this.getNoteTypeItems("insertNoteAfter") : null, | ||||
|                 items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null, | ||||
|                 enabled: insertNoteAfterEnabled && noSelectedNotes }, | ||||
|             { title: 'Insert child note <kbd data-command="createNoteInto"></kbd>', command: "insertChildNote", uiIcon: "bx bx-plus", | ||||
|                 items: notSearch ? await this.getNoteTypeItems("insertChildNote") : null, | ||||
|                 items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null, | ||||
|                 enabled: notSearch && noSelectedNotes }, | ||||
|             { title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash", | ||||
|                 enabled: isNotRoot && !isHoisted && parentNotSearch }, | ||||
|   | ||||
| @@ -485,7 +485,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { | ||||
|     } | ||||
|  | ||||
|     async createNoteForReferenceLink(title) { | ||||
|         const {note} = await noteCreateService.createNote(this.notePath, { | ||||
|         const {note} = await noteCreateService.createNoteWithTypePrompt(this.notePath, { | ||||
|             activate: false, | ||||
|             title: title | ||||
|         }); | ||||
|   | ||||
| @@ -305,7 +305,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | ||||
|     } | ||||
|  | ||||
|     async createNoteForReferenceLink(title) { | ||||
|         const {note} = await noteCreateService.createNote(this.notePath, { | ||||
|         const {note} = await noteCreateService.createNoteWithTypePrompt(this.notePath, { | ||||
|             activate: false, | ||||
|             title: title | ||||
|         }); | ||||
|   | ||||
| @@ -192,6 +192,7 @@ div.ui-tooltip { | ||||
| .dropdown-menu a:hover:not(.disabled), .dropdown-item:hover:not(.disabled) { | ||||
|     color: var(--hover-item-text-color) !important; | ||||
|     background-color: var(--hover-item-background-color) !important; | ||||
|     border-color: var(--hover-item-border-color) !important; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| @@ -210,17 +211,20 @@ div.ui-tooltip { | ||||
|     padding-bottom: 0; | ||||
| } | ||||
|  | ||||
| .dropdown-item { | ||||
| .dropdown-item, .dropdown-header { | ||||
|     color: var(--menu-text-color) !important; | ||||
|     border: 1px solid transparent !important; | ||||
| } | ||||
|  | ||||
| .dropdown-item.disabled, .dropdown-item.disabled kbd { | ||||
|     color: #aaa !important; | ||||
| } | ||||
|  | ||||
| .dropdown-item.active { | ||||
| .dropdown-item.active, .dropdown-item:focus { | ||||
|     color: var(--active-item-text-color) !important; | ||||
|     background-color: var(--active-item-background-color) !important; | ||||
|     border-color: var(--active-item-border-color) !important; | ||||
|     outline: none; | ||||
| } | ||||
|  | ||||
| .CodeMirror { | ||||
| @@ -473,8 +477,8 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th | ||||
| } | ||||
|  | ||||
| .algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor { | ||||
|     color: var(--hover-item-text-color); | ||||
|     background-color: var(--hover-item-background-color); | ||||
|     color: var(--active-item-text-color); | ||||
|     background-color: var(--active-item-background-color); | ||||
| } | ||||
|  | ||||
| .help-button { | ||||
|   | ||||
| @@ -35,11 +35,13 @@ | ||||
|     --input-text-color: #ccc; | ||||
|     --input-background-color: #333; | ||||
|  | ||||
|     --hover-item-text-color: black; | ||||
|     --hover-item-background-color: #777; | ||||
|     --hover-item-text-color: #ccc; | ||||
|     --hover-item-background-color: transparent; | ||||
|     --hover-item-border-color: #aaa; | ||||
|  | ||||
|     --active-item-text-color: black; | ||||
|     --active-item-background-color: #777; | ||||
|     --active-item-border-color: transparent; | ||||
|  | ||||
|     --menu-text-color: white; | ||||
|     --menu-background-color: #222; | ||||
|   | ||||
| @@ -40,10 +40,12 @@ html { | ||||
|     --input-background-color: transparent; | ||||
|  | ||||
|     --hover-item-text-color: black; | ||||
|     --hover-item-background-color: #ddd; | ||||
|     --hover-item-background-color: transparent; | ||||
|     --hover-item-border-color: #ccc; | ||||
|  | ||||
|     --active-item-text-color: black; | ||||
|     --active-item-background-color: #ddd; | ||||
|     --active-item-border-color: transparent; | ||||
|  | ||||
|     --menu-text-color: black; | ||||
|     --menu-background-color: white; | ||||
|   | ||||
| @@ -41,6 +41,7 @@ | ||||
| <%- include('dialogs/delete_notes.ejs') %> | ||||
| <%- include('dialogs/password_not_set.ejs') %> | ||||
| <%- include('dialogs/bulk_assign_attributes.ejs') %> | ||||
| <%- include('dialogs/note_type_chooser.ejs') %> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|     global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */ | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/views/dialogs/note_type_chooser.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/views/dialogs/note_type_chooser.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <style> | ||||
|     #note-type-dropdown { | ||||
|         position: relative; | ||||
|         font-size: large; | ||||
|         padding: 20px; | ||||
|         width: 100%; | ||||
|         margin-top: 15px; | ||||
|         max-height: 80vh; | ||||
|         overflow: auto; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <div id="note-type-chooser-dialog" class="modal mx-auto" tabindex="-1" role="dialog"> | ||||
|     <div class="modal-dialog" style="max-width: 500px;" role="document"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h5 class="modal-title mr-auto">Choose note type</h5> | ||||
|  | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close" style="margin-left: 0 !important;"> | ||||
|                     <span aria-hidden="true">×</span> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="modal-body"> | ||||
|                 Choose note type / template of the new note: | ||||
|  | ||||
|                 <div class="dropdown"> | ||||
|                     <button id="note-type-dropdown-trigger" type="button" style="display: none;" data-toggle="dropdown">Dropdown trigger</button> | ||||
|  | ||||
|                     <div id="note-type-dropdown" class="dropdown-menu"></div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
		Reference in New Issue
	
	Block a user