mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-21 08:20:24 +02:00
feat: Nested shapes handling
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
|||||||
getElementBounds,
|
getElementBounds,
|
||||||
} from "./bounds";
|
} from "./bounds";
|
||||||
import {
|
import {
|
||||||
|
getAllHoveredElementAtPoint,
|
||||||
getHoveredElementForBinding,
|
getHoveredElementForBinding,
|
||||||
hitElementItself,
|
hitElementItself,
|
||||||
intersectElementWithLineSegment,
|
intersectElementWithLineSegment,
|
||||||
@@ -301,6 +302,54 @@ const bindingStrategyForNewSimpleArrowEndpointDragging = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check and handle nested shapes
|
||||||
|
if (arrow.startBinding) {
|
||||||
|
const otherElement = elementsMap.get(
|
||||||
|
arrow.startBinding.elementId,
|
||||||
|
) as ExcalidrawBindableElement;
|
||||||
|
invariant(otherElement, "Other element must be in the elements map");
|
||||||
|
const startFocusElements = getAllHoveredElementAtPoint(
|
||||||
|
getGlobalFixedPointForBindableElement(
|
||||||
|
arrow.startBinding.fixedPoint,
|
||||||
|
otherElement,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
const startHoverElements = getAllHoveredElementAtPoint(
|
||||||
|
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
|
arrow,
|
||||||
|
0,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
hit &&
|
||||||
|
otherElement.id !== hit.id &&
|
||||||
|
(startHoverElements.find((el) => el.id === hit.id) ||
|
||||||
|
startFocusElements.find((el) => el.id === hit.id))
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
start: isMultiPoint
|
||||||
|
? { mode: undefined }
|
||||||
|
: {
|
||||||
|
mode: "orbit",
|
||||||
|
element: otherElement,
|
||||||
|
focusPoint: snapToCenter(
|
||||||
|
otherElement,
|
||||||
|
elementsMap,
|
||||||
|
origin ?? pointFrom<GlobalPoint>(arrow.x, arrow.y),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
end: { mode: "inside", element: hit, focusPoint: point },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Inside -> outside binding
|
// Inside -> outside binding
|
||||||
if (arrow.startBinding && arrow.startBinding.elementId !== hit?.id) {
|
if (arrow.startBinding && arrow.startBinding.elementId !== hit?.id) {
|
||||||
const otherElement = elementsMap.get(
|
const otherElement = elementsMap.get(
|
||||||
|
@@ -238,12 +238,12 @@ const bindingBorderTest = (
|
|||||||
: intersections.length > 0 && distance <= t;
|
: intersections.length > 0 && distance <= t;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getHoveredElementForBinding = (
|
export const getAllHoveredElementAtPoint = (
|
||||||
point: Readonly<GlobalPoint>,
|
point: Readonly<GlobalPoint>,
|
||||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
toleranceFn?: (element: ExcalidrawBindableElement) => number,
|
toleranceFn?: (element: ExcalidrawBindableElement) => number,
|
||||||
): NonDeleted<ExcalidrawBindableElement> | null => {
|
): NonDeleted<ExcalidrawBindableElement>[] => {
|
||||||
const candidateElements: NonDeleted<ExcalidrawBindableElement>[] = [];
|
const candidateElements: NonDeleted<ExcalidrawBindableElement>[] = [];
|
||||||
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||||
// because array is ordered from lower z-index to highest and we want element z-index
|
// because array is ordered from lower z-index to highest and we want element z-index
|
||||||
@@ -264,6 +264,22 @@ export const getHoveredElementForBinding = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return candidateElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHoveredElementForBinding = (
|
||||||
|
point: Readonly<GlobalPoint>,
|
||||||
|
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
|
toleranceFn?: (element: ExcalidrawBindableElement) => number,
|
||||||
|
): NonDeleted<ExcalidrawBindableElement> | null => {
|
||||||
|
const candidateElements = getAllHoveredElementAtPoint(
|
||||||
|
point,
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
toleranceFn,
|
||||||
|
);
|
||||||
|
|
||||||
if (!candidateElements || candidateElements.length === 0) {
|
if (!candidateElements || candidateElements.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user