Browse Source

Multipoint arrows now have single point commit in binding zones

Mark Tolmacs 6 months ago
parent
commit
11fe608f9a

+ 25 - 6
packages/element/src/linearElementEditor.ts

@@ -238,24 +238,23 @@ export class LinearElementEditor {
     });
   }
 
-  static getOutlineAvoidingPoint(
+  static getOutlineAvoidingPointOrNull(
     element: NonDeleted<ExcalidrawLinearElement>,
     coords: { x: number; y: number },
     pointIndex: number,
     app: AppClassProperties,
-  ): GlobalPoint {
-    const elbowed = isElbowArrow(element);
+  ) {
     const hoveredElement = getHoveredElementForBinding(
       coords,
       app.scene.getNonDeletedElements(),
       app.scene.getNonDeletedElementsMap(),
       app.state.zoom,
       true,
-      elbowed,
+      isElbowArrow(element),
     );
-    const p = pointFrom<GlobalPoint>(coords.x, coords.y);
 
     if (hoveredElement) {
+      const p = pointFrom<GlobalPoint>(coords.x, coords.y);
       const newPoints = Array.from(element.points);
       newPoints[pointIndex] = pointFrom<LocalPoint>(
         p[0] - element.x,
@@ -273,7 +272,27 @@ export class LinearElementEditor {
       );
     }
 
-    return p;
+    return null;
+  }
+
+  static getOutlineAvoidingPoint(
+    element: NonDeleted<ExcalidrawLinearElement>,
+    coords: { x: number; y: number },
+    pointIndex: number,
+    app: AppClassProperties,
+  ): GlobalPoint {
+    const p = LinearElementEditor.getOutlineAvoidingPointOrNull(
+      element,
+      coords,
+      pointIndex,
+      app,
+    );
+
+    if (p) {
+      return p;
+    }
+
+    return pointFrom<GlobalPoint>(coords.x, coords.y);
   }
 
   /**

+ 19 - 3
packages/excalidraw/actions/actionFinalize.tsx

@@ -91,10 +91,26 @@ export const actionFinalize = register({
         multiPointElement.type !== "freedraw" &&
         appState.lastPointerDownWith !== "touch"
       ) {
-        const { points, lastCommittedPoint } = multiPointElement;
+        const { x: rx, y: ry, points, lastCommittedPoint } = multiPointElement;
+        const lastGlobalPoint = pointFrom<GlobalPoint>(
+          rx + points[points.length - 1][0],
+          ry + points[points.length - 1][1],
+        );
+        const hoveredElementForBinding = getHoveredElementForBinding(
+          {
+            x: lastGlobalPoint[0],
+            y: lastGlobalPoint[1],
+          },
+          elements,
+          elementsMap,
+          app.state.zoom,
+          true,
+          isElbowArrow(multiPointElement),
+        );
         if (
-          !lastCommittedPoint ||
-          points[points.length - 1] !== lastCommittedPoint
+          !hoveredElementForBinding &&
+          (!lastCommittedPoint ||
+            points[points.length - 1] !== lastCommittedPoint)
         ) {
           mutateElement(multiPointElement, {
             points: multiPointElement.points.slice(0, -1),

+ 48 - 16
packages/excalidraw/components/App.tsx

@@ -5965,17 +5965,33 @@ class App extends React.Component<AppProps, AppState> {
         if (isPathALoop(points, this.state.zoom.value)) {
           setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
         }
+
+        const outlineGlobalPoint =
+          LinearElementEditor.getOutlineAvoidingPointOrNull(
+            multiElement,
+            {
+              x: scenePointerX,
+              y: scenePointerY,
+            },
+            multiElement.points.length - 1,
+            this,
+          );
+
+        const nextPoint = outlineGlobalPoint
+          ? pointFrom<LocalPoint>(
+              outlineGlobalPoint[0] - rx,
+              outlineGlobalPoint[1] - ry,
+            )
+          : pointFrom<LocalPoint>(
+              lastCommittedX + dxFromLastCommitted,
+              lastCommittedY + dyFromLastCommitted,
+            );
+
         // update last uncommitted point
         mutateElement(
           multiElement,
           {
-            points: [
-              ...points.slice(0, -1),
-              pointFrom<LocalPoint>(
-                lastCommittedX + dxFromLastCommitted,
-                lastCommittedY + dyFromLastCommitted,
-              ),
-            ],
+            points: [...points.slice(0, -1), nextPoint],
           },
           false,
           {
@@ -7715,18 +7731,34 @@ class App extends React.Component<AppProps, AppState> {
       }
 
       const { x: rx, y: ry, lastCommittedPoint } = multiElement;
+      const lastGlobalPoint = pointFrom<GlobalPoint>(
+        rx + multiElement.points[multiElement.points.length - 1][0],
+        ry + multiElement.points[multiElement.points.length - 1][1],
+      );
+      const hoveredElementForBinding = getHoveredElementForBinding(
+        {
+          x: lastGlobalPoint[0],
+          y: lastGlobalPoint[1],
+        },
+        this.scene.getNonDeletedElements(),
+        this.scene.getNonDeletedElementsMap(),
+        this.state.zoom,
+        true,
+        isElbowArrow(multiElement),
+      );
 
       // clicking inside commit zone → finalize arrow
       if (
-        multiElement.points.length > 1 &&
-        lastCommittedPoint &&
-        pointDistance(
-          pointFrom(
-            pointerDownState.origin.x - rx,
-            pointerDownState.origin.y - ry,
-          ),
-          lastCommittedPoint,
-        ) < LINE_CONFIRM_THRESHOLD
+        !!hoveredElementForBinding ||
+        (multiElement.points.length > 1 &&
+          lastCommittedPoint &&
+          pointDistance(
+            pointFrom(
+              pointerDownState.origin.x - rx,
+              pointerDownState.origin.y - ry,
+            ),
+            lastCommittedPoint,
+          ) < LINE_CONFIRM_THRESHOLD)
       ) {
         this.actionManager.executeAction(actionFinalize);
         return;