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

View File

@@ -2215,6 +2215,21 @@ const pointDraggingUpdates = (
? lineSegment(start.focusPoint, end.focusPoint)
: 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,
// so we look up the old binding to trigger updateBoundPoint
const endBindable = nextArrow.endBinding
@@ -2223,16 +2238,17 @@ const pointDraggingUpdates = (
nextArrow.endBinding.elementId,
)! as ExcalidrawBindableElement)
: null;
const endLocalPoint = endBindable
? updateBoundPoint(
nextArrow,
"endBinding",
nextArrow.endBinding,
endBindable,
elementsMap,
customIntersector,
) || nextArrow.points[nextArrow.points.length - 1]
: nextArrow.points[nextArrow.points.length - 1];
const endLocalPoint =
endBindable && !endIsDraggingOverStartElement
? updateBoundPoint(
nextArrow,
"endBinding",
nextArrow.endBinding,
endBindable,
elementsMap,
customIntersector,
) || nextArrow.points[nextArrow.points.length - 1]
: nextArrow.points[nextArrow.points.length - 1];
// We need to keep the simulated next arrow up-to-date, because
// updateBoundPoint looks at the opposite point
@@ -2247,16 +2263,17 @@ const pointDraggingUpdates = (
)! as ExcalidrawBindableElement)
: null;
const startLocalPoint = startBindable
? updateBoundPoint(
nextArrow,
"startBinding",
nextArrow.startBinding,
startBindable,
elementsMap,
customIntersector,
) || nextArrow.points[0]
: nextArrow.points[0];
const startLocalPoint =
startBindable && startIsDraggingOverEndElement
? updateBoundPoint(
nextArrow,
"startBinding",
nextArrow.startBinding,
startBindable,
elementsMap,
customIntersector,
) || nextArrow.points[0]
: nextArrow.points[0];
const endChanged =
pointDistance(