Bläddra i källkod

[skip ci] Change flipping

Mark Tolmacs 5 månader sedan
förälder
incheckning
bcbd418154
1 ändrade filer med 45 tillägg och 43 borttagningar
  1. 45 43
      packages/excalidraw/actions/actionFlip.ts

+ 45 - 43
packages/excalidraw/actions/actionFlip.ts

@@ -1,6 +1,6 @@
 import { getNonDeletedElements } from "@excalidraw/element";
 import {
-  bindOrUnbindLinearElements,
+  bindOrUnbindLinearElement,
   isBindingEnabled,
 } from "@excalidraw/element/binding";
 import { getCommonBoundingBox } from "@excalidraw/element/bounds";
@@ -12,15 +12,15 @@ import { deepCopyElement } from "@excalidraw/element/duplicate";
 import { resizeMultipleElements } from "@excalidraw/element/resizeElements";
 import {
   isArrowElement,
-  isElbowArrow,
-  isLinearElement,
+  isBindableElement,
+  isBindingElement,
 } from "@excalidraw/element/typeChecks";
 import { updateFrameMembershipOfSelectedElements } from "@excalidraw/element/frame";
 import { CODES, KEYS, arrayToMap } from "@excalidraw/common";
 
 import type {
   ExcalidrawArrowElement,
-  ExcalidrawElbowArrowElement,
+  ExcalidrawBindableElement,
   ExcalidrawElement,
   NonDeleted,
   NonDeletedSceneElementsMap,
@@ -160,52 +160,54 @@ const flipElements = (
     },
   );
 
-  bindOrUnbindLinearElements(
-    selectedElements.filter(isLinearElement),
-    elementsMap,
-    app.scene.getNonDeletedElements(),
-    app.scene,
-    isBindingEnabled(appState),
-    [],
-    appState.zoom,
+  const selectedBindables = selectedElements.filter(
+    (e): e is ExcalidrawBindableElement => isBindableElement(e),
   );
-
-  // ---------------------------------------------------------------------------
-  // flipping arrow elements (and potentially other) makes the selection group
-  // "move" across the canvas because of how arrows can bump against the "wall"
-  // of the selection, so we need to center the group back to the original
-  // position so that repeated flips don't accumulate the offset
-
-  const { elbowArrows, otherElements } = selectedElements.reduce(
-    (
-      acc: {
-        elbowArrows: ExcalidrawElbowArrowElement[];
-        otherElements: ExcalidrawElement[];
-      },
-      element,
-    ) =>
-      isElbowArrow(element)
-        ? { ...acc, elbowArrows: acc.elbowArrows.concat(element) }
-        : { ...acc, otherElements: acc.otherElements.concat(element) },
-    { elbowArrows: [], otherElements: [] },
-  );
-
   const { midX: newMidX, midY: newMidY } =
     getCommonBoundingBox(selectedElements);
   const [diffX, diffY] = [midX - newMidX, midY - newMidY];
-  otherElements.forEach((element) =>
-    mutateElement(element, {
-      x: element.x + diffX,
-      y: element.y + diffY,
-    }),
-  );
-  elbowArrows.forEach((element) =>
+
+  selectedElements.forEach((element) => {
+    fixBindings(element, selectedBindables, app, elementsMap);
+
     mutateElement(element, {
       x: element.x + diffX,
       y: element.y + diffY,
-    }),
-  );
-  // ---------------------------------------------------------------------------
+    });
+  });
 
   return selectedElements;
 };
+
+// BEHAVIOR: If you flip a binding element along with its bound elements,
+// the binding should be preserved. If your selected elements doesn't contain
+// the bound element(s), then remove the binding. Also do not "magically"
+// re-bind a binable just because the arrow endpoint is flipped into the
+// binding range. Rationale being the consistency with the fact that arrows
+// don't bind when the arrow is moved into the binding range by its shaft.
+const fixBindings = (
+  element: ExcalidrawElement,
+  selectedBindables: ExcalidrawBindableElement[],
+  app: AppClassProperties,
+  elementsMap: NonDeletedSceneElementsMap,
+) => {
+  if (isBindingElement(element)) {
+    let start = null;
+    let end = null;
+
+    if (isBindingEnabled(app.state)) {
+      start = element.startBinding
+        ? selectedBindables.find(
+            (e) => element.startBinding!.elementId === e.id,
+          ) ?? null
+        : null;
+      end = element.endBinding
+        ? selectedBindables.find(
+            (e) => element.endBinding!.elementId === e.id,
+          ) ?? null
+        : null;
+    }
+
+    bindOrUnbindLinearElement(element, start, end, elementsMap, app.scene);
+  }
+};