mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-09-27 15:31:33 +08:00
chore(client/ts): port canvas
This commit is contained in:
parent
2167948509
commit
7c7fd044c6
@ -234,7 +234,7 @@ function goToLink(evt: MouseEvent | JQuery.ClickEvent) {
|
||||
return goToLinkExt(evt, hrefLink, $link);
|
||||
}
|
||||
|
||||
function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | undefined, $link: JQuery<HTMLElement>) {
|
||||
function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | undefined, $link: JQuery<HTMLElement> | null) {
|
||||
if (hrefLink?.startsWith("data:")) {
|
||||
return true;
|
||||
}
|
||||
@ -242,7 +242,7 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | und
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
if (hrefLink?.startsWith("#fn")) {
|
||||
if (hrefLink?.startsWith("#fn") && $link) {
|
||||
return handleFootnote(hrefLink, $link);
|
||||
}
|
||||
|
||||
|
66
src/public/app/types.d.ts
vendored
66
src/public/app/types.d.ts
vendored
@ -54,6 +54,72 @@ declare global {
|
||||
|
||||
process?: ElectronProcess;
|
||||
glob?: CustomGlobals;
|
||||
React: {
|
||||
createElement(any, any?, any?);
|
||||
Fragment: any;
|
||||
useState({
|
||||
width: undefined,
|
||||
height: undefined
|
||||
});
|
||||
useRef(ref: null);
|
||||
useEffect(cb: () => void, args: unknown[]);
|
||||
useCallback(cb: (el, ev) => void, args: unknown[]);
|
||||
};
|
||||
ReactDOM: {
|
||||
unmountComponentAtNode(el: HTMLElement);
|
||||
createRoot(el: HTMLElement);
|
||||
}
|
||||
ExcalidrawLib: {
|
||||
getSceneVersion(el: unknown[]): number;
|
||||
exportToSvg(opts: {
|
||||
elements: ExcalidrawElement[],
|
||||
appState: ExcalidrawAppState,
|
||||
exportPadding: number,
|
||||
metadata: string,
|
||||
files: ExcalidrawElement[]
|
||||
}): Promise<HTMLElement>;
|
||||
updateScene,
|
||||
Excalidraw: unknown
|
||||
}
|
||||
EXCALIDRAW_ASSET_PATH: string;
|
||||
}
|
||||
|
||||
interface ExcalidrawApi {
|
||||
getSceneElements(): ExcalidrawElement[];
|
||||
getAppState(): ExcalidrawAppState;
|
||||
getFiles(): ExcalidrawElement[];
|
||||
updateScene(scene: ExcalidrawScene);
|
||||
updateLibrary(opts: { libraryItems?: ExcalidrawLibrary[], merge: boolean }): Promise<ExcalidrawLibrary[]>;
|
||||
addFiles(files: ExcalidrawElement[]);
|
||||
history: {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
interface ExcalidrawElement {
|
||||
fileId: number;
|
||||
}
|
||||
|
||||
interface ExcalidrawLibrary {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ExcalidrawScene {
|
||||
elements: unknown[];
|
||||
appState: ExcalidrawAppState;
|
||||
collaborators: unknown[];
|
||||
}
|
||||
|
||||
interface ExcalidrawAppState {
|
||||
scrollX?: number;
|
||||
scrollY?: number;
|
||||
zoom?: number;
|
||||
theme?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
offsetLeft?: number;
|
||||
offsetTop?: number;
|
||||
}
|
||||
|
||||
interface AutoCompleteConfig {
|
||||
|
@ -3,6 +3,7 @@ import TypeWidget from "./type_widget.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import server from "../../services/server.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
const TPL = `
|
||||
<div class="canvas-widget note-detail-canvas note-detail-printable note-detail">
|
||||
<style>
|
||||
@ -51,6 +52,17 @@ const TPL = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
interface CanvasContent {
|
||||
elements: ExcalidrawElement[],
|
||||
files: ExcalidrawElement[],
|
||||
appState: ExcalidrawAppState
|
||||
}
|
||||
|
||||
interface AttachmentMetadata {
|
||||
title: string;
|
||||
attachmentId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* # Canvas note with excalidraw
|
||||
* @author thfrei 2022-05-11
|
||||
@ -95,6 +107,24 @@ const TPL = `
|
||||
* - Make it easy to include a canvas note inside a text note
|
||||
*/
|
||||
export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
|
||||
private readonly SCENE_VERSION_INITIAL: number;
|
||||
private readonly SCENE_VERSION_ERROR: number;
|
||||
|
||||
private currentNoteId: string;
|
||||
private currentSceneVersion: number;
|
||||
private libraryChanged: boolean;
|
||||
private librarycache: ExcalidrawLibrary[];
|
||||
private attachmentMetadata: AttachmentMetadata[];
|
||||
private themeStyle!: string;
|
||||
private excalidrawApi!: ExcalidrawApi;
|
||||
private excalidrawWrapperRef!: {
|
||||
current: HTMLElement
|
||||
};
|
||||
|
||||
private $render!: JQuery<HTMLElement>;
|
||||
private reactHandlers!: JQuery<HTMLElement>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -145,6 +175,9 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
const React = window.React;
|
||||
const ReactDOM = window.ReactDOM;
|
||||
const renderElement = this.$render.get(0);
|
||||
if (!renderElement) {
|
||||
throw new Error("Unable to find element to render.");
|
||||
}
|
||||
|
||||
ReactDOM.unmountComponentAtNode(renderElement);
|
||||
const root = ReactDOM.createRoot(renderElement);
|
||||
@ -156,10 +189,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
|
||||
/**
|
||||
* called to populate the widget container with the note content
|
||||
*
|
||||
* @param {FNote} note
|
||||
*/
|
||||
async doRefresh(note) {
|
||||
async doRefresh(note: FNote) {
|
||||
// see if the note changed, since we do not get a new class for a new note
|
||||
const noteChanged = this.currentNoteId !== note.noteId;
|
||||
if (noteChanged) {
|
||||
@ -183,7 +214,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
* note into this fresh note. Probably due to that this note-instance does not get
|
||||
* newly instantiated?
|
||||
*/
|
||||
if (!blob.content?.trim()) {
|
||||
if (!blob?.content?.trim()) {
|
||||
const sceneData = {
|
||||
elements: [],
|
||||
appState: {
|
||||
@ -194,11 +225,11 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
|
||||
this.excalidrawApi.updateScene(sceneData);
|
||||
} else if (blob.content) {
|
||||
// load saved content into excalidraw canvas
|
||||
let content;
|
||||
let content: CanvasContent;
|
||||
|
||||
// load saved content into excalidraw canvas
|
||||
try {
|
||||
content = blob.getJsonContent();
|
||||
content = blob.getJsonContent() as CanvasContent;
|
||||
} catch (err) {
|
||||
console.error("Error parsing content. Probably note.type changed. Starting with empty canvas", note, blob, err);
|
||||
|
||||
@ -219,7 +250,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
appState.offsetLeft = boundingClientRect.left;
|
||||
appState.offsetTop = boundingClientRect.top;
|
||||
|
||||
const sceneData = {
|
||||
const sceneData: ExcalidrawScene = {
|
||||
elements,
|
||||
appState,
|
||||
collaborators: []
|
||||
@ -257,7 +288,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
// Extract libraryItems from the blobs
|
||||
const libraryItems = results.map((result) => result.blob.getJsonContentSafely()).filter((item) => !!item);
|
||||
const libraryItems = results.map((result) => result?.blob?.getJsonContentSafely()).filter((item) => !!item) as ExcalidrawLibrary[];
|
||||
|
||||
// Extract metadata for each attachment
|
||||
const metadata = results.map((result) => result.metadata);
|
||||
@ -297,7 +328,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
const files = this.excalidrawApi.getFiles();
|
||||
|
||||
// parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share
|
||||
const svg = await ExcalidrawLib.exportToSvg({
|
||||
const svg = await window.ExcalidrawLib.exportToSvg({
|
||||
elements,
|
||||
appState,
|
||||
exportPadding: 5, // 5 px padding
|
||||
@ -306,7 +337,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
});
|
||||
const svgString = svg.outerHTML;
|
||||
|
||||
const activeFiles = {};
|
||||
const activeFiles: Record<string, ExcalidrawElement> = {};
|
||||
elements.forEach((element) => {
|
||||
if (element.fileId) {
|
||||
activeFiles[element.fileId] = files[element.fileId];
|
||||
@ -474,12 +505,12 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
React.createElement(Excalidraw, {
|
||||
// this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu
|
||||
theme: this.themeStyle,
|
||||
excalidrawAPI: (api) => {
|
||||
excalidrawAPI: (api: ExcalidrawApi) => {
|
||||
this.excalidrawApi = api;
|
||||
},
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
onPaste: (data, event) => {
|
||||
onPaste: (data: unknown, event: unknown) => {
|
||||
console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event);
|
||||
},
|
||||
onLibraryChange: () => {
|
Loading…
x
Reference in New Issue
Block a user