chore(react/type_widget): finalize SVG split editor

This commit is contained in:
Elian Doran 2025-09-20 21:33:57 +03:00
parent d95ed4a5d2
commit cc19a217ad
No known key found for this signature in database
4 changed files with 9 additions and 187 deletions

View File

@ -67,6 +67,15 @@ export default function SvgSplitEditor({ ntxId, note, attachmentName, renderSvg,
server.post(`notes/${note.noteId}/attachments?matchBy=title`, payload);
}
// Save the SVG when entering a note only when it does not have an attachment.
useEffect(() => {
note?.getAttachments().then((attachments) => {
if (!attachments.find((a) => a.title === `${attachmentName}.svg`)) {
onSave();
}
});
}, [ note ]);
// Import/export
useTriliumEvent("exportSvg", ({ ntxId: eventNtxId }) => {
if (eventNtxId !== ntxId || !svg) return;

View File

@ -1,60 +0,0 @@
import { getThemeById } from "@triliumnext/codemirror";
import type FNote from "../../entities/fnote.js";
import options from "../../services/options.js";
import TypeWidget from "./type_widget.js";
import CodeMirror, { type EditorConfig } from "@triliumnext/codemirror";
import type { EventData } from "../../components/app_context.js";
/**
* An abstract {@link TypeWidget} which implements the CodeMirror editor, meant to be used as a parent for
* widgets requiring the editor.
*
* The widget handles the loading and initialization of the CodeMirror editor, as well as some common
* actions.
*
* The derived class must:
*
* - Define `$editor` in the constructor.
* - Call `super.doRender()` in the extended class.
* - Call `this._update(note, content)` in `#doRefresh(note)`.
*/
export default class AbstractCodeTypeWidget extends TypeWidget {
protected $editor!: JQuery<HTMLElement>;
protected codeEditor!: CodeMirror;
doRender() {
this.initialized = this.#initEditor();
}
async #initEditor() {
// Load the theme.
}
/**
* Can be extended in derived classes to add extra options to the CodeMirror constructor. The options are appended
* at the end, so it is possible to override the default values introduced by the abstract editor as well.
*
* @returns the extra options to be passed to the CodeMirror constructor.
*/
getExtraOpts(): Partial<EditorConfig> {
return {};
}
/**
* Called as soon as the CodeMirror library has been loaded and the editor was constructed. Can be extended in
* derived classes to add additional functionality or to register event handlers.
*
* By default, it does nothing.
*/
onEditorInitialized() {
// Do nothing by default.
}
focus() {
this.codeEditor.focus();
}
}

View File

@ -1,87 +0,0 @@
import type FNote from "../../entities/fnote.js";
import utils from "../../services/utils.js";
import EditableCodeTypeWidget from "./editable_code.js";
import TypeWidget from "./type_widget.js";
import Split from "split.js";
import { DEFAULT_GUTTER_SIZE } from "../../services/resizer.js";
import options from "../../services/options.js";
import type { EventData } from "../../components/app_context.js";
import type OnClickButtonWidget from "../buttons/onclick_button.js";
import type { EditorConfig } from "@triliumnext/codemirror";
export default abstract class AbstractSplitTypeWidget extends TypeWidget {
private splitInstance?: Split.Instance;
protected $preview!: JQuery<HTMLElement>;
private $editorCol!: JQuery<HTMLElement>;
private $previewCol!: JQuery<HTMLElement>;
private $editor!: JQuery<HTMLElement>;
private $errorContainer!: JQuery<HTMLElement>;
private editorTypeWidget: EditableCodeTypeWidget;
private layoutOrientation?: "horizontal" | "vertical";
private isReadOnly?: boolean;
constructor() {
super();
this.editorTypeWidget.updateBackgroundColor = () => {};
this.editorTypeWidget.isEnabled = () => true;
const defaultOptions = this.editorTypeWidget.getExtraOpts();
this.editorTypeWidget.getExtraOpts = () => {
return {
...defaultOptions,
...this.buildEditorExtraOptions()
};
};
}
doRender(): void {
// Preview pane
this.$previewCol = this.$widget.find(".note-detail-split-preview-col");
this.$preview = this.$widget.find(".note-detail-split-preview");
// Editor pane
this.$editorCol = this.$widget.find(".note-detail-split-editor-col");
this.$editor = this.$widget.find(".note-detail-split-editor");
this.$editor.append(this.editorTypeWidget.render());
this.$errorContainer = this.$widget.find(".note-detail-error-container");
this.#adjustLayoutOrientation();
// Preview pane buttons
const $previewButtons = this.$previewCol.find(".preview-buttons");
const previewButtons = this.buildPreviewButtons();
$previewButtons.toggle(previewButtons.length > 0);
for (const previewButton of previewButtons) {
const $button = previewButton.render();
$button.removeClass("button-widget")
.addClass("btn")
.addClass("tn-tool-button");
$previewButtons.append($button);
previewButton.refreshIcon();
}
super.doRender();
}
async doRefresh(note: FNote) {
this.#adjustLayoutOrientation();
if (!this.isReadOnly) {
await this.editorTypeWidget.initialized;
this.editorTypeWidget.noteContext = this.noteContext;
this.editorTypeWidget.spacedUpdate = this.spacedUpdate;
this.editorTypeWidget.doRefresh(note);
}
}
buildPreviewButtons(): OnClickButtonWidget[] {
return [];
}
getData() {
return this.editorTypeWidget.getData();
}
}

View File

@ -1,40 +0,0 @@
import type { EventData } from "../../components/app_context.js";
import type FNote from "../../entities/fnote.js";
import { t } from "../../services/i18n.js";
import server from "../../services/server.js";
import toast from "../../services/toast.js";
import utils from "../../services/utils.js";
import OnClickButtonWidget from "../buttons/onclick_button.js";
import AbstractSplitTypeWidget from "./abstract_split_type_widget.js";
export default abstract class AbstractSvgSplitTypeWidget extends AbstractSplitTypeWidget {
private $renderContainer!: JQuery<HTMLElement>;
private zoomHandler: () => void;
private zoomInstance?: SvgPanZoom.Instance;
private svg?: string;
doRender(): void {
super.doRender();
this.$preview.append(this.$renderContainer);
$(window).on("resize", this.zoomHandler);
}
async doRefresh(note: FNote) {
super.doRefresh(note);
const blob = await note?.getBlob();
const content = blob?.content || "";
this.onContentChanged(content, true);
// Save the SVG when entering a note only when it does not have an attachment.
this.note?.getAttachments().then((attachments) => {
const attachmentName = `${this.attachmentName}.svg`;
if (!attachments.find((a) => a.title === attachmentName)) {
this.#saveSvg();
}
});
}
}