chore(react/type_widgets): start porting canvas

This commit is contained in:
Elian Doran
2025-09-21 23:32:11 +03:00
parent e1ac319a7b
commit 592a8b2232
5 changed files with 78 additions and 78 deletions

View File

@@ -33,7 +33,8 @@ const TYPE_MAPPINGS: Record<ExtendedNoteType, () => 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.
};

View File

@@ -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%;
}

View File

@@ -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<ExcalidrawImperativeAPI>(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 (
<div className="canvas-widget note-detail-canvas note-detail-printable note-detail full-height">
<div className="canvas-render">
<div className="excalidraw-wrapper">
<Excalidraw
excalidrawAPI={api => apiRef.current = api}
/>
</div>
</div>
</div>
)
}

View File

@@ -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*/`
<div class="canvas-widget note-detail-canvas note-detail-printable note-detail">
<style>
.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 */
}
</style>
<!-- height here necessary. otherwise excalidraw not shown -->
<div class="canvas-render" style="height: 100%"></div>
</div>
`;
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;

View File

@@ -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<void>;
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 <CanvasElement
{...this.opts}
excalidrawAPI={api => {
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<AppState> = content.appState ?? {};
@@ -178,7 +157,6 @@ export default class Canvas {
function CanvasElement(opts: ExcalidrawProps) {
return (
<div className="excalidraw-wrapper">
<Excalidraw
{...opts}
onLinkOpen={useCallback((element: NonDeletedExcalidrawElement, event: CustomEvent) => {
@@ -196,6 +174,5 @@ function CanvasElement(opts: ExcalidrawProps) {
return linkService.goToLinkExt(nativeEvent, link, null);
}, [])}
/>
</div>
);
}