|
@@ -55,6 +55,7 @@ import { getBoundTextElement, handleBindTextResize } from "./textElement";
|
|
import {
|
|
import {
|
|
isArrowElement,
|
|
isArrowElement,
|
|
isBindableElement,
|
|
isBindableElement,
|
|
|
|
+ isBindingElement,
|
|
isBoundToContainer,
|
|
isBoundToContainer,
|
|
isElbowArrow,
|
|
isElbowArrow,
|
|
isFixedPointBinding,
|
|
isFixedPointBinding,
|
|
@@ -1422,7 +1423,7 @@ const getLinearElementEdgeCoors = (
|
|
);
|
|
);
|
|
};
|
|
};
|
|
|
|
|
|
-export const fixBindingsAfterDuplication = (
|
|
|
|
|
|
+export const fixDuplicatedBindingsAfterDuplication = (
|
|
newElements: ExcalidrawElement[],
|
|
newElements: ExcalidrawElement[],
|
|
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
|
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
|
duplicatedElementsMap: NonDeletedSceneElementsMap,
|
|
duplicatedElementsMap: NonDeletedSceneElementsMap,
|
|
@@ -1493,6 +1494,196 @@ export const fixBindingsAfterDuplication = (
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+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 = (
|
|
export const fixBindingsAfterDeletion = (
|
|
sceneElements: readonly ExcalidrawElement[],
|
|
sceneElements: readonly ExcalidrawElement[],
|
|
deletedElements: readonly ExcalidrawElement[],
|
|
deletedElements: readonly ExcalidrawElement[],
|