Browse Source

duplicate point on cmd+d (#1831)

David Luzar 5 năm trước cách đây
mục cha
commit
b1261eea70
4 tập tin đã thay đổi với 89 bổ sung47 xóa
  1. 37 0
      src/actions/actionDuplicateSelection.tsx
  2. 9 6
      src/actions/types.ts
  3. 42 40
      src/components/App.tsx
  4. 1 1
      src/locales/en.json

+ 37 - 0
src/actions/actionDuplicateSelection.tsx

@@ -8,10 +8,47 @@ import { ToolButton } from "../components/ToolButton";
 import { clone } from "../components/icons";
 import { t } from "../i18n";
 import { getShortcutKey } from "../utils";
+import { LinearElementEditor } from "../element/linearElementEditor";
+import { mutateElement } from "../element/mutateElement";
 
 export const actionDuplicateSelection = register({
   name: "duplicateSelection",
   perform: (elements, appState) => {
+    // duplicate point if selected while editing multi-point element
+    if (appState.editingLinearElement) {
+      const { activePointIndex, elementId } = appState.editingLinearElement;
+      const element = LinearElementEditor.getElement(elementId);
+      if (!element || activePointIndex === null) {
+        return false;
+      }
+      const { points } = element;
+      const selectedPoint = points[activePointIndex];
+      const nextPoint = points[activePointIndex + 1];
+      mutateElement(element, {
+        points: [
+          ...points.slice(0, activePointIndex + 1),
+          nextPoint
+            ? [
+                (selectedPoint[0] + nextPoint[0]) / 2,
+                (selectedPoint[1] + nextPoint[1]) / 2,
+              ]
+            : [selectedPoint[0] + 30, selectedPoint[1] + 30],
+          ...points.slice(activePointIndex + 1),
+        ],
+      });
+      return {
+        appState: {
+          ...appState,
+          editingLinearElement: {
+            ...appState.editingLinearElement,
+            activePointIndex: activePointIndex + 1,
+          },
+        },
+        elements,
+        commitToHistory: true,
+      };
+    }
+
     const groupIdMap = new Map();
     return {
       appState,

+ 9 - 6
src/actions/types.ts

@@ -2,12 +2,15 @@ import React from "react";
 import { ExcalidrawElement } from "../element/types";
 import { AppState } from "../types";
 
-export type ActionResult = {
-  elements?: readonly ExcalidrawElement[] | null;
-  appState?: AppState | null;
-  commitToHistory: boolean;
-  syncHistory?: boolean;
-};
+/** if false, the action should be prevented */
+export type ActionResult =
+  | {
+      elements?: readonly ExcalidrawElement[] | null;
+      appState?: AppState | null;
+      commitToHistory: boolean;
+      syncHistory?: boolean;
+    }
+  | false;
 
 type ActionFn = (
   elements: readonly ExcalidrawElement[],

+ 42 - 40
src/components/App.tsx

@@ -269,51 +269,53 @@ class App extends React.Component<any, AppState> {
     );
   }
 
-  private syncActionResult = withBatchedUpdates((res: ActionResult) => {
-    if (this.unmounted) {
-      return;
-    }
+  private syncActionResult = withBatchedUpdates(
+    (actionResult: ActionResult) => {
+      if (this.unmounted || actionResult === false) {
+        return;
+      }
 
-    let editingElement: AppState["editingElement"] | null = null;
-    if (res.elements) {
-      res.elements.forEach((element) => {
-        if (
-          this.state.editingElement?.id === element.id &&
-          this.state.editingElement !== element &&
-          isNonDeletedElement(element)
-        ) {
-          editingElement = element;
+      let editingElement: AppState["editingElement"] | null = null;
+      if (actionResult.elements) {
+        actionResult.elements.forEach((element) => {
+          if (
+            this.state.editingElement?.id === element.id &&
+            this.state.editingElement !== element &&
+            isNonDeletedElement(element)
+          ) {
+            editingElement = element;
+          }
+        });
+        globalSceneState.replaceAllElements(actionResult.elements);
+        if (actionResult.commitToHistory) {
+          history.resumeRecording();
         }
-      });
-      globalSceneState.replaceAllElements(res.elements);
-      if (res.commitToHistory) {
-        history.resumeRecording();
       }
-    }
 
-    if (res.appState || editingElement) {
-      if (res.commitToHistory) {
-        history.resumeRecording();
+      if (actionResult.appState || editingElement) {
+        if (actionResult.commitToHistory) {
+          history.resumeRecording();
+        }
+        this.setState(
+          (state) => ({
+            ...actionResult.appState,
+            editingElement:
+              editingElement || actionResult.appState?.editingElement || null,
+            isCollaborating: state.isCollaborating,
+            collaborators: state.collaborators,
+          }),
+          () => {
+            if (actionResult.syncHistory) {
+              history.setCurrentState(
+                this.state,
+                globalSceneState.getElementsIncludingDeleted(),
+              );
+            }
+          },
+        );
       }
-      this.setState(
-        (state) => ({
-          ...res.appState,
-          editingElement:
-            editingElement || res.appState?.editingElement || null,
-          isCollaborating: state.isCollaborating,
-          collaborators: state.collaborators,
-        }),
-        () => {
-          if (res.syncHistory) {
-            history.setCurrentState(
-              this.state,
-              globalSceneState.getElementsIncludingDeleted(),
-            );
-          }
-        },
-      );
-    }
-  });
+    },
+  );
 
   // Lifecycle
 

+ 1 - 1
src/locales/en.json

@@ -128,7 +128,7 @@
     "resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
     "rotate": "You can constrain angles by holding SHIFT while rotating",
     "lineEditor_info": "Double-click or press Enter to edit points",
-    "lineEditor_pointSelected": "Press Delete to remove point or drag to move",
+    "lineEditor_pointSelected": "Press Delete to remove point, CtrlOrCmd+D to duplicate, or drag to move",
     "lineEditor_nothingSelected": "Select a point to move or remove, or hold Alt and click to add new points"
   },
   "errorSplash": {