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

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Ryan Di 2024-07-27 21:02:00 +08:00 committed by GitHub
parent 2427e622b0
commit 7b36de0476
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 38 deletions

View file

@ -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 { findShapeByKey, getBoundTextShape, getElementShape } from "../shapes";
import type { GeometricShape } from "../../utils/geometry/shape";
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;

View file

@ -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,
) => { ): boolean => {
return textShape && isPointInShape([x, y], textShape); return !!textShape && isPointInShape([x, y], textShape);
}; };

View file

@ -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 || ? container?.boundElements?.find((ele) => ele.type === "text")?.id || null
null
: null; : null;
}; };

View file

@ -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;
};