mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 11:39:01 +01:00 
			
		
		
		
	component => type widget refactoring
This commit is contained in:
		
							parent
							
								
									a99c016818
								
							
						
					
					
						commit
						746181689f
					
				| @ -141,7 +141,7 @@ async function printActiveNote() { | ||||
| 
 | ||||
|     await libraryLoader.requireLibrary(libraryLoader.PRINT_THIS); | ||||
| 
 | ||||
|     $tabContext.$tabContent.find('.note-detail-component:visible').printThis({ | ||||
|     $tabContext.$tabContent.find('.note-detail-printable:visible').printThis({ | ||||
|         header: $("<h2>").text($tabContext.note && $tabContext.note.title).prop('outerHTML') , | ||||
|         importCSS: false, | ||||
|         loadCSS: [ | ||||
|  | ||||
| @ -60,6 +60,11 @@ class TabContext extends Component { | ||||
| 
 | ||||
|         bundleService.executeRelationBundles(this.note, 'runOnNoteView', this); | ||||
| 
 | ||||
|         if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|             // FIXME: there are probably more places where this should be done
 | ||||
|             protectedSessionHolder.touchProtectedSession(); | ||||
|         } | ||||
| 
 | ||||
|         this.trigger('tabNoteSwitched', {tabId: this.tabId}); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -16,25 +16,25 @@ const TPL = ` | ||||
| </div> | ||||
| `;
 | ||||
| 
 | ||||
| const componentClasses = { | ||||
|     'empty': "./detail/note_detail_empty.js", | ||||
|     'text': "./detail/note_detail_text.js", | ||||
|     'code': "./detail/note_detail_code.js", | ||||
|     'file': "./detail/note_detail_file.js", | ||||
|     'image': "./detail/note_detail_image.js", | ||||
|     'search': "./detail/note_detail_search.js", | ||||
|     'render': "./detail/note_detail_render.js", | ||||
|     'relation-map': "./detail/note_detail_relation_map.js", | ||||
|     'protected-session': "./detail/note_detail_protected_session.js", | ||||
|     'book': "./detail/note_detail_book.js" | ||||
| const typeWidgetClasses = { | ||||
|     'empty': "./type_widgets/note_detail_empty.js", | ||||
|     'text': "./type_widgets/text.js", | ||||
|     'code': "./type_widgets/code.js", | ||||
|     'file': "./type_widgets/file.js", | ||||
|     'image': "./type_widgets/note_detail_image.js", | ||||
|     'search': "./type_widgets/note_detail_search.js", | ||||
|     'render': "./type_widgets/note_detail_render.js", | ||||
|     'relation-map': "./type_widgets/note_detail_relation_map.js", | ||||
|     'protected-session': "./type_widgets/note_detail_protected_session.js", | ||||
|     'book': "./type_widgets/note_detail_book.js" | ||||
| }; | ||||
| 
 | ||||
| export default class NoteDetailWidget extends TabAwareWidget { | ||||
|     constructor(appContext) { | ||||
|         super(appContext); | ||||
| 
 | ||||
|         this.components = {}; | ||||
|         this.componentPromises = {}; | ||||
|         this.typeWidgets = {}; | ||||
|         this.typeWidgetPromises = {}; | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
| @ -43,18 +43,23 @@ export default class NoteDetailWidget extends TabAwareWidget { | ||||
|         return this.$widget; | ||||
|     } | ||||
| 
 | ||||
|     async noteSwitched() { | ||||
|         await this.initComponent(/**disableAutoBook*/); | ||||
|     async refresh() { | ||||
|         this.type = this.getWidgetType(/*disableAutoBook*/); | ||||
| 
 | ||||
|         for (const componentType in this.components) { | ||||
|             if (componentType !== this.type) { | ||||
|                 this.components[componentType].cleanup(); | ||||
|         if (!(this.type in this.typeWidgetPromises)) { | ||||
|             this.typeWidgetPromises[this.type] = this.initWidgetType(this.type); | ||||
|         } | ||||
| 
 | ||||
|         await this.typeWidgetPromises[this.type]; | ||||
| 
 | ||||
|         for (const typeWidget of Object.values(this.typeWidgets)) { | ||||
|             if (typeWidget.constructor.getType() !== this.type) { | ||||
|                 typeWidget.cleanup(); | ||||
|                 typeWidget.toggle(false); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.$widget.find('.note-detail-component').hide(); | ||||
| 
 | ||||
|         this.getComponent().show(); | ||||
|         this.getTypeWidget().toggle(true); | ||||
| 
 | ||||
|         this.setupClasses(); | ||||
|     } | ||||
| @ -75,36 +80,26 @@ export default class NoteDetailWidget extends TabAwareWidget { | ||||
|         this.$widget.toggleClass("protected", note.isProtected); | ||||
|     } | ||||
| 
 | ||||
|     getComponent() { | ||||
|         if (!this.components[this.type]) { | ||||
|             throw new Error("Could not find component for type: " + this.type); | ||||
|     getTypeWidget() { | ||||
|         if (!this.typeWidgets[this.type]) { | ||||
|             throw new Error("Could not find typeWidget for type: " + this.type); | ||||
|         } | ||||
| 
 | ||||
|         return this.components[this.type]; | ||||
|         return this.typeWidgets[this.type]; | ||||
|     } | ||||
|      | ||||
|     async initComponent(disableAutoBook = false) { | ||||
|         this.type = this.getComponentType(disableAutoBook); | ||||
|     async initWidgetType(type) { | ||||
|         const clazz = await import(typeWidgetClasses[type]); | ||||
| 
 | ||||
|         if (!(this.type in this.componentPromises)) { | ||||
|             this.componentPromises[this.type] = this.reallyInitComponent(this.type); | ||||
|         } | ||||
|         this.typeWidgets[this.type] = new clazz.default(this.appContext); | ||||
|         this.children.push(this.typeWidgets[this.type]); | ||||
| 
 | ||||
|         await this.componentPromises[this.type]; | ||||
|         this.typeWidgets[this.type].renderTo(this.$widget); | ||||
| 
 | ||||
|         this.typeWidgets[this.type].eventReceived('setTabContext', {tabContext: this.tabContext}); | ||||
|     } | ||||
| 
 | ||||
|     async reallyInitComponent(type) { | ||||
|         const clazz = await import(componentClasses[type]); | ||||
| 
 | ||||
|         this.components[this.type] = new clazz.default(this.appContext); | ||||
|         this.children.push(this.components[this.type]); | ||||
| 
 | ||||
|         this.components[this.type].renderTo(this.$widget); | ||||
| 
 | ||||
|         this.components[this.type].eventReceived('setTabContext', {tabContext: this.tabContext}); | ||||
|     } | ||||
| 
 | ||||
|     getComponentType(disableAutoBook) { | ||||
|     getWidgetType(disableAutoBook) { | ||||
|         const note = this.tabContext.note; | ||||
| 
 | ||||
|         if (!note) { | ||||
| @ -113,20 +108,15 @@ export default class NoteDetailWidget extends TabAwareWidget { | ||||
| 
 | ||||
|         let type = note.type; | ||||
| 
 | ||||
|         if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(note.content) && note.hasChildren()) { | ||||
|         if (type === 'text' && !disableAutoBook | ||||
|             && utils.isHtmlEmpty(note.content) | ||||
|             && note.hasChildren()) { | ||||
| 
 | ||||
|             type = 'book'; | ||||
|         } | ||||
| 
 | ||||
|         if (note.isProtected) { | ||||
|             if (protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|                 protectedSessionHolder.touchProtectedSession(); | ||||
|             } else { | ||||
|                 type = 'protected-session'; | ||||
| 
 | ||||
|                 // FIXME
 | ||||
|                 // user shouldn't be able to edit note title
 | ||||
|                 //this.$noteTitle.prop("readonly", true);
 | ||||
|             } | ||||
|         if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|             type = 'protected-session'; | ||||
|         } | ||||
| 
 | ||||
|         return type; | ||||
|  | ||||
| @ -135,6 +135,10 @@ export default class NoteTitleWidget extends TabAwareWidget { | ||||
| 
 | ||||
|         this.$noteTitle.val(note.title); | ||||
| 
 | ||||
|         if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|             this.$noteTitle.prop("readonly", true); | ||||
|         } | ||||
| 
 | ||||
|         this.$protectButton.toggleClass("active", note.isProtected); | ||||
|         this.$protectButton.prop("disabled", note.isProtected); | ||||
|         this.$unprotectButton.toggleClass("active", !note.isProtected); | ||||
|  | ||||
| @ -4,14 +4,16 @@ import toastService from "../../services/toast.js"; | ||||
| import server from "../../services/server.js"; | ||||
| import noteDetailService from "../../services/note_detail.js"; | ||||
| import keyboardActionService from "../../services/keyboard_actions.js"; | ||||
| import TabAwareWidget from "../tab_aware_widget.js"; | ||||
| import TypeWidget from "./type_widget.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail-code note-detail-component"> | ||||
| <div class="note-detail-code note-detail-printable"> | ||||
|     <div class="note-detail-code-editor"></div> | ||||
| </div>`; | ||||
| 
 | ||||
| class NoteDetailCode extends TabAwareWidget { | ||||
| class CodeTypeWidget extends TypeWidget { | ||||
|     static getType() { return "code"; } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$editor = this.$widget.find('.note-detail-code-editor'); | ||||
| @ -58,22 +60,19 @@ class NoteDetailCode extends TabAwareWidget { | ||||
|         //this.onNoteChange(() => this.tabContext.noteChanged());
 | ||||
|     } | ||||
|      | ||||
|     refresh() { | ||||
|         // lazy loading above can take time and tab might have been already switched to another note
 | ||||
|         if (this.tabContext.note && this.tabContext.note.type === 'code') { | ||||
|             // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
 | ||||
|             // we provide fallback
 | ||||
|             this.codeEditor.setValue(this.tabContext.note.content || ""); | ||||
|     doRefresh() { | ||||
|         // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
 | ||||
|         // we provide fallback
 | ||||
|         this.codeEditor.setValue(this.tabContext.note.content || ""); | ||||
| 
 | ||||
|             const info = CodeMirror.findModeByMIME(this.tabContext.note.mime); | ||||
|         const info = CodeMirror.findModeByMIME(this.tabContext.note.mime); | ||||
| 
 | ||||
|             if (info) { | ||||
|                 this.codeEditor.setOption("mode", info.mime); | ||||
|                 CodeMirror.autoLoadMode(this.codeEditor, info.mode); | ||||
|             } | ||||
| 
 | ||||
|             this.show(); | ||||
|         if (info) { | ||||
|             this.codeEditor.setOption("mode", info.mime); | ||||
|             CodeMirror.autoLoadMode(this.codeEditor, info.mode); | ||||
|         } | ||||
| 
 | ||||
|         this.show(); | ||||
|     } | ||||
| 
 | ||||
|     show() { | ||||
| @ -127,4 +126,4 @@ class NoteDetailCode extends TabAwareWidget { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default NoteDetailCode; | ||||
| export default CodeTypeWidget; | ||||
| @ -2,10 +2,10 @@ import utils from "../../services/utils.js"; | ||||
| import server from "../../services/server.js"; | ||||
| import toastService from "../../services/toast.js"; | ||||
| import noteDetailService from "../../services/note_detail.js"; | ||||
| import TabAwareWidget from "../tab_aware_widget.js"; | ||||
| import TypeWidget from "./type_widget.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail-file note-detail-component"> | ||||
| <div class="note-detail-file note-detail-printable"> | ||||
|     <style> | ||||
|     .note-detail-file { | ||||
|         padding: 10px; | ||||
| @ -55,7 +55,9 @@ const TPL = ` | ||||
|     <input type="file" class="file-upload-new-revision-input" style="display: none"> | ||||
| </div>`; | ||||
| 
 | ||||
| class NoteDetailFile extends TabAwareWidget{ | ||||
| class FileTypeWidget extends TypeWidget { | ||||
|     static getType() { return "file"; } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$fileNoteId = this.$widget.find(".file-note-id"); | ||||
| @ -115,7 +117,7 @@ class NoteDetailFile extends TabAwareWidget{ | ||||
|         return this.$widget; | ||||
|     } | ||||
| 
 | ||||
|     async refresh() { | ||||
|     async doRefresh() { | ||||
|         const attributes = await server.get('notes/' + this.tabContext.note.noteId + '/attributes'); | ||||
|         const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); | ||||
| 
 | ||||
| @ -155,4 +157,4 @@ class NoteDetailFile extends TabAwareWidget{ | ||||
|     scrollToTop() {} | ||||
| } | ||||
| 
 | ||||
| export default NoteDetailFile; | ||||
| export default FileTypeWidget; | ||||
| @ -3,6 +3,7 @@ import treeService from '../../services/tree.js'; | ||||
| import noteAutocompleteService from '../../services/note_autocomplete.js'; | ||||
| import mimeTypesService from '../../services/mime_types.js'; | ||||
| import TabAwareWidget from "../tab_aware_widget.js"; | ||||
| import TypeWidget from "./type_widget.js"; | ||||
| 
 | ||||
| const ENABLE_INSPECTOR = false; | ||||
| 
 | ||||
| @ -41,7 +42,7 @@ const mentionSetup = { | ||||
| }; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail-text note-detail-component"> | ||||
| <div class="note-detail-text note-detail-printable"> | ||||
|     <style> | ||||
|     .note-detail-text h1 { font-size: 2.0em; } | ||||
|     .note-detail-text h2 { font-size: 1.8em; } | ||||
| @ -72,7 +73,9 @@ const TPL = ` | ||||
| </div> | ||||
| `;
 | ||||
| 
 | ||||
| class NoteDetailText extends TabAwareWidget { | ||||
| class TextTypeWidget extends TypeWidget { | ||||
|     static getType() { return "text"; } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$editor = this.$widget.find('.note-detail-text-editor'); | ||||
| @ -130,15 +133,12 @@ class NoteDetailText extends TabAwareWidget { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async refresh() { | ||||
|         // lazy loading above can take time and tab might have been already switched to another note
 | ||||
|         if (this.tabContext.note && this.tabContext.note.type === 'text') { | ||||
|             this.textEditor.isReadOnly = await this.isReadOnly(); | ||||
|     async doRefresh() { | ||||
|         this.textEditor.isReadOnly = await this.isReadOnly(); | ||||
| 
 | ||||
|             this.$widget.show(); | ||||
|         this.$widget.show(); | ||||
| 
 | ||||
|             this.textEditor.setData(this.tabContext.note.content); | ||||
|         } | ||||
|         this.textEditor.setData(this.tabContext.note.content); | ||||
|     } | ||||
| 
 | ||||
|     getContent() { | ||||
| @ -188,4 +188,4 @@ class NoteDetailText extends TabAwareWidget { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default NoteDetailText; | ||||
| export default TextTypeWidget; | ||||
							
								
								
									
										18
									
								
								src/public/javascripts/widgets/type_widgets/type_widget.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/public/javascripts/widgets/type_widgets/type_widget.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| import TabAwareWidget from "../tab_aware_widget.js"; | ||||
| 
 | ||||
| export default class TypeWidget extends TabAwareWidget { | ||||
|     // for overriding
 | ||||
|     static getType() {} | ||||
| 
 | ||||
|     doRefresh() {} | ||||
| 
 | ||||
|     refresh() { | ||||
|         if (!this.tabContext.note || this.tabContext.note.type !== this.constructor.getType()) { | ||||
|             this.toggle(false); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.doRefresh(); | ||||
|     } | ||||
| } | ||||
| @ -105,14 +105,3 @@ span.fancytree-expander { | ||||
|     border-width: 2px; | ||||
|     border-style: solid; | ||||
| } | ||||
| 
 | ||||
| .note-detail-component-wrapper { | ||||
|     position: relative; | ||||
|     overflow: auto; | ||||
|     flex-direction: column; | ||||
|     /* for some reason detail overflows a little bit so we subtract few pixels */ | ||||
|     height: calc(100% - 25px); | ||||
|     /* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */ | ||||
|     padding-left: 35px; | ||||
|     padding-top: 10px; | ||||
| } | ||||
| @ -100,25 +100,6 @@ span.fancytree-node.muted { opacity: 0.6; } | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .note-detail-component-wrapper { | ||||
|     flex-grow: 100; | ||||
|     position: relative; | ||||
|     overflow: auto; | ||||
|     flex-basis: content; | ||||
|     height: 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     min-height: 500px; | ||||
|     overflow-wrap: break-word; /* otherwise CKEditor fails miserably on super long lines */ | ||||
|     font-family: var(--detail-font-family); | ||||
|     font-size: var(--detail-font-size); | ||||
| } | ||||
| 
 | ||||
| .note-detail-component { | ||||
|     flex-grow: 100; | ||||
|     display: none; | ||||
| } | ||||
| 
 | ||||
| /** we disable shield background when in zen mode because I couldn't get it to stay static | ||||
|     (it kept growing with content) */ | ||||
| #container:not(.zen-mode) .note-tab-content.protected { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-book note-detail-component"> | ||||
| <div class="note-detail-book note-detail-printable"> | ||||
|     <div class="btn-group floating-button" style="right: 20px; top: 20px;"> | ||||
|         <button type="button" | ||||
|                 class="expand-children-button btn icon-button bx bx-move-vertical" | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-empty note-detail-component"> | ||||
| <div class="note-detail-empty note-detail-printable"> | ||||
|     <div class="form-group"> | ||||
|         <label>Open note by typing note's title into input below or choose a note in the tree.</label> | ||||
|         <div class="input-group"> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-image note-detail-component"> | ||||
| <div class="note-detail-image note-detail-printable"> | ||||
|     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> | ||||
|         <button class="image-download btn btn-sm btn-primary" type="button">Download</button> | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="protected-session-password-component note-detail-component"> | ||||
| <div class="protected-session-password-component note-detail-printable"> | ||||
|     <form class="protected-session-password-form"> | ||||
|         <div class="form-group"> | ||||
|             <label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-relation-map note-detail-component"> | ||||
| <div class="note-detail-relation-map note-detail-printable"> | ||||
|     <button class="relation-map-create-child-note btn btn-sm floating-button" type="button" | ||||
|             title="Create new child note and add it into this relation map"> | ||||
|         <span class="bx bx-folder-plus"></span> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-render note-detail-component"> | ||||
| <div class="note-detail-render note-detail-printable"> | ||||
|     <div class="note-detail-render-help alert alert-warning"> | ||||
|         <p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p> | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <div class="note-detail-search note-detail-component"> | ||||
| <div class="note-detail-search note-detail-printable"> | ||||
|     <div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;"> | ||||
|         <strong>Search string:    </strong> | ||||
|         <textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea> | ||||
|  | ||||
| @ -53,12 +53,12 @@ | ||||
|                         </button> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="note-detail-component-wrapper"> | ||||
|                         <div class="note-detail-text note-detail-component" tabindex="10000"> | ||||
|                     <div class="note-detail-printable-wrapper"> | ||||
|                         <div class="note-detail-text note-detail-printable" tabindex="10000"> | ||||
|                             <div class="note-detail-text-editor"></div> | ||||
|                         </div> | ||||
| 
 | ||||
|                         <div class="note-detail-code note-detail-component"> | ||||
|                         <div class="note-detail-code note-detail-printable"> | ||||
|                             <div class="note-detail-code-editor"></div> | ||||
|                         </div> | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam