chore(react/type_widget): port read only code basic functionality

This commit is contained in:
Elian Doran 2025-09-20 08:57:47 +03:00
parent 77e7c414b6
commit f72087acc3
No known key found for this signature in database
6 changed files with 74 additions and 49 deletions

View File

@ -15,6 +15,7 @@ import WebView from "./type_widgets/WebView";
import "./NoteDetail.css";
import File from "./type_widgets/File";
import Image from "./type_widgets/Image";
import ReadOnlyCode from "./type_widgets/code/ReadOnlyCode";
/**
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
@ -72,6 +73,7 @@ function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: T
case "webView": return <WebView {...props} />
case "file": return <File {...props} />
case "image": return <Image {...props} />
case "readOnlyCode": return <ReadOnlyCode {...props} />
default: break;
}
}

View File

@ -0,0 +1,39 @@
import { useEffect, useRef } from "preact/hooks";
import { EditorConfig, default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { useTriliumOptionBool } from "../../react/hooks";
interface CodeMirrorProps extends Omit<EditorConfig, "parent"> {
content: string;
mime: string;
className?: string;
}
export default function CodeMirror({ className, content, mime, ...extraOpts }: CodeMirrorProps) {
const parentRef = useRef<HTMLPreElement>(null);
const codeEditorRef = useRef<VanillaCodeMirror>(null);
const [ codeLineWrapEnabled ] = useTriliumOptionBool("codeLineWrapEnabled");
useEffect(() => {
if (!parentRef.current) return;
const codeEditor = new VanillaCodeMirror({
parent: parentRef.current,
lineWrapping: codeLineWrapEnabled,
...extraOpts
});
codeEditorRef.current = codeEditor;
return () => codeEditor.destroy();
}, []);
useEffect(() => {
const codeEditor = codeEditorRef.current;
codeEditor?.setText(content ?? "");
codeEditor?.setMimeType(mime);
codeEditor?.clearHistory();
}, [content]);
return (
<pre ref={parentRef} className={className} />
)
}

View File

@ -0,0 +1,28 @@
import { useEffect, useState } from "preact/hooks";
import { TypeWidgetProps } from "../type_widget";
import "./code.css";
import CodeMirror from "./CodeMirror";
import utils from "../../../services/utils";
import { useNoteBlob } from "../../react/hooks";
export default function ReadOnlyCode({ note, viewScope }: TypeWidgetProps) {
const [ content, setContent ] = useState("");
const blob = useNoteBlob(note);
useEffect(() => {
if (!blob) return;
const isFormattable = note.type === "text" && viewScope?.viewMode === "source";
setContent(isFormattable ? utils.formatHtml(blob.content) : blob.content);
}, [ blob ]);
return (
<div class="note-detail-readonly-code note-detail-printable">
<CodeMirror
className="note-detail-readonly-code-content"
content={content}
mime={note.mime}
readOnly
/>
</div>
)
}

View File

@ -0,0 +1,4 @@
.note-detail-readonly-code {
min-height: 50px;
position: relative;
}

View File

@ -29,12 +29,6 @@ export default class AbstractCodeTypeWidget extends TypeWidget {
}
async #initEditor() {
this.codeEditor = new CodeMirror({
parent: this.$editor[0],
lineWrapping: options.is("codeLineWrapEnabled"),
...this.getExtraOpts()
});
// Load the theme.
const themeId = options.get("codeNoteTheme");
if (themeId?.startsWith(DEFAULT_PREFIX)) {
@ -65,18 +59,6 @@ export default class AbstractCodeTypeWidget extends TypeWidget {
// Do nothing by default.
}
/**
* Must be called by the derived classes in `#doRefresh(note)` in order to react to changes.
*
* @param the note that was changed.
* @param new content of the note.
*/
_update(note: { mime: string }, content: string) {
this.codeEditor.setText(content);
this.codeEditor.setMimeType(note.mime);
this.codeEditor.clearHistory();
}
show() {
this.$widget.show();
this.updateBackgroundColor();

View File

@ -4,48 +4,18 @@ import AbstractCodeTypeWidget from "./abstract_code_type_widget.js";
import utils from "../../services/utils.js";
const TPL = /*html*/`
<div class="note-detail-readonly-code note-detail-printable">
<style>
.note-detail-readonly-code {
min-height: 50px;
position: relative;
}
</style>
<pre class="note-detail-readonly-code-content"></pre>
</div>`;
`;
export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
static getType() {
return "readOnlyCode";
}
doRender() {
this.$widget = $(TPL);
this.contentSized();
this.$editor = this.$widget.find(".note-detail-readonly-code-content");
super.doRender();
}
async doRefresh(note: FNote) {
const blob = await this.note?.getBlob();
if (!blob) return;
const isFormattable = note.type === "text" && this.noteContext?.viewScope?.viewMode === "source";
const content = isFormattable ? utils.formatHtml(blob.content) : blob.content;
this._update(note, content);
this.show();
}
getExtraOpts() {
return {
readOnly: true
};
}
async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) {
if (!this.isNoteContext(ntxId)) {
return;