mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-19 07:20:21 +02:00
fix: Multi-point arrows and linears
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
@@ -44,3 +44,14 @@ exports[`Test Linear Elements > Test bound text element > should resize and posi
|
||||
"Online whiteboard
|
||||
collaboration made easy"
|
||||
`;
|
||||
|
||||
exports[`Test Linear Elements > Test bound text element > should wrap the bound text when arrow bound container moves 1`] = `
|
||||
"Online whiteboard
|
||||
collaboration made easy"
|
||||
`;
|
||||
|
||||
exports[`Test Linear Elements > Test bound text element > should wrap the bound text when arrow bound container moves 2`] = `
|
||||
"Online whiteboard
|
||||
collaboration made
|
||||
easy"
|
||||
`;
|
||||
|
@@ -257,8 +257,8 @@ describe("binding for simple arrows", () => {
|
||||
const arrow = API.getSelectedElement() as ExcalidrawLinearElement;
|
||||
expect(arrow.x).toBe(10);
|
||||
expect(arrow.y).toBe(10);
|
||||
expect(arrow.width).toBeCloseTo(86.4669660940663);
|
||||
expect(arrow.height).toBeCloseTo(86.46696609406821);
|
||||
expect(arrow.width).toBeCloseTo(85.75985931287957);
|
||||
expect(arrow.height).toBeCloseTo(85.75985931288186);
|
||||
|
||||
// Should bind to the rectangle since endpoint is inside
|
||||
expect(arrow.startBinding).toBe(null);
|
||||
@@ -280,8 +280,8 @@ describe("binding for simple arrows", () => {
|
||||
// Check if the arrow moved
|
||||
expect(arrow.x).toBe(10);
|
||||
expect(arrow.y).toBe(10);
|
||||
expect(arrow.width).toBeCloseTo(235);
|
||||
expect(arrow.height).toBeCloseTo(117.5);
|
||||
expect(arrow.width).toBeCloseTo(234);
|
||||
expect(arrow.height).toBeCloseTo(117);
|
||||
|
||||
// Restore bindable
|
||||
mouse.reset();
|
||||
@@ -309,49 +309,9 @@ describe("binding for simple arrows", () => {
|
||||
expect(arrow.width).toBeCloseTo(86, 0);
|
||||
expect(arrow.height).toBeCloseTo(86, 0);
|
||||
});
|
||||
|
||||
it("should happen even if the arrow is not pointing at the element", () => {
|
||||
// Create a rectangle positioned so the extended arrow segment will miss it
|
||||
const rect = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
|
||||
API.setElements([rect]);
|
||||
|
||||
// Draw an arrow that doesn't point at the rectangle (extended segment will miss)
|
||||
UI.clickTool("arrow");
|
||||
mouse.reset();
|
||||
mouse.downAt(125, 93); // Start point
|
||||
mouse.moveTo(175, 93); // End point - arrow direction is horizontal, misses rectangle
|
||||
mouse.up();
|
||||
|
||||
const arrow = API.getSelectedElement() as ExcalidrawLinearElement;
|
||||
|
||||
// Should create a fixed point binding since the extended line segment
|
||||
// from the last arrow segment misses the rectangle
|
||||
expect(arrow.startBinding?.elementId).toBe(rect.id);
|
||||
expect(arrow.startBinding).toHaveProperty("fixedPoint");
|
||||
expect(
|
||||
(arrow.startBinding as FixedPointBinding).fixedPoint[0],
|
||||
).toBeGreaterThanOrEqual(0);
|
||||
expect(
|
||||
(arrow.startBinding as FixedPointBinding).fixedPoint[0],
|
||||
).toBeLessThanOrEqual(1);
|
||||
expect(
|
||||
(arrow.startBinding as FixedPointBinding).fixedPoint[1],
|
||||
).toBeLessThanOrEqual(0.5);
|
||||
expect(
|
||||
(arrow.startBinding as FixedPointBinding).fixedPoint[1],
|
||||
).toBeLessThanOrEqual(1);
|
||||
expect(arrow.endBinding).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("", () => {
|
||||
describe("additional binding behavior", () => {
|
||||
beforeEach(async () => {
|
||||
mouse.reset();
|
||||
|
||||
@@ -411,8 +371,8 @@ describe("binding for simple arrows", () => {
|
||||
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 50,
|
||||
y: 5,
|
||||
size: 70,
|
||||
});
|
||||
|
||||
expect(arrow.endBinding?.elementId).toBe(rectangle.id);
|
||||
@@ -435,9 +395,9 @@ describe("binding for simple arrows", () => {
|
||||
height: 500,
|
||||
});
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: 210,
|
||||
x: 190,
|
||||
y: 250,
|
||||
width: 180,
|
||||
width: 220,
|
||||
height: 1,
|
||||
});
|
||||
expect(arrow.startBinding?.elementId).toBe(rectLeft.id);
|
||||
@@ -475,11 +435,11 @@ describe("binding for simple arrows", () => {
|
||||
|
||||
UI.clickTool("arrow");
|
||||
mouse.reset();
|
||||
mouse.clickAt(210, 250);
|
||||
mouse.clickAt(190, 250);
|
||||
mouse.moveTo(300, 200);
|
||||
mouse.clickAt(300, 200);
|
||||
mouse.moveTo(390, 251);
|
||||
mouse.clickAt(390, 251);
|
||||
mouse.moveTo(410, 251);
|
||||
mouse.clickAt(410, 251);
|
||||
|
||||
const arrow = API.getSelectedElement() as ExcalidrawArrowElement;
|
||||
|
||||
@@ -517,9 +477,9 @@ describe("binding for simple arrows", () => {
|
||||
height: 500,
|
||||
});
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: 210,
|
||||
x: 190,
|
||||
y: 250,
|
||||
width: 177,
|
||||
width: 217,
|
||||
height: 1,
|
||||
});
|
||||
expect(arrow.startBinding?.elementId).toBe(rectLeft.id);
|
||||
@@ -668,7 +628,7 @@ describe("binding for simple arrows", () => {
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 50,
|
||||
size: 65,
|
||||
});
|
||||
|
||||
expect(arrow.endBinding?.elementId).toBe(text.id);
|
||||
@@ -693,7 +653,7 @@ describe("binding for simple arrows", () => {
|
||||
it("should unbind on text element deletion by submitting empty text", async () => {
|
||||
const text = API.createElement({
|
||||
type: "text",
|
||||
text: "ola",
|
||||
text: "¡olá!",
|
||||
x: 60,
|
||||
y: 0,
|
||||
width: 100,
|
||||
@@ -705,7 +665,7 @@ describe("binding for simple arrows", () => {
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 50,
|
||||
size: 65,
|
||||
});
|
||||
|
||||
expect(arrow.endBinding?.elementId).toBe(text.id);
|
||||
|
@@ -814,7 +814,7 @@ describe("duplication z-order", () => {
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: -100,
|
||||
y: 50,
|
||||
width: 95,
|
||||
width: 115,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
|
@@ -131,6 +131,11 @@ describe("elbow arrow segment move", () => {
|
||||
});
|
||||
|
||||
describe("elbow arrow routing", () => {
|
||||
beforeEach(async () => {
|
||||
localStorage.clear();
|
||||
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
||||
});
|
||||
|
||||
it("can properly generate orthogonal arrow points", () => {
|
||||
const scene = new Scene();
|
||||
const arrow = API.createElement({
|
||||
@@ -194,9 +199,9 @@ describe("elbow arrow routing", () => {
|
||||
|
||||
expect(arrow.points).toEqual([
|
||||
[0, 0],
|
||||
[45, 0],
|
||||
[45, 200],
|
||||
[90, 200],
|
||||
[44, 0],
|
||||
[44, 200],
|
||||
[88, 200],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -235,9 +240,9 @@ describe("elbow arrow ui", () => {
|
||||
expect(h.state.currentItemArrowType).toBe(ARROW_TYPE.elbow);
|
||||
|
||||
mouse.reset();
|
||||
mouse.moveTo(-43, -99);
|
||||
mouse.moveTo(-53, -99);
|
||||
mouse.click();
|
||||
mouse.moveTo(43, 99);
|
||||
mouse.moveTo(53, 99);
|
||||
mouse.click();
|
||||
|
||||
const arrow = h.scene.getSelectedElements(
|
||||
@@ -248,9 +253,9 @@ describe("elbow arrow ui", () => {
|
||||
expect(arrow.elbowed).toBe(true);
|
||||
expect(arrow.points).toEqual([
|
||||
[0, 0],
|
||||
[45, 0],
|
||||
[45, 200],
|
||||
[90, 200],
|
||||
[44, 0],
|
||||
[44, 200],
|
||||
[88, 200],
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -272,9 +277,9 @@ describe("elbow arrow ui", () => {
|
||||
UI.clickOnTestId("elbow-arrow");
|
||||
|
||||
mouse.reset();
|
||||
mouse.moveTo(-43, -99);
|
||||
mouse.moveTo(-53, -99);
|
||||
mouse.click();
|
||||
mouse.moveTo(43, 99);
|
||||
mouse.moveTo(53, 99);
|
||||
mouse.click();
|
||||
|
||||
const arrow = h.scene.getSelectedElements(
|
||||
@@ -290,9 +295,11 @@ describe("elbow arrow ui", () => {
|
||||
|
||||
expect(arrow.points.map((point) => point.map(Math.round))).toEqual([
|
||||
[0, 0],
|
||||
[35, 0],
|
||||
[35, 165],
|
||||
[103, 165],
|
||||
[36, 0],
|
||||
[36, 90],
|
||||
[28, 90],
|
||||
[28, 164],
|
||||
[101, 164],
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -314,9 +321,9 @@ describe("elbow arrow ui", () => {
|
||||
UI.clickOnTestId("elbow-arrow");
|
||||
|
||||
mouse.reset();
|
||||
mouse.moveTo(-43, -99);
|
||||
mouse.moveTo(-53, -99);
|
||||
mouse.click();
|
||||
mouse.moveTo(43, 99);
|
||||
mouse.moveTo(53, 99);
|
||||
mouse.click();
|
||||
|
||||
const arrow = h.scene.getSelectedElements(
|
||||
@@ -346,9 +353,9 @@ describe("elbow arrow ui", () => {
|
||||
expect(duplicatedArrow.elbowed).toBe(true);
|
||||
expect(duplicatedArrow.points).toEqual([
|
||||
[0, 0],
|
||||
[45, 0],
|
||||
[45, 200],
|
||||
[90, 200],
|
||||
[44, 0],
|
||||
[44, 200],
|
||||
[88, 200],
|
||||
]);
|
||||
expect(arrow.startBinding).not.toBe(null);
|
||||
expect(arrow.endBinding).not.toBe(null);
|
||||
@@ -372,9 +379,9 @@ describe("elbow arrow ui", () => {
|
||||
UI.clickOnTestId("elbow-arrow");
|
||||
|
||||
mouse.reset();
|
||||
mouse.moveTo(-43, -99);
|
||||
mouse.moveTo(-53, -99);
|
||||
mouse.click();
|
||||
mouse.moveTo(43, 99);
|
||||
mouse.moveTo(53, 99);
|
||||
mouse.click();
|
||||
|
||||
const arrow = h.scene.getSelectedElements(
|
||||
@@ -401,8 +408,8 @@ describe("elbow arrow ui", () => {
|
||||
expect(duplicatedArrow.points).toEqual([
|
||||
[0, 0],
|
||||
[0, 100],
|
||||
[90, 100],
|
||||
[90, 200],
|
||||
[88, 100],
|
||||
[88, 200],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@@ -1303,7 +1303,7 @@ describe("Test Linear Elements", () => {
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: -10,
|
||||
y: 250,
|
||||
width: 400,
|
||||
width: 410,
|
||||
height: 1,
|
||||
});
|
||||
|
||||
@@ -1316,7 +1316,7 @@ describe("Test Linear Elements", () => {
|
||||
const textElement = h.elements[2] as ExcalidrawTextElementWithContainer;
|
||||
|
||||
expect(arrow.endBinding?.elementId).toBe(rect.id);
|
||||
expect(arrow.width).toBeCloseTo(405);
|
||||
expect(arrow.width).toBeCloseTo(404);
|
||||
expect(rect.x).toBe(400);
|
||||
expect(rect.y).toBe(0);
|
||||
expect(
|
||||
@@ -1335,7 +1335,7 @@ describe("Test Linear Elements", () => {
|
||||
mouse.downAt(rect.x, rect.y);
|
||||
mouse.moveTo(200, 0);
|
||||
mouse.upAt(200, 0);
|
||||
expect(arrow.width).toBeCloseTo(205);
|
||||
expect(arrow.width).toBeCloseTo(204);
|
||||
expect(rect.x).toBe(200);
|
||||
expect(rect.y).toBe(0);
|
||||
expect(handleBindTextResizeSpy).toHaveBeenCalledWith(
|
||||
|
@@ -510,12 +510,12 @@ describe("arrow element", () => {
|
||||
h.state,
|
||||
)[0] as ExcalidrawElbowArrowElement;
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.06);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
|
||||
UI.resize(rectangle, "se", [-200, -150]);
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.06);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
});
|
||||
|
||||
@@ -538,11 +538,11 @@ describe("arrow element", () => {
|
||||
h.state,
|
||||
)[0] as ExcalidrawElbowArrowElement;
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.06);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
|
||||
UI.resize([rectangle, arrow], "nw", [300, 350]);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(-0.05);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(-0.06);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.25);
|
||||
});
|
||||
});
|
||||
@@ -1350,8 +1350,8 @@ describe("multiple selection", () => {
|
||||
|
||||
expect(boundArrow.x).toBeCloseTo(380 * scaleX);
|
||||
expect(boundArrow.y).toBeCloseTo(240 * scaleY);
|
||||
expect(boundArrow.points[1][0]).toBeCloseTo(64.1246);
|
||||
expect(boundArrow.points[1][1]).toBeCloseTo(-85.4995);
|
||||
expect(boundArrow.points[1][0]).toBeCloseTo(66.3157);
|
||||
expect(boundArrow.points[1][1]).toBeCloseTo(-88.421);
|
||||
|
||||
expect(arrowLabelPos.x + arrowLabel.width / 2).toBeCloseTo(
|
||||
boundArrow.x + boundArrow.points[1][0] / 2,
|
||||
|
Reference in New Issue
Block a user