fix: Existing arrow nested bindable

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2025-09-07 17:56:34 +02:00
parent eb9efc261a
commit be56e84596

View File

@@ -305,15 +305,16 @@ const bindingStrategyForNewSimpleArrowEndpointDragging = (
// Check and handle nested shapes // Check and handle nested shapes
if (hit && arrow.startBinding) { if (hit && arrow.startBinding) {
const otherElement = elementsMap.get( const startBinding = arrow.startBinding;
arrow.startBinding.elementId,
) as ExcalidrawBindableElement;
invariant(otherElement, "Other element must be in the elements map");
const allHits = getAllHoveredElementAtPoint(point, elements, elementsMap); const allHits = getAllHoveredElementAtPoint(point, elements, elementsMap);
if (allHits.find((el) => el.id === otherElement.id)) { if (allHits.find((el) => el.id === startBinding.elementId)) {
const otherElement = elementsMap.get(
arrow.startBinding.elementId,
) as ExcalidrawBindableElement;
invariant(otherElement, "Other element must be in the elements map");
const otherIsTransparent = isTransparent(otherElement.backgroundColor); const otherIsTransparent = isTransparent(otherElement.backgroundColor);
return { return {
@@ -414,6 +415,20 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
const isMultiPoint = arrow.points.length > 2; const isMultiPoint = arrow.points.length > 2;
const hit = getHoveredElementForBinding(point, elements, elementsMap); const hit = getHoveredElementForBinding(point, elements, elementsMap);
const isNested = oppositeBinding
? getAllHoveredElementAtPoint(point, elements, elementsMap).some(
(el) => el.id === oppositeBinding.elementId,
)
: false;
const oppositeElement =
isNested && oppositeBinding
? (elementsMap.get(
oppositeBinding.elementId,
) as ExcalidrawBindableElement)
: null;
const otherIsTransparent = oppositeElement
? isTransparent(oppositeElement.backgroundColor)
: false;
// If the global bind mode is in free binding mode, just bind // If the global bind mode is in free binding mode, just bind
// where the pointer is and keep the other end intact // where the pointer is and keep the other end intact
@@ -424,7 +439,10 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
) { ) {
current = hit current = hit
? { ? {
element: hit, element:
!isNested || !oppositeElement || otherIsTransparent
? hit
: oppositeElement,
focusPoint: point, focusPoint: point,
mode: "inside", mode: "inside",
} }
@@ -440,10 +458,8 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
} }
// The dragged point is inside the hovered bindable element // The dragged point is inside the hovered bindable element
// The opposite binding is on the same element
// eslint-disable-next-line no-lonely-if
if (oppositeBinding) { if (oppositeBinding) {
// The opposite binding is on the same element
if (oppositeBinding.elementId === hit.id) { if (oppositeBinding.elementId === hit.id) {
// 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") {
@@ -460,14 +476,23 @@ const bindingStrategyForSimpleArrowEndpointDragging = (
return { current, other: isMultiPoint ? { mode: undefined } : other }; return { current, other: isMultiPoint ? { mode: undefined } : other };
} }
} }
// The opposite binding is on a different element // The opposite binding is on a different element (or nested)
// eslint-disable-next-line no-else-return // eslint-disable-next-line no-else-return
else { else {
current = { // Handle the nested element case
element: hit, if (isNested && oppositeElement && !otherIsTransparent) {
mode: "orbit", current = {
focusPoint: snapToCenter(hit, elementsMap, point), element: oppositeElement,
}; mode: "inside",
focusPoint: point,
};
} else {
current = {
element: hit,
mode: "orbit",
focusPoint: snapToCenter(hit, elementsMap, point),
};
}
return { current, other: isMultiPoint ? { mode: undefined } : other }; return { current, other: isMultiPoint ? { mode: undefined } : other };
} }