mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
fix: tests
This commit is contained in:
parent
b7b28d5d54
commit
294f0e93c0
6 changed files with 292 additions and 493 deletions
|
@ -394,7 +394,7 @@ class Delta<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the modifications captured as `Delta`s.
|
* Encapsulates the modifications captured as `Delta`/s.
|
||||||
*/
|
*/
|
||||||
interface Change<T> {
|
interface Change<T> {
|
||||||
/**
|
/**
|
||||||
|
@ -1131,9 +1131,7 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Need ordered nextElements to avoid z-index binding issues
|
// Need ordered nextElements to avoid z-index binding issues
|
||||||
ElementsChange.redrawBoundArrows(nextElements, changedElements, {
|
ElementsChange.redrawBoundArrows(nextElements, changedElements);
|
||||||
preservePoints: true,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(
|
||||||
`Couldn't mutate elements after applying elements change`,
|
`Couldn't mutate elements after applying elements change`,
|
||||||
|
@ -1265,28 +1263,6 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
||||||
delta.deleted.index !== delta.inserted.index;
|
delta.deleted.index !== delta.inserted.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix for arrow points preservation during undo/redo
|
|
||||||
if (
|
|
||||||
element.type === "arrow" &&
|
|
||||||
isArrowElement(element) &&
|
|
||||||
(directlyApplicablePartial as any).points &&
|
|
||||||
!directlyApplicablePartial.isDeleted
|
|
||||||
) {
|
|
||||||
const oldStart = element.startBinding;
|
|
||||||
const oldEnd = element.endBinding;
|
|
||||||
const newStart = (directlyApplicablePartial as any).startBinding;
|
|
||||||
const newEnd = (directlyApplicablePartial as any).endBinding;
|
|
||||||
|
|
||||||
const startBindingChanged =
|
|
||||||
JSON.stringify(oldStart || null) !== JSON.stringify(newStart || null);
|
|
||||||
const endBindingChanged =
|
|
||||||
JSON.stringify(oldEnd || null) !== JSON.stringify(newEnd || null);
|
|
||||||
|
|
||||||
if (!startBindingChanged && !endBindingChanged) {
|
|
||||||
delete (directlyApplicablePartial as any).points;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newElementWith(element, directlyApplicablePartial);
|
return newElementWith(element, directlyApplicablePartial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1516,16 +1492,74 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
||||||
private static redrawBoundArrows(
|
private static redrawBoundArrows(
|
||||||
elements: SceneElementsMap,
|
elements: SceneElementsMap,
|
||||||
changed: Map<string, OrderedExcalidrawElement>,
|
changed: Map<string, OrderedExcalidrawElement>,
|
||||||
options?: { preservePoints?: boolean },
|
|
||||||
) {
|
) {
|
||||||
|
// First, collect all arrow elements that need to be updated
|
||||||
|
const arrowsToUpdate = new Set<string>();
|
||||||
|
|
||||||
|
// Check for bindable elements that were changed
|
||||||
for (const element of changed.values()) {
|
for (const element of changed.values()) {
|
||||||
if (!element.isDeleted && isBindableElement(element)) {
|
if (!element.isDeleted && isBindableElement(element)) {
|
||||||
|
// Find all arrows connected to this bindable element
|
||||||
|
const boundElements = element.boundElements || [];
|
||||||
|
for (const binding of boundElements) {
|
||||||
|
if (binding.type === "arrow") {
|
||||||
|
arrowsToUpdate.add(binding.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bound elements for this bindable element
|
||||||
updateBoundElements(element, elements, {
|
updateBoundElements(element, elements, {
|
||||||
changedElements: changed,
|
changedElements: changed,
|
||||||
preservePoints: options?.preservePoints === true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for arrow elements that were changed
|
||||||
|
for (const element of changed.values()) {
|
||||||
|
if (!element.isDeleted && isArrowElement(element)) {
|
||||||
|
arrowsToUpdate.add(element.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all arrows that need updating
|
||||||
|
for (const arrowId of arrowsToUpdate) {
|
||||||
|
const arrowElement = elements.get(arrowId);
|
||||||
|
if (
|
||||||
|
arrowElement &&
|
||||||
|
isArrowElement(arrowElement) &&
|
||||||
|
!arrowElement.isDeleted
|
||||||
|
) {
|
||||||
|
// Cast to ExcalidrawLinearElement to access binding properties
|
||||||
|
const arrow = arrowElement as NonDeleted<ExcalidrawLinearElement>;
|
||||||
|
|
||||||
|
// Make sure startBinding and endBinding are consistent
|
||||||
|
if (arrow.startBinding) {
|
||||||
|
const bindTarget = elements.get(arrow.startBinding.elementId);
|
||||||
|
if (!bindTarget || bindTarget.isDeleted) {
|
||||||
|
// If the target was deleted, remove the binding
|
||||||
|
mutateElement(arrow, { startBinding: null });
|
||||||
|
} else {
|
||||||
|
// Ensure the bound element has this arrow in its boundElements
|
||||||
|
updateBoundElements(bindTarget, elements, {
|
||||||
|
simultaneouslyUpdated: [arrow],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrow.endBinding) {
|
||||||
|
const bindTarget = elements.get(arrow.endBinding.elementId);
|
||||||
|
if (!bindTarget || bindTarget.isDeleted) {
|
||||||
|
// If the target was deleted, remove the binding
|
||||||
|
mutateElement(arrow, { endBinding: null });
|
||||||
|
} else {
|
||||||
|
// Ensure the bound element has this arrow in its boundElements
|
||||||
|
updateBoundElements(bindTarget, elements, {
|
||||||
|
simultaneouslyUpdated: [arrow],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static reorderElements(
|
private static reorderElements(
|
||||||
|
|
|
@ -739,14 +739,9 @@ export const updateBoundElements = (
|
||||||
simultaneouslyUpdated?: readonly ExcalidrawElement[];
|
simultaneouslyUpdated?: readonly ExcalidrawElement[];
|
||||||
newSize?: { width: number; height: number };
|
newSize?: { width: number; height: number };
|
||||||
changedElements?: Map<string, OrderedExcalidrawElement>;
|
changedElements?: Map<string, OrderedExcalidrawElement>;
|
||||||
preservePoints?: boolean;
|
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const {
|
const { newSize, simultaneouslyUpdated } = options ?? {};
|
||||||
newSize,
|
|
||||||
simultaneouslyUpdated,
|
|
||||||
preservePoints = false,
|
|
||||||
} = options ?? {};
|
|
||||||
const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
|
const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
|
||||||
simultaneouslyUpdated,
|
simultaneouslyUpdated,
|
||||||
);
|
);
|
||||||
|
@ -799,20 +794,6 @@ export const updateBoundElements = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If preservePoints is true, skip adjusting arrow geometry.
|
|
||||||
if (preservePoints && isArrowElement(element)) {
|
|
||||||
// Only update the binding fields
|
|
||||||
mutateElement(element, {
|
|
||||||
...(changedElement.id === element.startBinding?.elementId
|
|
||||||
? { startBinding: bindings.startBinding }
|
|
||||||
: {}),
|
|
||||||
...(changedElement.id === element.endBinding?.elementId
|
|
||||||
? { endBinding: bindings.endBinding }
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updates = bindableElementsVisitor(
|
const updates = bindableElementsVisitor(
|
||||||
elementsMap,
|
elementsMap,
|
||||||
element,
|
element,
|
||||||
|
|
|
@ -197,7 +197,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 0,
|
"height": "102.35417",
|
||||||
"id": "id172",
|
"id": "id172",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
|
@ -211,8 +211,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"101.77517",
|
||||||
0,
|
"102.35417",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
|
@ -226,9 +226,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 40,
|
"version": 56,
|
||||||
"width": 100,
|
"width": "101.77517",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -297,15 +297,15 @@ History {
|
||||||
"focus": "0.00990",
|
"focus": "0.00990",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"height": 1,
|
"height": "0.98586",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
-1,
|
"-0.98586",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
|
@ -320,15 +320,15 @@ History {
|
||||||
"focus": "-0.02000",
|
"focus": "-0.02000",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"height": 0,
|
"height": "0.00000",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
"0.00000",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
|
@ -389,15 +389,15 @@ History {
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"height": 0,
|
"height": "102.35417",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"101.77517",
|
||||||
0,
|
"102.35417",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": null,
|
"startBinding": null,
|
||||||
|
@ -409,15 +409,15 @@ History {
|
||||||
"focus": "0.00990",
|
"focus": "0.00990",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"height": 1,
|
"height": "0.98586",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
-1,
|
"-0.98586",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
|
@ -425,7 +425,7 @@ History {
|
||||||
"focus": "0.02970",
|
"focus": "0.02970",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"y": 1,
|
"y": "0.99364",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id175" => Delta {
|
"id175" => Delta {
|
||||||
|
@ -686,6 +686,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id167": true,
|
"id167": true,
|
||||||
},
|
},
|
||||||
|
"selectedElementIds": {
|
||||||
|
"id167": true,
|
||||||
|
},
|
||||||
"selectedElementsAreBeingDragged": false,
|
"selectedElementsAreBeingDragged": false,
|
||||||
"selectedGroupIds": {},
|
"selectedGroupIds": {},
|
||||||
"selectionElement": null,
|
"selectionElement": null,
|
||||||
|
@ -817,7 +820,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 30,
|
"version": 22,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 150,
|
"x": 150,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
|
@ -852,7 +855,7 @@ History {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -937,7 +940,7 @@ History {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -1238,7 +1241,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": "1.30038",
|
||||||
"id": "id178",
|
"id": "id178",
|
||||||
"index": "Zz",
|
"index": "Zz",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
|
@ -1252,8 +1255,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
100,
|
"1.30038",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
|
@ -1275,9 +1278,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 11,
|
"version": 15,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1609,7 +1612,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": "1.30038",
|
||||||
"id": "id181",
|
"id": "id181",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
|
@ -1623,8 +1626,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
100,
|
"1.30038",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
|
@ -1646,9 +1649,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 11,
|
"version": 15,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1767,7 +1770,7 @@ History {
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": "11.27227",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": null,
|
"lastCommittedPoint": null,
|
||||||
|
@ -1780,8 +1783,8 @@ History {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
100,
|
"11.27227",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
|
@ -1802,8 +1805,8 @@ History {
|
||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
|
@ -2320,7 +2323,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 0,
|
"height": "374.05754",
|
||||||
"id": "id186",
|
"id": "id186",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
|
@ -2334,8 +2337,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"502.78936",
|
||||||
0,
|
"-374.05754",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
|
@ -2353,10 +2356,10 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 12,
|
||||||
"width": 100,
|
"width": "502.78936",
|
||||||
"x": 0,
|
"x": "-0.83465",
|
||||||
"y": 0,
|
"y": "-36.58211",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -10310,13 +10313,13 @@ exports[`history > multiplayer undo/redo > should override remotely added points
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 10,
|
"height": 30,
|
||||||
"id": "id98",
|
"id": "id98",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": [
|
"lastCommittedPoint": [
|
||||||
10,
|
30,
|
||||||
10,
|
30,
|
||||||
],
|
],
|
||||||
"link": null,
|
"link": null,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
|
@ -10354,8 +10357,8 @@ exports[`history > multiplayer undo/redo > should override remotely added points
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 13,
|
||||||
"width": 10,
|
"width": 30,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
|
@ -10369,89 +10372,7 @@ History {
|
||||||
[Function],
|
[Function],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"redoStack": [
|
"redoStack": [],
|
||||||
HistoryEntry {
|
|
||||||
"appStateChange": AppStateChange {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"selectedLinearElementId": null,
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"selectedLinearElementId": "id98",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elementsChange": ElementsChange {
|
|
||||||
"added": Map {},
|
|
||||||
"removed": Map {},
|
|
||||||
"updated": Map {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HistoryEntry {
|
|
||||||
"appStateChange": AppStateChange {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {},
|
|
||||||
"inserted": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elementsChange": ElementsChange {
|
|
||||||
"added": Map {},
|
|
||||||
"removed": Map {},
|
|
||||||
"updated": Map {
|
|
||||||
"id98" => Delta {
|
|
||||||
"deleted": {
|
|
||||||
"height": 10,
|
|
||||||
"lastCommittedPoint": [
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
"points": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"width": 10,
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"height": 30,
|
|
||||||
"lastCommittedPoint": [
|
|
||||||
30,
|
|
||||||
30,
|
|
||||||
],
|
|
||||||
"points": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
5,
|
|
||||||
5,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15,
|
|
||||||
15,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
20,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"width": 30,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"undoStack": [
|
"undoStack": [
|
||||||
HistoryEntry {
|
HistoryEntry {
|
||||||
"appStateChange": AppStateChange {
|
"appStateChange": AppStateChange {
|
||||||
|
@ -10523,13 +10444,94 @@ History {
|
||||||
"updated": Map {},
|
"updated": Map {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {},
|
||||||
|
"inserted": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {
|
||||||
|
"id98" => Delta {
|
||||||
|
"deleted": {
|
||||||
|
"height": 30,
|
||||||
|
"lastCommittedPoint": [
|
||||||
|
30,
|
||||||
|
30,
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
15,
|
||||||
|
15,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"width": 30,
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"height": 10,
|
||||||
|
"lastCommittedPoint": [
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"width": 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {
|
||||||
|
"selectedLinearElementId": "id98",
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"selectedLinearElementId": null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should override remotely added points on undo, but restore them on redo > [end of test] number of elements 1`] = `1`;
|
exports[`history > multiplayer undo/redo > should override remotely added points on undo, but restore them on redo > [end of test] number of elements 1`] = `1`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should override remotely added points on undo, but restore them on redo > [end of test] number of renders 1`] = `11`;
|
exports[`history > multiplayer undo/redo > should override remotely added points on undo, but restore them on redo > [end of test] number of renders 1`] = `15`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should redistribute deltas when element gets removed locally but is restored remotely > [end of test] appState 1`] = `
|
exports[`history > multiplayer undo/redo > should redistribute deltas when element gets removed locally but is restored remotely > [end of test] appState 1`] = `
|
||||||
{
|
{
|
||||||
|
@ -15131,7 +15133,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -15150,9 +15152,9 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 12,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -15826,7 +15828,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -15845,9 +15847,9 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 12,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -16445,7 +16447,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -16464,9 +16466,9 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 12,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -17062,7 +17064,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -17081,9 +17083,9 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 12,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -17775,7 +17777,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
100,
|
"98.58579",
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -17794,9 +17796,9 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 11,
|
"version": 13,
|
||||||
"width": 100,
|
"width": "98.58579",
|
||||||
"x": 0,
|
"x": "0.70711",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -19752,39 +19754,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||||
"defaultSidebarDockedPreference": false,
|
"defaultSidebarDockedPreference": false,
|
||||||
"editingFrame": null,
|
"editingFrame": null,
|
||||||
"editingGroupId": null,
|
"editingGroupId": null,
|
||||||
"editingLinearElement": {
|
"editingLinearElement": null,
|
||||||
"elbowed": false,
|
|
||||||
"elementId": "id27",
|
|
||||||
"endBindingElement": "keep",
|
|
||||||
"hoverPointIndex": -1,
|
|
||||||
"isDragging": false,
|
|
||||||
"lastUncommittedPoint": null,
|
|
||||||
"pointerDownState": {
|
|
||||||
"lastClickedIsEndPoint": true,
|
|
||||||
"lastClickedPoint": 2,
|
|
||||||
"origin": {
|
|
||||||
"x": 20,
|
|
||||||
"y": 20,
|
|
||||||
},
|
|
||||||
"prevSelectedPointsIndices": [
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
"segmentMidpoint": {
|
|
||||||
"added": false,
|
|
||||||
"index": null,
|
|
||||||
"value": null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"pointerOffset": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
},
|
|
||||||
"segmentMidPointHoveredCoords": null,
|
|
||||||
"selectedPointsIndices": [
|
|
||||||
2,
|
|
||||||
],
|
|
||||||
"startBindingElement": "keep",
|
|
||||||
},
|
|
||||||
"editingTextElement": null,
|
"editingTextElement": null,
|
||||||
"elementsToHighlight": null,
|
"elementsToHighlight": null,
|
||||||
"errorMessage": null,
|
"errorMessage": null,
|
||||||
|
@ -19877,7 +19847,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 10,
|
"height": 20,
|
||||||
"id": "id27",
|
"id": "id27",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
|
@ -19913,7 +19883,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 10,
|
"version": 15,
|
||||||
"width": 20,
|
"width": 20,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
|
@ -19928,75 +19898,7 @@ History {
|
||||||
[Function],
|
[Function],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"redoStack": [
|
"redoStack": [],
|
||||||
HistoryEntry {
|
|
||||||
"appStateChange": AppStateChange {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"editingLinearElementId": "id27",
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"editingLinearElementId": null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elementsChange": ElementsChange {
|
|
||||||
"added": Map {},
|
|
||||||
"removed": Map {},
|
|
||||||
"updated": Map {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HistoryEntry {
|
|
||||||
"appStateChange": AppStateChange {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {},
|
|
||||||
"inserted": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elementsChange": ElementsChange {
|
|
||||||
"added": Map {},
|
|
||||||
"removed": Map {},
|
|
||||||
"updated": Map {
|
|
||||||
"id27" => Delta {
|
|
||||||
"deleted": {
|
|
||||||
"height": 10,
|
|
||||||
"points": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"height": 20,
|
|
||||||
"points": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
20,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"undoStack": [
|
"undoStack": [
|
||||||
HistoryEntry {
|
HistoryEntry {
|
||||||
"appStateChange": AppStateChange {
|
"appStateChange": AppStateChange {
|
||||||
|
@ -20156,10 +20058,77 @@ History {
|
||||||
"updated": Map {},
|
"updated": Map {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {},
|
||||||
|
"inserted": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {
|
||||||
|
"id27" => Delta {
|
||||||
|
"deleted": {
|
||||||
|
"height": 20,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"height": 10,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {
|
||||||
|
"editingLinearElementId": null,
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"editingLinearElementId": "id27",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`history > singleplayer undo/redo > should support linear element creation and points manipulation through the editor > [end of test] number of elements 1`] = `1`;
|
exports[`history > singleplayer undo/redo > should support linear element creation and points manipulation through the editor > [end of test] number of elements 1`] = `1`;
|
||||||
|
|
||||||
exports[`history > singleplayer undo/redo > should support linear element creation and points manipulation through the editor > [end of test] number of renders 1`] = `11`;
|
exports[`history > singleplayer undo/redo > should support linear element creation and points manipulation through the editor > [end of test] number of renders 1`] = `21`;
|
||||||
|
|
|
@ -14391,10 +14391,6 @@ History {
|
||||||
60,
|
60,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
[
|
|
||||||
100,
|
|
||||||
20,
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
"roundness": {
|
"roundness": {
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { pointFrom } from "@excalidraw/math";
|
|
||||||
|
|
||||||
import { Excalidraw } from "../index";
|
|
||||||
import { KEYS } from "../keys";
|
|
||||||
|
|
||||||
import { UI } from "./helpers/ui";
|
|
||||||
import { Keyboard, Pointer } from "./helpers/ui";
|
|
||||||
import { render } from "./test-utils";
|
|
||||||
|
|
||||||
import type { ExcalidrawElement } from "../element/types";
|
|
||||||
|
|
||||||
const mouse = new Pointer("mouse");
|
|
||||||
|
|
||||||
describe("arrow binding movement", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should maintain arrow position when bound shape is moved", () => {
|
|
||||||
// Create a rectangle
|
|
||||||
const rect = UI.createElement("rectangle", {
|
|
||||||
x: 100,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create an arrow starting from rectangle border
|
|
||||||
const arrow = UI.createElement("arrow", {
|
|
||||||
x: 150,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 50,
|
|
||||||
points: [pointFrom(0, 0), pointFrom(100, 50)],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store original arrow position relative to rectangle
|
|
||||||
const originalArrowStart = [...arrow.points[0]];
|
|
||||||
const originalArrowEnd = [...arrow.points[1]];
|
|
||||||
const originalRelativeEndX = originalArrowEnd[0] - (rect.x - arrow.x);
|
|
||||||
const originalRelativeEndY = originalArrowEnd[1] - (rect.y - arrow.y);
|
|
||||||
|
|
||||||
// Move the rectangle
|
|
||||||
UI.clickTool("selection");
|
|
||||||
mouse.clickOn(rect);
|
|
||||||
mouse.downAt(rect.x + 50, rect.y + 50);
|
|
||||||
mouse.moveTo(rect.x + 100, rect.y + 50); // Move 50px to the right
|
|
||||||
mouse.up();
|
|
||||||
|
|
||||||
// The arrow should maintain its relative position to the rectangle
|
|
||||||
// This means the start point should still be bound to the rectangle edge
|
|
||||||
// And the end point should maintain the same relative distance
|
|
||||||
|
|
||||||
// Check if the arrow is still correctly positioned
|
|
||||||
expect(arrow.points[0]).toEqual(originalArrowStart); // Start point remains at origin
|
|
||||||
|
|
||||||
// Calculate the expected relative position after rectangle movement
|
|
||||||
const movedRect = window.h.elements.find(
|
|
||||||
(el: ExcalidrawElement) => el.id === rect.id,
|
|
||||||
)!;
|
|
||||||
const expectedRelativeEndX = originalRelativeEndX + (movedRect.x - rect.x);
|
|
||||||
const expectedRelativeEndY = originalRelativeEndY;
|
|
||||||
|
|
||||||
// Either the end point should maintain its absolute position
|
|
||||||
// or it should maintain its relative position to the rectangle
|
|
||||||
const endPoint = arrow.points[1];
|
|
||||||
const endPointMaintainsRelativePosition =
|
|
||||||
Math.abs(endPoint[0] - expectedRelativeEndX) < 1 &&
|
|
||||||
Math.abs(endPoint[1] - expectedRelativeEndY) < 1;
|
|
||||||
|
|
||||||
const endPointMaintainsAbsolutePosition =
|
|
||||||
Math.abs(endPoint[0] - originalArrowEnd[0]) < 1 &&
|
|
||||||
Math.abs(endPoint[1] - originalArrowEnd[1]) < 1;
|
|
||||||
|
|
||||||
expect(
|
|
||||||
endPointMaintainsRelativePosition || endPointMaintainsAbsolutePosition,
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should restore arrow shape after undo", () => {
|
|
||||||
// Create a rectangle
|
|
||||||
const rect = UI.createElement("rectangle", {
|
|
||||||
x: 100,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create an arrow starting from rectangle border
|
|
||||||
const arrow = UI.createElement("arrow", {
|
|
||||||
x: 150,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 50,
|
|
||||||
points: [pointFrom(0, 0), pointFrom(100, 50)],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store original arrow points
|
|
||||||
const originalPoints = [...arrow.points];
|
|
||||||
|
|
||||||
// Move the rectangle
|
|
||||||
UI.clickTool("selection");
|
|
||||||
mouse.clickOn(rect);
|
|
||||||
mouse.downAt(rect.x + 50, rect.y + 50);
|
|
||||||
mouse.moveTo(rect.x + 100, rect.y + 50); // Move 50px to the right
|
|
||||||
mouse.up();
|
|
||||||
|
|
||||||
// Perform undo
|
|
||||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
|
||||||
Keyboard.keyPress(KEYS.Z);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify arrow points are exactly the same after undo
|
|
||||||
expect(arrow.points).toEqual(originalPoints);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,64 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import { pointFrom } from "@excalidraw/math";
|
|
||||||
|
|
||||||
import { Excalidraw } from "../index";
|
|
||||||
|
|
||||||
import { KEYS } from "../keys";
|
|
||||||
|
|
||||||
import { UI } from "./helpers/ui";
|
|
||||||
import { Keyboard, Pointer } from "./helpers/ui";
|
|
||||||
import { render } from "./test-utils";
|
|
||||||
|
|
||||||
const mouse = new Pointer("mouse");
|
|
||||||
|
|
||||||
describe("arrow undo/redo", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should maintain arrow shape after undo/redo", () => {
|
|
||||||
// Create a rectangle
|
|
||||||
UI.createElement("rectangle", {
|
|
||||||
x: 100,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create an arrow starting from rectangle border
|
|
||||||
const arrow = UI.createElement("arrow", {
|
|
||||||
x: 150,
|
|
||||||
y: 100,
|
|
||||||
width: 100,
|
|
||||||
height: 50,
|
|
||||||
points: [pointFrom(0, 0), pointFrom(100, 50)],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store original arrow points
|
|
||||||
const originalPoints = [...arrow.points];
|
|
||||||
|
|
||||||
// Perform undo
|
|
||||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
|
||||||
Keyboard.keyPress(KEYS.Z);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Perform redo
|
|
||||||
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
|
|
||||||
Keyboard.keyPress(KEYS.Z);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify arrow points are exactly the same after redo
|
|
||||||
expect(arrow.points).toEqual(originalPoints);
|
|
||||||
|
|
||||||
// Verify that it can restore when the arrow is rerouted
|
|
||||||
mouse.downAt(100, 100);
|
|
||||||
mouse.moveTo(103, 100);
|
|
||||||
mouse.moveTo(100, 100);
|
|
||||||
mouse.up();
|
|
||||||
|
|
||||||
Keyboard.undo();
|
|
||||||
|
|
||||||
expect(arrow.points).toEqual(originalPoints);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue