From c1ce197b30c81f68963b8dd919d22ca37aa68b39 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Fri, 15 Aug 2025 19:27:55 +0200 Subject: [PATCH] fix(eraser): Remove binding from the other element --- packages/excalidraw/components/App.tsx | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index dacc771443..7770436ea1 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -243,6 +243,7 @@ import { getBindingStrategyForDraggingBindingElementEndpoints, getStartGlobalEndLocalPointsForSimpleArrowBinding, snapToCenter, + mutateElement, } from "@excalidraw/element"; import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math"; @@ -10507,6 +10508,67 @@ class App extends React.Component { private eraseElements = () => { let didChange = false; + + // Binding is double accounted on both elements and if one of them is + // deleted, the binding should be removed + this.elementsPendingErasure.forEach((id) => { + const element = this.scene.getElement(id); + if (isBindingElement(element)) { + if (element.startBinding) { + const bindable = this.scene.getElement( + element.startBinding.elementId, + )!; + // NOTE: We use the raw mutateElement() because we don't want history + // entries or multiplayer updates + mutateElement(bindable, this.scene.getElementsMapIncludingDeleted(), { + boundElements: bindable.boundElements!.filter( + (e) => e.id !== element.id, + ), + }); + } + if (element.endBinding) { + const bindable = this.scene.getElement(element.endBinding.elementId)!; + // NOTE: We use the raw mutateElement() because we don't want history + // entries or multiplayer updates + mutateElement(bindable, this.scene.getElementsMapIncludingDeleted(), { + boundElements: bindable.boundElements!.filter( + (e) => e.id !== element.id, + ), + }); + } + } else if (isBindableElement(element)) { + element.boundElements?.forEach((boundElement) => { + if (boundElement.type === "arrow") { + const arrow = this.scene.getElement( + boundElement.id, + ) as ExcalidrawArrowElement; + if (arrow?.startBinding?.elementId === element.id) { + // NOTE: We use the raw mutateElement() because we don't want history + // entries or multiplayer updates + mutateElement( + arrow, + this.scene.getElementsMapIncludingDeleted(), + { + startBinding: null, + }, + ); + } + if (arrow?.endBinding?.elementId === element.id) { + // NOTE: We use the raw mutateElement() because we don't want history + // entries or multiplayer updates + mutateElement( + arrow, + this.scene.getElementsMapIncludingDeleted(), + { + endBinding: null, + }, + ); + } + } + }); + } + }); + const elements = this.scene.getElementsIncludingDeleted().map((ele) => { if ( this.elementsPendingErasure.has(ele.id) ||