From c087d3c331307fb8c232f83abd6e877b13de39df Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 7 Feb 2025 19:23:12 +0200 Subject: [PATCH] fix(toc): doc type not rendering the first time --- src/public/app/widgets/toc.ts | 61 ++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/public/app/widgets/toc.ts b/src/public/app/widgets/toc.ts index a232fe6ce..1958da572 100644 --- a/src/public/app/widgets/toc.ts +++ b/src/public/app/widgets/toc.ts @@ -54,9 +54,15 @@ const TPL = `
`; +interface Toc { + $toc: JQuery, + headingCount: number +} + export default class TocWidget extends RightPanelWidget { private $toc!: JQuery; + private tocLabelValue?: string | null; get widgetTitle() { return t("toc.table_of_contents"); @@ -101,9 +107,9 @@ export default class TocWidget extends RightPanelWidget { this.toggleInt(!!this.noteContext?.viewScope?.tocPreviousVisible); - const tocLabel = note.getLabel("toc"); + this.tocLabelValue = note.getLabelValue("toc"); - if (tocLabel?.value === "hide") { + if (this.tocLabelValue === "hide") { this.toggleInt(false); this.triggerCommand("reEvaluateRightPaneVisibility"); return; @@ -113,36 +119,47 @@ export default class TocWidget extends RightPanelWidget { return; } - let $toc: JQuery | null = null; - let headingCount = 0; - // Check for type text unconditionally in case alwaysShowWidget is set if (this.note.type === "text") { const blob = await note.getBlob(); if (blob) { - ({ $toc, headingCount } = await this.getToc(blob.content)); - } - } else if (this.note.type === "doc") { - const $contentEl = await this.noteContext.getContentElement(); - if ($contentEl) { - const content = $contentEl.html(); - ({ $toc, headingCount } = await this.getToc(content)); - } else { - console.warn("Unable to get content element for doctype"); + const toc = await this.getToc(blob.content); + this.#updateToc(toc); } + return; } + if (this.note.type === "doc") { + /** + * For document note types, we obtain the content directly from the DOM since it allows us to obtain processed data without + * requesting data twice. However, when immediately navigating to a new note the new document is not yet attached to the hierarchy, + * resulting in an empty TOC. The fix is to simply wait for it to pop up. + */ + setTimeout(async () => { + const $contentEl = await this.noteContext?.getContentElement(); + if ($contentEl) { + const content = $contentEl.html(); + const toc = await this.getToc(content); + this.#updateToc(toc); + } else { + console.warn("Unable to get content element for doctype"); + } + }, 10); + } + } + + #updateToc({ $toc, headingCount }: Toc) { this.$toc.empty(); if ($toc) { this.$toc.append($toc); } - if ((tocLabel?.value === "" || tocLabel?.value === "show") || headingCount >= (options.getInt("minTocHeadings") ?? 0)) { - this.toggleInt(true); - this.noteContext.viewScope.tocPreviousVisible = true; - } else { - this.toggleInt(false); - this.noteContext.viewScope.tocPreviousVisible = false; + const tocLabelValue = this.tocLabelValue; + + const visible = (tocLabelValue === "" || tocLabelValue === "show") || headingCount >= (options.getInt("minTocHeadings") ?? 0); + this.toggleInt(visible); + if (this.noteContext?.viewScope) { + this.noteContext.viewScope.tocPreviousVisible = visible; } this.triggerCommand("reEvaluateRightPaneVisibility"); @@ -202,7 +219,7 @@ export default class TocWidget extends RightPanelWidget { * with an onclick event that will cause the document to scroll to * the desired position. */ - async getToc(html: string) { + async getToc(html: string): Promise { // Regular expression for headings

...

using non-greedy // matching and backreferences const headingTagsRegex = /]*>(.*?)<\/h\1>/gi; @@ -251,7 +268,7 @@ export default class TocWidget extends RightPanelWidget { $toc = this.pullLeft($toc); return { - $toc: $toc, + $toc, headingCount }; }