Skip to content

Commit 319e283

Browse files
committed
refactor(react/type_widgets): separate persistence from canvas
1 parent 0b740bb commit 319e283

File tree

4 files changed

+83
-77
lines changed

4 files changed

+83
-77
lines changed

apps/client/src/widgets/note_types.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export const TYPE_MAPPINGS: Record<ExtendedNoteType, NoteTypeMapping> = {
118118
printable: true
119119
},
120120
canvas: {
121-
view: () => import("./type_widgets/Canvas"),
121+
view: () => import("./type_widgets/canvas/Canvas"),
122122
className: "note-detail-canvas",
123123
printable: true,
124124
isFullHeight: true
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Excalidraw } from "@excalidraw/excalidraw";
2+
import { TypeWidgetProps } from "../type_widget";
3+
import "@excalidraw/excalidraw/index.css";
4+
import { useNoteLabelBoolean } from "../../react/hooks";
5+
import { useCallback, useMemo, useRef } from "preact/hooks";
6+
import { type ExcalidrawImperativeAPI, type AppState } from "@excalidraw/excalidraw/types";
7+
import options from "../../../services/options";
8+
import "./Canvas.css";
9+
import { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types";
10+
import { goToLinkExt } from "../../../services/link";
11+
import useCanvasPersistence from "./persistence";
12+
13+
// currently required by excalidraw, in order to allows self-hosting fonts locally.
14+
// this avoids making excalidraw load the fonts from an external CDN.
15+
window.EXCALIDRAW_ASSET_PATH = `${window.location.pathname}/node_modules/@excalidraw/excalidraw/dist/prod`;
16+
17+
export default function Canvas({ note, noteContext }: TypeWidgetProps) {
18+
const apiRef = useRef<ExcalidrawImperativeAPI>(null);
19+
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
20+
const themeStyle = useMemo(() => {
21+
const documentStyle = window.getComputedStyle(document.documentElement);
22+
return documentStyle.getPropertyValue("--theme-style")?.trim() as AppState["theme"];
23+
}, []);
24+
const persistence = useCanvasPersistence(note, noteContext, apiRef, themeStyle, isReadOnly);
25+
26+
/** Use excalidraw's native zoom instead of the global zoom. */
27+
const onWheel = useCallback((e: MouseEvent) => {
28+
if (e.ctrlKey) {
29+
e.preventDefault();
30+
e.stopPropagation();
31+
}
32+
}, []);
33+
34+
const onLinkOpen = useCallback((element: NonDeletedExcalidrawElement, event: CustomEvent) => {
35+
let link = element.link;
36+
if (!link) {
37+
return false;
38+
}
39+
40+
if (link.startsWith("root/")) {
41+
link = "#" + link;
42+
}
43+
44+
const { nativeEvent } = event.detail;
45+
event.preventDefault();
46+
return goToLinkExt(nativeEvent, link, null);
47+
}, []);
48+
49+
return (
50+
<div className="canvas-render" onWheel={onWheel}>
51+
<div className="excalidraw-wrapper">
52+
<Excalidraw
53+
excalidrawAPI={api => apiRef.current = api}
54+
theme={themeStyle}
55+
viewModeEnabled={isReadOnly || options.is("databaseReadonly")}
56+
zenModeEnabled={false}
57+
isCollaborating={false}
58+
detectScroll={false}
59+
handleKeyboardGlobally={false}
60+
autoFocus={false}
61+
UIOptions={{
62+
canvasActions: {
63+
saveToActiveFile: false,
64+
export: false
65+
}
66+
}}
67+
onLinkOpen={onLinkOpen}
68+
{...persistence}
69+
/>
70+
</div>
71+
</div>
72+
)
73+
}

apps/client/src/widgets/type_widgets/Canvas.tsx renamed to apps/client/src/widgets/type_widgets/canvas/persistence.ts

Lines changed: 9 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,25 @@
1-
import { Excalidraw, exportToSvg, getSceneVersion } from "@excalidraw/excalidraw";
2-
import { TypeWidgetProps } from "./type_widget";
3-
import "@excalidraw/excalidraw/index.css";
4-
import { useEditorSpacedUpdate, useNoteLabelBoolean } from "../react/hooks";
5-
import { useCallback, useMemo, useRef } from "preact/hooks";
6-
import { type ExcalidrawImperativeAPI, type AppState, type BinaryFileData, LibraryItem, ExcalidrawProps } from "@excalidraw/excalidraw/types";
7-
import options from "../../services/options";
8-
import "./Canvas.css";
9-
import FNote from "../../entities/fnote";
101
import { RefObject } from "preact";
11-
import server from "../../services/server";
2+
import NoteContext from "../../../components/note_context";
3+
import FNote from "../../../entities/fnote";
4+
import { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem } from "@excalidraw/excalidraw/types";
5+
import { useRef } from "preact/hooks";
6+
import { useEditorSpacedUpdate } from "../../react/hooks";
127
import { ExcalidrawElement, NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types";
13-
import { goToLinkExt } from "../../services/link";
14-
import NoteContext from "../../components/note_context";
15-
16-
// currently required by excalidraw, in order to allows self-hosting fonts locally.
17-
// this avoids making excalidraw load the fonts from an external CDN.
18-
window.EXCALIDRAW_ASSET_PATH = `${window.location.pathname}/node_modules/@excalidraw/excalidraw/dist/prod`;
8+
import { exportToSvg, getSceneVersion } from "@excalidraw/excalidraw";
9+
import server from "../../../services/server";
1910

2011
interface AttachmentMetadata {
2112
title: string;
2213
attachmentId: string;
2314
}
2415

25-
interface CanvasContent {
16+
export interface CanvasContent {
2617
elements: ExcalidrawElement[];
2718
files: BinaryFileData[];
2819
appState: Partial<AppState>;
2920
}
3021

31-
export default function Canvas({ note, noteContext }: TypeWidgetProps) {
32-
const apiRef = useRef<ExcalidrawImperativeAPI>(null);
33-
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
34-
const themeStyle = useMemo(() => {
35-
const documentStyle = window.getComputedStyle(document.documentElement);
36-
return documentStyle.getPropertyValue("--theme-style")?.trim() as AppState["theme"];
37-
}, []);
38-
const persistence = usePersistence(note, noteContext, apiRef, themeStyle, isReadOnly);
39-
40-
/** Use excalidraw's native zoom instead of the global zoom. */
41-
const onWheel = useCallback((e: MouseEvent) => {
42-
if (e.ctrlKey) {
43-
e.preventDefault();
44-
e.stopPropagation();
45-
}
46-
}, []);
47-
48-
const onLinkOpen = useCallback((element: NonDeletedExcalidrawElement, event: CustomEvent) => {
49-
let link = element.link;
50-
if (!link) {
51-
return false;
52-
}
53-
54-
if (link.startsWith("root/")) {
55-
link = "#" + link;
56-
}
57-
58-
const { nativeEvent } = event.detail;
59-
event.preventDefault();
60-
return goToLinkExt(nativeEvent, link, null);
61-
}, []);
62-
63-
return (
64-
<div className="canvas-render" onWheel={onWheel}>
65-
<div className="excalidraw-wrapper">
66-
<Excalidraw
67-
excalidrawAPI={api => apiRef.current = api}
68-
theme={themeStyle}
69-
viewModeEnabled={isReadOnly || options.is("databaseReadonly")}
70-
zenModeEnabled={false}
71-
isCollaborating={false}
72-
detectScroll={false}
73-
handleKeyboardGlobally={false}
74-
autoFocus={false}
75-
UIOptions={{
76-
canvasActions: {
77-
saveToActiveFile: false,
78-
export: false
79-
}
80-
}}
81-
onLinkOpen={onLinkOpen}
82-
{...persistence}
83-
/>
84-
</div>
85-
</div>
86-
)
87-
}
88-
89-
function usePersistence(note: FNote, noteContext: NoteContext | null | undefined, apiRef: RefObject<ExcalidrawImperativeAPI>, theme: AppState["theme"], isReadOnly: boolean): Partial<ExcalidrawProps> {
22+
export default function useCanvasPersistence(note: FNote, noteContext: NoteContext | null | undefined, apiRef: RefObject<ExcalidrawImperativeAPI>, theme: AppState["theme"], isReadOnly: boolean): Partial<ExcalidrawProps> {
9023
const libraryChanged = useRef(false);
9124

9225
/**

0 commit comments

Comments
 (0)