refactor(react/type_widget): separate Trilium-specific implementation

This commit is contained in:
Elian Doran 2025-09-20 11:22:48 +03:00
parent 43dcdf8925
commit f496caa92c
No known key found for this signature in database
2 changed files with 54 additions and 50 deletions

View File

@ -1,15 +1,16 @@
import { useEffect, useRef, useState } from "preact/hooks";
import { default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { getThemeById, default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { TypeWidgetProps } from "../type_widget";
import "./code.css";
import CodeMirror, { CodeMirrorProps } from "./CodeMirror";
import utils from "../../../services/utils";
import { useEditorSpacedUpdate, useNoteBlob, useSyncedRef, useTriliumOptionBool } from "../../react/hooks";
import { useEditorSpacedUpdate, useNoteBlob, useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { t } from "../../../services/i18n";
import appContext from "../../../components/app_context";
import TouchBar, { TouchBarButton } from "../../react/TouchBar";
import keyboard_actions from "../../../services/keyboard_actions";
import { refToJQuerySelector } from "../../react/react_utils";
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWidgetProps) {
const [ content, setContent ] = useState("");
@ -24,12 +25,11 @@ export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWi
return (
<div className="note-detail-readonly-code note-detail-printable">
<CodeEditor
note={note} parentComponent={parentComponent}
ntxId={ntxId} note={note} parentComponent={parentComponent}
className="note-detail-readonly-code-content"
content={content}
mime={note.mime}
readOnly
ntxId={ntxId}
/>
</div>
)
@ -63,10 +63,9 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent }: T
return (
<div className="note-detail-code note-detail-printable">
<CodeEditor
note={note} parentComponent={parentComponent}
ntxId={ntxId} note={note} parentComponent={parentComponent}
editorRef={editorRef} containerRef={containerRef}
className="note-detail-code-editor"
ntxId={ntxId}
placeholder={t("editable_code.placeholder")}
vimKeybindings={vimKeymapEnabled}
tabIndex={300}
@ -87,8 +86,12 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent }: T
)
}
function CodeEditor({ note, parentComponent, containerRef: externalContainerRef, ...editorProps }: Omit<CodeMirrorProps, "onThemeChange"> & Pick<TypeWidgetProps, "note" | "parentComponent">) {
function CodeEditor({ note, parentComponent, ntxId, containerRef: externalContainerRef, editorRef: externalEditorRef, ...editorProps }: Omit<CodeMirrorProps, "onThemeChange" | "lineWrapping"> & Pick<TypeWidgetProps, "note" | "parentComponent" | "ntxId">) {
const codeEditorRef = useRef<VanillaCodeMirror>(null);
const containerRef = useSyncedRef(externalContainerRef);
const initialized = useRef($.Deferred());
const [ codeLineWrapEnabled ] = useTriliumOptionBool("codeLineWrapEnabled");
const [ codeNoteTheme ] = useTriliumOption("codeNoteTheme");
// React to background color.
const [ backgroundColor, setBackgroundColor ] = useState<string>();
@ -100,14 +103,47 @@ function CodeEditor({ note, parentComponent, containerRef: externalContainerRef,
};
}, [ backgroundColor ]);
// React to theme changes.
useEffect(() => {
if (codeEditorRef.current && codeNoteTheme.startsWith(DEFAULT_PREFIX)) {
const theme = getThemeById(codeNoteTheme.substring(DEFAULT_PREFIX.length));
if (theme) {
codeEditorRef.current.setTheme(theme).then(() => {
if (note?.mime === "text/x-sqlite;schema=trilium") return;
const editor = containerRef.current?.querySelector(".cm-editor");
if (!editor) return;
const style = window.getComputedStyle(editor);
setBackgroundColor(style.backgroundColor);
});
}
}
}, [ codeEditorRef, codeNoteTheme ]);
useTriliumEvent("executeWithCodeEditor", async ({ resolve, ntxId: eventNtxId }) => {
if (eventNtxId !== ntxId) return;
await initialized.current.promise();
resolve(codeEditorRef.current!);
});
useTriliumEvent("executeWithContentElement", async ({ resolve, ntxId: eventNtxId}) => {
if (eventNtxId !== ntxId) return;
await initialized.current.promise();
resolve(refToJQuerySelector(containerRef));
});
return <CodeMirror
{...editorProps}
editorRef={codeEditorRef}
containerRef={containerRef}
onThemeChange={note?.mime !== "text/x-sqlite;schema=trilium" ? () => {
const editor = containerRef.current?.querySelector(".cm-editor");
if (!editor) return;
const style = window.getComputedStyle(editor);
setBackgroundColor(style.backgroundColor);
} : undefined}
lineWrapping={codeLineWrapEnabled}
onInitialized={() => {
if (externalContainerRef && containerRef.current) {
externalContainerRef.current = containerRef.current;
}
if (externalEditorRef && codeEditorRef.current) {
externalEditorRef.current = codeEditorRef.current;
}
initialized.current.resolve();
}}
/>
}

