From 023291744379205903da832bacd47f5ab9075a38 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Mon, 29 Sep 2025 10:03:07 +0200 Subject: [PATCH] feat: No angle lock over bindable elements Signed-off-by: Mark Tolmacs --- packages/element/src/linearElementEditor.ts | 26 +++++++++++++-- packages/excalidraw/components/App.tsx | 37 ++++++++++++++------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/packages/element/src/linearElementEditor.ts b/packages/element/src/linearElementEditor.ts index 3ff2a49fd8..e030c180bb 100644 --- a/packages/element/src/linearElementEditor.ts +++ b/packages/element/src/linearElementEditor.ts @@ -25,6 +25,7 @@ import { import { deconstructLinearOrFreeDrawElement, + getHoveredElementForBinding, isPathALoop, moveArrowAboveBindable, type Store, @@ -301,11 +302,21 @@ export class LinearElementEditor { const customLineAngle = linearElementEditor.customLineAngle ?? determineCustomLinearAngle(pivotPoint, element.points[idx]); + const hoveredElement = getHoveredElementForBinding( + pointFrom(scenePointerX, scenePointerY), + elements, + elementsMap, + ); // Determine if point movement should happen and how much let deltaX = 0; let deltaY = 0; - if (shouldRotateWithDiscreteAngle(event)) { + if ( + shouldRotateWithDiscreteAngle(event) && + !hoveredElement && + !element.startBinding && + !element.endBinding + ) { const [width, height] = LinearElementEditor._getShiftLockedDelta( element, elementsMap, @@ -453,11 +464,22 @@ export class LinearElementEditor { const endIsSelected = selectedPointsIndices.includes( element.points.length - 1, ); + const hoveredElement = getHoveredElementForBinding( + pointFrom(scenePointerX, scenePointerY), + elements, + elementsMap, + ); // Determine if point movement should happen and how much let deltaX = 0; let deltaY = 0; - if (shouldRotateWithDiscreteAngle(event) && singlePointDragged) { + if ( + shouldRotateWithDiscreteAngle(event) && + singlePointDragged && + !hoveredElement && + !element.startBinding && + !element.endBinding + ) { const [width, height] = LinearElementEditor._getShiftLockedDelta( element, elementsMap, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index c71be79ce8..2a3a2e359b 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -1119,25 +1119,38 @@ class App extends React.Component { : endDragged ? "endBinding" : null; - const isAlreadyInsideBindingToSameElement = startDragged - ? arrow.startBinding?.mode === "inside" && - arrow.startBinding?.elementId === hoveredElement?.id + const otherBinding = startDragged + ? "endBinding" : endDragged - ? arrow.endBinding?.mode === "inside" && - arrow.endBinding?.elementId === hoveredElement?.id - : false; + ? "startBinding" + : null; + const isAlreadyInsideBindingToSameElement = + (otherBinding && + arrow[otherBinding]?.mode === "inside" && + arrow[otherBinding]?.elementId === hoveredElement?.id) || + (currentBinding && arrow[currentBinding]?.mode === "inside"); + if ( currentBinding && + otherBinding && arrow[currentBinding]?.mode === "inside" && - hoveredElement?.id !== arrow[currentBinding]?.elementId + hoveredElement?.id !== arrow[currentBinding]?.elementId && + arrow[otherBinding]?.elementId !== arrow[currentBinding]?.elementId ) { // Update binding out of place to orbit mode - this.scene.mutateElement(arrow, { - [currentBinding]: { - ...arrow[currentBinding], - mode: "orbit", + this.scene.mutateElement( + arrow, + { + [currentBinding]: { + ...arrow[currentBinding], + mode: "orbit", + }, }, - }); + { + informMutation: false, + isDragging: true, + }, + ); } if (