|
@@ -49,7 +49,11 @@ import type { ElementUpdate } from "./mutateElement";
|
|
|
import { mutateElement } from "./mutateElement";
|
|
|
import type Scene from "../scene/Scene";
|
|
|
import { LinearElementEditor } from "./linearElementEditor";
|
|
|
-import { arrayToMap, tupleToCoors } from "../utils";
|
|
|
+import {
|
|
|
+ arrayToMap,
|
|
|
+ isBindingFallthroughEnabled,
|
|
|
+ tupleToCoors,
|
|
|
+} from "../utils";
|
|
|
import { KEYS } from "../keys";
|
|
|
import { getBoundTextElement, handleBindTextResize } from "./textElement";
|
|
|
import { aabbForElement, getElementShape, pointInsideBounds } from "../shapes";
|
|
@@ -75,6 +79,7 @@ import {
|
|
|
clamp,
|
|
|
} from "../../math";
|
|
|
import { segmentIntersectRectangleElement } from "../../utils/geometry/shape";
|
|
|
+import { getElementsAtPosition } from "../scene/comparisons";
|
|
|
|
|
|
export type SuggestedBinding =
|
|
|
| NonDeleted<ExcalidrawBindableElement>
|
|
@@ -425,7 +430,8 @@ export const maybeBindLinearElement = (
|
|
|
elements,
|
|
|
elementsMap,
|
|
|
appState.zoom,
|
|
|
- isElbowArrow(linearElement) && isElbowArrow(linearElement),
|
|
|
+ isElbowArrow(linearElement),
|
|
|
+ isElbowArrow(linearElement),
|
|
|
);
|
|
|
|
|
|
if (hoveredElement !== null) {
|
|
@@ -558,7 +564,64 @@ export const getHoveredElementForBinding = (
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
zoom?: AppState["zoom"],
|
|
|
fullShape?: boolean,
|
|
|
+ considerAllElements?: boolean,
|
|
|
): NonDeleted<ExcalidrawBindableElement> | null => {
|
|
|
+ if (considerAllElements) {
|
|
|
+ let cullRest = false;
|
|
|
+ const candidateElements = getElementsAtPosition(
|
|
|
+ elements,
|
|
|
+ (element) =>
|
|
|
+ isBindableElement(element, false) &&
|
|
|
+ bindingBorderTest(
|
|
|
+ element,
|
|
|
+ pointerCoords,
|
|
|
+ elementsMap,
|
|
|
+ zoom,
|
|
|
+ (fullShape ||
|
|
|
+ !isBindingFallthroughEnabled(
|
|
|
+ element as ExcalidrawBindableElement,
|
|
|
+ )) &&
|
|
|
+ // disable fullshape snapping for frame elements so we
|
|
|
+ // can bind to frame children
|
|
|
+ !isFrameLikeElement(element),
|
|
|
+ ),
|
|
|
+ ).filter((element) => {
|
|
|
+ if (cullRest) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isBindingFallthroughEnabled(element as ExcalidrawBindableElement)) {
|
|
|
+ cullRest = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }) as NonDeleted<ExcalidrawBindableElement>[] | null;
|
|
|
+
|
|
|
+ // Return early if there are no candidates or just one candidate
|
|
|
+ if (!candidateElements || candidateElements.length === 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (candidateElements.length === 1) {
|
|
|
+ return candidateElements[0] as NonDeleted<ExcalidrawBindableElement>;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Prefer the shape with the border being tested (if any)
|
|
|
+ const borderTestElements = candidateElements.filter((element) =>
|
|
|
+ bindingBorderTest(element, pointerCoords, elementsMap, zoom, false),
|
|
|
+ );
|
|
|
+ if (borderTestElements.length === 1) {
|
|
|
+ return borderTestElements[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Prefer smaller shapes
|
|
|
+ return candidateElements
|
|
|
+ .sort(
|
|
|
+ (a, b) => b.width ** 2 + b.height ** 2 - (a.width ** 2 + a.height ** 2),
|
|
|
+ )
|
|
|
+ .pop() as NonDeleted<ExcalidrawBindableElement>;
|
|
|
+ }
|
|
|
+
|
|
|
const hoveredElement = getElementAtPosition(
|
|
|
elements,
|
|
|
(element) =>
|
|
@@ -570,7 +633,8 @@ export const getHoveredElementForBinding = (
|
|
|
zoom,
|
|
|
// disable fullshape snapping for frame elements so we
|
|
|
// can bind to frame children
|
|
|
- fullShape && !isFrameLikeElement(element),
|
|
|
+ (fullShape || !isBindingFallthroughEnabled(element)) &&
|
|
|
+ !isFrameLikeElement(element),
|
|
|
),
|
|
|
);
|
|
|
|
|
@@ -1220,6 +1284,8 @@ const getElligibleElementForBindingElement = (
|
|
|
elements,
|
|
|
elementsMap,
|
|
|
zoom,
|
|
|
+ isElbowArrow(linearElement),
|
|
|
+ isElbowArrow(linearElement),
|
|
|
);
|
|
|
};
|
|
|
|