fix: keep orig elem in place on alt-duplication

This commit is contained in:
dwelle 2025-04-16 19:40:07 +02:00
parent 58f7d33d80
commit 47562f62f4
12 changed files with 400 additions and 516 deletions

View file

@ -56,7 +56,6 @@ import { getBoundTextElement, handleBindTextResize } from "./textElement";
import {
isArrowElement,
isBindableElement,
isBindingElement,
isBoundToContainer,
isElbowArrow,
isFixedPointBinding,
@ -1409,19 +1408,19 @@ const getLinearElementEdgeCoors = (
};
export const fixDuplicatedBindingsAfterDuplication = (
newElements: ExcalidrawElement[],
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
duplicatedElementsMap: NonDeletedSceneElementsMap,
duplicatedElements: ExcalidrawElement[],
origIdToDuplicateId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
duplicateElementsMap: NonDeletedSceneElementsMap,
) => {
for (const element of newElements) {
if ("boundElements" in element && element.boundElements) {
Object.assign(element, {
boundElements: element.boundElements.reduce(
for (const duplicateElement of duplicatedElements) {
if ("boundElements" in duplicateElement && duplicateElement.boundElements) {
Object.assign(duplicateElement, {
boundElements: duplicateElement.boundElements.reduce(
(
acc: Mutable<NonNullable<ExcalidrawElement["boundElements"]>>,
binding,
) => {
const newBindingId = oldIdToDuplicatedId.get(binding.id);
const newBindingId = origIdToDuplicateId.get(binding.id);
if (newBindingId) {
acc.push({ ...binding, id: newBindingId });
}
@ -1432,46 +1431,47 @@ export const fixDuplicatedBindingsAfterDuplication = (
});
}
if ("containerId" in element && element.containerId) {
Object.assign(element, {
containerId: oldIdToDuplicatedId.get(element.containerId) ?? null,
if ("containerId" in duplicateElement && duplicateElement.containerId) {
Object.assign(duplicateElement, {
containerId:
origIdToDuplicateId.get(duplicateElement.containerId) ?? null,
});
}
if ("endBinding" in element && element.endBinding) {
const newEndBindingId = oldIdToDuplicatedId.get(
element.endBinding.elementId,
if ("endBinding" in duplicateElement && duplicateElement.endBinding) {
const newEndBindingId = origIdToDuplicateId.get(
duplicateElement.endBinding.elementId,
);
Object.assign(element, {
Object.assign(duplicateElement, {
endBinding: newEndBindingId
? {
...element.endBinding,
...duplicateElement.endBinding,
elementId: newEndBindingId,
}
: null,
});
}
if ("startBinding" in element && element.startBinding) {
const newEndBindingId = oldIdToDuplicatedId.get(
element.startBinding.elementId,
if ("startBinding" in duplicateElement && duplicateElement.startBinding) {
const newEndBindingId = origIdToDuplicateId.get(
duplicateElement.startBinding.elementId,
);
Object.assign(element, {
Object.assign(duplicateElement, {
startBinding: newEndBindingId
? {
...element.startBinding,
...duplicateElement.startBinding,
elementId: newEndBindingId,
}
: null,
});
}
if (isElbowArrow(element)) {
if (isElbowArrow(duplicateElement)) {
Object.assign(
element,
updateElbowArrowPoints(element, duplicatedElementsMap, {
duplicateElement,
updateElbowArrowPoints(duplicateElement, duplicateElementsMap, {
points: [
element.points[0],
element.points[element.points.length - 1],
duplicateElement.points[0],
duplicateElement.points[duplicateElement.points.length - 1],
],
}),
);
@ -1479,196 +1479,6 @@ export const fixDuplicatedBindingsAfterDuplication = (
}
};
const fixReversedBindingsForBindables = (
original: ExcalidrawBindableElement,
duplicate: ExcalidrawBindableElement,
originalElements: Map<string, ExcalidrawElement>,
elementsWithClones: ExcalidrawElement[],
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
) => {
original.boundElements?.forEach((binding, idx) => {
if (binding.type !== "arrow") {
return;
}
const oldArrow = elementsWithClones.find((el) => el.id === binding.id);
if (!isBindingElement(oldArrow)) {
return;
}
if (originalElements.has(binding.id)) {
// Linked arrow is in the selection, so find the duplicate pair
const newArrowId = oldIdToDuplicatedId.get(binding.id) ?? binding.id;
const newArrow = elementsWithClones.find(
(el) => el.id === newArrowId,
)! as ExcalidrawArrowElement;
mutateElement(newArrow, {
startBinding:
oldArrow.startBinding?.elementId === binding.id
? {
...oldArrow.startBinding,
elementId: duplicate.id,
}
: newArrow.startBinding,
endBinding:
oldArrow.endBinding?.elementId === binding.id
? {
...oldArrow.endBinding,
elementId: duplicate.id,
}
: newArrow.endBinding,
});
mutateElement(duplicate, {
boundElements: [
...(duplicate.boundElements ?? []).filter(
(el) => el.id !== binding.id && el.id !== newArrowId,
),
{
type: "arrow",
id: newArrowId,
},
],
});
} else {
// Linked arrow is outside the selection,
// so we move the binding to the duplicate
mutateElement(oldArrow, {
startBinding:
oldArrow.startBinding?.elementId === original.id
? {
...oldArrow.startBinding,
elementId: duplicate.id,
}
: oldArrow.startBinding,
endBinding:
oldArrow.endBinding?.elementId === original.id
? {
...oldArrow.endBinding,
elementId: duplicate.id,
}
: oldArrow.endBinding,
});
mutateElement(duplicate, {
boundElements: [
...(duplicate.boundElements ?? []),
{
type: "arrow",
id: oldArrow.id,
},
],
});
mutateElement(original, {
boundElements:
original.boundElements?.filter((_, i) => i !== idx) ?? null,
});
}
});
};
const fixReversedBindingsForArrows = (
original: ExcalidrawArrowElement,
duplicate: ExcalidrawArrowElement,
originalElements: Map<string, ExcalidrawElement>,
bindingProp: "startBinding" | "endBinding",
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
elementsWithClones: ExcalidrawElement[],
) => {
const oldBindableId = original[bindingProp]?.elementId;
if (oldBindableId) {
if (originalElements.has(oldBindableId)) {
// Linked element is in the selection
const newBindableId =
oldIdToDuplicatedId.get(oldBindableId) ?? oldBindableId;
const newBindable = elementsWithClones.find(
(el) => el.id === newBindableId,
) as ExcalidrawBindableElement;
mutateElement(duplicate, {
[bindingProp]: {
...original[bindingProp],
elementId: newBindableId,
},
});
mutateElement(newBindable, {
boundElements: [
...(newBindable.boundElements ?? []).filter(
(el) => el.id !== original.id && el.id !== duplicate.id,
),
{
id: duplicate.id,
type: "arrow",
},
],
});
} else {
// Linked element is outside the selection
const originalBindable = elementsWithClones.find(
(el) => el.id === oldBindableId,
);
if (originalBindable) {
mutateElement(duplicate, {
[bindingProp]: original[bindingProp],
});
mutateElement(original, {
[bindingProp]: null,
});
mutateElement(originalBindable, {
boundElements: [
...(originalBindable.boundElements?.filter(
(el) => el.id !== original.id,
) ?? []),
{
id: duplicate.id,
type: "arrow",
},
],
});
}
}
}
};
export const fixReversedBindings = (
originalElements: Map<string, ExcalidrawElement>,
elementsWithClones: ExcalidrawElement[],
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
) => {
for (const original of originalElements.values()) {
const duplicate = elementsWithClones.find(
(el) => el.id === oldIdToDuplicatedId.get(original.id),
)!;
if (isBindableElement(original) && isBindableElement(duplicate)) {
fixReversedBindingsForBindables(
original,
duplicate,
originalElements,
elementsWithClones,
oldIdToDuplicatedId,
);
} else if (isArrowElement(original) && isArrowElement(duplicate)) {
fixReversedBindingsForArrows(
original,
duplicate,
originalElements,
"startBinding",
oldIdToDuplicatedId,
elementsWithClones,
);
fixReversedBindingsForArrows(
original,
duplicate,
originalElements,
"endBinding",
oldIdToDuplicatedId,
elementsWithClones,
);
}
}
};
export const fixBindingsAfterDeletion = (
sceneElements: readonly ExcalidrawElement[],
deletedElements: readonly ExcalidrawElement[],

View file

@ -36,10 +36,7 @@ import {
import { getBoundTextElement, getContainerElement } from "./textElement";
import {
fixDuplicatedBindingsAfterDuplication,
fixReversedBindings,
} from "./binding";
import { fixDuplicatedBindingsAfterDuplication } from "./binding";
import type {
ElementsMap,
@ -60,16 +57,14 @@ import type {
* multiple elements at once, share this map
* amongst all of them
* @param element Element to duplicate
* @param overrides Any element properties to override
*/
export const duplicateElement = <TElement extends ExcalidrawElement>(
editingGroupId: AppState["editingGroupId"],
groupIdMapForOperation: Map<GroupId, GroupId>,
element: TElement,
overrides?: Partial<TElement>,
randomizeSeed?: boolean,
): Readonly<TElement> => {
let copy = deepCopyElement(element);
const copy = deepCopyElement(element);
if (isTestEnv()) {
__test__defineOrigId(copy, element.id);
@ -92,9 +87,6 @@ export const duplicateElement = <TElement extends ExcalidrawElement>(
return groupIdMapForOperation.get(groupId)!;
},
);
if (overrides) {
copy = Object.assign(copy, overrides);
}
return copy;
};
@ -102,9 +94,13 @@ export const duplicateElements = (
opts: {
elements: readonly ExcalidrawElement[];
randomizeSeed?: boolean;
overrides?: (
originalElement: ExcalidrawElement,
) => Partial<ExcalidrawElement>;
overrides?: (data: {
origElement: ExcalidrawElement;
origIdToDuplicateId: Map<
ExcalidrawElement["id"],
ExcalidrawElement["id"]
>;
}) => Partial<ExcalidrawElement>;
} & (
| {
/**
@ -132,14 +128,6 @@ export const duplicateElements = (
editingGroupId: AppState["editingGroupId"];
selectedGroupIds: AppState["selectedGroupIds"];
};
/**
* If true, duplicated elements are inserted _before_ specified
* elements. Case: alt-dragging elements to duplicate them.
*
* TODO: remove this once (if) we stop replacing the original element
* with the duplicated one in the scene array.
*/
reverseOrder: boolean;
}
),
) => {
@ -153,8 +141,6 @@ export const duplicateElements = (
selectedGroupIds: {},
} as const);
const reverseOrder = opts.type === "in-place" ? opts.reverseOrder : false;
// Ids of elements that have already been processed so we don't push them
// into the array twice if we end up backtracking when retrieving
// discontiguous group of elements (can happen due to a bug, or in edge
@ -167,10 +153,17 @@ export const duplicateElements = (
// loop over them.
const processedIds = new Map<ExcalidrawElement["id"], true>();
const groupIdMap = new Map();
const newElements: ExcalidrawElement[] = [];
const oldElements: ExcalidrawElement[] = [];
const oldIdToDuplicatedId = new Map();
const duplicatedElementsMap = new Map<string, ExcalidrawElement>();
const duplicatedElements: ExcalidrawElement[] = [];
const origElements: ExcalidrawElement[] = [];
const origIdToDuplicateId = new Map<
ExcalidrawElement["id"],
ExcalidrawElement["id"]
>();
const duplicateIdToOrigElement = new Map<
ExcalidrawElement["id"],
ExcalidrawElement
>();
const duplicateElementsMap = new Map<string, ExcalidrawElement>();
const elementsMap = arrayToMap(elements) as ElementsMap;
const _idsOfElementsToDuplicate =
opts.type === "in-place"
@ -188,7 +181,7 @@ export const duplicateElements = (
elements = normalizeElementOrder(elements);
const elementsWithClones: ExcalidrawElement[] = elements.slice();
const elementsWithDuplicates: ExcalidrawElement[] = elements.slice();
// helper functions
// -------------------------------------------------------------------------
@ -214,17 +207,17 @@ export const duplicateElements = (
appState.editingGroupId,
groupIdMap,
element,
opts.overrides?.(element),
opts.randomizeSeed,
);
processedIds.set(newElement.id, true);
duplicatedElementsMap.set(newElement.id, newElement);
oldIdToDuplicatedId.set(element.id, newElement.id);
duplicateElementsMap.set(newElement.id, newElement);
origIdToDuplicateId.set(element.id, newElement.id);
duplicateIdToOrigElement.set(newElement.id, element);
oldElements.push(element);
newElements.push(newElement);
origElements.push(element);
duplicatedElements.push(newElement);
acc.push(newElement);
return acc;
@ -248,21 +241,12 @@ export const duplicateElements = (
return;
}
if (reverseOrder && index < 1) {
elementsWithClones.unshift(...castArray(elements));
if (index > elementsWithDuplicates.length - 1) {
elementsWithDuplicates.push(...castArray(elements));
return;
}
if (!reverseOrder && index > elementsWithClones.length - 1) {
elementsWithClones.push(...castArray(elements));
return;
}
elementsWithClones.splice(
index + (reverseOrder ? 0 : 1),
0,
...castArray(elements),
);
elementsWithDuplicates.splice(index + 1, 0, ...castArray(elements));
};
const frameIdsToDuplicate = new Set(
@ -294,13 +278,9 @@ export const duplicateElements = (
: [element],
);
const targetIndex = reverseOrder
? elementsWithClones.findIndex((el) => {
return el.groupIds?.includes(groupId);
})
: findLastIndex(elementsWithClones, (el) => {
return el.groupIds?.includes(groupId);
});
const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
return el.groupIds?.includes(groupId);
});
insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements));
continue;
@ -318,7 +298,7 @@ export const duplicateElements = (
const frameChildren = getFrameChildren(elements, frameId);
const targetIndex = findLastIndex(elementsWithClones, (el) => {
const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
return el.frameId === frameId || el.id === frameId;
});
@ -335,7 +315,7 @@ export const duplicateElements = (
if (hasBoundTextElement(element)) {
const boundTextElement = getBoundTextElement(element, elementsMap);
const targetIndex = findLastIndex(elementsWithClones, (el) => {
const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
return (
el.id === element.id ||
("containerId" in el && el.containerId === element.id)
@ -344,7 +324,7 @@ export const duplicateElements = (
if (boundTextElement) {
insertBeforeOrAfterIndex(
targetIndex + (reverseOrder ? -1 : 0),
targetIndex,
copyElements([element, boundTextElement]),
);
} else {
@ -357,7 +337,7 @@ export const duplicateElements = (
if (isBoundToContainer(element)) {
const container = getContainerElement(element, elementsMap);
const targetIndex = findLastIndex(elementsWithClones, (el) => {
const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
return el.id === element.id || el.id === container?.id;
});
@ -377,7 +357,7 @@ export const duplicateElements = (
// -------------------------------------------------------------------------
insertBeforeOrAfterIndex(
findLastIndex(elementsWithClones, (el) => el.id === element.id),
findLastIndex(elementsWithDuplicates, (el) => el.id === element.id),
copyElements(element),
);
}
@ -385,28 +365,37 @@ export const duplicateElements = (
// ---------------------------------------------------------------------------
fixDuplicatedBindingsAfterDuplication(
newElements,
oldIdToDuplicatedId,
duplicatedElementsMap as NonDeletedSceneElementsMap,
duplicatedElements,
origIdToDuplicateId,
duplicateElementsMap as NonDeletedSceneElementsMap,
);
if (reverseOrder) {
fixReversedBindings(
_idsOfElementsToDuplicate,
elementsWithClones,
oldIdToDuplicatedId,
);
}
bindElementsToFramesAfterDuplication(
elementsWithClones,
oldElements,
oldIdToDuplicatedId,
elementsWithDuplicates,
origElements,
origIdToDuplicateId,
);
if (opts.overrides) {
for (const copy of duplicatedElements) {
const origElement = duplicateIdToOrigElement.get(copy.id);
if (origElement) {
Object.assign(
copy,
opts.overrides({
origElement,
origIdToDuplicateId,
}),
);
}
}
}
return {
newElements,
elementsWithClones,
duplicatedElements,
duplicateElementsMap,
elementsWithDuplicates,
origIdToDuplicateId,
};
};

View file

@ -41,30 +41,28 @@ import type {
// --------------------------- Frame State ------------------------------------
export const bindElementsToFramesAfterDuplication = (
nextElements: readonly ExcalidrawElement[],
oldElements: readonly ExcalidrawElement[],
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
origElements: readonly ExcalidrawElement[],
origIdToDuplicateId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
) => {
const nextElementMap = arrayToMap(nextElements) as Map<
ExcalidrawElement["id"],
ExcalidrawElement
>;
for (const element of oldElements) {
for (const element of origElements) {
if (element.frameId) {
// use its frameId to get the new frameId
const nextElementId = oldIdToDuplicatedId.get(element.id);
const nextFrameId = oldIdToDuplicatedId.get(element.frameId);
if (nextElementId) {
const nextElement = nextElementMap.get(nextElementId);
if (nextElement) {
mutateElement(
nextElement,
{
frameId: nextFrameId ?? element.frameId,
},
false,
);
}
const nextElementId = origIdToDuplicateId.get(element.id);
const nextFrameId = origIdToDuplicateId.get(element.frameId);
const nextElement = nextElementId && nextElementMap.get(nextElementId);
if (nextElement) {
mutateElement(
nextElement,
{
frameId: nextFrameId ?? null,
},
false,
);
}
}
}

View file

@ -67,7 +67,7 @@ describe("duplicating single elements", () => {
points: [pointFrom<LocalPoint>(1, 2), pointFrom<LocalPoint>(3, 4)],
});
const copy = duplicateElement(null, new Map(), element, undefined, true);
const copy = duplicateElement(null, new Map(), element, true);
assertCloneObjects(element, copy);
@ -173,7 +173,7 @@ describe("duplicating multiple elements", () => {
// -------------------------------------------------------------------------
const origElements = [rectangle1, text1, arrow1, arrow2, text2] as const;
const { newElements: clonedElements } = duplicateElements({
const { duplicatedElements } = duplicateElements({
type: "everything",
elements: origElements,
});
@ -181,10 +181,10 @@ describe("duplicating multiple elements", () => {
// generic id in-equality checks
// --------------------------------------------------------------------------
expect(origElements.map((e) => e.type)).toEqual(
clonedElements.map((e) => e.type),
duplicatedElements.map((e) => e.type),
);
origElements.forEach((origElement, idx) => {
const clonedElement = clonedElements[idx];
const clonedElement = duplicatedElements[idx];
expect(origElement).toEqual(
expect.objectContaining({
id: expect.not.stringMatching(clonedElement.id),
@ -217,12 +217,12 @@ describe("duplicating multiple elements", () => {
});
// --------------------------------------------------------------------------
const clonedArrows = clonedElements.filter(
const clonedArrows = duplicatedElements.filter(
(e) => e.type === "arrow",
) as ExcalidrawLinearElement[];
const [clonedRectangle, clonedText1, , clonedArrow2, clonedArrowLabel] =
clonedElements as any as typeof origElements;
duplicatedElements as any as typeof origElements;
expect(clonedText1.containerId).toBe(clonedRectangle.id);
expect(
@ -327,10 +327,10 @@ describe("duplicating multiple elements", () => {
// -------------------------------------------------------------------------
const origElements = [rectangle1, text1, arrow1, arrow2, arrow3] as const;
const { newElements: clonedElements } = duplicateElements({
const duplicatedElements = duplicateElements({
type: "everything",
elements: origElements,
}) as any as { newElements: typeof origElements };
}).duplicatedElements as any as typeof origElements;
const [
clonedRectangle,
@ -338,7 +338,7 @@ describe("duplicating multiple elements", () => {
clonedArrow1,
clonedArrow2,
clonedArrow3,
] = clonedElements;
] = duplicatedElements;
expect(clonedRectangle.boundElements).toEqual([
{ id: clonedArrow1.id, type: "arrow" },
@ -374,12 +374,12 @@ describe("duplicating multiple elements", () => {
});
const origElements = [rectangle1, rectangle2, rectangle3] as const;
const { newElements: clonedElements } = duplicateElements({
const { duplicatedElements } = duplicateElements({
type: "everything",
elements: origElements,
}) as any as { newElements: typeof origElements };
});
const [clonedRectangle1, clonedRectangle2, clonedRectangle3] =
clonedElements;
duplicatedElements;
expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]);
expect(rectangle2.groupIds[0]).not.toBe(clonedRectangle2.groupIds[0]);
@ -399,7 +399,7 @@ describe("duplicating multiple elements", () => {
});
const {
newElements: [clonedRectangle1],
duplicatedElements: [clonedRectangle1],
} = duplicateElements({ type: "everything", elements: [rectangle1] });
expect(typeof clonedRectangle1.groupIds[0]).toBe("string");
@ -503,8 +503,8 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: rectangle1.id },
{ id: rectangle1.id, selected: true },
{ id: rectangle1.id },
{ [ORIG_ID]: rectangle1.id, selected: true },
{ id: rectangle2.id },
{ id: rectangle3.id },
]);
@ -538,8 +538,8 @@ describe("duplication z-order", () => {
assertElements(h.elements, [
{ id: rectangle1.id },
{ id: rectangle2.id },
{ [ORIG_ID]: rectangle3.id },
{ id: rectangle3.id, selected: true },
{ id: rectangle3.id },
{ [ORIG_ID]: rectangle3.id, selected: true },
]);
});
@ -569,8 +569,8 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: rectangle1.id },
{ id: rectangle1.id, selected: true },
{ id: rectangle1.id },
{ [ORIG_ID]: rectangle1.id, selected: true },
{ id: rectangle2.id },
{ id: rectangle3.id },
]);
@ -605,12 +605,12 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: rectangle1.id },
{ [ORIG_ID]: rectangle2.id },
{ [ORIG_ID]: rectangle3.id },
{ id: rectangle1.id, selected: true },
{ id: rectangle2.id, selected: true },
{ id: rectangle3.id, selected: true },
{ id: rectangle1.id },
{ id: rectangle2.id },
{ id: rectangle3.id },
{ [ORIG_ID]: rectangle1.id, selected: true },
{ [ORIG_ID]: rectangle2.id, selected: true },
{ [ORIG_ID]: rectangle3.id, selected: true },
]);
});
@ -625,13 +625,14 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: rectangle.id },
{ id: rectangle.id },
{ id: text.id, containerId: rectangle.id },
{ [ORIG_ID]: rectangle.id, selected: true },
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(rectangle.id)?.id,
selected: true,
},
{ id: rectangle.id, selected: true },
{ id: text.id, containerId: rectangle.id, selected: true },
]);
});
@ -646,13 +647,14 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: rectangle.id },
{ id: rectangle.id },
{ id: text.id, containerId: rectangle.id },
{ [ORIG_ID]: rectangle.id, selected: true },
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(rectangle.id)?.id,
selected: true,
},
{ id: rectangle.id, selected: true },
{ id: text.id, containerId: rectangle.id, selected: true },
]);
});
@ -668,13 +670,14 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: arrow.id },
{ id: arrow.id },
{ id: text.id, containerId: arrow.id },
{ [ORIG_ID]: arrow.id, selected: true },
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(arrow.id)?.id,
selected: true,
},
{ id: arrow.id, selected: true },
{ id: text.id, containerId: arrow.id, selected: true },
]);
});
@ -690,13 +693,14 @@ describe("duplication z-order", () => {
});
assertElements(h.elements, [
{ [ORIG_ID]: arrow.id },
{ id: arrow.id },
{ id: text.id, containerId: arrow.id },
{ [ORIG_ID]: arrow.id, selected: true },
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(arrow.id)?.id,
selected: true,
},
{ id: arrow.id, selected: true },
{ id: text.id, containerId: arrow.id, selected: true },
]);
});