From 3697618266a6410da27717230b15f5c7b9938cee Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 20 Oct 2023 13:16:23 +0200 Subject: [PATCH] feat: support `props.locked` for `setActiveTool` (#7153) Co-authored-by: Aakansha Doshi --- src/components/App.tsx | 7 +++--- src/cursor.ts | 2 ++ src/tests/tool.test.tsx | 55 +++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 8 ++++-- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/tests/tool.test.tsx diff --git a/src/components/App.tsx b/src/components/App.tsx index 44ecdb4d..880462bb 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3249,7 +3249,7 @@ class App extends React.Component { }); setActiveTool = ( - tool: + tool: ( | ( | { type: Exclude } | { @@ -3257,7 +3257,8 @@ class App extends React.Component { insertOnCanvasDirectly?: boolean; } ) - | { type: "custom"; customType: string }, + | { type: "custom"; customType: string } + ) & { locked?: boolean }, ) => { const nextActiveTool = updateActiveTool(this.state, tool); if (nextActiveTool.type === "hand") { @@ -4714,7 +4715,7 @@ class App extends React.Component { pointerDownState, ); } else if (this.state.activeTool.type === "custom") { - setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO); + setCursorForShape(this.interactiveCanvas, this.state); } else if (this.state.activeTool.type === "frame") { this.createFrameElementOnPointerDown(pointerDownState); } else if (this.state.activeTool.type === "laser") { diff --git a/src/cursor.ts b/src/cursor.ts index 364ce155..c39c2efc 100644 --- a/src/cursor.ts +++ b/src/cursor.ts @@ -99,5 +99,7 @@ export const setCursorForShape = ( interactiveCanvas.style.cursor = `url(${url}), auto`; } else if (!["image", "custom"].includes(appState.activeTool.type)) { interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR; + } else { + interactiveCanvas.style.cursor = CURSOR_TYPE.AUTO; } }; diff --git a/src/tests/tool.test.tsx b/src/tests/tool.test.tsx new file mode 100644 index 00000000..84a43c6b --- /dev/null +++ b/src/tests/tool.test.tsx @@ -0,0 +1,55 @@ +import { Excalidraw } from "../packages/excalidraw/index"; +import { ExcalidrawImperativeAPI } from "../types"; +import { resolvablePromise } from "../utils"; +import { render } from "./test-utils"; +import { Pointer } from "./helpers/ui"; + +describe("setActiveTool()", () => { + const h = window.h; + + let excalidrawAPI: ExcalidrawImperativeAPI; + + const mouse = new Pointer("mouse"); + + beforeEach(async () => { + const excalidrawAPIPromise = resolvablePromise(); + await render( + excalidrawAPIPromise.resolve(api as any)} />, + ); + excalidrawAPI = await excalidrawAPIPromise; + }); + + it("should expose setActiveTool on package API", () => { + expect(excalidrawAPI.setActiveTool).toBeDefined(); + expect(excalidrawAPI.setActiveTool).toBe(h.app.setActiveTool); + }); + + it("should set the active tool type", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "rectangle" }); + expect(h.state.activeTool.type).toBe("rectangle"); + + mouse.down(10, 10); + mouse.up(20, 20); + + expect(h.state.activeTool.type).toBe("selection"); + }); + + it("should support tool locking", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "rectangle", locked: true }); + expect(h.state.activeTool.type).toBe("rectangle"); + + mouse.down(10, 10); + mouse.up(20, 20); + + expect(h.state.activeTool.type).toBe("rectangle"); + }); + + it("should set custom tool", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "custom", customType: "comment" }); + expect(h.state.activeTool.type).toBe("custom"); + expect(h.state.activeTool.customType).toBe("comment"); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index f9513920..d5f952fa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -363,18 +363,21 @@ export const distance = (x: number, y: number) => Math.abs(x - y); export const updateActiveTool = ( appState: Pick, - data: ( + data: (( | { type: ToolType; } | { type: "custom"; customType: string } - ) & { lastActiveToolBeforeEraser?: ActiveTool | null }, + ) & { locked?: boolean }) & { + lastActiveToolBeforeEraser?: ActiveTool | null; + }, ): AppState["activeTool"] => { if (data.type === "custom") { return { ...appState.activeTool, type: "custom", customType: data.customType, + locked: data.locked ?? appState.activeTool.locked, }; } @@ -386,6 +389,7 @@ export const updateActiveTool = ( : data.lastActiveToolBeforeEraser, type: data.type, customType: null, + locked: data.locked ?? appState.activeTool.locked, }; };