mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	add a button to temporarily hide TOC, closes #3555
This commit is contained in:
		
							parent
							
								
									64e7150765
								
							
						
					
					
						commit
						a7b103e07a
					
				@ -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>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					export default class TocWidget extends RightPanelWidget {
 | 
				
			||||||
 * Find a heading node in the parent's children given its index.
 | 
					    constructor() {
 | 
				
			||||||
 *
 | 
					        super();
 | 
				
			||||||
 * @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
 | 
					        this.closeTocButton = new CloseTocButton();
 | 
				
			||||||
        // document named as "heading" plus the level, eg "heading2",
 | 
					        this.child(this.closeTocButton);
 | 
				
			||||||
        // "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 {
 | 
					 | 
				
			||||||
    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");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user