fix: Overlap behavior

This commit is contained in:
Mark Tolmacs
2025-09-05 16:03:24 +02:00
parent 109ff756f5
commit b01eea9eb4
2 changed files with 36 additions and 35 deletions

View File

@@ -3,6 +3,7 @@ import {
arrayToMap, arrayToMap,
invariant, invariant,
isAlwaysInsideBinding, isAlwaysInsideBinding,
isTransparent,
} from "@excalidraw/common"; } from "@excalidraw/common";
import { import {
@@ -303,49 +304,31 @@ const bindingStrategyForNewSimpleArrowEndpointDragging = (
} }
// Check and handle nested shapes // Check and handle nested shapes
if (arrow.startBinding) { if (hit && arrow.startBinding) {
const otherElement = elementsMap.get( const otherElement = elementsMap.get(
arrow.startBinding.elementId, arrow.startBinding.elementId,
) as ExcalidrawBindableElement; ) as ExcalidrawBindableElement;
invariant(otherElement, "Other element must be in the elements map");
const startFocusElements = getAllHoveredElementAtPoint(
getGlobalFixedPointForBindableElement(
arrow.startBinding.fixedPoint,
otherElement,
elementsMap,
),
elements,
elementsMap,
);
const startHoverElements = getAllHoveredElementAtPoint(
LinearElementEditor.getPointAtIndexGlobalCoordinates(
arrow,
0,
elementsMap,
),
elements,
elementsMap,
);
if ( invariant(otherElement, "Other element must be in the elements map");
hit &&
otherElement.id !== hit.id && const allHits = getAllHoveredElementAtPoint(point, elements, elementsMap);
(startHoverElements.find((el) => el.id === hit.id) ||
startFocusElements.find((el) => el.id === hit.id)) if (allHits.find((el) => el.id === otherElement.id)) {
) { const otherIsTransparent = isTransparent(otherElement.backgroundColor);
return { return {
start: isMultiPoint start: isMultiPoint
? { mode: undefined } ? { mode: undefined }
: { : {
mode: "orbit", mode: "inside",
element: otherElement, element: otherElement,
focusPoint: snapToCenter( focusPoint: origin ?? pointFrom<GlobalPoint>(arrow.x, arrow.y),
otherElement, },
elementsMap, end: {
origin ?? pointFrom<GlobalPoint>(arrow.x, arrow.y), mode: "inside",
), element: otherIsTransparent ? hit : otherElement,
focusPoint: point,
}, },
end: { mode: "inside", element: hit, focusPoint: point },
}; };
} }
} }

View File

@@ -117,6 +117,7 @@ type PointMoveOtherUpdates = {
startBinding?: FixedPointBinding | null; startBinding?: FixedPointBinding | null;
endBinding?: FixedPointBinding | null; endBinding?: FixedPointBinding | null;
moveMidPointsWithElement?: boolean | null; moveMidPointsWithElement?: boolean | null;
suggestedBinding?: AppState["suggestedBinding"] | null;
}; };
export class LinearElementEditor { export class LinearElementEditor {
@@ -484,6 +485,7 @@ export class LinearElementEditor {
} }
// Apply the point movement if needed // Apply the point movement if needed
let suggestedBinding: AppState["suggestedBinding"] = null;
if (deltaX || deltaY) { if (deltaX || deltaY) {
const { positions, updates } = pointDraggingUpdates( const { positions, updates } = pointDraggingUpdates(
selectedPointsIndices, selectedPointsIndices,
@@ -497,6 +499,13 @@ export class LinearElementEditor {
LinearElementEditor.movePoints(element, app.scene, positions, updates); LinearElementEditor.movePoints(element, app.scene, positions, updates);
// Set the suggested binding from the updates if available
if (isBindingElement(element, false)) {
if (isBindingEnabled(app.state) && (startIsSelected || endIsSelected)) {
suggestedBinding = updates?.suggestedBinding ?? null;
}
}
// Move the arrow over the bindable object in terms of z-index // Move the arrow over the bindable object in terms of z-index
if (isBindingElement(element) && startIsSelected !== endIsSelected) { if (isBindingElement(element) && startIsSelected !== endIsSelected) {
moveArrowAboveBindable( moveArrowAboveBindable(
@@ -522,7 +531,6 @@ export class LinearElementEditor {
} }
// Suggest bindings for first and last point if selected // Suggest bindings for first and last point if selected
let suggestedBinding: AppState["suggestedBinding"] = null;
if (isBindingElement(element, false)) { if (isBindingElement(element, false)) {
if (isBindingEnabled(app.state) && (startIsSelected || endIsSelected)) { if (isBindingEnabled(app.state) && (startIsSelected || endIsSelected)) {
suggestedBinding = maybeSuggestBindingsForBindingElementAtCoords( suggestedBinding = maybeSuggestBindingsForBindingElementAtCoords(
@@ -2086,7 +2094,9 @@ const pointDraggingUpdates = (
); );
// Generate the next bindings for the arrow // Generate the next bindings for the arrow
const updates: PointMoveOtherUpdates = {}; const updates: PointMoveOtherUpdates = {
suggestedBinding: null,
};
if (start.mode === null) { if (start.mode === null) {
updates.startBinding = null; updates.startBinding = null;
} else if (start.mode) { } else if (start.mode) {
@@ -2101,6 +2111,10 @@ const pointDraggingUpdates = (
start.focusPoint, start.focusPoint,
), ),
}; };
if (startIsDragged) {
updates.suggestedBinding = start.element;
}
} }
if (end.mode === null) { if (end.mode === null) {
updates.endBinding = null; updates.endBinding = null;
@@ -2116,6 +2130,10 @@ const pointDraggingUpdates = (
end.focusPoint, end.focusPoint,
), ),
}; };
if (endIsDragged) {
updates.suggestedBinding = end.element;
}
} }
// Simulate the updated arrow for the bind point calculation // Simulate the updated arrow for the bind point calculation