mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +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