Sfoglia il codice sorgente

feat: Enter and Exit line editor via context menu (#5719)

* feat: Enter and exit line editor via context menu

* Add tests

* fix

* review fixes

* fix
Aakansha Doshi 2 anni fa
parent
commit
6c1246ef77

+ 49 - 0
src/actions/actionLinearEditor.ts

@@ -0,0 +1,49 @@
+import { getNonDeletedElements } from "../element";
+import { LinearElementEditor } from "../element/linearElementEditor";
+import { isLinearElement } from "../element/typeChecks";
+import { ExcalidrawLinearElement } from "../element/types";
+import { getSelectedElements } from "../scene";
+import { register } from "./register";
+
+export const actionToggleLinearEditor = register({
+  name: "toggleLinearEditor",
+  trackEvent: {
+    category: "element",
+  },
+  contextItemPredicate: (elements, appState) => {
+    const selectedElements = getSelectedElements(elements, appState);
+    if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
+      return true;
+    }
+    return false;
+  },
+  perform(elements, appState, _, app) {
+    const selectedElement = getSelectedElements(
+      getNonDeletedElements(elements),
+      appState,
+      true,
+    )[0] as ExcalidrawLinearElement;
+
+    const editingLinearElement =
+      appState.editingLinearElement?.elementId === selectedElement.id
+        ? null
+        : new LinearElementEditor(selectedElement, app.scene);
+    return {
+      appState: {
+        ...appState,
+        editingLinearElement,
+      },
+      commitToHistory: false,
+    };
+  },
+  contextItemLabel: (elements, appState) => {
+    const selectedElement = getSelectedElements(
+      getNonDeletedElements(elements),
+      appState,
+      true,
+    )[0] as ExcalidrawLinearElement;
+    return appState.editingLinearElement?.elementId === selectedElement.id
+      ? "labels.lineEditor.exit"
+      : "labels.lineEditor.edit";
+  },
+});

+ 1 - 0
src/actions/index.ts

@@ -85,3 +85,4 @@ export { actionToggleStats } from "./actionToggleStats";
 export { actionUnbindText, actionBindText } from "./actionBoundText";
 export { actionLink } from "../element/Hyperlink";
 export { actionToggleLock } from "./actionToggleLock";
+export { actionToggleLinearEditor } from "./actionLinearEditor";

+ 2 - 1
src/actions/types.ts

@@ -111,7 +111,8 @@ export type ActionName =
   | "hyperlink"
   | "eraser"
   | "bindText"
-  | "toggleLock";
+  | "toggleLock"
+  | "toggleLinearEditor";
 
 export type PanelComponentProps = {
   elements: readonly ExcalidrawElement[];

+ 8 - 0
src/components/App.tsx

@@ -34,6 +34,7 @@ import {
   actionUngroup,
   actionLink,
   actionToggleLock,
+  actionToggleLinearEditor,
 } from "../actions";
 import { createRedoAction, createUndoAction } from "../actions/actionHistory";
 import { ActionManager } from "../actions/manager";
@@ -5876,6 +5877,12 @@ class App extends React.Component<AppProps, AppState> {
       this.actionManager.getAppState(),
     );
 
+    const mayBeAllowToggleLineEditing =
+      actionToggleLinearEditor.contextItemPredicate(
+        this.actionManager.getElementsIncludingDeleted(),
+        this.actionManager.getAppState(),
+      );
+
     const separator = "separator";
 
     const elements = this.scene.getNonDeletedElements();
@@ -6017,6 +6024,7 @@ class App extends React.Component<AppProps, AppState> {
             maybeFlipHorizontal && actionFlipHorizontal,
             maybeFlipVertical && actionFlipVertical,
             (maybeFlipHorizontal || maybeFlipVertical) && separator,
+            mayBeAllowToggleLineEditing && actionToggleLinearEditor,
             actionLink.contextItemPredicate(elements, this.state) && actionLink,
             actionDuplicateSelection,
             actionToggleLock,

+ 5 - 0
src/locales/en.json

@@ -114,6 +114,11 @@
       "create": "Create link",
       "label": "Link"
     },
+    "lineEditor": {
+      "edit": "Edit line",
+      "exit": "Exit line editor"
+    },
+
     "elementLock": {
       "lock": "Lock",
       "unlock": "Unlock",

+ 38 - 1
src/tests/linearElementEditor.test.tsx

@@ -5,11 +5,12 @@ import { centerPoint } from "../math";
 import { reseed } from "../random";
 import * as Renderer from "../renderer/renderScene";
 import { Keyboard, Pointer } from "./helpers/ui";
-import { screen, render, fireEvent } from "./test-utils";
+import { screen, render, fireEvent, GlobalTestState } from "./test-utils";
 import { API } from "../tests/helpers/api";
 import { Point } from "../types";
 import { KEYS } from "../keys";
 import { LinearElementEditor } from "../element/linearElementEditor";
+import { queryByText } from "@testing-library/react";
 
 const renderScene = jest.spyOn(Renderer, "renderScene");
 
@@ -150,6 +151,42 @@ describe(" Test Linear Elements", () => {
     `);
   });
 
+  it("should allow entering and exiting line editor via context menu", () => {
+    createTwoPointerLinearElement("line");
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: midpoint[0],
+      clientY: midpoint[1],
+    });
+    // Enter line editor
+    let contextMenu = document.querySelector(".context-menu");
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: midpoint[0],
+      clientY: midpoint[1],
+    });
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Edit line")!);
+
+    expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
+
+    // Exiting line editor
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: midpoint[0],
+      clientY: midpoint[1],
+    });
+    contextMenu = document.querySelector(".context-menu");
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: midpoint[0],
+      clientY: midpoint[1],
+    });
+    fireEvent.click(
+      queryByText(contextMenu as HTMLElement, "Exit line editor")!,
+    );
+    expect(h.state.editingLinearElement?.elementId).toBeUndefined();
+  });
+
   describe("Inside editor", () => {
     it("should allow dragging line from midpoint in 2 pointer lines", async () => {
       createTwoPointerLinearElement("line");