mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	add a button to temporarily hide TOC, closes #3555
This commit is contained in:
		| @@ -15,6 +15,8 @@ class NoteContext extends Component { | |||||||
|         this.ntxId = ntxId || utils.randomString(4); |         this.ntxId = ntxId || utils.randomString(4); | ||||||
|         this.hoistedNoteId = hoistedNoteId; |         this.hoistedNoteId = hoistedNoteId; | ||||||
|         this.mainNtxId = mainNtxId; |         this.mainNtxId = mainNtxId; | ||||||
|  |  | ||||||
|  |         this.resetViewScope(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setEmpty() { |     setEmpty() { | ||||||
| @@ -27,6 +29,8 @@ class NoteContext extends Component { | |||||||
|             noteContext: this, |             noteContext: this, | ||||||
|             notePath: this.notePath |             notePath: this.notePath | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         this.resetViewScope(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     isEmpty() { |     isEmpty() { | ||||||
| @@ -47,7 +51,7 @@ class NoteContext extends Component { | |||||||
|         this.notePath = resolvedNotePath; |         this.notePath = resolvedNotePath; | ||||||
|         ({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath)); |         ({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath)); | ||||||
|  |  | ||||||
|         this.readOnlyTemporarilyDisabled = false; |         this.resetViewScope(); | ||||||
|  |  | ||||||
|         this.saveToRecentNotes(resolvedNotePath); |         this.saveToRecentNotes(resolvedNotePath); | ||||||
|  |  | ||||||
| @@ -60,6 +64,14 @@ class NoteContext extends Component { | |||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         await this.setHoistedNoteIfNeeded(); | ||||||
|  |  | ||||||
|  |         if (utils.isMobile()) { | ||||||
|  |             this.triggerCommand('setActiveScreen', {screen: 'detail'}); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async setHoistedNoteIfNeeded() { | ||||||
|         if (this.hoistedNoteId === 'root' |         if (this.hoistedNoteId === 'root' | ||||||
|             && this.notePath.startsWith("root/_hidden") |             && this.notePath.startsWith("root/_hidden") | ||||||
|             && !this.note.hasLabel("keepCurrentHoisting") |             && !this.note.hasLabel("keepCurrentHoisting") | ||||||
| @@ -76,10 +88,6 @@ class NoteContext extends Component { | |||||||
|  |  | ||||||
|             await this.setHoistedNoteId(hoistedNoteId); |             await this.setHoistedNoteId(hoistedNoteId); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (utils.isMobile()) { |  | ||||||
|             this.triggerCommand('setActiveScreen', {screen: 'detail'}); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     getSubContexts() { |     getSubContexts() { | ||||||
| @@ -201,7 +209,7 @@ class NoteContext extends Component { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async isReadOnly() { |     async isReadOnly() { | ||||||
|         if (this.readOnlyTemporarilyDisabled) { |         if (this.viewScope.readOnlyTemporarilyDisabled) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -277,6 +285,13 @@ class NoteContext extends Component { | |||||||
|             ntxId: this.ntxId |             ntxId: this.ntxId | ||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     resetViewScope() { | ||||||
|  |         // view scope contains data specific to one note context and one "view". | ||||||
|  |         // it is used to e.g. make read-only note temporarily editable or to hide TOC | ||||||
|  |         // this is reset after navigating to a different note | ||||||
|  |         this.viewScope = {}; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| export default NoteContext; | export default NoteContext; | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import froca from "../services/froca.js"; | |||||||
| export default class RootCommandExecutor extends Component { | export default class RootCommandExecutor extends Component { | ||||||
|     editReadOnlyNoteCommand() { |     editReadOnlyNoteCommand() { | ||||||
|         const noteContext = appContext.tabManager.getActiveContext(); |         const noteContext = appContext.tabManager.getActiveContext(); | ||||||
|         noteContext.readOnlyTemporarilyDisabled = true; |         noteContext.viewScope.readOnlyTemporarilyDisabled = true; | ||||||
|  |  | ||||||
|         appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext }); |         appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext }); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ export default class EditButton extends OnClickButtonWidget { | |||||||
|             .title("Edit this note") |             .title("Edit this note") | ||||||
|             .titlePlacement("bottom") |             .titlePlacement("bottom") | ||||||
|             .onClick(widget => { |             .onClick(widget => { | ||||||
|                 this.noteContext.readOnlyTemporarilyDisabled = true; |                 this.noteContext.viewScope.readOnlyTemporarilyDisabled = true; | ||||||
|  |  | ||||||
|                 appContext.triggerEvent('readOnlyTemporarilyDisabled', {noteContext: this.noteContext}); |                 appContext.triggerEvent('readOnlyTemporarilyDisabled', {noteContext: this.noteContext}); | ||||||
|  |  | ||||||
| @@ -56,7 +56,7 @@ export default class EditButton extends OnClickButtonWidget { | |||||||
|                 && attr.name.toLowerCase().includes("readonly") |                 && attr.name.toLowerCase().includes("readonly") | ||||||
|                 && attributeService.isAffecting(attr, this.note) |                 && attributeService.isAffecting(attr, this.note) | ||||||
|         )) { |         )) { | ||||||
|             this.noteContext.readOnlyTemporarilyDisabled = false; |             this.noteContext.viewScope.readOnlyTemporarilyDisabled = false; | ||||||
|  |  | ||||||
|             this.refresh(); |             this.refresh(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -24,17 +24,17 @@ export default class RightPaneContainer extends FlexContainer { | |||||||
|             // we'll reevaluate the visibility based on events which are probable to cause visibility change |             // we'll reevaluate the visibility based on events which are probable to cause visibility change | ||||||
|             // but these events needs to be finished and only then we check |             // but these events needs to be finished and only then we check | ||||||
|             if (promise) { |             if (promise) { | ||||||
|                 promise.then(() => this.reevaluateIsEnabledCommand()); |                 promise.then(() => this.reEvaluateRightPaneVisibilityCommand()); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 this.reevaluateIsEnabledCommand(); |                 this.reEvaluateRightPaneVisibilityCommand(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return promise; |         return promise; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     reevaluateIsEnabledCommand() { |     reEvaluateRightPaneVisibilityCommand() { | ||||||
|         const oldToggle = !this.isHiddenInt(); |         const oldToggle = !this.isHiddenInt(); | ||||||
|         const newToggle = this.isEnabled(); |         const newToggle = this.isEnabled(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ | |||||||
| import attributeService from "../services/attributes.js"; | import attributeService from "../services/attributes.js"; | ||||||
| import RightPanelWidget from "./right_panel_widget.js"; | import RightPanelWidget from "./right_panel_widget.js"; | ||||||
| import options from "../services/options.js"; | import options from "../services/options.js"; | ||||||
|  | import OnClickButtonWidget from "./buttons/onclick_button.js"; | ||||||
|  |  | ||||||
| const TPL = `<div class="toc-widget"> | const TPL = `<div class="toc-widget"> | ||||||
|     <style> |     <style> | ||||||
| @@ -24,6 +25,7 @@ const TPL = `<div class="toc-widget"> | |||||||
|             padding: 10px; |             padding: 10px; | ||||||
|             contain: none;  |             contain: none;  | ||||||
|             overflow: auto; |             overflow: auto; | ||||||
|  |             position: relative; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         .toc ol { |         .toc ol { | ||||||
| @@ -41,53 +43,39 @@ const TPL = `<div class="toc-widget"> | |||||||
|         .toc li:hover { |         .toc li:hover { | ||||||
|             font-weight: bold; |             font-weight: bold; | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         .close-toc { | ||||||
|  |             position: absolute; | ||||||
|  |             top: 2px; | ||||||
|  |             right: 2px; | ||||||
|  |         } | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <span class="toc"></span> |     <span class="toc"></span> | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Find a heading node in the parent's children given its index. |  | ||||||
|  * |  | ||||||
|  * @param {Element} parent Parent node to find a headingIndex'th in. |  | ||||||
|  * @param {uint} headingIndex Index for the heading |  | ||||||
|  * @returns {Element|null} Heading node with the given index, null couldn't be |  | ||||||
|  *          found (ie malformed like nested headings, etc.) |  | ||||||
|  */ |  | ||||||
| function findHeadingNodeByIndex(parent, headingIndex) { |  | ||||||
|     let headingNode = null; |  | ||||||
|     for (let i = 0; i < parent.childCount; ++i) { |  | ||||||
|         let child = parent.getChild(i); |  | ||||||
|  |  | ||||||
|         // Headings appear as flattened top level children in the CKEditor |  | ||||||
|         // document named as "heading" plus the level, eg "heading2", |  | ||||||
|         // "heading3", "heading2", etc. and not nested wrt the heading level. If |  | ||||||
|         // a heading node is found, decrement the headingIndex until zero is |  | ||||||
|         // reached |  | ||||||
|         if (child.name.startsWith("heading")) { |  | ||||||
|             if (headingIndex === 0) { |  | ||||||
|                 headingNode = child; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             headingIndex--; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return headingNode; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default class TocWidget extends RightPanelWidget { | export default class TocWidget extends RightPanelWidget { | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |  | ||||||
|  |         this.closeTocButton = new CloseTocButton(); | ||||||
|  |         this.child(this.closeTocButton); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     get widgetTitle() { |     get widgetTitle() { | ||||||
|         return "Table of Contents"; |         return "Table of Contents"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     isEnabled() { |     isEnabled() { | ||||||
|         return super.isEnabled() && this.note.type === 'text'; |         return super.isEnabled() | ||||||
|  |             && this.note.type === 'text' | ||||||
|  |             && !this.noteContext.viewScope.tocTemporarilyHidden; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async doRenderBody() { |     async doRenderBody() { | ||||||
|         this.$body.empty().append($(TPL)); |         this.$body.empty().append($(TPL)); | ||||||
|         this.$toc = this.$body.find('.toc'); |         this.$toc = this.$body.find('.toc'); | ||||||
|  |         this.$body.find('.toc-widget').append(this.closeTocButton.render()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async refreshWithNote(note) { |     async refreshWithNote(note) { | ||||||
| @@ -95,7 +83,7 @@ export default class TocWidget extends RightPanelWidget { | |||||||
|  |  | ||||||
|         if (tocLabel?.value === 'hide') { |         if (tocLabel?.value === 'hide') { | ||||||
|             this.toggleInt(false); |             this.toggleInt(false); | ||||||
|             this.triggerCommand("reevaluateIsEnabled"); |             this.triggerCommand("reEvaluateRightPaneVisibility"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -112,7 +100,7 @@ export default class TocWidget extends RightPanelWidget { | |||||||
|             || headingCount >= options.getInt('minTocHeadings') |             || headingCount >= options.getInt('minTocHeadings') | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         this.triggerCommand("reevaluateIsEnabled"); |         this.triggerCommand("reEvaluateRightPaneVisibility"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -252,6 +240,12 @@ export default class TocWidget extends RightPanelWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async closeTocCommand() { | ||||||
|  |         this.noteContext.viewScope.tocTemporarilyHidden = true; | ||||||
|  |         await this.refresh(); | ||||||
|  |         this.triggerCommand('reEvaluateRightPaneVisibility'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async entitiesReloadedEvent({loadResults}) { |     async entitiesReloadedEvent({loadResults}) { | ||||||
|         if (loadResults.isNoteContentReloaded(this.noteId)) { |         if (loadResults.isNoteContentReloaded(this.noteId)) { | ||||||
|             await this.refresh(); |             await this.refresh(); | ||||||
| @@ -263,3 +257,49 @@ export default class TocWidget extends RightPanelWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Find a heading node in the parent's children given its index. | ||||||
|  |  * | ||||||
|  |  * @param {Element} parent Parent node to find a headingIndex'th in. | ||||||
|  |  * @param {uint} headingIndex Index for the heading | ||||||
|  |  * @returns {Element|null} Heading node with the given index, null couldn't be | ||||||
|  |  *          found (ie malformed like nested headings, etc.) | ||||||
|  |  */ | ||||||
|  | function findHeadingNodeByIndex(parent, headingIndex) { | ||||||
|  |     let headingNode = null; | ||||||
|  |     for (let i = 0; i < parent.childCount; ++i) { | ||||||
|  |         let child = parent.getChild(i); | ||||||
|  |  | ||||||
|  |         // Headings appear as flattened top level children in the CKEditor | ||||||
|  |         // document named as "heading" plus the level, eg "heading2", | ||||||
|  |         // "heading3", "heading2", etc. and not nested wrt the heading level. If | ||||||
|  |         // a heading node is found, decrement the headingIndex until zero is | ||||||
|  |         // reached | ||||||
|  |         if (child.name.startsWith("heading")) { | ||||||
|  |             if (headingIndex === 0) { | ||||||
|  |                 headingNode = child; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             headingIndex--; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return headingNode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class CloseTocButton extends OnClickButtonWidget { | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |  | ||||||
|  |         this.icon("bx-x") | ||||||
|  |             .title("Close TOC") | ||||||
|  |             .titlePlacement("bottom") | ||||||
|  |             .onClick((widget, e) => { | ||||||
|  |                 e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                 widget.triggerCommand("closeToc"); | ||||||
|  |             }) | ||||||
|  |             .class("icon-action close-toc"); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user