Bläddra i källkod

fix: linear elements not selected on pointer up from hitting its bound text (#8285)

Co-authored-by: dwelle <[email protected]>
Ryan Di 1 år sedan
förälder
incheckning
7b36de0476

+ 2 - 34
packages/excalidraw/components/App.tsx

@@ -224,8 +224,7 @@ import type {
   ScrollBars,
   ScrollBars,
 } from "../scene/types";
 } from "../scene/types";
 import { getStateForZoom } from "../scene/zoom";
 import { getStateForZoom } from "../scene/zoom";
-import { findShapeByKey, getElementShape } from "../shapes";
-import type { GeometricShape } from "../../utils/geometry/shape";
+import { findShapeByKey, getBoundTextShape, getElementShape } from "../shapes";
 import { getSelectionBoxShape } from "../../utils/geometry/shape";
 import { getSelectionBoxShape } from "../../utils/geometry/shape";
 import { isPointInShape } from "../../utils/collision";
 import { isPointInShape } from "../../utils/collision";
 import type {
 import type {
@@ -4515,37 +4514,6 @@ class App extends React.Component<AppProps, AppState> {
     return null;
     return null;
   }
   }
 
 
-  private getBoundTextShape(element: ExcalidrawElement): GeometricShape | null {
-    const boundTextElement = getBoundTextElement(
-      element,
-      this.scene.getNonDeletedElementsMap(),
-    );
-
-    if (boundTextElement) {
-      if (element.type === "arrow") {
-        return getElementShape(
-          {
-            ...boundTextElement,
-            // arrow's bound text accurate position is not stored in the element's property
-            // but rather calculated and returned from the following static method
-            ...LinearElementEditor.getBoundTextElementPosition(
-              element,
-              boundTextElement,
-              this.scene.getNonDeletedElementsMap(),
-            ),
-          },
-          this.scene.getNonDeletedElementsMap(),
-        );
-      }
-      return getElementShape(
-        boundTextElement,
-        this.scene.getNonDeletedElementsMap(),
-      );
-    }
-
-    return null;
-  }
-
   private getElementAtPosition(
   private getElementAtPosition(
     x: number,
     x: number,
     y: number,
     y: number,
@@ -4677,7 +4645,7 @@ class App extends React.Component<AppProps, AppState> {
     const hitBoundTextOfElement = hitElementBoundText(
     const hitBoundTextOfElement = hitElementBoundText(
       x,
       x,
       y,
       y,
-      this.getBoundTextShape(element),
+      getBoundTextShape(element, this.scene.getNonDeletedElementsMap()),
     );
     );
     if (hitBoundTextOfElement) {
     if (hitBoundTextOfElement) {
       return true;
       return true;

+ 9 - 2
packages/excalidraw/element/collision.ts

@@ -18,6 +18,7 @@ import {
   isImageElement,
   isImageElement,
   isTextElement,
   isTextElement,
 } from "./typeChecks";
 } from "./typeChecks";
+import { getBoundTextShape } from "../shapes";
 
 
 export const shouldTestInside = (element: ExcalidrawElement) => {
 export const shouldTestInside = (element: ExcalidrawElement) => {
   if (element.type === "arrow") {
   if (element.type === "arrow") {
@@ -97,6 +98,12 @@ export const hitElementBoundingBoxOnly = (
 ) => {
 ) => {
   return (
   return (
     !hitElementItself(hitArgs) &&
     !hitElementItself(hitArgs) &&
+    // bound text is considered part of the element (even if it's outside the bounding box)
+    !hitElementBoundText(
+      hitArgs.x,
+      hitArgs.y,
+      getBoundTextShape(hitArgs.element, elementsMap),
+    ) &&
     hitElementBoundingBox(hitArgs.x, hitArgs.y, hitArgs.element, elementsMap)
     hitElementBoundingBox(hitArgs.x, hitArgs.y, hitArgs.element, elementsMap)
   );
   );
 };
 };
@@ -105,6 +112,6 @@ export const hitElementBoundText = (
   x: number,
   x: number,
   y: number,
   y: number,
   textShape: GeometricShape | null,
   textShape: GeometricShape | null,
-) => {
-  return textShape && isPointInShape([x, y], textShape);
+): boolean => {
+  return !!textShape && isPointInShape([x, y], textShape);
 };
 };

+ 1 - 2
packages/excalidraw/element/textElement.ts

@@ -635,8 +635,7 @@ export const getMaxCharWidth = (font: FontString) => {
 
 
 export const getBoundTextElementId = (container: ExcalidrawElement | null) => {
 export const getBoundTextElementId = (container: ExcalidrawElement | null) => {
   return container?.boundElements?.length
   return container?.boundElements?.length
-    ? container?.boundElements?.filter((ele) => ele.type === "text")[0]?.id ||
-        null
+    ? container?.boundElements?.find((ele) => ele.type === "text")?.id || null
     : null;
     : null;
 };
 };
 
 

+ 30 - 0
packages/excalidraw/shapes.tsx

@@ -20,6 +20,8 @@ import {
 } from "./components/icons";
 } from "./components/icons";
 import { getElementAbsoluteCoords } from "./element";
 import { getElementAbsoluteCoords } from "./element";
 import { shouldTestInside } from "./element/collision";
 import { shouldTestInside } from "./element/collision";
+import { LinearElementEditor } from "./element/linearElementEditor";
+import { getBoundTextElement } from "./element/textElement";
 import type { ElementsMap, ExcalidrawElement } from "./element/types";
 import type { ElementsMap, ExcalidrawElement } from "./element/types";
 import { KEYS } from "./keys";
 import { KEYS } from "./keys";
 import { ShapeCache } from "./scene/ShapeCache";
 import { ShapeCache } from "./scene/ShapeCache";
@@ -159,3 +161,31 @@ export const getElementShape = (
     }
     }
   }
   }
 };
 };
+
+export const getBoundTextShape = (
+  element: ExcalidrawElement,
+  elementsMap: ElementsMap,
+): GeometricShape | null => {
+  const boundTextElement = getBoundTextElement(element, elementsMap);
+
+  if (boundTextElement) {
+    if (element.type === "arrow") {
+      return getElementShape(
+        {
+          ...boundTextElement,
+          // arrow's bound text accurate position is not stored in the element's property
+          // but rather calculated and returned from the following static method
+          ...LinearElementEditor.getBoundTextElementPosition(
+            element,
+            boundTextElement,
+            elementsMap,
+          ),
+        },
+        elementsMap,
+      );
+    }
+    return getElementShape(boundTextElement, elementsMap);
+  }
+
+  return null;
+};