mirror of
https://github.com/zadam/trilium.git
synced 2025-11-05 04:45:47 +01:00
chore(react/type_widget): save on change
This commit is contained in:
@@ -1,22 +1,30 @@
|
||||
import { HTMLProps, useEffect, useRef } from "preact/compat";
|
||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type WatchdogConfig } from "@triliumnext/ckeditor5";
|
||||
import { HTMLProps, RefObject, useEffect, useRef, useState } from "preact/compat";
|
||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type WatchdogConfig, CKTextEditor } from "@triliumnext/ckeditor5";
|
||||
import { buildConfig, BuildEditorOptions } from "./config";
|
||||
import { Editor } from "tabulator-tables";
|
||||
|
||||
interface CKEditorWithWatchdogProps extends Pick<HTMLProps<HTMLDivElement>, "className" | "tabIndex"> {
|
||||
content?: string;
|
||||
isClassicEditor?: boolean;
|
||||
watchdogRef: RefObject<EditorWatchdog>;
|
||||
watchdogConfig?: WatchdogConfig;
|
||||
buildEditorOpts: Omit<BuildEditorOptions, "isClassicEditor">;
|
||||
onNotificationWarning?: (evt: any, data: any) => void;
|
||||
onWatchdogStateChange?: (watchdog: EditorWatchdog<any>) => void;
|
||||
onChange: () => void;
|
||||
}
|
||||
|
||||
export default function CKEditorWithWatchdog({ className, tabIndex, isClassicEditor, watchdogConfig, buildEditorOpts, onNotificationWarning, onWatchdogStateChange }: CKEditorWithWatchdogProps) {
|
||||
export default function CKEditorWithWatchdog({ content, className, tabIndex, isClassicEditor, watchdogRef: externalWatchdogRef, watchdogConfig, buildEditorOpts, onNotificationWarning, onWatchdogStateChange, onChange }: CKEditorWithWatchdogProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const watchdogRef = useRef<EditorWatchdog>(null);
|
||||
const [ editor, setEditor ] = useState<CKTextEditor>();
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
const watchdog = buildWatchdog(!!isClassicEditor, watchdogConfig);
|
||||
watchdogRef.current = watchdog;
|
||||
externalWatchdogRef.current = watchdog;
|
||||
watchdog.setCreator(async () => {
|
||||
const editor = await buildEditor(container, !!isClassicEditor, {
|
||||
...buildEditorOpts,
|
||||
@@ -27,6 +35,8 @@ export default function CKEditorWithWatchdog({ className, tabIndex, isClassicEdi
|
||||
editor.plugins.get("Notification").on("show:warning", onNotificationWarning);
|
||||
}
|
||||
|
||||
setEditor(editor);
|
||||
|
||||
return editor;
|
||||
});
|
||||
|
||||
@@ -39,6 +49,16 @@ export default function CKEditorWithWatchdog({ className, tabIndex, isClassicEdi
|
||||
return () => watchdog.destroy();
|
||||
}, []);
|
||||
|
||||
// React to content changes.
|
||||
useEffect(() => editor?.setData(content ?? ""), [ editor, content ]);
|
||||
|
||||
// React to on change listener.
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
editor.model.document.on("change:data", onChange);
|
||||
return () => editor.model.document.off("change:data", onChange);
|
||||
}, [ editor, onChange ]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={className} tabIndex={tabIndex}>
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import dialog from "../../../services/dialog";
|
||||
import toast from "../../../services/toast";
|
||||
import { isMobile } from "../../../services/utils";
|
||||
import { useNoteLabel, useTriliumOption } from "../../react/hooks";
|
||||
import utils, { isMobile } from "../../../services/utils";
|
||||
import { useEditorSpacedUpdate, useNoteLabel, useTriliumOption } from "../../react/hooks";
|
||||
import { TypeWidgetProps } from "../type_widget";
|
||||
import CKEditorWithWatchdog from "./CKEditorWithWatchdog";
|
||||
import "./EditableText.css";
|
||||
import type { EditorWatchdog } from "@triliumnext/ckeditor5";
|
||||
import { EditorWatchdog } from "@triliumnext/ckeditor5";
|
||||
|
||||
/**
|
||||
* The editor can operate into two distinct modes:
|
||||
@@ -14,15 +15,40 @@ import type { EditorWatchdog } from "@triliumnext/ckeditor5";
|
||||
* - Decoupled mode, in which the editing toolbar is actually added on the client side (in {@link ClassicEditorToolbar}), see https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for an example on how the decoupled editor works.
|
||||
*/
|
||||
export default function EditableText({ note }: TypeWidgetProps) {
|
||||
const [ content, setContent ] = useState<string>();
|
||||
const watchdogRef = useRef<EditorWatchdog>(null);
|
||||
const [ language ] = useNoteLabel(note, "language");
|
||||
const [ textNoteEditorType ] = useTriliumOption("textNoteEditorType");
|
||||
const isClassicEditor = isMobile() || textNoteEditorType === "ckeditor-classic";
|
||||
const spacedUpdate = useEditorSpacedUpdate({
|
||||
note,
|
||||
getData() {
|
||||
const editor = watchdogRef.current?.editor;
|
||||
if (!editor) {
|
||||
// There is nothing to save, most likely a result of the editor crashing and reinitializing.
|
||||
return;
|
||||
}
|
||||
|
||||
const content = editor.getData() ?? "";
|
||||
|
||||
// if content is only tags/whitespace (typically <p> </p>), then just make it empty,
|
||||
// this is important when setting a new note to code
|
||||
return {
|
||||
content: utils.isHtmlEmpty(content) ? "" : content
|
||||
};
|
||||
},
|
||||
onContentChange(newContent) {
|
||||
setContent(newContent);
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="note-detail-editable-text note-detail-printable">
|
||||
<CKEditorWithWatchdog
|
||||
{note && <CKEditorWithWatchdog
|
||||
className="note-detail-editable-text-editor use-tn-links" tabIndex={300}
|
||||
content={content}
|
||||
isClassicEditor={isClassicEditor}
|
||||
watchdogRef={watchdogRef}
|
||||
watchdogConfig={{
|
||||
// An average number of milliseconds between the last editor errors (defaults to 5000). When the period of time between errors is lower than that and the crashNumberLimit is also reached, the watchdog changes its state to crashedPermanently, and it stops restarting the editor. This prevents an infinite restart loop.
|
||||
minimumNonErrorTimePeriod: 5000,
|
||||
@@ -37,7 +63,8 @@ export default function EditableText({ note }: TypeWidgetProps) {
|
||||
}}
|
||||
onNotificationWarning={onNotificationWarning}
|
||||
onWatchdogStateChange={onWatchdogStateChange}
|
||||
/>
|
||||
onChange={() => spacedUpdate.scheduleUpdate()}
|
||||
/>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,8 +74,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
||||
}
|
||||
}
|
||||
|
||||
editor.model.document.on("change:data", () => this.spacedUpdate.scheduleUpdate());
|
||||
|
||||
if (import.meta.env.VITE_CKEDITOR_ENABLE_INSPECTOR === "true") {
|
||||
const CKEditorInspector = (await import("@ckeditor/ckeditor5-inspector")).default;
|
||||
CKEditorInspector.attach(editor);
|
||||
@@ -102,27 +100,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
||||
const newContentLanguage = this.note?.getLabelValue("language");
|
||||
if (this.contentLanguage !== newContentLanguage) {
|
||||
await this.reinitializeWithData(data);
|
||||
} else {
|
||||
this.watchdog.editor?.setData(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getData() {
|
||||
if (!this.watchdog.editor) {
|
||||
// There is nothing to save, most likely a result of the editor crashing and reinitializing.
|
||||
return;
|
||||
}
|
||||
|
||||
const content = this.watchdog.editor?.getData() ?? "";
|
||||
|
||||
// if content is only tags/whitespace (typically <p> </p>), then just make it empty,
|
||||
// this is important when setting a new note to code
|
||||
return {
|
||||
content: utils.isHtmlEmpty(content) ? "" : content
|
||||
};
|
||||
}
|
||||
|
||||
focus() {
|
||||
const editor = this.watchdog.editor;
|
||||
if (editor) {
|
||||
|
||||
Reference in New Issue
Block a user