From 592a8b2232e3d38e58a684464e648e4b44346eec Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 21 Sep 2025 23:32:11 +0300 Subject: [PATCH] chore(react/type_widgets): start porting canvas --- apps/client/src/widgets/NoteDetail.tsx | 3 +- .../src/widgets/type_widgets/Canvas.css | 34 ++++++++++++ .../src/widgets/type_widgets/Canvas.tsx | 41 ++++++++++++++ .../src/widgets/type_widgets_old/canvas.ts | 55 +------------------ .../widgets/type_widgets_old/canvas_el.tsx | 23 -------- 5 files changed, 78 insertions(+), 78 deletions(-) create mode 100644 apps/client/src/widgets/type_widgets/Canvas.css create mode 100644 apps/client/src/widgets/type_widgets/Canvas.tsx diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index e2906449f..415d73904 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -33,7 +33,8 @@ const TYPE_MAPPINGS: Record Promise<{ default: TypeWidge "attachmentList": async () => (await import("./type_widgets/Attachment")).AttachmentList, "attachmentDetail": async () => (await import("./type_widgets/Attachment")).AttachmentDetail, "readOnlyText": () => import("./type_widgets/text/ReadOnlyText"), - "render": () => import("./type_widgets/Render") + "render": () => import("./type_widgets/Render"), + "canvas": () => import("./type_widgets/Canvas") // TODO: finalize the record. }; diff --git a/apps/client/src/widgets/type_widgets/Canvas.css b/apps/client/src/widgets/type_widgets/Canvas.css new file mode 100644 index 000000000..cf5a2804a --- /dev/null +++ b/apps/client/src/widgets/type_widgets/Canvas.css @@ -0,0 +1,34 @@ +.excalidraw .App-menu_top .buttonList { + display: flex; +} + +/* Conflict between excalidraw and bootstrap classes keeps the menu hidden */ +/* https://github.com/zadam/trilium/issues/3780 */ +/* https://github.com/excalidraw/excalidraw/issues/6567 */ +.excalidraw .dropdown-menu { + display: block; +} + +.excalidraw-wrapper { + height: 100%; +} + +:root[dir="ltr"] +.excalidraw +.layer-ui__wrapper +.zen-mode-transition.App-menu_bottom--transition-left { + transform: none; +} + +/* collaboration not possible so hide the button */ +.CollabButton { + display: none !important; +} + +.library-button { + display: none !important; /* library won't work without extra support which isn't currently implemented */ +} + +.note-detail-canvas > .canvas-render { + height: 100%; +} \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/Canvas.tsx b/apps/client/src/widgets/type_widgets/Canvas.tsx new file mode 100644 index 000000000..0ed6cb7b7 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/Canvas.tsx @@ -0,0 +1,41 @@ +import { Excalidraw } from "@excalidraw/excalidraw"; +import { TypeWidgetProps } from "./type_widget"; +import "@excalidraw/excalidraw/index.css"; +import { useNoteBlob } from "../react/hooks"; +import { useEffect, useRef } from "preact/hooks"; +import type { ExcalidrawImperativeAPI, Theme } from "@excalidraw/excalidraw/types"; +import "./Canvas.css"; + +export default function Canvas({ note }: TypeWidgetProps) { + const apiRef = useRef(null); + const blob = useNoteBlob(note); + + useEffect(() => { + const documentStyle = window.getComputedStyle(document.documentElement); + const themeStyle = documentStyle.getPropertyValue("--theme-style")?.trim() as Theme; + + const api = apiRef.current; + const content = blob?.content; + if (!api) return; + if (!content?.trim()) { + api.updateScene({ + elements: [], + appState: { + theme: themeStyle + } + }); + } + }, [ blob ]); + + return ( +
+
+
+ apiRef.current = api} + /> +
+
+
+ ) +} diff --git a/apps/client/src/widgets/type_widgets_old/canvas.ts b/apps/client/src/widgets/type_widgets_old/canvas.ts index bc619acea..4e72a776b 100644 --- a/apps/client/src/widgets/type_widgets_old/canvas.ts +++ b/apps/client/src/widgets/type_widgets_old/canvas.ts @@ -3,55 +3,12 @@ import server from "../../services/server.js"; import type FNote from "../../entities/fnote.js"; import options from "../../services/options.js"; import type { LibraryItem } from "@excalidraw/excalidraw/types"; -import type { Theme } from "@excalidraw/excalidraw/element/types"; import type Canvas from "./canvas_el.js"; import { CanvasContent } from "./canvas_el.js"; import { renderReactWidget } from "../react/react_utils.jsx"; import SpacedUpdate from "../../services/spaced_update.js"; import protected_session_holder from "../../services/protected_session_holder.js"; -const TPL = /*html*/` -
- - -
-
-`; - - - interface AttachmentMetadata { title: string; attachmentId: string; @@ -151,10 +108,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget { }); } - static getType() { - return "canvas"; - } - doRender() { this.$widget = $(TPL); this.$widget.bind("mousewheel DOMMouseScroll", (event) => { @@ -165,10 +118,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { } }); - this.$widget.toggleClass("full-height", true); this.$render = this.$widget.find(".canvas-render"); - const documentStyle = window.getComputedStyle(document.documentElement); - this.themeStyle = documentStyle.getPropertyValue("--theme-style")?.trim() as Theme; this.#init(); @@ -229,9 +179,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget { // get note from backend and put into canvas const blob = await note.getBlob(); - // before we load content into excalidraw, make sure excalidraw has loaded - await this.canvasInstance.waitForApiToBecomeAvailable(); - /** * new and empty note - make sure that canvas is empty. * If we do not set it manually, we occasionally get some "bleeding" from another @@ -239,7 +186,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { * newly instantiated? */ if (!blob?.content?.trim()) { - this.canvasInstance.resetScene(this.themeStyle); + } else if (blob.content) { let content: CanvasContent; diff --git a/apps/client/src/widgets/type_widgets_old/canvas_el.tsx b/apps/client/src/widgets/type_widgets_old/canvas_el.tsx index b5018325d..b345e7e08 100644 --- a/apps/client/src/widgets/type_widgets_old/canvas_el.tsx +++ b/apps/client/src/widgets/type_widgets_old/canvas_el.tsx @@ -1,4 +1,3 @@ -import "@excalidraw/excalidraw/index.css"; import { Excalidraw, getSceneVersion, exportToSvg } from "@excalidraw/excalidraw"; import { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem } from "@excalidraw/excalidraw/types"; import { ExcalidrawElement, NonDeletedExcalidrawElement, Theme } from "@excalidraw/excalidraw/element/types"; @@ -19,7 +18,6 @@ export default class Canvas { private currentSceneVersion: number; private opts: ExcalidrawProps; private excalidrawApi!: ExcalidrawImperativeAPI; - private initializedPromise: JQuery.Deferred; constructor(opts: ExcalidrawProps) { this.opts = opts; @@ -27,19 +25,9 @@ export default class Canvas { this.initializedPromise = $.Deferred(); } - async waitForApiToBecomeAvailable() { - while (!this.excalidrawApi) { - await this.initializedPromise; - } - } - createCanvasElement() { return { - this.excalidrawApi = api; - this.initializedPromise.resolve(); - }} /> } @@ -80,15 +68,6 @@ export default class Canvas { return !!this.excalidrawApi; } - resetScene(theme: Theme) { - this.excalidrawApi.updateScene({ - elements: [], - appState: { - theme - } - }); - } - loadData(content: CanvasContent, theme: Theme) { const { elements, files } = content; const appState: Partial = content.appState ?? {}; @@ -178,7 +157,6 @@ export default class Canvas { function CanvasElement(opts: ExcalidrawProps) { return ( -
{ @@ -196,6 +174,5 @@ function CanvasElement(opts: ExcalidrawProps) { return linkService.goToLinkExt(nativeEvent, link, null); }, [])} /> -
); }