chore(react/type_widget): render included notes in read-only text

This commit is contained in:
Elian Doran 2025-09-21 20:27:58 +03:00
parent 2f3c2bbac8
commit a8007b9063
No known key found for this signature in database
4 changed files with 47 additions and 42 deletions

View File

@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef } from "preact/hooks"; import { useEffect, useMemo, useRef } from "preact/hooks";
import { TypeWidgetProps } from "../type_widget"; import { TypeWidgetProps } from "../type_widget";
import "./ReadOnlyText.css"; import "./ReadOnlyText.css";
import { useNoteBlob, useNoteLabel } from "../../react/hooks"; import { useNoteBlob, useNoteLabel, useTriliumEvent } from "../../react/hooks";
import RawHtml from "../../react/RawHtml"; import RawHtml from "../../react/RawHtml";
// we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes // we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes
@ -11,19 +11,28 @@ import "@triliumnext/ckeditor5";
import FNote from "../../../entities/fnote"; import FNote from "../../../entities/fnote";
import { getLocaleById } from "../../../services/i18n"; import { getLocaleById } from "../../../services/i18n";
import { getMermaidConfig } from "../../../services/mermaid"; import { getMermaidConfig } from "../../../services/mermaid";
import { loadIncludedNote, refreshIncludedNote } from "./utils";
export default function ReadOnlyText({ note }: TypeWidgetProps) { export default function ReadOnlyText({ note }: TypeWidgetProps) {
const blob = useNoteBlob(note); const blob = useNoteBlob(note);
const contentRef = useRef<HTMLDivElement>(null); const contentRef = useRef<HTMLDivElement>(null);
const { isRtl } = useNoteLanguage(note); const { isRtl } = useNoteLanguage(note);
// Apply necessary transforms.
useEffect(() => { useEffect(() => {
const container = contentRef.current; const container = contentRef.current;
if (!container) return; if (!container) return;
applyInlineMermaid(container); applyInlineMermaid(container);
applyIncludedNotes(container);
}, [ blob ]); }, [ blob ]);
// React to included note changes.
useTriliumEvent("refreshIncludedNote", ({ noteId }) => {
if (!contentRef.current) return;
refreshIncludedNote(contentRef.current, noteId);
});
return ( return (
<div <div
className="note-detail-readonly-text note-detail-printable" className="note-detail-readonly-text note-detail-printable"
@ -67,3 +76,13 @@ async function applyInlineMermaid(container: HTMLDivElement) {
mermaid.initialize(getMermaidConfig()); mermaid.initialize(getMermaidConfig());
mermaid.run({ nodes }); mermaid.run({ nodes });
} }
function applyIncludedNotes(container: HTMLDivElement) {
const includedNotes = container.querySelectorAll("section.include-note");
for (const includedNote of includedNotes) {
const noteId = (includedNote as HTMLElement).dataset.noteId;
if (!noteId) continue;
loadIncludedNote(noteId, $(includedNote as HTMLElement));
}
}

View File

@ -0,0 +1,27 @@
import content_renderer from "../../../services/content_renderer";
import froca from "../../../services/froca";
import link from "../../../services/link";
export async function loadIncludedNote(noteId: string, $el: JQuery<HTMLElement>) {
const note = await froca.getNote(noteId);
if (!note) return;
const $wrapper = $('<div class="include-note-wrapper">');
const $link = await link.createLink(note.noteId, {
showTooltip: false
});
$wrapper.empty().append($('<h4 class="include-note-title">').append($link));
const { $renderedContent, type } = await content_renderer.getRenderedContent(note);
$wrapper.append($(`<div class="include-note-content type-${type}">`).append($renderedContent));
$el.empty().append($wrapper);
}
export function refreshIncludedNote(container: HTMLDivElement, noteId: string) {
const includedNotes = container.querySelectorAll(`section[data-note-id="${noteId}"]`);
for (const includedNote of includedNotes) {
loadIncludedNote(noteId, $(includedNote as HTMLElement));
}
}

View File

@ -79,38 +79,10 @@ export default class AbstractTextTypeWidget extends TypeWidget {
return null; return null;
} }
async loadIncludedNote(noteId: string, $el: JQuery<HTMLElement>) {
const note = await froca.getNote(noteId);
if (note) {
const $wrapper = $('<div class="include-note-wrapper">');
const $link = await linkService.createLink(note.noteId, {
showTooltip: false
});
$wrapper.empty().append($('<h4 class="include-note-title">').append($link));
const { $renderedContent, type } = await contentRenderer.getRenderedContent(note);
$wrapper.append($(`<div class="include-note-content type-${type}">`).append($renderedContent));
$el.empty().append($wrapper);
}
}
async loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string | null = null) { async loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string | null = null) {
await linkService.loadReferenceLinkTitle($el, href); await linkService.loadReferenceLinkTitle($el, href);
} }
refreshIncludedNote($container: JQuery<HTMLElement>, noteId: string) {
if ($container) {
$container.find(`section[data-note-id="${noteId}"]`).each((_, el) => {
this.loadIncludedNote(noteId, $(el));
});
}
}
refreshCodeBlockOptions() { refreshCodeBlockOptions() {
const wordWrap = options.is("codeBlockWordWrap"); const wordWrap = options.is("codeBlockWordWrap");
this.$widget.toggleClass("word-wrap", wordWrap); this.$widget.toggleClass("word-wrap", wordWrap);

View File

@ -37,26 +37,13 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
this.loadReferenceLinkTitle($(el)); this.loadReferenceLinkTitle($(el));
}); });
this.$content.find("section").each((_, el) => {
const noteId = $(el).attr("data-note-id");
if (noteId) {
this.loadIncludedNote(noteId, $(el));
}
});
if (this.$content.find("span.math-tex").length > 0) { if (this.$content.find("span.math-tex").length > 0) {
renderMathInElement(this.$content[0], { trust: true }); renderMathInElement(this.$content[0], { trust: true });
} }
await this.#applyInlineMermaid();
await formatCodeBlocks(this.$content); await formatCodeBlocks(this.$content);
} }
async refreshIncludedNoteEvent({ noteId }: EventData<"refreshIncludedNote">) {
this.refreshIncludedNote(this.$content, noteId);
}
async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) { async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) {
if (!this.isNoteContext(ntxId)) { if (!this.isNoteContext(ntxId)) {
return; return;