mirror of
https://github.com/TriliumNext/Trilium.git
synced 2026-10-12 02:09:59 +00:00
client/text editor/canvas: patch Excalidraw to disable responsive mode on the desktop layout
This commit is contained in:
@@ -3,6 +3,8 @@ import { type ComponentChildren, type RefObject, render } from "preact";
|
||||
import { act } from "preact/test-utils";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { CanvasEditorProps } from "./Canvas";
|
||||
|
||||
const excalidrawProps = vi.fn((_props: ExcalidrawProps) => undefined);
|
||||
vi.mock("@excalidraw/excalidraw", () => ({
|
||||
Excalidraw: (props: ExcalidrawProps) => {
|
||||
@@ -46,7 +48,7 @@ describe("CanvasEditor", () => {
|
||||
container.remove();
|
||||
});
|
||||
|
||||
async function mount(isEmbedded?: boolean, children?: ComponentChildren) {
|
||||
async function mount(props: Partial<CanvasEditorProps> = {}) {
|
||||
await act(async () => {
|
||||
render(
|
||||
<CanvasEditor
|
||||
@@ -54,10 +56,8 @@ describe("CanvasEditor", () => {
|
||||
isReadOnly={false}
|
||||
colorScheme="light"
|
||||
persistence={{}}
|
||||
isEmbedded={isEmbedded}
|
||||
>
|
||||
{children}
|
||||
</CanvasEditor>,
|
||||
{...props}
|
||||
/>,
|
||||
container
|
||||
);
|
||||
});
|
||||
@@ -73,7 +73,7 @@ describe("CanvasEditor", () => {
|
||||
}
|
||||
|
||||
it("reads its position again before pointer input once the drawing moved", async () => {
|
||||
const excalidraw = await mount(true);
|
||||
const excalidraw = await mount({ isEmbedded: true });
|
||||
point(excalidraw);
|
||||
expect(refresh).not.toHaveBeenCalled();
|
||||
|
||||
@@ -94,8 +94,26 @@ describe("CanvasEditor", () => {
|
||||
});
|
||||
|
||||
it("renders its children inside Excalidraw", async () => {
|
||||
const excalidraw = await mount(true, <span className="excalidraw-child" />);
|
||||
const excalidraw = await mount({
|
||||
isEmbedded: true,
|
||||
children: <span className="excalidraw-child" />
|
||||
});
|
||||
|
||||
expect(excalidraw?.querySelector(".excalidraw-child")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("keeps Excalidraw's desktop layout at any size only with `isDesktopLayout`", async () => {
|
||||
const getFormFactor = () => excalidrawProps.mock.lastCall?.[0].UIOptions?.getFormFactor;
|
||||
|
||||
await mount({ isDesktopLayout: true });
|
||||
const desktopFormFactor = getFormFactor();
|
||||
expect(desktopFormFactor?.(320, 200)).toBe("desktop");
|
||||
|
||||
// Excalidraw renders again when a function in `UIOptions` changes.
|
||||
await mount({ isDesktopLayout: true, isReadOnly: true });
|
||||
expect(getFormFactor()).toBe(desktopFormFactor);
|
||||
|
||||
await mount();
|
||||
expect(getFormFactor()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,13 +47,16 @@ export interface CanvasEditorProps {
|
||||
persistence: Partial<ExcalidrawProps>;
|
||||
/** Whether the editor moves on the page, as inside a text note that scrolls. */
|
||||
isEmbedded?: boolean;
|
||||
/** Whether Excalidraw keeps its desktop layout however small the editor is. */
|
||||
isDesktopLayout?: boolean;
|
||||
/** Rendered inside Excalidraw, where its hooks such as `useI18n()` work. */
|
||||
children?: ComponentChildren;
|
||||
}
|
||||
|
||||
/** The Excalidraw editor of a canvas note or drawing, loaded and saved by `persistence`. */
|
||||
export function CanvasEditor({
|
||||
apiRef, isReadOnly, colorScheme, persistence, isEmbedded = false, children
|
||||
apiRef, isReadOnly, colorScheme, persistence, isEmbedded = false, isDesktopLayout = false,
|
||||
children
|
||||
}: CanvasEditorProps) {
|
||||
const [ locale ] = useTriliumOption("locale");
|
||||
const noteDrop = useCanvasNoteDrop(apiRef, isReadOnly);
|
||||
@@ -129,7 +132,8 @@ export function CanvasEditor({
|
||||
canvasActions: {
|
||||
saveToActiveFile: false,
|
||||
export: false
|
||||
}
|
||||
},
|
||||
getFormFactor: isDesktopLayout ? getDesktopFormFactor : undefined
|
||||
}}
|
||||
onLinkOpen={onLinkOpen}
|
||||
validateEmbeddable={validateEmbeddable}
|
||||
@@ -143,6 +147,11 @@ export function CanvasEditor({
|
||||
)
|
||||
}
|
||||
|
||||
/** The form factor of Excalidraw's desktop layout, for an editor of any size. */
|
||||
function getDesktopFormFactor() {
|
||||
return "desktop" as const;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a Trilium note ID from an embeddable's link, accepting the canonical note-path form
|
||||
* (`root/<noteId>` or `#root/<noteId>`). Returns `null` for anything else so non-note embeds are
|
||||
|
||||
@@ -7,8 +7,11 @@ vi.mock("@excalidraw/excalidraw", () => ({ exportToSvg: vi.fn() }));
|
||||
vi.mock("./Canvas", () => ({ CanvasEditor: MockCanvasEditor }));
|
||||
vi.mock("./persistence", () => ({ useCanvasDrawingPersistence: () => ({}) }));
|
||||
|
||||
const canvasEditorProps = vi.fn();
|
||||
|
||||
/** Renders Excalidraw's focusable container after a render, as once its language loads. */
|
||||
function MockCanvasEditor() {
|
||||
function MockCanvasEditor(props: { isDesktopLayout?: boolean }) {
|
||||
canvasEditorProps(props);
|
||||
const [ isLoaded, setIsLoaded ] = useState(false);
|
||||
useEffect(() => setIsLoaded(true), []);
|
||||
return isLoaded ? <div className="excalidraw" tabIndex={0} /> : <span className="loading" />;
|
||||
@@ -323,4 +326,20 @@ describe("CanvasDrawing", () => {
|
||||
expect(document.activeElement).toBe(editable);
|
||||
editable.remove();
|
||||
});
|
||||
|
||||
it("keeps the desktop layout of Excalidraw in the desktop layout of Trilium only", async () => {
|
||||
const device = window.glob.device;
|
||||
await mount();
|
||||
expect(canvasEditorProps).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ isDesktopLayout: true })
|
||||
);
|
||||
|
||||
render(null, box);
|
||||
window.glob.device = "mobile";
|
||||
await mount();
|
||||
window.glob.device = device;
|
||||
expect(canvasEditorProps).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ isDesktopLayout: false })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import type FAttachment from "../../../entities/fattachment";
|
||||
import type FNote from "../../../entities/fnote";
|
||||
import type { AttachmentEditor } from "../../../services/content_renderer";
|
||||
import options from "../../../services/options";
|
||||
import { isDesktop } from "../../../services/utils";
|
||||
import { useColorScheme } from "../../react/hooks";
|
||||
import { CanvasEditor } from "./Canvas";
|
||||
import CanvasDrawingMenu from "./CanvasDrawingMenu";
|
||||
@@ -49,6 +50,7 @@ export default function CanvasDrawing({ attachment, editor }: CanvasDrawingProps
|
||||
colorScheme={colorScheme}
|
||||
persistence={persistence}
|
||||
isEmbedded
|
||||
isDesktopLayout={isDesktop()}
|
||||
>
|
||||
<CanvasDrawingMenu apiRef={apiRef} isEditable={isEditable} />
|
||||
{isEditable && <CanvasEmbedTools rootRef={rootRef} apiRef={apiRef} />}
|
||||
|
||||
@@ -29,9 +29,12 @@ const CODE_CLASSES = [
|
||||
* Excalidraw names the parts to review.
|
||||
*/
|
||||
describe("Excalidraw contract of the canvas drawing", () => {
|
||||
const productionDir = dirname(createRequire(import.meta.url).resolve("@excalidraw/excalidraw"));
|
||||
// Vitest resolves the `development` export, so both folders come from `dist`.
|
||||
const entry = createRequire(import.meta.url).resolve("@excalidraw/excalidraw");
|
||||
const distDir = join(dirname(entry), "..");
|
||||
const productionDir = join(distDir, "prod");
|
||||
// The minified bundle renames the objects of the translations and the zoom constants.
|
||||
const developmentDir = join(productionDir, "../dev");
|
||||
const developmentDir = join(distDir, "dev");
|
||||
|
||||
it("renders every class and test id that the drawing selects", () => {
|
||||
const bundle = readBundle(productionDir, [ ".js", ".css" ]);
|
||||
@@ -86,6 +89,14 @@ describe("Excalidraw contract of the canvas drawing", () => {
|
||||
|
||||
expect(source).toContain(`varDEFAULT_SIDEBAR={name:"${LIBRARY_SIDEBAR}"`);
|
||||
});
|
||||
|
||||
it("takes the layout from `UIOptions.getFormFactor`, which the Trilium patch adds", () => {
|
||||
const unpatched = [ productionDir, developmentDir ].filter((dir) => {
|
||||
const source = readBundle(dir, [ ".js" ]).replace(/\s+/g, "");
|
||||
return !source.includes("this.props.UIOptions.getFormFactor?.(");
|
||||
});
|
||||
expect(unpatched).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
/** The text of the scripts or stylesheets in `dir`, without its locales and fonts. */
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+4
-4
@@ -85,7 +85,7 @@ overrides:
|
||||
extract-zip>yauzl: 3.4.0
|
||||
|
||||
patchedDependencies:
|
||||
'@excalidraw/[email protected]': 859531a8ff622291b895d36bcd33e07ced6fcb3bcc90631ccaf776789b03f0c2
|
||||
'@excalidraw/[email protected]': dab54354a2d9ff87cb224deac1f930f25e7767a1bbf8a76bd8d55dcf2481715e
|
||||
[email protected]: 8acc288f8e9cde3c8a0c6e89f0b3796aeeb8251b88db9d1ba313e2352a591d68
|
||||
[email protected]: 09dfeb6f635c52ef565ca21aec3f70eb129577661f85eb67262092e8cb9f8548
|
||||
|
||||
@@ -207,7 +207,7 @@ importers:
|
||||
dependencies:
|
||||
'@excalidraw/excalidraw':
|
||||
specifier: 0.18.1
|
||||
version: 0.18.1(patch_hash=859531a8ff622291b895d36bcd33e07ced6fcb3bcc90631ccaf776789b03f0c2)([email protected]([email protected]))([email protected])
|
||||
version: 0.18.1(patch_hash=dab54354a2d9ff87cb224deac1f930f25e7767a1bbf8a76bd8d55dcf2481715e)([email protected]([email protected]))([email protected])
|
||||
'@floating-ui/dom':
|
||||
specifier: 1.8.0
|
||||
version: 1.8.0
|
||||
@@ -857,7 +857,7 @@ importers:
|
||||
dependencies:
|
||||
'@excalidraw/excalidraw':
|
||||
specifier: 0.18.1
|
||||
version: 0.18.1(patch_hash=859531a8ff622291b895d36bcd33e07ced6fcb3bcc90631ccaf776789b03f0c2)([email protected]([email protected]))([email protected])
|
||||
version: 0.18.1(patch_hash=dab54354a2d9ff87cb224deac1f930f25e7767a1bbf8a76bd8d55dcf2481715e)([email protected]([email protected]))([email protected])
|
||||
'@floating-ui/dom':
|
||||
specifier: 1.8.0
|
||||
version: 1.8.0
|
||||
@@ -13735,7 +13735,7 @@ snapshots:
|
||||
'@eslint/core': 1.2.1
|
||||
levn: 0.4.1
|
||||
|
||||
'@excalidraw/[email protected](patch_hash=859531a8ff622291b895d36bcd33e07ced6fcb3bcc90631ccaf776789b03f0c2)([email protected]([email protected]))([email protected])':
|
||||
'@excalidraw/[email protected](patch_hash=dab54354a2d9ff87cb224deac1f930f25e7767a1bbf8a76bd8d55dcf2481715e)([email protected]([email protected]))([email protected])':
|
||||
dependencies:
|
||||
'@braintree/sanitize-url': 6.0.2
|
||||
'@excalidraw/laser-pointer': 1.3.1
|
||||
|
||||
Reference in New Issue
Block a user