Ver Fonte

feat: support `props.locked` for `setActiveTool` (#7153)

Co-authored-by: Aakansha Doshi <[email protected]>
David Luzar há 1 ano atrás
pai
commit
3697618266
4 ficheiros alterados com 67 adições e 5 exclusões
  1. 4 3
      src/components/App.tsx
  2. 2 0
      src/cursor.ts
  3. 55 0
      src/tests/tool.test.tsx
  4. 6 2
      src/utils.ts

+ 4 - 3
src/components/App.tsx

@@ -3249,7 +3249,7 @@ class App extends React.Component<AppProps, AppState> {
   });
 
   setActiveTool = (
-    tool:
+    tool: (
       | (
           | { type: Exclude<ToolType, "image"> }
           | {
@@ -3257,7 +3257,8 @@ class App extends React.Component<AppProps, AppState> {
               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<AppProps, AppState> {
         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") {

+ 2 - 0
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;
   }
 };

+ 55 - 0
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<ExcalidrawImperativeAPI>();
+    await render(
+      <Excalidraw ref={(api) => 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");
+  });
+});

+ 6 - 2
src/utils.ts

@@ -363,18 +363,21 @@ export const distance = (x: number, y: number) => Math.abs(x - y);
 
 export const updateActiveTool = (
   appState: Pick<AppState, "activeTool">,
-  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,
   };
 };