mirror of
https://github.com/zadam/trilium.git
synced 2025-11-11 17:08:58 +01:00
chore(react/type_widgets): bring back add include to note
This commit is contained in:
parent
0ac428b57a
commit
3673162a48
@ -34,6 +34,7 @@ import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx
|
|||||||
import type RootContainer from "../widgets/containers/root_container.js";
|
import type RootContainer from "../widgets/containers/root_container.js";
|
||||||
import { SqlExecuteResults } from "@triliumnext/commons";
|
import { SqlExecuteResults } from "@triliumnext/commons";
|
||||||
import { AddLinkOpts } from "../widgets/dialogs/add_link.jsx";
|
import { AddLinkOpts } from "../widgets/dialogs/add_link.jsx";
|
||||||
|
import { IncludeNoteOpts } from "../widgets/dialogs/include_note.jsx";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootContainer;
|
getRootWidget: (appContext: AppContext) => RootContainer;
|
||||||
@ -223,7 +224,7 @@ export type CommandMappings = {
|
|||||||
showPasswordNotSet: CommandData;
|
showPasswordNotSet: CommandData;
|
||||||
showProtectedSessionPasswordDialog: CommandData;
|
showProtectedSessionPasswordDialog: CommandData;
|
||||||
showUploadAttachmentsDialog: CommandData & { noteId: string };
|
showUploadAttachmentsDialog: CommandData & { noteId: string };
|
||||||
showIncludeNoteDialog: CommandData & { textTypeWidget: EditableTextTypeWidget };
|
showIncludeNoteDialog: CommandData & IncludeNoteOpts;
|
||||||
showAddLinkDialog: CommandData & AddLinkOpts;
|
showAddLinkDialog: CommandData & AddLinkOpts;
|
||||||
closeProtectedSessionPasswordDialog: CommandData;
|
closeProtectedSessionPasswordDialog: CommandData;
|
||||||
copyImageReferenceToClipboard: CommandData;
|
copyImageReferenceToClipboard: CommandData;
|
||||||
|
|||||||
@ -10,15 +10,20 @@ import tree from "../../services/tree";
|
|||||||
import froca from "../../services/froca";
|
import froca from "../../services/froca";
|
||||||
import EditableTextTypeWidget, { type BoxSize } from "../type_widgets_old/editable_text";
|
import EditableTextTypeWidget, { type BoxSize } from "../type_widgets_old/editable_text";
|
||||||
import { useTriliumEvent } from "../react/hooks";
|
import { useTriliumEvent } from "../react/hooks";
|
||||||
|
import { CKEditorApi } from "../type_widgets/text/CKEditorWithWatchdog";
|
||||||
|
|
||||||
|
export interface IncludeNoteOpts {
|
||||||
|
editorApi: CKEditorApi;
|
||||||
|
}
|
||||||
|
|
||||||
export default function IncludeNoteDialog() {
|
export default function IncludeNoteDialog() {
|
||||||
const [textTypeWidget, setTextTypeWidget] = useState<EditableTextTypeWidget>();
|
const editorApiRef = useRef<CKEditorApi>(null);
|
||||||
const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
|
const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
|
||||||
const [boxSize, setBoxSize] = useState("medium");
|
const [boxSize, setBoxSize] = useState("medium");
|
||||||
const [shown, setShown] = useState(false);
|
const [shown, setShown] = useState(false);
|
||||||
|
|
||||||
useTriliumEvent("showIncludeNoteDialog", ({ textTypeWidget }) => {
|
useTriliumEvent("showIncludeNoteDialog", ({ editorApi }) => {
|
||||||
setTextTypeWidget(textTypeWidget);
|
editorApiRef.current = editorApi;
|
||||||
setShown(true);
|
setShown(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -32,12 +37,9 @@ export default function IncludeNoteDialog() {
|
|||||||
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
||||||
onHidden={() => setShown(false)}
|
onHidden={() => setShown(false)}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
if (!suggestion?.notePath || !textTypeWidget) {
|
if (!suggestion?.notePath || !editorApiRef.current) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setShown(false);
|
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" />}
|
footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />}
|
||||||
show={shown}
|
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);
|
const noteId = tree.getNoteIdFromUrl(notePath);
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
return;
|
return;
|
||||||
@ -79,8 +81,8 @@ async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWid
|
|||||||
if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
|
if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
|
||||||
// there's no benefit to use insert note functionlity for images,
|
// there's no benefit to use insert note functionlity for images,
|
||||||
// so we'll just add an IMG tag
|
// so we'll just add an IMG tag
|
||||||
textTypeWidget.addImage(noteId);
|
editorApi.addImage(noteId);
|
||||||
} else {
|
} else {
|
||||||
textTypeWidget.addIncludeNote(noteId, boxSize);
|
editorApi.addIncludeNote(noteId, boxSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,9 @@ import { PopupEditor, ClassicEditor, EditorWatchdog, type WatchdogConfig, CKText
|
|||||||
import { buildConfig, BuildEditorOptions } from "./config";
|
import { buildConfig, BuildEditorOptions } from "./config";
|
||||||
import { useLegacyImperativeHandlers } from "../../react/hooks";
|
import { useLegacyImperativeHandlers } from "../../react/hooks";
|
||||||
import link from "../../../services/link";
|
import link from "../../../services/link";
|
||||||
|
import froca from "../../../services/froca";
|
||||||
|
|
||||||
|
export type BoxSize = "small" | "medium" | "full";
|
||||||
|
|
||||||
export interface CKEditorApi {
|
export interface CKEditorApi {
|
||||||
/** returns true if user selected some text, false if there's no selection */
|
/** returns true if user selected some text, false if there's no selection */
|
||||||
@ -10,6 +13,8 @@ export interface CKEditorApi {
|
|||||||
getSelectedText(): string;
|
getSelectedText(): string;
|
||||||
addLink(notePath: string, linkTitle: string | null, externalLink?: boolean): void;
|
addLink(notePath: string, linkTitle: string | null, externalLink?: boolean): void;
|
||||||
addLinkToEditor(linkHref: string, linkTitle: string): 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"> {
|
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({
|
useLegacyImperativeHandlers({
|
||||||
|
|||||||
@ -2,13 +2,14 @@ import { useRef, useState } from "preact/hooks";
|
|||||||
import dialog from "../../../services/dialog";
|
import dialog from "../../../services/dialog";
|
||||||
import toast from "../../../services/toast";
|
import toast from "../../../services/toast";
|
||||||
import utils, { deferred, isMobile } from "../../../services/utils";
|
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 { TypeWidgetProps } from "../type_widget";
|
||||||
import CKEditorWithWatchdog, { CKEditorApi } from "./CKEditorWithWatchdog";
|
import CKEditorWithWatchdog, { CKEditorApi } from "./CKEditorWithWatchdog";
|
||||||
import "./EditableText.css";
|
import "./EditableText.css";
|
||||||
import { CKTextEditor, ClassicEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
|
import { CKTextEditor, ClassicEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
|
||||||
import Component from "../../../components/component";
|
import Component from "../../../components/component";
|
||||||
import options from "../../../services/options";
|
import options from "../../../services/options";
|
||||||
|
import { loadIncludedNote, refreshIncludedNote } from "./utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor can operate into two distinct modes:
|
* The editor can operate into two distinct modes:
|
||||||
@ -67,16 +68,30 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
|||||||
editor?.editing.view.focus();
|
editor?.editing.view.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
useTriliumEvent("addLinkToText", ({ ntxId: eventNtxId }) => {
|
useLegacyImperativeHandlers({
|
||||||
if (eventNtxId !== ntxId || !editorApiRef.current) return;
|
addLinkToTextCommand() {
|
||||||
parentComponent?.triggerCommand("showAddLinkDialog", {
|
if (!editorApiRef.current) return;
|
||||||
text: editorApiRef.current.getSelectedText(),
|
parentComponent?.triggerCommand("showAddLinkDialog", {
|
||||||
hasSelection: editorApiRef.current.hasSelection(),
|
text: editorApiRef.current.getSelectedText(),
|
||||||
async addLink(notePath, linkTitle, externalLink) {
|
hasSelection: editorApiRef.current.hasSelection(),
|
||||||
await waitForEditor();
|
async addLink(notePath, linkTitle, externalLink) {
|
||||||
return editorApiRef.current?.addLink(notePath, linkTitle, externalLink);
|
await waitForEditor();
|
||||||
}
|
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() {
|
async function waitForEditor() {
|
||||||
|
|||||||
@ -39,6 +39,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro
|
|||||||
|
|
||||||
// React to included note changes.
|
// React to included note changes.
|
||||||
useTriliumEvent("refreshIncludedNote", ({ noteId }) => {
|
useTriliumEvent("refreshIncludedNote", ({ noteId }) => {
|
||||||
|
console.log("Refresh ", noteId);
|
||||||
if (!contentRef.current) return;
|
if (!contentRef.current) return;
|
||||||
refreshIncludedNote(contentRef.current, noteId);
|
refreshIncludedNote(contentRef.current, noteId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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 { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
|
||||||
import { updateTemplateCache } from "./ckeditor/snippets.js";
|
import { updateTemplateCache } from "./ckeditor/snippets.js";
|
||||||
|
|
||||||
export type BoxSize = "small" | "medium" | "full";
|
|
||||||
|
|
||||||
export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
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) {
|
async createNoteForReferenceLink(title: string) {
|
||||||
if (!this.notePath) {
|
if (!this.notePath) {
|
||||||
return;
|
return;
|
||||||
@ -153,10 +121,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
return resp.note.getBestNotePathString();
|
return resp.note.getBestNotePathString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshIncludedNoteEvent({ noteId }: EventData<"refreshIncludedNote">) {
|
|
||||||
this.refreshIncludedNote(this.$editor, noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async reinitialize() {
|
async reinitialize() {
|
||||||
const data = this.watchdog.editor?.getData();
|
const data = this.watchdog.editor?.getData();
|
||||||
await this.reinitializeWithData(data ?? "");
|
await this.reinitializeWithData(data ?? "");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user