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.hoistedNoteId = hoistedNoteId; | ||||
|         this.mainNtxId = mainNtxId; | ||||
|  | ||||
|         this.resetViewScope(); | ||||
|     } | ||||
|  | ||||
|     setEmpty() { | ||||
| @@ -27,6 +29,8 @@ class NoteContext extends Component { | ||||
|             noteContext: this, | ||||
|             notePath: this.notePath | ||||
|         }); | ||||
|  | ||||
|         this.resetViewScope(); | ||||
|     } | ||||
|  | ||||
|     isEmpty() { | ||||
| @@ -47,7 +51,7 @@ class NoteContext extends Component { | ||||
|         this.notePath = resolvedNotePath; | ||||
|         ({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath)); | ||||
|  | ||||
|         this.readOnlyTemporarilyDisabled = false; | ||||
|         this.resetViewScope(); | ||||
|  | ||||
|         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' | ||||
|             && this.notePath.startsWith("root/_hidden") | ||||
|             && !this.note.hasLabel("keepCurrentHoisting") | ||||
| @@ -76,10 +88,6 @@ class NoteContext extends Component { | ||||
|  | ||||
|             await this.setHoistedNoteId(hoistedNoteId); | ||||
|         } | ||||
|  | ||||
|         if (utils.isMobile()) { | ||||
|             this.triggerCommand('setActiveScreen', {screen: 'detail'}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getSubContexts() { | ||||
| @@ -201,7 +209,7 @@ class NoteContext extends Component { | ||||
|     } | ||||
|  | ||||
|     async isReadOnly() { | ||||
|         if (this.readOnlyTemporarilyDisabled) { | ||||
|         if (this.viewScope.readOnlyTemporarilyDisabled) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| @@ -277,6 +285,13 @@ class NoteContext extends Component { | ||||
|             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; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import froca from "../services/froca.js"; | ||||
| export default class RootCommandExecutor extends Component { | ||||
|     editReadOnlyNoteCommand() { | ||||
|         const noteContext = appContext.tabManager.getActiveContext(); | ||||
|         noteContext.readOnlyTemporarilyDisabled = true; | ||||
|         noteContext.viewScope.readOnlyTemporarilyDisabled = true; | ||||
|  | ||||
|         appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext }); | ||||
|     } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ export default class EditButton extends OnClickButtonWidget { | ||||
|             .title("Edit this note") | ||||
|             .titlePlacement("bottom") | ||||
|             .onClick(widget => { | ||||
|                 this.noteContext.readOnlyTemporarilyDisabled = true; | ||||
|                 this.noteContext.viewScope.readOnlyTemporarilyDisabled = true; | ||||
|  | ||||
|                 appContext.triggerEvent('readOnlyTemporarilyDisabled', {noteContext: this.noteContext}); | ||||
|  | ||||
| @@ -56,7 +56,7 @@ export default class EditButton extends OnClickButtonWidget { | ||||
|                 && attr.name.toLowerCase().includes("readonly") | ||||
|                 && attributeService.isAffecting(attr, this.note) | ||||
|         )) { | ||||
|             this.noteContext.readOnlyTemporarilyDisabled = false; | ||||
|             this.noteContext.viewScope.readOnlyTemporarilyDisabled = false; | ||||
|  | ||||
|             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 | ||||
|             // but these events needs to be finished and only then we check | ||||
|             if (promise) { | ||||
|                 promise.then(() => this.reevaluateIsEnabledCommand()); | ||||
|                 promise.then(() => this.reEvaluateRightPaneVisibilityCommand()); | ||||
|             } | ||||
|             else { | ||||
|                 this.reevaluateIsEnabledCommand(); | ||||
|                 this.reEvaluateRightPaneVisibilityCommand(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return promise; | ||||
|     } | ||||
|  | ||||
|     reevaluateIsEnabledCommand() { | ||||
|     reEvaluateRightPaneVisibilityCommand() { | ||||
|         const oldToggle = !this.isHiddenInt(); | ||||
|         const newToggle = this.isEnabled(); | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| import attributeService from "../services/attributes.js"; | ||||
| import RightPanelWidget from "./right_panel_widget.js"; | ||||
| import options from "../services/options.js"; | ||||
| import OnClickButtonWidget from "./buttons/onclick_button.js"; | ||||
|  | ||||
| const TPL = `<div class="toc-widget"> | ||||
|     <style> | ||||
| @@ -24,6 +25,7 @@ const TPL = `<div class="toc-widget"> | ||||
|             padding: 10px; | ||||
|             contain: none;  | ||||
|             overflow: auto; | ||||
|             position: relative; | ||||
|         } | ||||
|          | ||||
|         .toc ol { | ||||
| @@ -41,53 +43,39 @@ const TPL = `<div class="toc-widget"> | ||||
|         .toc li:hover { | ||||
|             font-weight: bold; | ||||
|         } | ||||
|          | ||||
|         .close-toc { | ||||
|             position: absolute; | ||||
|             top: 2px; | ||||
|             right: 2px; | ||||
|         } | ||||
|     </style> | ||||
|  | ||||
|     <span class="toc"></span> | ||||
| </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 { | ||||
|     constructor() { | ||||
|         super(); | ||||
|  | ||||
|         this.closeTocButton = new CloseTocButton(); | ||||
|         this.child(this.closeTocButton); | ||||
|     } | ||||
|  | ||||
|     get widgetTitle() { | ||||
|         return "Table of Contents"; | ||||
|     } | ||||
|  | ||||
|     isEnabled() { | ||||
|         return super.isEnabled() && this.note.type === 'text'; | ||||
|         return super.isEnabled() | ||||
|             && this.note.type === 'text' | ||||
|             && !this.noteContext.viewScope.tocTemporarilyHidden; | ||||
|     } | ||||
|  | ||||
|     async doRenderBody() { | ||||
|         this.$body.empty().append($(TPL)); | ||||
|         this.$toc = this.$body.find('.toc'); | ||||
|         this.$body.find('.toc-widget').append(this.closeTocButton.render()); | ||||
|     } | ||||
|  | ||||
|     async refreshWithNote(note) { | ||||
| @@ -95,7 +83,7 @@ export default class TocWidget extends RightPanelWidget { | ||||
|  | ||||
|         if (tocLabel?.value === 'hide') { | ||||
|             this.toggleInt(false); | ||||
|             this.triggerCommand("reevaluateIsEnabled"); | ||||
|             this.triggerCommand("reEvaluateRightPaneVisibility"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -112,7 +100,7 @@ export default class TocWidget extends RightPanelWidget { | ||||
|             || 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}) { | ||||
|         if (loadResults.isNoteContentReloaded(this.noteId)) { | ||||
|             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