mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 04:51:31 +08:00 
			
		
		
		
	widgetizing note detail WIP
This commit is contained in:
		
							parent
							
								
									8b9c235465
								
							
						
					
					
						commit
						c9bc4ad108
					
				| @ -10,6 +10,7 @@ import keyboardActionService from "./keyboard_actions.js"; | ||||
| import TabRowWidget from "./tab_row.js"; | ||||
| import NoteTitleWidget from "../widgets/note_title.js"; | ||||
| import PromotedAttributesWidget from "../widgets/promoted_attributes.js"; | ||||
| import NoteDetailWidget from "../widgets/note_detail.js"; | ||||
| 
 | ||||
| class AppContext { | ||||
|     constructor() { | ||||
| @ -29,29 +30,40 @@ class AppContext { | ||||
| 
 | ||||
|         $("#global-menu-wrapper").after(contents); | ||||
| 
 | ||||
|         this.promotedAttributes = new PromotedAttributesWidget(this); | ||||
|         $("#center-pane").prepend(this.promotedAttributes.render()); | ||||
| 
 | ||||
|         this.noteTitleWidget = new NoteTitleWidget(this); | ||||
|         $("#center-pane").prepend(this.noteTitleWidget.render()); | ||||
| 
 | ||||
|         this.noteTreeWidget = new NoteTreeWidget(this); | ||||
| 
 | ||||
|         this.widgets = [ | ||||
|         const leftPaneWidgets = [ | ||||
|             new GlobalButtonsWidget(this), | ||||
|             new SearchBoxWidget(this), | ||||
|             new SearchResultsWidget(this), | ||||
|             this.noteTreeWidget | ||||
|         ]; | ||||
| 
 | ||||
|         for (const widget of this.widgets) { | ||||
|         for (const widget of leftPaneWidgets) { | ||||
|             const $widget = widget.render(); | ||||
| 
 | ||||
|             $leftPane.append($widget); | ||||
|         } | ||||
| 
 | ||||
|         this.widgets.push(this.noteTitleWidget); | ||||
|         this.widgets.push(this.promotedAttributes); | ||||
|         const $centerPane = $("#center-pane"); | ||||
| 
 | ||||
|         const centerPaneWidgets = [ | ||||
|             new NoteTitleWidget(this), | ||||
|             new PromotedAttributesWidget(this), | ||||
|             new NoteDetailWidget(this) | ||||
|         ]; | ||||
| 
 | ||||
|         for (const widget of centerPaneWidgets) { | ||||
|             const $widget = widget.render(); | ||||
| 
 | ||||
|             $centerPane.append($widget); | ||||
|         } | ||||
| 
 | ||||
|         this.widgets = [ | ||||
|             this.tabRow, | ||||
|             ...leftPaneWidgets, | ||||
|             ...centerPaneWidgets | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     trigger(name, data) { | ||||
|  | ||||
| @ -5,6 +5,11 @@ import server from "./server.js"; | ||||
| import noteDetailService from "./note_detail.js"; | ||||
| import keyboardActionService from "./keyboard_actions.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail-code note-detail-component"> | ||||
|     <div class="note-detail-code-editor"></div> | ||||
| </div>`; | ||||
| 
 | ||||
| class NoteDetailCode { | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -39,13 +39,46 @@ const mentionSetup = { | ||||
|     ] | ||||
| }; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail-text note-detail-component"> | ||||
|     <style> | ||||
|     .note-detail-text h1 { font-size: 2.0em; } | ||||
|     .note-detail-text h2 { font-size: 1.8em; } | ||||
|     .note-detail-text h3 { font-size: 1.6em; } | ||||
|     .note-detail-text h4 { font-size: 1.4em; } | ||||
|     .note-detail-text h5 { font-size: 1.2em; } | ||||
|     .note-detail-text h6 { font-size: 1.1em; } | ||||
|      | ||||
|     .note-detail-text { | ||||
|         overflow: auto; | ||||
|         font-family: var(--detail-text-font-family); | ||||
|     } | ||||
|      | ||||
|     .note-detail-text-editor { | ||||
|         padding-top: 10px; | ||||
|         border: 0 !important; | ||||
|         box-shadow: none !important; | ||||
|         /* This is because with empty content height of editor is 0 and it's impossible to click into it */ | ||||
|         min-height: 500px; | ||||
|     } | ||||
|      | ||||
|     .note-detail-text p:first-child, .note-detail-text::before { | ||||
|         margin-top: 0; | ||||
|     } | ||||
|     </style> | ||||
| 
 | ||||
|     <div class="note-detail-text-editor" tabindex="10000"></div> | ||||
| </div> | ||||
| `;
 | ||||
| 
 | ||||
| class NoteDetailText { | ||||
|     /** | ||||
|      * @param {TabContext} ctx | ||||
|      */ | ||||
|     constructor(ctx) { | ||||
|     constructor(ctx, $parent) { | ||||
|         this.$component = $(TPL); | ||||
|         $parent.append(this.$component); | ||||
|         this.ctx = ctx; | ||||
|         this.$component = ctx.$tabContent.find('.note-detail-text'); | ||||
|         this.$editorEl = this.$component.find('.note-detail-text-editor'); | ||||
|         this.textEditorPromise = null; | ||||
|         this.textEditor = null; | ||||
|  | ||||
| @ -8,21 +8,6 @@ import optionsService from "./options.js"; | ||||
| import Sidebar from "./sidebar.js"; | ||||
| import appContext from "./app_context.js"; | ||||
| 
 | ||||
| const $tabContentsContainer = $("#note-tab-container"); | ||||
| 
 | ||||
| const componentClasses = { | ||||
|     'empty': "./note_detail_empty.js", | ||||
|     'text': "./note_detail_text.js", | ||||
|     'code': "./note_detail_code.js", | ||||
|     'file': "./note_detail_file.js", | ||||
|     'image': "./note_detail_image.js", | ||||
|     'search': "./note_detail_search.js", | ||||
|     'render': "./note_detail_render.js", | ||||
|     'relation-map': "./note_detail_relation_map.js", | ||||
|     'protected-session': "./note_detail_protected_session.js", | ||||
|     'book': "./note_detail_book.js" | ||||
| }; | ||||
| 
 | ||||
| let showSidebarInNewTab = true; | ||||
| 
 | ||||
| optionsService.addLoadListener(options => { | ||||
| @ -49,15 +34,8 @@ class TabContext { | ||||
| 
 | ||||
|         this.initialized = true; | ||||
| 
 | ||||
|         this.$tabContent = $(".note-tab-content-template").clone(); | ||||
|         this.$tabContent.removeClass('note-tab-content-template'); | ||||
|         this.$tabContent.attr('data-tab-id', this.tabId); | ||||
|         this.$tabContent.hide(); | ||||
|         this.$tabContent = $("<div>"); // FIXME
 | ||||
| 
 | ||||
|         $tabContentsContainer.append(this.$tabContent); | ||||
| 
 | ||||
|         this.$noteDetailComponents = this.$tabContent.find(".note-detail-component"); | ||||
|         this.$scriptArea = this.$tabContent.find(".note-detail-script-area"); | ||||
|         this.noteChangeDisabled = false; | ||||
|         this.isNoteChanged = false; | ||||
|         this.attributes = new Attributes(this); | ||||
| @ -69,20 +47,6 @@ class TabContext { | ||||
| 
 | ||||
|             this.sidebar = new Sidebar(this, sidebarState); | ||||
|         } | ||||
| 
 | ||||
|         this.components = {}; | ||||
| 
 | ||||
|         await this.initComponent(); | ||||
|     } | ||||
| 
 | ||||
|     async initComponent(disableAutoBook = false) { | ||||
|         this.type = this.getComponentType(disableAutoBook); | ||||
| 
 | ||||
|         if (!(this.type in this.components)) { | ||||
|             const clazz = await import(componentClasses[this.type]); | ||||
| 
 | ||||
|             this.components[this.type] = new clazz.default(this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async setNote(note, notePath) { | ||||
| @ -95,8 +59,6 @@ class TabContext { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.$scriptArea.empty(); | ||||
| 
 | ||||
|         if (utils.isDesktop()) { | ||||
|             this.attributes.refreshAttributes(); | ||||
|         } else { | ||||
| @ -115,7 +77,7 @@ class TabContext { | ||||
|         this.noteChangeDisabled = true; | ||||
| 
 | ||||
|         try { | ||||
|             await this.renderComponent(); | ||||
| 
 | ||||
|         } finally { | ||||
|             this.noteChangeDisabled = false; | ||||
|         } | ||||
| @ -137,7 +99,8 @@ class TabContext { | ||||
|         bundleService.executeRelationBundles(this.note, 'runOnNoteView', this); | ||||
| 
 | ||||
|         // after loading new note make sure editor is scrolled to the top
 | ||||
|         this.getComponent().scrollToTop(); | ||||
|         // FIXME
 | ||||
|         //this.getComponent().scrollToTop();
 | ||||
| 
 | ||||
|         appContext.trigger('activeNoteChanged'); | ||||
|     } | ||||
| @ -146,18 +109,15 @@ class TabContext { | ||||
|         if (!this.initialized) { | ||||
|             await this.initTabContent(); | ||||
| 
 | ||||
|             this.$tabContent.show(); // show immediately so that user can see something
 | ||||
| 
 | ||||
|             if (this.note) { | ||||
|                 await this.setNote(this.note, this.notePath); | ||||
|             } | ||||
|             else { | ||||
|                 // FIXME
 | ||||
|                 await this.renderComponent(); // render empty page
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.$tabContent.show(); | ||||
| 
 | ||||
|         if (this.sidebar) { | ||||
|             this.sidebar.show(); | ||||
|         } | ||||
| @ -167,18 +127,7 @@ class TabContext { | ||||
|     } | ||||
| 
 | ||||
|     async renderComponent(disableAutoBook = false) { | ||||
|         await this.initComponent(disableAutoBook); | ||||
| 
 | ||||
|         for (const componentType in this.components) { | ||||
|             if (componentType !== this.type) { | ||||
|                 this.components[componentType].cleanup(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.$noteDetailComponents.hide(); | ||||
| 
 | ||||
|         this.getComponent().show(); | ||||
|         await this.getComponent().render(); | ||||
|         // FIXME
 | ||||
|     } | ||||
| 
 | ||||
|     setTitleBar() { | ||||
| @ -235,36 +184,11 @@ class TabContext { | ||||
|     } | ||||
| 
 | ||||
|     getComponent() { | ||||
|         if (!this.components[this.type]) { | ||||
|             throw new Error("Could not find component for type: " + this.type); | ||||
|         } | ||||
| 
 | ||||
|         return this.components[this.type]; | ||||
|         // FIXME
 | ||||
|     } | ||||
| 
 | ||||
|     getComponentType(disableAutoBook) { | ||||
|         if (!this.note) { | ||||
|             return "empty"; | ||||
|         } | ||||
| 
 | ||||
|         let type = this.note.type; | ||||
| 
 | ||||
|         if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(this.note.content) && this.note.hasChildren()) { | ||||
|             type = 'book'; | ||||
|         } | ||||
| 
 | ||||
|         if (this.note.isProtected) { | ||||
|             if (protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|                 protectedSessionHolder.touchProtectedSession(); | ||||
|             } else { | ||||
|                 type = 'protected-session'; | ||||
| 
 | ||||
|                 // user shouldn't be able to edit note title
 | ||||
|                 this.$noteTitle.prop("readonly", true); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return type; | ||||
|     // FIXME
 | ||||
|     } | ||||
| 
 | ||||
|     async activate() { | ||||
| @ -272,6 +196,8 @@ class TabContext { | ||||
|     } | ||||
| 
 | ||||
|     async saveNote() { | ||||
|         return; // FIXME
 | ||||
| 
 | ||||
|         if (this.note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|             return; | ||||
|         } | ||||
| @ -316,8 +242,8 @@ class TabContext { | ||||
| 
 | ||||
|         this.isNoteChanged = true; | ||||
| 
 | ||||
|         // FIMXE: trigger noteChanged event
 | ||||
|         this.$savedIndicator.fadeOut(); | ||||
|         // FIXME: trigger noteChanged event
 | ||||
|         //this.$savedIndicator.fadeOut();
 | ||||
|     } | ||||
| 
 | ||||
|     async remove() { | ||||
|  | ||||
							
								
								
									
										108
									
								
								src/public/javascripts/widgets/note_detail.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/public/javascripts/widgets/note_detail.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| import TabAwareWidget from "./tab_aware_widget.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import protectedSessionHolder from "../services/protected_session_holder.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="note-detail"> | ||||
|     <style> | ||||
|     .note-detail-content { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         flex-grow: 100; | ||||
|         height: 100%; | ||||
|         width: 100%; | ||||
|     }   | ||||
|     </style> | ||||
| </div> | ||||
| `;
 | ||||
| 
 | ||||
| const componentClasses = { | ||||
|     'empty': "../services/note_detail_empty.js", | ||||
|     'text': "../services/note_detail_text.js", | ||||
|     'code': "../services/note_detail_code.js", | ||||
|     'file': "../services/note_detail_file.js", | ||||
|     'image': "../services/note_detail_image.js", | ||||
|     'search': "../services/note_detail_search.js", | ||||
|     'render': "../services/note_detail_render.js", | ||||
|     'relation-map': "../services/note_detail_relation_map.js", | ||||
|     'protected-session': "../services/note_detail_protected_session.js", | ||||
|     'book': "../services/note_detail_book.js" | ||||
| }; | ||||
| 
 | ||||
| export default class NoteDetailWidget extends TabAwareWidget { | ||||
|     constructor(appContext) { | ||||
|         super(appContext); | ||||
| 
 | ||||
|         this.components = {}; | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
| 
 | ||||
|         return this.$widget; | ||||
|     } | ||||
| 
 | ||||
|     async activeTabChanged() { | ||||
|         await this.initComponent(/**disableAutoBook*/); | ||||
| 
 | ||||
|         for (const componentType in this.components) { | ||||
|             // FIXME
 | ||||
|             this.components[componentType].ctx = this.tabContext; | ||||
| 
 | ||||
|             if (componentType !== this.type) { | ||||
|                 this.components[componentType].cleanup(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.$widget.find('.note-detail-component').hide(); | ||||
| 
 | ||||
|         this.getComponent().show(); | ||||
|         await this.getComponent().render(); | ||||
|     } | ||||
| 
 | ||||
|     getComponent() { | ||||
|         if (!this.components[this.type]) { | ||||
|             throw new Error("Could not find component for type: " + this.type); | ||||
|         } | ||||
| 
 | ||||
|         return this.components[this.type]; | ||||
|     } | ||||
| 
 | ||||
|     async initComponent(disableAutoBook = false) { | ||||
|         this.type = this.getComponentType(disableAutoBook); | ||||
| 
 | ||||
|         if (!(this.type in this.components)) { | ||||
|             const clazz = await import(componentClasses[this.type]); | ||||
| 
 | ||||
|             this.components[this.type] = new clazz.default(this, this.$widget); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getComponentType(disableAutoBook) { | ||||
|         const note = this.tabContext.note; | ||||
| 
 | ||||
|         if (!note) { | ||||
|             return "empty"; | ||||
|         } | ||||
| 
 | ||||
|         let type = note.type; | ||||
| 
 | ||||
|         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);
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return type; | ||||
|     } | ||||
| } | ||||
| @ -100,14 +100,6 @@ span.fancytree-node.muted { opacity: 0.6; } | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .note-detail-content { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     flex-grow: 100; | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .note-detail-component-wrapper { | ||||
|     flex-grow: 100; | ||||
|     position: relative; | ||||
| @ -127,30 +119,6 @@ span.fancytree-node.muted { opacity: 0.6; } | ||||
|     display: none; | ||||
| } | ||||
| 
 | ||||
| .note-detail-text h1 { font-size: 2.0em; } | ||||
| .note-detail-text h2 { font-size: 1.8em; } | ||||
| .note-detail-text h3 { font-size: 1.6em; } | ||||
| .note-detail-text h4 { font-size: 1.4em; } | ||||
| .note-detail-text h5 { font-size: 1.2em; } | ||||
| .note-detail-text h6 { font-size: 1.1em; } | ||||
| 
 | ||||
| .note-detail-text { | ||||
|     overflow: auto; | ||||
|     font-family: var(--detail-text-font-family); | ||||
| } | ||||
| 
 | ||||
| .note-detail-text-editor { | ||||
|     padding-top: 10px; | ||||
|     border: 0 !important; | ||||
|     box-shadow: none !important; | ||||
|     /* This is because with empty content height of editor is 0 and it's impossible to click into it */ | ||||
|     min-height: 500px; | ||||
| } | ||||
| 
 | ||||
| .note-detail-text p:first-child, .note-detail-text::before { | ||||
|     margin-top: 0; | ||||
| } | ||||
| 
 | ||||
| /** 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,33 +0,0 @@ | ||||
| <div id="center-pane"> | ||||
|     <div id="note-tab-container"> | ||||
|         <div class="note-tab-content note-tab-content-template"> | ||||
|             <div class="note-detail-content"> | ||||
|                 <div class="note-detail-component-wrapper"> | ||||
|                     <div class="note-detail-text note-detail-component"> | ||||
|                         <div class="note-detail-text-editor" tabindex="10000"></div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="note-detail-code note-detail-component"> | ||||
|                         <div class="note-detail-code-editor"></div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <% include details/empty.ejs %> | ||||
| 
 | ||||
|                     <% include details/search.ejs %> | ||||
| 
 | ||||
|                     <% include details/render.ejs %> | ||||
| 
 | ||||
|                     <% include details/file.ejs %> | ||||
| 
 | ||||
|                     <% include details/image.ejs %> | ||||
| 
 | ||||
|                     <% include details/relation_map.ejs %> | ||||
| 
 | ||||
|                     <% include details/protected_session_password.ejs %> | ||||
| 
 | ||||
|                     <% include details/book.ejs %> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| @ -136,7 +136,7 @@ | ||||
|     <div style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;"> | ||||
|         <div id="left-pane" class="hide-in-zen-mode"></div> | ||||
| 
 | ||||
|         <% include center.ejs %> | ||||
|         <div id="center-pane"></div> | ||||
| 
 | ||||
|         <% include sidebar.ejs %> | ||||
|     </div> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam