chore(react/type_widgets): bring back add include to note

This commit is contained in:
Elian Doran 2025-09-25 13:51:07 +03:00
parent 0ac428b57a
commit 3673162a48
No known key found for this signature in database
6 changed files with 76 additions and 59 deletions

View File

@ -34,6 +34,7 @@ import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx
import type RootContainer from "../widgets/containers/root_container.js";
import { SqlExecuteResults } from "@triliumnext/commons";
import { AddLinkOpts } from "../widgets/dialogs/add_link.jsx";
import { IncludeNoteOpts } from "../widgets/dialogs/include_note.jsx";
interface Layout {
getRootWidget: (appContext: AppContext) => RootContainer;
@ -223,7 +224,7 @@ export type CommandMappings = {
showPasswordNotSet: CommandData;
showProtectedSessionPasswordDialog: CommandData;
showUploadAttachmentsDialog: CommandData & { noteId: string };
showIncludeNoteDialog: CommandData & { textTypeWidget: EditableTextTypeWidget };
showIncludeNoteDialog: CommandData & IncludeNoteOpts;
showAddLinkDialog: CommandData & AddLinkOpts;
closeProtectedSessionPasswordDialog: CommandData;
copyImageReferenceToClipboard: CommandData;

View File

@ -10,15 +10,20 @@ import tree from "../../services/tree";
import froca from "../../services/froca";
import EditableTextTypeWidget, { type BoxSize } from "../type_widgets_old/editable_text";
import { useTriliumEvent } from "../react/hooks";
import { CKEditorApi } from "../type_widgets/text/CKEditorWithWatchdog";
export interface IncludeNoteOpts {
editorApi: CKEditorApi;
}
export default function IncludeNoteDialog() {
const [textTypeWidget, setTextTypeWidget] = useState<EditableTextTypeWidget>();
const editorApiRef = useRef<CKEditorApi>(null);
const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
const [boxSize, setBoxSize] = useState("medium");
const [shown, setShown] = useState(false);
useTriliumEvent("showIncludeNoteDialog", ({ textTypeWidget }) => {
setTextTypeWidget(textTypeWidget);
useTriliumEvent("showIncludeNoteDialog", ({ editorApi }) => {
editorApiRef.current = editorApi;
setShown(true);
});
@ -32,12 +37,9 @@ export default function IncludeNoteDialog() {
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
onHidden={() => setShown(false)}
onSubmit={() => {
if (!suggestion?.notePath || !textTypeWidget) {
return;
}
if (!suggestion?.notePath || !editorApiRef.current) return;
setShown(false);
includeNote(suggestion.notePath, textTypeWidget, boxSize as BoxSize);
includeNote(suggestion.notePath, editorApiRef.current, boxSize as BoxSize);
}}
footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />}
show={shown}
@ -69,7 +71,7 @@ export default function IncludeNoteDialog() {
)
}
async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWidget, boxSize: BoxSize) {
async function includeNote(notePath: string, editorApi: CKEditorApi, boxSize: BoxSize) {
const noteId = tree.getNoteIdFromUrl(notePath);
if (!noteId) {
return;
@ -79,8 +81,8 @@ async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWid
if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
// there's no benefit to use insert note functionlity for images,
// so we'll just add an IMG tag
textTypeWidget.addImage(noteId);
editorApi.addImage(noteId);
} else {
textTypeWidget.addIncludeNote(noteId, boxSize);
editorApi.addIncludeNote(noteId, boxSize);
}
}

View File

@ -3,6 +3,9 @@ import { PopupEditor, ClassicEditor, EditorWatchdog, type WatchdogConfig, CKText
import { buildConfig, BuildEditorOptions } from "./config";
import { useLegacyImperativeHandlers } from "../../react/hooks";
import link from "../../../services/link";
import froca from "../../../services/froca";
export type BoxSize = "small" | "medium" | "full";
export interface CKEditorApi {
/** returns true if user selected some text, false if there's no selection */
@ -10,6 +13,8 @@ export interface CKEditorApi {
getSelectedText(): string;
addLink(notePath: string, linkTitle: string | null, externalLink?: boolean): void;
addLinkToEditor(linkHref: string, linkTitle: string): void;
addIncludeNote(noteId: string, boxSize?: BoxSize): void;
addImage(noteId: string): Promise<void>;
}
interface CKEditorWithWatchdogProps extends Pick<HTMLProps<HTMLDivElement>, "className" | "tabIndex"> {
@ -78,6 +83,35 @@ export default function CKEditorWithWatchdog({ content, contentLanguage, classNa
}
});
},
addIncludeNote(noteId, boxSize) {
const editor = watchdogRef.current?.editor;
if (!editor) return;
editor?.model.change((writer) => {
// Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure
editor?.model.insertContent(
writer.createElement("includeNote", {
noteId: noteId,
boxSize: boxSize
})
);
});
},
async addImage(noteId) {
const editor = watchdogRef.current?.editor;
if (!editor) return;
const note = await froca.getNote(noteId);
if (!note) return;
editor.model.change(() => {
const encodedTitle = encodeURIComponent(note.title);
const src = `api/images/${note.noteId}/${encodedTitle}`;
editor?.execute("insertImage", { source: src });
});
},
}));
useLegacyImperativeHandlers({

View File

@ -2,13 +2,14 @@ import { useRef, useState } from "preact/hooks";
import dialog from "../../../services/dialog";
import toast from "../../../services/toast";
import utils, { deferred, isMobile } from "../../../services/utils";
import { useEditorSpacedUpdate, useKeyboardShortcuts, useNoteLabel, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { useEditorSpacedUpdate, useKeyboardShortcuts, useLegacyImperativeHandlers, useNoteLabel, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { TypeWidgetProps } from "../type_widget";
import CKEditorWithWatchdog, { CKEditorApi } from "./CKEditorWithWatchdog";
import "./EditableText.css";
import { CKTextEditor, ClassicEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
import Component from "../../../components/component";
import options from "../../../services/options";
import { loadIncludedNote, refreshIncludedNote } from "./utils";
/**
* The editor can operate into two distinct modes:
@ -67,8 +68,9 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
editor?.editing.view.focus();
});
useTriliumEvent("addLinkToText", ({ ntxId: eventNtxId }) => {
if (eventNtxId !== ntxId || !editorApiRef.current) return;
useLegacyImperativeHandlers({
addLinkToTextCommand() {
if (!editorApiRef.current) return;
parentComponent?.triggerCommand("showAddLinkDialog", {
text: editorApiRef.current.getSelectedText(),
hasSelection: editorApiRef.current.hasSelection(),
@ -77,6 +79,19 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
return editorApiRef.current?.addLink(notePath, linkTitle, externalLink);
}
});
},
addIncludeNoteToTextCommand() {
if (!editorApiRef.current) return;
parentComponent?.triggerCommand("showIncludeNoteDialog", {
editorApi: editorApiRef.current,
});
},
loadIncludedNote
});
useTriliumEvent("refreshIncludedNote", ({ noteId }) => {
if (!containerRef.current) return;
refreshIncludedNote(containerRef.current, noteId);
});
async function waitForEditor() {

View File

@ -39,6 +39,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro
// React to included note changes.
useTriliumEvent("refreshIncludedNote", ({ noteId }) => {
console.log("Refresh ", noteId);
if (!contentRef.current) return;
refreshIncludedNote(contentRef.current, noteId);
});

View File

@ -14,7 +14,6 @@ import type FNote from "../../entities/fnote.js";
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
import { updateTemplateCache } from "./ckeditor/snippets.js";
export type BoxSize = "small" | "medium" | "full";
export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
@ -105,37 +104,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
}
addIncludeNoteToTextCommand() {
this.triggerCommand("showIncludeNoteDialog", { textTypeWidget: this });
}
addIncludeNote(noteId: string, boxSize?: BoxSize) {
this.watchdog.editor?.model.change((writer) => {
// Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure
this.watchdog.editor?.model.insertContent(
writer.createElement("includeNote", {
noteId: noteId,
boxSize: boxSize
})
);
});
}
async addImage(noteId: string) {
const note = await froca.getNote(noteId);
if (!note || !this.watchdog.editor) {
return;
}
this.watchdog.editor.model.change((writer) => {
const encodedTitle = encodeURIComponent(note.title);
const src = `api/images/${note.noteId}/${encodedTitle}`;
this.watchdog.editor?.execute("insertImage", { source: src });
});
}
async createNoteForReferenceLink(title: string) {
if (!this.notePath) {
return;
@ -153,10 +121,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return resp.note.getBestNotePathString();
}
async refreshIncludedNoteEvent({ noteId }: EventData<"refreshIncludedNote">) {
this.refreshIncludedNote(this.$editor, noteId);
}
async reinitialize() {
const data = this.watchdog.editor?.getData();
await this.reinitializeWithData(data ?? "");