Browse Source

Inline binding logic fix

Mark Tolmacs 4 months ago
parent
commit
4efa6f69e5

+ 37 - 9
packages/element/src/binding.ts

@@ -223,7 +223,7 @@ const bindOrUnbindLinearElementEdge = (
   }
 };
 
-const getOriginalBindingsIfStillCloseToArrowEnds = (
+export const getOriginalBindingsIfStillCloseToArrowEnds = (
   linearElement: NonDeleted<ExcalidrawLinearElement>,
   elementsMap: NonDeletedSceneElementsMap,
   zoom?: AppState["zoom"],
@@ -418,21 +418,35 @@ export const getSuggestedBindingsForArrows = (
 export const maybeBindLinearElement = (
   linearElement: NonDeleted<ExcalidrawLinearElement>,
   appState: AppState,
-  pointerCoords: { x: number; y: number },
   elementsMap: NonDeletedSceneElementsMap,
   elements: readonly NonDeletedExcalidrawElement[],
 ): void => {
-  if (appState.startBoundElement != null) {
-    bindLinearElement(
+  const start = tupleToCoors(
+    LinearElementEditor.getPointAtIndexGlobalCoordinates(
       linearElement,
-      appState.startBoundElement,
-      "start",
+      0,
       elementsMap,
-    );
-  }
+    ),
+  );
+  const end = tupleToCoors(
+    LinearElementEditor.getPointAtIndexGlobalCoordinates(
+      linearElement,
+      -1,
+      elementsMap,
+    ),
+  );
+
+  const otherHoveredElement = getHoveredElementForBinding(
+    start,
+    elements,
+    elementsMap,
+    appState.zoom,
+    isElbowArrow(linearElement),
+    isElbowArrow(linearElement),
+  );
 
   const hoveredElement = getHoveredElementForBinding(
-    pointerCoords,
+    end,
     elements,
     elementsMap,
     appState.zoom,
@@ -440,6 +454,20 @@ export const maybeBindLinearElement = (
     isElbowArrow(linearElement),
   );
 
+  // Inside the same element there is no binding to the shape
+  if (hoveredElement === otherHoveredElement) {
+    return;
+  }
+
+  if (appState.startBoundElement != null) {
+    bindLinearElement(
+      linearElement,
+      appState.startBoundElement,
+      "start",
+      elementsMap,
+    );
+  }
+
   if (hoveredElement !== null) {
     if (
       !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(

+ 8 - 4
packages/element/src/elbowArrow.ts

@@ -50,7 +50,6 @@ import { isBindableElement } from "./typeChecks";
 import {
   type ExcalidrawElbowArrowElement,
   type NonDeletedSceneElementsMap,
-  type SceneElementsMap,
 } from "./types";
 
 import { aabbForElement, pointInsideBounds } from "./shapes";
@@ -1237,6 +1236,14 @@ const getElbowArrowData = (
         true,
         true,
       ) || null;
+
+    // Inside the same element there is no binding to the shape
+    if (hoveredStartElement === hoveredEndElement) {
+      hoveredStartElement = null;
+      hoveredEndElement = null;
+      arrow.startBinding = null;
+      arrow.endBinding = null;
+    }
   } else {
     hoveredStartElement = arrow.startBinding
       ? getBindableElementForId(arrow.startBinding.elementId, elementsMap) ||
@@ -1278,14 +1285,12 @@ const getElbowArrowData = (
   const startHeading = getBindPointHeading(
     startGlobalPoint,
     endGlobalPoint,
-    elementsMap,
     hoveredStartElement,
     origStartGlobalPoint,
   );
   const endHeading = getBindPointHeading(
     endGlobalPoint,
     startGlobalPoint,
-    elementsMap,
     hoveredEndElement,
     origEndGlobalPoint,
   );
@@ -2257,7 +2262,6 @@ const getGlobalPoint = (
 const getBindPointHeading = (
   p: GlobalPoint,
   otherPoint: GlobalPoint,
-  elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
   hoveredElement: ExcalidrawBindableElement | null | undefined,
   origPoint: GlobalPoint,
 ): Heading =>

+ 17 - 11
packages/element/tests/binding.test.tsx

@@ -10,6 +10,8 @@ import { API } from "@excalidraw/excalidraw/tests/helpers/api";
 import { UI, Pointer, Keyboard } from "@excalidraw/excalidraw/tests/helpers/ui";
 import { fireEvent, render } from "@excalidraw/excalidraw/tests/test-utils";
 
+import "@excalidraw/utils/test-utils";
+
 import { getTransformHandles } from "../src/transformHandles";
 
 const { h } = window;
@@ -510,7 +512,7 @@ describe("element binding", () => {
     "allow non-binding simple (complex) arrow creation while start and end" +
       " points are in the same shape",
     () => {
-      UI.createElement("rectangle", {
+      const rect = UI.createElement("rectangle", {
         x: 0,
         y: 0,
         width: 100,
@@ -526,9 +528,10 @@ describe("element binding", () => {
 
       expect(arrow.startBinding).toBe(null);
       expect(arrow.endBinding).toBe(null);
+      expect(rect.boundElements).toEqual(null);
       expect(arrow.points).toCloselyEqualPoints([
         [0, 0],
-        [95, 95],
+        [92.2855, 92.2855],
       ]);
 
       const rect2 = API.createElement({
@@ -552,32 +555,35 @@ describe("element binding", () => {
 
       expect(arrow2.startBinding).toBe(null);
       expect(arrow2.endBinding).toBe(null);
+      expect(rect2.boundElements).toEqual(null);
       expect(arrow2.points).toCloselyEqualPoints([
         [0, 0],
-        [95, 95],
+        [92.2855, 92.2855],
       ]);
 
-      UI.createElement("rectangle", {
+      const rect3 = UI.createElement("rectangle", {
         x: 0,
-        y: 0,
+        y: 300,
         width: 100,
         height: 100,
       });
 
       const arrow3 = UI.createElement("arrow", {
-        x: 5,
-        y: 5,
-        height: 95,
-        width: 95,
+        x: 10,
+        y: 310,
+        height: 85,
+        width: 84,
         elbowed: true,
       });
 
       expect(arrow3.startBinding).toBe(null);
       expect(arrow3.endBinding).toBe(null);
+      expect(rect3.boundElements).toEqual(null);
       expect(arrow3.points).toCloselyEqualPoints([
         [0, 0],
-        [45, 45],
-        [95, 95],
+        [0, 42.5],
+        [84, 42.5],
+        [84, 85],
       ]);
     },
   );

+ 1 - 7
packages/excalidraw/actions/actionFinalize.tsx

@@ -13,7 +13,7 @@ import {
   isLinearElement,
 } from "@excalidraw/element/typeChecks";
 
-import { KEYS, arrayToMap, updateActiveTool } from "@excalidraw/common";
+import { KEYS, updateActiveTool } from "@excalidraw/common";
 import { isPathALoop } from "@excalidraw/element/shapes";
 
 import { isInvisiblySmallElement } from "@excalidraw/element/sizeHelpers";
@@ -153,15 +153,9 @@ export const actionFinalize = register({
         !isLoop &&
         multiPointElement.points.length > 1
       ) {
-        const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates(
-          multiPointElement,
-          -1,
-          arrayToMap(elements),
-        );
         maybeBindLinearElement(
           multiPointElement,
           appState,
-          { x, y },
           elementsMap,
           elements,
         );

+ 0 - 9
packages/excalidraw/components/App.tsx

@@ -2770,7 +2770,6 @@ class App extends React.Component<AppProps, AppState> {
     this.updateEmbeddables();
     const elements = this.scene.getElementsIncludingDeleted();
     const elementsMap = this.scene.getElementsMapIncludingDeleted();
-    const nonDeletedElementsMap = this.scene.getNonDeletedElementsMap();
 
     if (!this.state.showWelcomeScreen && !elements.length) {
       this.setState({ showWelcomeScreen: true });
@@ -2927,13 +2926,6 @@ class App extends React.Component<AppProps, AppState> {
       maybeBindLinearElement(
         multiElement,
         this.state,
-        tupleToCoors(
-          LinearElementEditor.getPointAtIndexGlobalCoordinates(
-            multiElement,
-            -1,
-            nonDeletedElementsMap,
-          ),
-        ),
         this.scene.getNonDeletedElementsMap(),
         this.scene.getNonDeletedElements(),
       );
@@ -9174,7 +9166,6 @@ class App extends React.Component<AppProps, AppState> {
             maybeBindLinearElement(
               newElement,
               this.state,
-              pointerCoords,
               this.scene.getNonDeletedElementsMap(),
               this.scene.getNonDeletedElements(),
             );