Action duplication binding fix 1st iter

This commit is contained in:
Mark Tolmacs 2025-03-11 11:20:12 +01:00
parent 99d8bff175
commit 9cc4ea6ba6
2 changed files with 107 additions and 8 deletions

View file

@ -2,7 +2,6 @@ import { ToolButton } from "../components/ToolButton";
import { DuplicateIcon } from "../components/icons"; import { DuplicateIcon } from "../components/icons";
import { DEFAULT_GRID_SIZE } from "../constants"; import { DEFAULT_GRID_SIZE } from "../constants";
import { duplicateElement, getNonDeletedElements } from "../element"; import { duplicateElement, getNonDeletedElements } from "../element";
import { fixBindingsAfterDuplication } from "../element/binding";
import { import {
bindTextToShapeAfterDuplication, bindTextToShapeAfterDuplication,
getBoundTextElement, getBoundTextElement,
@ -10,6 +9,8 @@ import {
} from "../element/textElement"; } from "../element/textElement";
import { import {
hasBoundTextElement, hasBoundTextElement,
isArrowElement,
isBindableElement,
isBoundToContainer, isBoundToContainer,
isFrameLikeElement, isFrameLikeElement,
} from "../element/typeChecks"; } from "../element/typeChecks";
@ -40,11 +41,18 @@ import {
invariant, invariant,
} from "../utils"; } from "../utils";
import { type ElementUpdate, mutateElement } from "../element/mutateElement";
import { register } from "./register"; import { register } from "./register";
import type { ActionResult } from "./types"; import type { ActionResult } from "./types";
import type { ExcalidrawElement } from "../element/types"; import type {
BoundElement,
ExcalidrawArrowElement,
ExcalidrawElement,
} from "../element/types";
import type { AppState } from "../types"; import type { AppState } from "../types";
import type { Mutable } from "../utility-types";
export const actionDuplicateSelection = register({ export const actionDuplicateSelection = register({
name: "duplicateSelection", name: "duplicateSelection",
@ -325,11 +333,103 @@ const duplicateElements = (
oldElements, oldElements,
oldIdToDuplicatedId, oldIdToDuplicatedId,
); );
fixBindingsAfterDuplication( // fixBindingsAfterDuplication(
elementsWithClones, // elementsWithClones,
oldElements, // oldElements,
oldIdToDuplicatedId, // oldIdToDuplicatedId,
// );
newElements
.map((element) => {
oldElements.includes(element) && console.error("oldElements", element);
if (isArrowElement(element)) {
const updates: Mutable<ElementUpdate<ExcalidrawArrowElement>> = {};
if (element.startBinding) {
const startCloneElementId = oldIdToDuplicatedId.get(
element.startBinding.elementId,
); );
if (startCloneElementId) {
// The connected element was duplicated, so we need to update the binding
updates.startBinding = {
...element.startBinding,
elementId: startCloneElementId,
};
} else {
// The connected element was not duplicated, so we need to remove the binding
updates.startBinding = null;
}
}
if (element.endBinding) {
const endCloneElementId = oldIdToDuplicatedId.get(
element.endBinding.elementId,
);
if (endCloneElementId) {
// The connected element was duplicated, so we need to update the binding
updates.endBinding = {
...element.endBinding,
elementId: endCloneElementId,
};
} else {
// The connected element was not duplicated, so we need to remove the binding
updates.endBinding = null;
}
}
if (Object.keys(updates).length > 0) {
// Only update the element if there are updates to apply
return {
element,
updates,
};
}
} else if (isBindableElement(element)) {
if (element.boundElements?.length) {
const clonedBoundElements = element.boundElements
?.map((definition) => {
const clonedBoundElementId = oldIdToDuplicatedId.get(
definition.id,
);
if (clonedBoundElementId) {
// The connected element was duplicated, so we need to update the binding
return {
...definition,
id: clonedBoundElementId,
};
}
// The connected element was not duplicated, so we need to remove the binding
return null;
})
.filter(
(definition): definition is BoundElement => definition !== null,
);
if (clonedBoundElements?.length) {
return {
element,
updates: {
boundElements: clonedBoundElements,
},
};
}
}
}
return null;
})
.forEach((change) => {
if (!change) {
return;
}
mutateElement(change.element, change.updates);
});
bindElementsToFramesAfterDuplication( bindElementsToFramesAfterDuplication(
elementsWithClones, elementsWithClones,
oldElements, oldElements,

View file

@ -658,7 +658,6 @@ export const duplicateElement = <TElement extends ExcalidrawElement>(
} }
copy.id = regenerateId(); copy.id = regenerateId();
copy.boundElements = null;
copy.updated = getUpdatedTimestamp(); copy.updated = getUpdatedTimestamp();
copy.seed = randomInteger(); copy.seed = randomInteger();
copy.groupIds = getNewGroupIdsForDuplication( copy.groupIds = getNewGroupIdsForDuplication(