fix: Inside-inside during drag

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2025-10-29 19:48:19 +01:00
parent 36ae4339ac
commit f4ab372ed6
2 changed files with 43 additions and 24 deletions

View File

@@ -128,6 +128,7 @@ export const bindOrUnbindBindingElement = (
appState, appState,
{ {
...opts, ...opts,
finalize: true,
}, },
); );
@@ -396,7 +397,7 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
elements: readonly Ordered<NonDeletedExcalidrawElement>[], elements: readonly Ordered<NonDeletedExcalidrawElement>[],
globalBindMode: AppState["bindMode"], globalBindMode: AppState["bindMode"],
arrow: NonDeleted<ExcalidrawArrowElement>, arrow: NonDeleted<ExcalidrawArrowElement>,
shiftKey?: boolean, finalize?: boolean,
): { current: BindingStrategy; other: BindingStrategy } => { ): { current: BindingStrategy; other: BindingStrategy } => {
let current: BindingStrategy = { mode: undefined }; let current: BindingStrategy = { mode: undefined };
let other: BindingStrategy = { mode: undefined }; let other: BindingStrategy = { mode: undefined };
@@ -461,7 +462,7 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
// The opposite binding is on the binding gap of the same element // The opposite binding is on the binding gap of the same element
if (oppositeBinding.mode === "orbit") { if (oppositeBinding.mode === "orbit") {
current = { element: hit, mode: "orbit", focusPoint: point }; current = { element: hit, mode: "orbit", focusPoint: point };
other = { mode: null }; other = { mode: finalize ? null : undefined };
return { current, other: isMultiPoint ? { mode: undefined } : other }; return { current, other: isMultiPoint ? { mode: undefined } : other };
} }
@@ -517,6 +518,7 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = (
opts?: { opts?: {
newArrow?: boolean; newArrow?: boolean;
shiftKey?: boolean; shiftKey?: boolean;
finalize?: boolean;
}, },
): { start: BindingStrategy; end: BindingStrategy } => { ): { start: BindingStrategy; end: BindingStrategy } => {
const globalBindMode = appState.bindMode || "orbit"; const globalBindMode = appState.bindMode || "orbit";
@@ -600,7 +602,7 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = (
elements, elements,
globalBindMode, globalBindMode,
arrow, arrow,
opts?.shiftKey, opts?.finalize,
); );
return { start: current, end: other }; return { start: current, end: other };
@@ -623,7 +625,7 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = (
elements, elements,
globalBindMode, globalBindMode,
arrow, arrow,
opts?.shiftKey, opts?.finalize,
); );
return { start: other, end: current }; return { start: other, end: current };

View File

@@ -2215,6 +2215,21 @@ const pointDraggingUpdates = (
? lineSegment(start.focusPoint, end.focusPoint) ? lineSegment(start.focusPoint, end.focusPoint)
: undefined; : undefined;
// Needed to handle a special case where an existing arrow is dragged over
// the same element it is bound to on the other side
const startIsDraggingOverEndElement =
element.endBinding &&
nextArrow.startBinding &&
app.state.bindMode === "inside" &&
endIsDragged &&
nextArrow.startBinding.elementId === element.endBinding.elementId;
const endIsDraggingOverStartElement =
element.startBinding &&
nextArrow.endBinding &&
app.state.bindMode === "inside" &&
startIsDragged &&
element.startBinding.elementId === nextArrow.endBinding.elementId;
// We need to update the non-dragged point too if bound, // We need to update the non-dragged point too if bound,
// so we look up the old binding to trigger updateBoundPoint // so we look up the old binding to trigger updateBoundPoint
const endBindable = nextArrow.endBinding const endBindable = nextArrow.endBinding
@@ -2223,16 +2238,17 @@ const pointDraggingUpdates = (
nextArrow.endBinding.elementId, nextArrow.endBinding.elementId,
)! as ExcalidrawBindableElement) )! as ExcalidrawBindableElement)
: null; : null;
const endLocalPoint = endBindable const endLocalPoint =
? updateBoundPoint( endBindable && !endIsDraggingOverStartElement
nextArrow, ? updateBoundPoint(
"endBinding", nextArrow,
nextArrow.endBinding, "endBinding",
endBindable, nextArrow.endBinding,
elementsMap, endBindable,
customIntersector, elementsMap,
) || nextArrow.points[nextArrow.points.length - 1] customIntersector,
: nextArrow.points[nextArrow.points.length - 1]; ) || nextArrow.points[nextArrow.points.length - 1]
: nextArrow.points[nextArrow.points.length - 1];
// We need to keep the simulated next arrow up-to-date, because // We need to keep the simulated next arrow up-to-date, because
// updateBoundPoint looks at the opposite point // updateBoundPoint looks at the opposite point
@@ -2247,16 +2263,17 @@ const pointDraggingUpdates = (
)! as ExcalidrawBindableElement) )! as ExcalidrawBindableElement)
: null; : null;
const startLocalPoint = startBindable const startLocalPoint =
? updateBoundPoint( startBindable && startIsDraggingOverEndElement
nextArrow, ? updateBoundPoint(
"startBinding", nextArrow,
nextArrow.startBinding, "startBinding",
startBindable, nextArrow.startBinding,
elementsMap, startBindable,
customIntersector, elementsMap,
) || nextArrow.points[0] customIntersector,
: nextArrow.points[0]; ) || nextArrow.points[0]
: nextArrow.points[0];
const endChanged = const endChanged =
pointDistance( pointDistance(