mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	WIP per-tab hoisting
This commit is contained in:
		| @@ -80,14 +80,13 @@ export default class Entrypoints extends Component { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async toggleNoteHoistingCommand() { |     async toggleNoteHoistingCommand() { | ||||||
|         const note = appContext.tabManager.getActiveTabNote(); |         const tabContext = appContext.tabManager.getActiveTabContext(); | ||||||
|  |  | ||||||
|         const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); |         if (tabContext.note.noteId === tabContext.hoistedNoteId) { | ||||||
|         if (note.noteId === hoistedNoteId) { |             await tabContext.unhoist(); | ||||||
|             hoistedNoteService.unhoist(); |  | ||||||
|         } |         } | ||||||
|         else if (note.type !== 'search') { |         else if (tabContext.note.type !== 'search') { | ||||||
|             hoistedNoteService.setHoistedNoteId(note.noteId); |             await tabContext.setHoistedNoteId(tabContext.note.noteId); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import options from './options.js'; | import options from './options.js'; | ||||||
| import appContext from "./app_context.js"; | import appContext from "./app_context.js"; | ||||||
| import treeService from "./tree.js"; | import treeService from "./tree.js"; | ||||||
| import treeCache from "./tree_cache.js"; |  | ||||||
|  |  | ||||||
| function getHoistedNoteId() { | function getHoistedNoteId() { | ||||||
|     return options.get('hoistedNoteId'); |     return options.get('hoistedNoteId'); | ||||||
| @@ -14,9 +13,6 @@ async function setHoistedNoteId(noteId) { | |||||||
|  |  | ||||||
|     await options.save('hoistedNoteId', noteId); |     await options.save('hoistedNoteId', noteId); | ||||||
|  |  | ||||||
|     await treeCache.loadInitialTree(); |  | ||||||
|  |  | ||||||
|     // FIXME - just use option load event |  | ||||||
|     appContext.triggerEvent('hoistedNoteChanged', {noteId}); |     appContext.triggerEvent('hoistedNoteChanged', {noteId}); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,10 +11,11 @@ class TabContext extends Component { | |||||||
|     /** |     /** | ||||||
|      * @param {string|null} tabId |      * @param {string|null} tabId | ||||||
|      */ |      */ | ||||||
|     constructor(tabId = null) { |     constructor(tabId = null, hoistedNoteId = 'root') { | ||||||
|         super(); |         super(); | ||||||
|  |  | ||||||
|         this.tabId = tabId || utils.randomString(4); |         this.tabId = tabId || utils.randomString(4); | ||||||
|  |         this.hoistedNoteId = hoistedNoteId; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setEmpty() { |     setEmpty() { | ||||||
| @@ -123,10 +124,24 @@ class TabContext extends Component { | |||||||
|         return { |         return { | ||||||
|             tabId: this.tabId, |             tabId: this.tabId, | ||||||
|             notePath: this.notePath, |             notePath: this.notePath, | ||||||
|  |             hoistedNoteId: this.hoistedNoteId, | ||||||
|             active: this.isActive() |             active: this.isActive() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async unhoist() { | ||||||
|  |         await this.setHoistedNoteId('root'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async setHoistedNoteId(noteIdToHoist) { | ||||||
|  |         this.hoistedNoteId = noteIdToHoist; | ||||||
|  |  | ||||||
|  |         await this.triggerEvent('hoistedNoteChanged', { | ||||||
|  |             noteId: noteIdToHoist, | ||||||
|  |             tabId: this.tabId | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async entitiesReloadedEvent({loadResults}) { |     async entitiesReloadedEvent({loadResults}) { | ||||||
|         if (loadResults.isNoteReloaded(this.noteId)) { |         if (loadResults.isNoteReloaded(this.noteId)) { | ||||||
|             const note = await treeCache.getNote(this.noteId); |             const note = await treeCache.getNote(this.noteId); | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ export default class TabManager extends Component { | |||||||
|  |  | ||||||
|         await this.tabsUpdate.allowUpdateWithoutChange(async () => { |         await this.tabsUpdate.allowUpdateWithoutChange(async () => { | ||||||
|             for (const tab of filteredTabs) { |             for (const tab of filteredTabs) { | ||||||
|                 await this.openTabWithNote(tab.notePath, tab.active, tab.tabId); |                 await this.openTabWithNote(tab.notePath, tab.active, tab.tabId, tab.hoistedNoteId); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -184,8 +184,8 @@ export default class TabManager extends Component { | |||||||
|         await tabContext.setEmpty(); |         await tabContext.setEmpty(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async openEmptyTab(tabId) { |     async openEmptyTab(tabId, hoistedNoteId) { | ||||||
|         const tabContext = new TabContext(tabId); |         const tabContext = new TabContext(tabId, hoistedNoteId); | ||||||
|         this.child(tabContext); |         this.child(tabContext); | ||||||
|  |  | ||||||
|         await this.triggerEvent('newTabOpened', {tabContext}); |         await this.triggerEvent('newTabOpened', {tabContext}); | ||||||
| @@ -193,7 +193,7 @@ export default class TabManager extends Component { | |||||||
|         return tabContext; |         return tabContext; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async openTabWithNote(notePath, activate, tabId = null) { |     async openTabWithNote(notePath, activate, tabId = null, hoistedNoteId = 'root') { | ||||||
|         const tabContext = await this.openEmptyTab(tabId); |         const tabContext = await this.openEmptyTab(tabId); | ||||||
|  |  | ||||||
|         if (notePath) { |         if (notePath) { | ||||||
| @@ -336,16 +336,4 @@ export default class TabManager extends Component { | |||||||
|  |  | ||||||
|         this.triggerCommand('openInWindow', {notePath}); |         this.triggerCommand('openInWindow', {notePath}); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async hoistedNoteChangedEvent({hoistedNoteId}) { |  | ||||||
|         if (hoistedNoteId === 'root') { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (const tc of this.tabContexts.splice()) { |  | ||||||
|             if (tc.notePath && !tc.notePath.split("/").includes(hoistedNoteId)) { |  | ||||||
|                 await this.removeTab(tc.tabId); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -254,8 +254,7 @@ class TreeCache { | |||||||
|                 console.trace(`Can't find note "${noteId}"`); |                 console.trace(`Can't find note "${noteId}"`); | ||||||
|  |  | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } else { | ||||||
|             else { |  | ||||||
|                 return this.notes[noteId]; |                 return this.notes[noteId]; | ||||||
|             } |             } | ||||||
|         }).filter(note => !!note); |         }).filter(note => !!note); | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| import treeService from './tree.js'; | import treeService from './tree.js'; | ||||||
| import treeCache from "./tree_cache.js"; | import treeCache from "./tree_cache.js"; | ||||||
| import hoistedNoteService from './hoisted_note.js'; |  | ||||||
| import clipboard from './clipboard.js'; | import clipboard from './clipboard.js'; | ||||||
| import protectedSessionHolder from "./protected_session_holder.js"; |  | ||||||
| import noteCreateService from "./note_create.js"; | import noteCreateService from "./note_create.js"; | ||||||
| import contextMenu from "./context_menu.js"; | import contextMenu from "./context_menu.js"; | ||||||
|  | import appContext from "./app_context.js"; | ||||||
|  |  | ||||||
| class TreeContextMenu { | class TreeContextMenu { | ||||||
|     /** |     /** | ||||||
| @@ -40,7 +39,7 @@ class TreeContextMenu { | |||||||
|         const note = await treeCache.getNote(this.node.data.noteId); |         const note = await treeCache.getNote(this.node.data.noteId); | ||||||
|         const branch = treeCache.getBranch(this.node.data.branchId); |         const branch = treeCache.getBranch(this.node.data.branchId); | ||||||
|         const isNotRoot = note.noteId !== 'root'; |         const isNotRoot = note.noteId !== 'root'; | ||||||
|         const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId(); |         const isHoisted = note.noteId === appContext.tabManager.getActiveTabContext().hoistedNoteId; | ||||||
|         const parentNote = isNotRoot ? await treeCache.getNote(branch.parentNoteId) : null; |         const parentNote = isNotRoot ? await treeCache.getNote(branch.parentNoteId) : null; | ||||||
|  |  | ||||||
|         // some actions don't support multi-note so they are disabled when notes are selected |         // some actions don't support multi-note so they are disabled when notes are selected | ||||||
| @@ -69,7 +68,7 @@ class TreeContextMenu { | |||||||
|             { title: 'Search in subtree <kbd data-command="searchInSubtree"></kbd>', command: "searchInSubtree", uiIcon: "search", |             { title: 'Search in subtree <kbd data-command="searchInSubtree"></kbd>', command: "searchInSubtree", uiIcon: "search", | ||||||
|                 enabled: notSearch && noSelectedNotes }, |                 enabled: notSearch && noSelectedNotes }, | ||||||
|             isHoisted ? null : { title: 'Hoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, |             isHoisted ? null : { title: 'Hoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, | ||||||
|             !isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-command="ToggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "arrow-from-bottom" }, |             !isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "arrow-from-bottom" }, | ||||||
|             { title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "empty", |             { title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "empty", | ||||||
|                 enabled: isNotRoot && parentNotSearch && noSelectedNotes}, |                 enabled: isNotRoot && parentNotSearch && noSelectedNotes}, | ||||||
|             { title: "Advanced", uiIcon: "empty", enabled: true, items: [ |             { title: "Advanced", uiIcon: "empty", enabled: true, items: [ | ||||||
|   | |||||||
| @@ -263,9 +263,11 @@ export default class NoteDetailWidget extends TabAwareWidget { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hoistedNoteChangedEvent() { |     hoistedNoteChangedEvent({tabId}) { | ||||||
|  |         if (this.isTab(tabId)) { | ||||||
|             this.refresh(); |             this.refresh(); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async entitiesReloadedEvent({loadResults}) { |     async entitiesReloadedEvent({loadResults}) { | ||||||
|         if (loadResults.isNoteContentReloaded(this.noteId, this.componentId) |         if (loadResults.isNoteContentReloaded(this.noteId, this.componentId) | ||||||
|   | |||||||
| @@ -290,7 +290,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|         this.$tree.fancytree({ |         this.$tree.fancytree({ | ||||||
|             titlesTabbable: true, |             titlesTabbable: true, | ||||||
|             keyboard: true, |             keyboard: true, | ||||||
|             extensions: ["dnd5", "clones"], |             extensions: ["dnd5", "clones", "filter"], | ||||||
|             source: treeData, |             source: treeData, | ||||||
|             scrollOfs: { |             scrollOfs: { | ||||||
|                 top: 100, |                 top: 100, | ||||||
| @@ -341,6 +341,11 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|             }, |             }, | ||||||
|             expand: (event, data) => this.setExpanded(data.node.data.branchId, true), |             expand: (event, data) => this.setExpanded(data.node.data.branchId, true), | ||||||
|             collapse: (event, data) => this.setExpanded(data.node.data.branchId, false), |             collapse: (event, data) => this.setExpanded(data.node.data.branchId, false), | ||||||
|  |             filter: { | ||||||
|  |                 counter: false, | ||||||
|  |                 mode: "hide", | ||||||
|  |                 autoExpand: true | ||||||
|  |             }, | ||||||
|             dnd5: { |             dnd5: { | ||||||
|                 autoExpandMS: 600, |                 autoExpandMS: 600, | ||||||
|                 dragStart: (node, data) => { |                 dragStart: (node, data) => { | ||||||
| @@ -446,21 +451,6 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|                 const node = data.node; |                 const node = data.node; | ||||||
|                 const $span = $(node.span); |                 const $span = $(node.span); | ||||||
|  |  | ||||||
|                 if (node.data.noteId !== 'root' |  | ||||||
|                     && node.data.noteId === hoistedNoteService.getHoistedNoteId() |  | ||||||
|                     && $span.find('.unhoist-button').length === 0) { |  | ||||||
|  |  | ||||||
|                     const action = await keyboardActionsService.getAction('unhoist'); |  | ||||||
|                     let shortcuts = action.effectiveShortcuts.join(','); |  | ||||||
|                     shortcuts = shortcuts ? `(${shortcuts})` : ''; |  | ||||||
|  |  | ||||||
|                     const unhoistButton = $(`<span class="unhoist-button-wrapper" title="Unhoist current note to show the whole note tree ${shortcuts}">[<a class="unhoist-button">unhoist</a>]</span>`); |  | ||||||
|  |  | ||||||
|                     // prepending since appending could push out (when note title is too long) |  | ||||||
|                     // the button too much to the right so that it's not visible |  | ||||||
|                     $span.prepend(unhoistButton); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 const note = await treeCache.getNote(node.data.noteId); |                 const note = await treeCache.getNote(node.data.noteId); | ||||||
|  |  | ||||||
|                 if (note.type === 'search' && $span.find('.refresh-search-button').length === 0) { |                 if (note.type === 'search' && $span.find('.refresh-search-button').length === 0) { | ||||||
| @@ -512,17 +502,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|     prepareRootNode() { |     prepareRootNode() { | ||||||
|         const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); |         const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); | ||||||
|  |  | ||||||
|         let hoistedBranch; |         return this.prepareNode(treeCache.getBranch('root')); | ||||||
|  |  | ||||||
|         if (hoistedNoteId === 'root') { |  | ||||||
|             hoistedBranch = treeCache.getBranch('root'); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             const hoistedNote = treeCache.getNoteFromCache(hoistedNoteId); |  | ||||||
|             hoistedBranch = hoistedNote.getBranches()[0]; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return this.prepareNode(hoistedBranch); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -897,6 +877,8 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|                 newActiveNode.makeVisible({scrollIntoView: true}); |                 newActiveNode.makeVisible({scrollIntoView: true}); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         this.filterHoistedBranch(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async refreshSearch() { |     async refreshSearch() { | ||||||
| @@ -1156,8 +1138,16 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hoistedNoteChangedEvent() { |     async hoistedNoteChangedEvent({tabId}) { | ||||||
|         this.reloadTreeFromCache(); |         if (this.isTab(tabId)) { | ||||||
|  |             this.filterHoistedBranch(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     filterHoistedBranch() { | ||||||
|  |         if (this.tabContext) { | ||||||
|  |             this.tree.filterBranches(node => node.data.noteId === this.tabContext.hoistedNoteId); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     treeCacheReloadedEvent() { |     treeCacheReloadedEvent() { | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ export default class BookTypeWidget extends TypeWidget { | |||||||
|                 .append(' if you want to add some text.')); |                 .append(' if you want to add some text.')); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const noteListRenderer = new NoteListRenderer(note, await note.getChildNotes()); |         const noteListRenderer = new NoteListRenderer(note, note.getChildNoteIds()); | ||||||
|  |  | ||||||
|         this.$content.append(await noteListRenderer.renderList()); |         this.$content.append(await noteListRenderer.renderList()); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -81,6 +81,16 @@ span.fancytree-node.dotted .fancytree-title { text-decoration: dotted; } | |||||||
| span.fancytree-node.bold .fancytree-title { font-weight: bold; } | span.fancytree-node.bold .fancytree-title { font-weight: bold; } | ||||||
| span.fancytree-node.muted { opacity: 0.6; } | span.fancytree-node.muted { opacity: 0.6; } | ||||||
|  |  | ||||||
|  | /** following will hide ancestors of hoisted (filtered) note */ | ||||||
|  | .fancytree-submatch:not(.fancytree-match) { | ||||||
|  |     display: none !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** resets indent of hoisted note */ | ||||||
|  | .fancytree-submatch:not(.fancytree-match) + ul { | ||||||
|  |     padding: 0 !important; | ||||||
|  | } | ||||||
|  |  | ||||||
| .note-title[readonly] { | .note-title[readonly] { | ||||||
|     background: inherit; |     background: inherit; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ function getNotesAndBranchesAndAttributes(noteIds) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function getTree(req) { | function getTree(req) { | ||||||
|     const subTreeNoteId = req.query.subTreeNoteId || optionService.getOption('hoistedNoteId'); |     const subTreeNoteId = req.query.subTreeNoteId || 'root'; | ||||||
|  |  | ||||||
|     // FIXME: this query does not return ascendants of template notes |     // FIXME: this query does not return ascendants of template notes | ||||||
|     const noteIds = sql.getColumn(` |     const noteIds = sql.getColumn(` | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user