View File

@ -1,39 +1,20 @@
import { useEffect, useRef } from "preact/hooks";
import { EditorConfig, getThemeById, default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { refToJQuerySelector } from "../../react/react_utils";
import { EditorConfig, default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { useSyncedRef } from "../../react/hooks";
import { RefObject } from "preact";
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
export interface CodeMirrorProps extends Omit<EditorConfig, "parent"> {
content: string;
mime: string;
className?: string;
ntxId: string | null | undefined;
editorRef?: RefObject<VanillaCodeMirror>;
containerRef?: RefObject<HTMLPreElement>;
onThemeChange?: () => void;
onInitialized?: () => void;
}
export default function CodeMirror({ className, content, mime, ntxId, editorRef: externalEditorRef, containerRef: externalContainerRef, onThemeChange, ...extraOpts }: CodeMirrorProps) {
export default function CodeMirror({ className, content, mime, editorRef: externalEditorRef, containerRef: externalContainerRef, onInitialized, ...extraOpts }: CodeMirrorProps) {
const parentRef = useSyncedRef(externalContainerRef);
const codeEditorRef = useRef<VanillaCodeMirror>();
const [ codeLineWrapEnabled ] = useTriliumOptionBool("codeLineWrapEnabled");
const [ codeNoteTheme ] = useTriliumOption("codeNoteTheme");
const initialized = $.Deferred();
// Integration within Trilium's event system.
useTriliumEvent("executeWithCodeEditor", async ({ resolve, ntxId: eventNtxId }) => {
if (eventNtxId !== ntxId) return;
await initialized.promise();
resolve(codeEditorRef.current!);
});
useTriliumEvent("executeWithContentElement", async ({ resolve, ntxId: eventNtxId}) => {
if (eventNtxId !== ntxId) return;
await initialized.promise();
resolve(refToJQuerySelector(parentRef));
});
// Create CodeMirror instance.
useEffect(() => {
@ -41,30 +22,17 @@ export default function CodeMirror({ className, content, mime, ntxId, editorRef:
const codeEditor = new VanillaCodeMirror({
parent: parentRef.current,
lineWrapping: codeLineWrapEnabled,
...extraOpts
});
codeEditorRef.current = codeEditor;
if (externalEditorRef) {
externalEditorRef.current = codeEditor;
}
initialized.resolve();
onInitialized?.();
return () => codeEditor.destroy();
}, []);
// React to theme changes.
useEffect(() => {
if (codeEditorRef.current && codeNoteTheme.startsWith(DEFAULT_PREFIX)) {
const theme = getThemeById(codeNoteTheme.substring(DEFAULT_PREFIX.length));
if (theme) {
codeEditorRef.current.setTheme(theme).then(() => {
onThemeChange?.();
});
}
}
}, [ codeEditorRef, codeNoteTheme ]);
// React to text changes.
useEffect(() => {
const codeEditor = codeEditorRef.current;