Fix delayed bind mode for multiElement arrows

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2025-08-29 21:12:50 +02:00
parent f3e99a96dd
commit 547332942e
2 changed files with 147 additions and 120 deletions

View File

@@ -19,6 +19,7 @@ import {
shouldRotateWithDiscreteAngle,
getGridPoint,
invariant,
isShallowEqual,
} from "@excalidraw/common";
import {
@@ -279,9 +280,105 @@ export class LinearElementEditor {
});
}
/**
* @returns whether point was dragged
*/
static handlePointerMove(
event: PointerEvent,
app: AppClassProperties,
scenePointerX: number,
scenePointerY: number,
linearElementEditor: LinearElementEditor,
): Pick<AppState, "suggestedBinding" | "selectedLinearElement"> | null {
const elementsMap = app.scene.getNonDeletedElementsMap();
const elements = app.scene.getNonDeletedElements();
const { elementId } = linearElementEditor;
const element = LinearElementEditor.getElement(elementId, elementsMap);
invariant(element, "Element being dragged must exist in the scene");
invariant(element.points.length > 1, "Element must have at least 2 points");
const idx = element.points.length - 1;
const point = element.points[idx];
const pivotPoint = element.points[idx - 1];
const customLineAngle =
linearElementEditor.customLineAngle ??
determineCustomLinearAngle(pivotPoint, element.points[idx]);
// Determine if point movement should happen and how much
let deltaX = 0;
let deltaY = 0;
if (shouldRotateWithDiscreteAngle(event)) {
const [width, height] = LinearElementEditor._getShiftLockedDelta(
element,
elementsMap,
pivotPoint,
pointFrom(scenePointerX, scenePointerY),
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
customLineAngle,
);
const target = pointFrom<LocalPoint>(
width + pivotPoint[0],
height + pivotPoint[1],
);
deltaX = target[0] - point[0];
deltaY = target[1] - point[1];
} else {
const newDraggingPointPosition = LinearElementEditor.createPointAt(
element,
elementsMap,
scenePointerX - linearElementEditor.pointerOffset.x,
scenePointerY - linearElementEditor.pointerOffset.y,
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
);
deltaX = newDraggingPointPosition[0] - point[0];
deltaY = newDraggingPointPosition[1] - point[1];
}
// Apply the point movement if needed
if (deltaX || deltaY) {
const { positions, updates } = pointDraggingUpdates(
[idx],
deltaX,
deltaY,
elementsMap,
element,
elements,
app,
);
LinearElementEditor.movePoints(element, app.scene, positions, updates);
}
// Suggest bindings for first and last point if selected
let suggestedBinding: AppState["suggestedBinding"] = null;
if (isBindingElement(element, false)) {
suggestedBinding = maybeSuggestBindingsForBindingElementAtCoords(
element,
"end",
app.scene,
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
);
}
const newLinearElementEditor = {
...linearElementEditor,
customLineAngle,
};
if (
app.state.selectedLinearElement?.customLineAngle === customLineAngle &&
(!suggestedBinding ||
isShallowEqual(app.state.suggestedBinding ?? [], suggestedBinding))
) {
return null;
}
return {
selectedLinearElement: newLinearElementEditor,
suggestedBinding,
};
}
static handlePointDragging(
event: PointerEvent,
app: AppClassProperties,
@@ -913,7 +1010,7 @@ export class LinearElementEditor {
return pointsEqual(point1, point2);
}
static handlePointerMove(
static handlePointerMoveInEditMode(
event: React.PointerEvent<HTMLCanvasElement>,
scenePointerX: number,
scenePointerY: number,