mirror of
https://github.com/zadam/trilium.git
synced 2025-01-15 19:51:57 +08:00
add a button to temporarily hide TOC, closes #3555
This commit is contained in:
parent
64e7150765
commit
a7b103e07a
5 changed files with 98 additions and 43 deletions
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue