diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index a6fc3572a9..a1d75fd348 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -1015,10 +1015,11 @@ class App extends React.Component { /** * Whether the active tool captures the primary pointer instead of the - * view-mode drag-to-pan — the laser always does; while non-interactive, - * any tool allowed via `interaction.enabled.tools` does. (Editing tools - * capture the pointer trivially since view mode implies they're not - * active; this predicate only matters where view-mode gates apply.) + * view-mode drag-to-pan — the laser and host-implemented custom tools do; + * while non-interactive, any tool allowed via + * `interaction.enabled.tools` does. (Editing tools capture the pointer + * trivially since view mode implies they're not active; this predicate only + * matters where view-mode gates apply.) */ public isActiveToolPointerCapturing(): boolean { if (!this.isInteractionEnabled()) { @@ -1026,7 +1027,10 @@ class App extends React.Component { // is inert — including the laser return this.isToolSupported(this.state.activeTool.type); } - return this.state.activeTool.type === "laser"; + return ( + this.state.activeTool.type === "laser" || + this.state.activeTool.type === "custom" + ); } /** Whether Excalidraw's full default UI is rendered. */ diff --git a/packages/excalidraw/tests/viewMode.test.tsx b/packages/excalidraw/tests/viewMode.test.tsx index 38cb6f989e..a720a7464f 100644 --- a/packages/excalidraw/tests/viewMode.test.tsx +++ b/packages/excalidraw/tests/viewMode.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { vi } from "vitest"; import { CURSOR_TYPE, KEYS } from "@excalidraw/common"; @@ -8,6 +9,7 @@ import { API } from "./helpers/api"; import { Keyboard, Pointer, UI } from "./helpers/ui"; import { getTextEditor, updateTextEditor } from "./queries/dom"; import { + act, fireEvent, render, GlobalTestState, @@ -59,6 +61,48 @@ describe("view mode", () => { }); }); + it("dispatches pointer events to custom tools in view mode", () => { + const onPointerDown = vi.fn(); + const onPointerUp = vi.fn(); + + GlobalTestState.renderResult.rerender( + , + ); + + API.setAppState({ viewModeEnabled: true }); + act(() => { + h.app.setActiveTool({ + type: "custom", + customType: "comment", + locked: true, + }); + }); + expect(h.app.isInteractionEnabled()).toBe(true); + + const { scrollX, scrollY } = h.state; + + mouse.reset(); + mouse.downAt(50, 50); + mouse.moveTo(100, 100); + mouse.upAt(100, 100); + + expect(onPointerDown).toHaveBeenCalledTimes(1); + expect(onPointerDown.mock.calls[0][0]).toMatchObject({ + type: "custom", + customType: "comment", + }); + expect(onPointerUp).toHaveBeenCalledTimes(1); + expect(onPointerUp.mock.calls[0][0]).toMatchObject({ + type: "custom", + customType: "comment", + }); + expect([h.state.scrollX, h.state.scrollY]).toEqual([scrollX, scrollY]); + }); + it("cursor should stay as grabbing type when hovering over canvas elements", async () => { // create a rectangle, then hover over it – cursor should be // move type for mouse and grab for touch & pen