mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Fixing duplicate
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
parent
d05c163aec
commit
35c4c074d7
11 changed files with 184 additions and 246 deletions
|
@ -8440,10 +8440,25 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
idsOfElementsToDuplicate: new Map(
|
idsOfElementsToDuplicate: new Map(
|
||||||
selectedElements.map((el) => [el.id, el]),
|
selectedElements.map((el) => [el.id, el]),
|
||||||
),
|
),
|
||||||
|
overrides: (el) => {
|
||||||
|
const origEl = pointerDownState.originalElements.get(el.id)!;
|
||||||
|
return {
|
||||||
|
x: origEl.x,
|
||||||
|
y: origEl.y,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
clonedElements.forEach((element) => {
|
||||||
|
pointerDownState.originalElements.set(element.id, element);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mappedNewSceneElements = this.props.onDuplicate?.(
|
||||||
|
elementsWithClones,
|
||||||
|
elements,
|
||||||
|
);
|
||||||
|
|
||||||
const nextSceneElements = syncMovedIndices(
|
const nextSceneElements = syncMovedIndices(
|
||||||
elementsWithClones,
|
mappedNewSceneElements || elementsWithClones,
|
||||||
arrayToMap(clonedElements),
|
arrayToMap(clonedElements),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ import { getBoundTextElement, handleBindTextResize } from "./textElement";
|
||||||
import {
|
import {
|
||||||
isArrowElement,
|
isArrowElement,
|
||||||
isBindableElement,
|
isBindableElement,
|
||||||
isBindingElement,
|
|
||||||
isBoundToContainer,
|
isBoundToContainer,
|
||||||
isElbowArrow,
|
isElbowArrow,
|
||||||
isFixedPointBinding,
|
isFixedPointBinding,
|
||||||
|
@ -59,6 +58,10 @@ import {
|
||||||
isTextElement,
|
isTextElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
|
import { updateElbowArrowPoints } from "./elbowArrow";
|
||||||
|
|
||||||
|
import type { Mutable } from "../utility-types";
|
||||||
|
|
||||||
import type { Bounds } from "./bounds";
|
import type { Bounds } from "./bounds";
|
||||||
import type { ElementUpdate } from "./mutateElement";
|
import type { ElementUpdate } from "./mutateElement";
|
||||||
import type {
|
import type {
|
||||||
|
@ -974,7 +977,6 @@ export const bindPointToSnapToElementOutline = (
|
||||||
otherPoint,
|
otherPoint,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FIXED_BINDING_DISTANCE,
|
|
||||||
)[0];
|
)[0];
|
||||||
} else {
|
} else {
|
||||||
intersection = intersectElementWithLineSegment(
|
intersection = intersectElementWithLineSegment(
|
||||||
|
@ -1147,7 +1149,7 @@ export const snapToMid = (
|
||||||
) {
|
) {
|
||||||
// LEFT
|
// LEFT
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(x - 2 * FIXED_BINDING_DISTANCE, center[1]),
|
pointFrom(x - FIXED_BINDING_DISTANCE, center[1]),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
|
@ -1158,7 +1160,7 @@ export const snapToMid = (
|
||||||
) {
|
) {
|
||||||
// TOP
|
// TOP
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(center[0], y - 2 * FIXED_BINDING_DISTANCE),
|
pointFrom(center[0], y - FIXED_BINDING_DISTANCE),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
|
@ -1169,7 +1171,7 @@ export const snapToMid = (
|
||||||
) {
|
) {
|
||||||
// RIGHT
|
// RIGHT
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(x + width + 2 * FIXED_BINDING_DISTANCE, center[1]),
|
pointFrom(x + width + FIXED_BINDING_DISTANCE, center[1]),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
|
@ -1180,7 +1182,7 @@ export const snapToMid = (
|
||||||
) {
|
) {
|
||||||
// DOWN
|
// DOWN
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(center[0], y + height + 2 * FIXED_BINDING_DISTANCE),
|
pointFrom(center[0], y + height + FIXED_BINDING_DISTANCE),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
|
@ -1412,107 +1414,75 @@ const getLinearElementEdgeCoors = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// We need to:
|
|
||||||
// 1: Update elements not selected to point to duplicated elements
|
|
||||||
// 2: Update duplicated elements to point to other duplicated elements
|
|
||||||
export const fixBindingsAfterDuplication = (
|
export const fixBindingsAfterDuplication = (
|
||||||
sceneElements: readonly ExcalidrawElement[],
|
newElements: ExcalidrawElement[],
|
||||||
oldElements: readonly ExcalidrawElement[],
|
|
||||||
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
||||||
// There are three copying mechanisms: Copy-paste, duplication and alt-drag.
|
duplicatedElementsMap: NonDeletedSceneElementsMap,
|
||||||
// Only when alt-dragging the new "duplicates" act as the "old", while
|
) => {
|
||||||
// the "old" elements act as the "new copy" - essentially working reverse
|
for (const element of newElements) {
|
||||||
// to the other two.
|
if ("boundElements" in element && element.boundElements) {
|
||||||
duplicatesServeAsOld?: "duplicatesServeAsOld" | undefined,
|
Object.assign(element, {
|
||||||
): void => {
|
boundElements: element.boundElements.reduce(
|
||||||
// First collect all the binding/bindable elements, so we only update
|
(
|
||||||
// each once, regardless of whether they were duplicated or not.
|
acc: Mutable<NonNullable<ExcalidrawElement["boundElements"]>>,
|
||||||
const allBoundElementIds: Set<ExcalidrawElement["id"]> = new Set();
|
binding,
|
||||||
const allBindableElementIds: Set<ExcalidrawElement["id"]> = new Set();
|
) => {
|
||||||
const shouldReverseRoles = duplicatesServeAsOld === "duplicatesServeAsOld";
|
const newBindingId = oldIdToDuplicatedId.get(binding.id);
|
||||||
const duplicateIdToOldId = new Map(
|
if (newBindingId) {
|
||||||
[...oldIdToDuplicatedId].map(([key, value]) => [value, key]),
|
acc.push({ ...binding, id: newBindingId });
|
||||||
);
|
}
|
||||||
oldElements.forEach((oldElement) => {
|
return acc;
|
||||||
const { boundElements } = oldElement;
|
},
|
||||||
if (boundElements != null && boundElements.length > 0) {
|
[],
|
||||||
boundElements.forEach((boundElement) => {
|
),
|
||||||
if (shouldReverseRoles && !oldIdToDuplicatedId.has(boundElement.id)) {
|
|
||||||
allBoundElementIds.add(boundElement.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
allBindableElementIds.add(oldIdToDuplicatedId.get(oldElement.id)!);
|
|
||||||
}
|
}
|
||||||
if (isBindingElement(oldElement)) {
|
|
||||||
if (oldElement.startBinding != null) {
|
if ("containerId" in element && element.containerId) {
|
||||||
const { elementId } = oldElement.startBinding;
|
Object.assign(element, {
|
||||||
if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
|
containerId: oldIdToDuplicatedId.get(element.containerId) ?? null,
|
||||||
allBindableElementIds.add(elementId);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldElement.endBinding != null) {
|
|
||||||
const { elementId } = oldElement.endBinding;
|
|
||||||
if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
|
|
||||||
allBindableElementIds.add(elementId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldElement.startBinding != null || oldElement.endBinding != null) {
|
|
||||||
allBoundElementIds.add(oldIdToDuplicatedId.get(oldElement.id)!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Update the linear elements
|
if ("endBinding" in element && element.endBinding) {
|
||||||
(
|
const newEndBindingId = oldIdToDuplicatedId.get(
|
||||||
sceneElements.filter(({ id }) =>
|
element.endBinding.elementId,
|
||||||
allBoundElementIds.has(id),
|
);
|
||||||
) as ExcalidrawLinearElement[]
|
Object.assign(element, {
|
||||||
).forEach((element) => {
|
endBinding: newEndBindingId
|
||||||
const { startBinding, endBinding } = element;
|
? {
|
||||||
mutateElement(element, {
|
...element.endBinding,
|
||||||
startBinding: newBindingAfterDuplication(
|
elementId: newEndBindingId,
|
||||||
startBinding,
|
}
|
||||||
oldIdToDuplicatedId,
|
: null,
|
||||||
),
|
});
|
||||||
endBinding: newBindingAfterDuplication(endBinding, oldIdToDuplicatedId),
|
}
|
||||||
});
|
if ("startBinding" in element && element.startBinding) {
|
||||||
});
|
const newEndBindingId = oldIdToDuplicatedId.get(
|
||||||
|
element.startBinding.elementId,
|
||||||
|
);
|
||||||
|
Object.assign(element, {
|
||||||
|
startBinding: newEndBindingId
|
||||||
|
? {
|
||||||
|
...element.startBinding,
|
||||||
|
elementId: newEndBindingId,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Update the bindable shapes
|
if (isElbowArrow(element)) {
|
||||||
sceneElements
|
Object.assign(
|
||||||
.filter(({ id }) => allBindableElementIds.has(id))
|
element,
|
||||||
.forEach((bindableElement) => {
|
updateElbowArrowPoints(element, duplicatedElementsMap, {
|
||||||
const oldElementId = duplicateIdToOldId.get(bindableElement.id);
|
points: [
|
||||||
const boundElements = sceneElements.find(
|
element.points[0],
|
||||||
({ id }) => id === oldElementId,
|
element.points[element.points.length - 1],
|
||||||
)?.boundElements;
|
],
|
||||||
|
}),
|
||||||
if (boundElements && boundElements.length > 0) {
|
);
|
||||||
mutateElement(bindableElement, {
|
}
|
||||||
boundElements: boundElements.map((boundElement) =>
|
|
||||||
oldIdToDuplicatedId.has(boundElement.id)
|
|
||||||
? {
|
|
||||||
id: oldIdToDuplicatedId.get(boundElement.id)!,
|
|
||||||
type: boundElement.type,
|
|
||||||
}
|
|
||||||
: boundElement,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const newBindingAfterDuplication = (
|
|
||||||
binding: PointBinding | null,
|
|
||||||
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
|
||||||
): PointBinding | null => {
|
|
||||||
if (binding == null) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
...binding,
|
|
||||||
elementId: oldIdToDuplicatedId.get(binding.elementId) ?? binding.elementId,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fixBindingsAfterDeletion = (
|
export const fixBindingsAfterDeletion = (
|
||||||
|
|
|
@ -209,6 +209,7 @@ describe("duplicating multiple elements", () => {
|
||||||
type: clonedText1.type,
|
type: clonedText1.type,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
expect(clonedRectangle.type).toBe("rectangle");
|
||||||
|
|
||||||
clonedArrows.forEach((arrow) => {
|
clonedArrows.forEach((arrow) => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -302,9 +303,9 @@ describe("duplicating multiple elements", () => {
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
const origElements = [rectangle1, text1, arrow1, arrow2, arrow3] as const;
|
const origElements = [rectangle1, text1, arrow1, arrow2, arrow3] as const;
|
||||||
const clonedElements = duplicateElements(
|
const { newElements: clonedElements } = duplicateElements(
|
||||||
origElements,
|
origElements,
|
||||||
) as any as typeof origElements;
|
) as any as { newElements: typeof origElements };
|
||||||
const [
|
const [
|
||||||
clonedRectangle,
|
clonedRectangle,
|
||||||
clonedText1,
|
clonedText1,
|
||||||
|
@ -324,7 +325,7 @@ describe("duplicating multiple elements", () => {
|
||||||
elementId: clonedRectangle.id,
|
elementId: clonedRectangle.id,
|
||||||
});
|
});
|
||||||
expect(clonedArrow2.endBinding).toBe(null);
|
expect(clonedArrow2.endBinding).toBe(null);
|
||||||
|
console.log(clonedArrow3);
|
||||||
expect(clonedArrow3.startBinding).toBe(null);
|
expect(clonedArrow3.startBinding).toBe(null);
|
||||||
expect(clonedArrow3.endBinding).toEqual({
|
expect(clonedArrow3.endBinding).toEqual({
|
||||||
...arrow3.endBinding,
|
...arrow3.endBinding,
|
||||||
|
@ -348,9 +349,9 @@ describe("duplicating multiple elements", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const origElements = [rectangle1, rectangle2, rectangle3] as const;
|
const origElements = [rectangle1, rectangle2, rectangle3] as const;
|
||||||
const clonedElements = duplicateElements(
|
const { newElements: clonedElements } = duplicateElements(
|
||||||
origElements,
|
origElements,
|
||||||
) as any as typeof origElements;
|
) as any as { newElements: typeof origElements };
|
||||||
const [clonedRectangle1, clonedRectangle2, clonedRectangle3] =
|
const [clonedRectangle1, clonedRectangle2, clonedRectangle3] =
|
||||||
clonedElements;
|
clonedElements;
|
||||||
|
|
||||||
|
|
|
@ -28,17 +28,12 @@ import { bumpVersion } from "./mutateElement";
|
||||||
import {
|
import {
|
||||||
hasBoundTextElement,
|
hasBoundTextElement,
|
||||||
isBoundToContainer,
|
isBoundToContainer,
|
||||||
isElbowArrow,
|
|
||||||
isFrameLikeElement,
|
isFrameLikeElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
import {
|
import { getBoundTextElement, getContainerElement } from "./textElement";
|
||||||
bindTextToShapeAfterDuplication,
|
|
||||||
getBoundTextElement,
|
|
||||||
getContainerElement,
|
|
||||||
} from "./textElement";
|
|
||||||
|
|
||||||
import { updateElbowArrowPoints } from "./elbowArrow";
|
import { fixBindingsAfterDuplication } from "./binding";
|
||||||
|
|
||||||
import type { AppState } from "../types";
|
import type { AppState } from "../types";
|
||||||
import type { Mutable } from "../utility-types";
|
import type { Mutable } from "../utility-types";
|
||||||
|
@ -315,83 +310,12 @@ export const duplicateElements = (
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const fixBindingsAfterDuplication = (
|
|
||||||
newElements: ExcalidrawElement[],
|
|
||||||
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
|
||||||
duplicatedElementsMap: NonDeletedSceneElementsMap,
|
|
||||||
) => {
|
|
||||||
for (const element of newElements) {
|
|
||||||
if ("boundElements" in element && element.boundElements) {
|
|
||||||
Object.assign(element, {
|
|
||||||
boundElements: element.boundElements.reduce(
|
|
||||||
(
|
|
||||||
acc: Mutable<NonNullable<ExcalidrawElement["boundElements"]>>,
|
|
||||||
binding,
|
|
||||||
) => {
|
|
||||||
const newBindingId = oldIdToDuplicatedId.get(binding.id);
|
|
||||||
if (newBindingId) {
|
|
||||||
acc.push({ ...binding, id: newBindingId });
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("endBinding" in element && element.endBinding) {
|
|
||||||
const newEndBindingId = oldIdToDuplicatedId.get(
|
|
||||||
element.endBinding.elementId,
|
|
||||||
);
|
|
||||||
Object.assign(element, {
|
|
||||||
endBinding: newEndBindingId
|
|
||||||
? {
|
|
||||||
...element.endBinding,
|
|
||||||
elementId: newEndBindingId,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ("startBinding" in element && element.startBinding) {
|
|
||||||
const newEndBindingId = oldIdToDuplicatedId.get(
|
|
||||||
element.startBinding.elementId,
|
|
||||||
);
|
|
||||||
Object.assign(element, {
|
|
||||||
startBinding: newEndBindingId
|
|
||||||
? {
|
|
||||||
...element.startBinding,
|
|
||||||
elementId: newEndBindingId,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isElbowArrow(element)) {
|
|
||||||
Object.assign(
|
|
||||||
element,
|
|
||||||
updateElbowArrowPoints(element, duplicatedElementsMap, {
|
|
||||||
points: [
|
|
||||||
element.points[0],
|
|
||||||
element.points[element.points.length - 1],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fixBindingsAfterDuplication(
|
fixBindingsAfterDuplication(
|
||||||
newElements,
|
newElements,
|
||||||
oldIdToDuplicatedId,
|
oldIdToDuplicatedId,
|
||||||
duplicatedElementsMap as NonDeletedSceneElementsMap,
|
duplicatedElementsMap as NonDeletedSceneElementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
bindTextToShapeAfterDuplication(
|
|
||||||
elementsWithClones,
|
|
||||||
oldElements,
|
|
||||||
oldIdToDuplicatedId,
|
|
||||||
);
|
|
||||||
|
|
||||||
bindElementsToFramesAfterDuplication(
|
bindElementsToFramesAfterDuplication(
|
||||||
elementsWithClones,
|
elementsWithClones,
|
||||||
oldElements,
|
oldElements,
|
||||||
|
|
|
@ -358,7 +358,7 @@ describe("elbow arrow ui", () => {
|
||||||
expect(arrow.endBinding).not.toBe(null);
|
expect(arrow.endBinding).not.toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps arrow shape when only the bound arrow is duplicated", async () => {
|
it("changes arrow shape to unbind variant if only the connected elbow arrow is duplicated", async () => {
|
||||||
UI.createElement("rectangle", {
|
UI.createElement("rectangle", {
|
||||||
x: -150,
|
x: -150,
|
||||||
y: -150,
|
y: -150,
|
||||||
|
@ -404,8 +404,8 @@ describe("elbow arrow ui", () => {
|
||||||
expect(duplicatedArrow.elbowed).toBe(true);
|
expect(duplicatedArrow.elbowed).toBe(true);
|
||||||
expect(duplicatedArrow.points).toEqual([
|
expect(duplicatedArrow.points).toEqual([
|
||||||
[0, 0],
|
[0, 0],
|
||||||
[45, 0],
|
[0, 100],
|
||||||
[45, 200],
|
[90, 100],
|
||||||
[90, 200],
|
[90, 200],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
TEXT_ALIGN,
|
TEXT_ALIGN,
|
||||||
VERTICAL_ALIGN,
|
VERTICAL_ALIGN,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { getFontString, arrayToMap } from "../utils";
|
import { getFontString } from "../utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resetOriginalContainerCache,
|
resetOriginalContainerCache,
|
||||||
|
@ -112,48 +112,6 @@ export const redrawTextBoundingBox = (
|
||||||
mutateElement(textElement, boundTextUpdates, informMutation);
|
mutateElement(textElement, boundTextUpdates, informMutation);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const bindTextToShapeAfterDuplication = (
|
|
||||||
newElements: ExcalidrawElement[],
|
|
||||||
oldElements: ExcalidrawElement[],
|
|
||||||
oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>,
|
|
||||||
): void => {
|
|
||||||
const newElementsMap = arrayToMap(newElements) as Map<
|
|
||||||
ExcalidrawElement["id"],
|
|
||||||
ExcalidrawElement
|
|
||||||
>;
|
|
||||||
oldElements.forEach((element) => {
|
|
||||||
const newElementId = oldIdToDuplicatedId.get(element.id) as string;
|
|
||||||
const boundTextElementId = getBoundTextElementId(element);
|
|
||||||
|
|
||||||
if (boundTextElementId) {
|
|
||||||
const newTextElementId = oldIdToDuplicatedId.get(boundTextElementId);
|
|
||||||
if (newTextElementId) {
|
|
||||||
const newContainer = newElementsMap.get(newElementId);
|
|
||||||
if (newContainer) {
|
|
||||||
mutateElement(newContainer, {
|
|
||||||
boundElements: (element.boundElements || [])
|
|
||||||
.filter(
|
|
||||||
(boundElement) =>
|
|
||||||
boundElement.id !== newTextElementId &&
|
|
||||||
boundElement.id !== boundTextElementId,
|
|
||||||
)
|
|
||||||
.concat({
|
|
||||||
type: "text",
|
|
||||||
id: newTextElementId,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const newTextElement = newElementsMap.get(newTextElementId);
|
|
||||||
if (newTextElement && isTextElement(newTextElement)) {
|
|
||||||
mutateElement(newTextElement, {
|
|
||||||
containerId: newContainer ? newElementId : null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleBindTextResize = (
|
export const handleBindTextResize = (
|
||||||
container: NonDeletedExcalidrawElement,
|
container: NonDeletedExcalidrawElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
|
|
|
@ -924,11 +924,12 @@ History {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": null,
|
"startBinding": null,
|
||||||
|
"y": 0,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id166",
|
"elementId": "id166",
|
||||||
"focus": -0,
|
"focus": "0.00000",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"points": [
|
"points": [
|
||||||
|
@ -943,9 +944,10 @@ History {
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id165",
|
"elementId": "id165",
|
||||||
"focus": 0,
|
"focus": "-0.00000",
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
|
"y": "0.00000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,73 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`duplicate element on move when ALT is clicked > rectangle 5`] = `
|
||||||
|
{
|
||||||
|
"angle": 0,
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"boundElements": null,
|
||||||
|
"customData": undefined,
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"frameId": null,
|
||||||
|
"groupIds": [],
|
||||||
|
"height": 50,
|
||||||
|
"id": "id0",
|
||||||
|
"index": "a0",
|
||||||
|
"isDeleted": false,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"opacity": 100,
|
||||||
|
"roughness": 1,
|
||||||
|
"roundness": {
|
||||||
|
"type": 3,
|
||||||
|
},
|
||||||
|
"seed": 1278240551,
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"type": "rectangle",
|
||||||
|
"updated": 1,
|
||||||
|
"version": 5,
|
||||||
|
"versionNonce": 23633383,
|
||||||
|
"width": 30,
|
||||||
|
"x": -10,
|
||||||
|
"y": 60,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`duplicate element on move when ALT is clicked > rectangle 6`] = `
|
||||||
|
{
|
||||||
|
"angle": 0,
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"boundElements": null,
|
||||||
|
"customData": undefined,
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"frameId": null,
|
||||||
|
"groupIds": [],
|
||||||
|
"height": 50,
|
||||||
|
"id": "id2",
|
||||||
|
"index": "a1",
|
||||||
|
"isDeleted": false,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"opacity": 100,
|
||||||
|
"roughness": 1,
|
||||||
|
"roundness": {
|
||||||
|
"type": 3,
|
||||||
|
},
|
||||||
|
"seed": 238820263,
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"type": "rectangle",
|
||||||
|
"updated": 1,
|
||||||
|
"version": 6,
|
||||||
|
"versionNonce": 1604849351,
|
||||||
|
"width": 30,
|
||||||
|
"x": 30,
|
||||||
|
"y": 20,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`move element > rectangle 5`] = `
|
exports[`move element > rectangle 5`] = `
|
||||||
{
|
{
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
|
|
|
@ -2153,8 +2153,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 20,
|
"x": 10,
|
||||||
"y": 20,
|
"y": 10,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10643,8 +10643,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 20,
|
"x": 10,
|
||||||
"y": 20,
|
"y": 10,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10676,8 +10676,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 40,
|
"x": 30,
|
||||||
"y": 20,
|
"y": 10,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10709,8 +10709,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 60,
|
"x": 50,
|
||||||
"y": 20,
|
"y": 10,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
|
|
@ -174,9 +174,9 @@ describe("duplicate element on move when ALT is clicked", () => {
|
||||||
expect(h.state.selectionElement).toBeNull();
|
expect(h.state.selectionElement).toBeNull();
|
||||||
expect(h.elements.length).toEqual(2);
|
expect(h.elements.length).toEqual(2);
|
||||||
|
|
||||||
// previous element should stay intact
|
// behavior should be the same as Ctrl+D
|
||||||
expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
|
expect([h.elements[0].x, h.elements[0].y]).toEqual([-10, 60]);
|
||||||
expect([h.elements[1].x, h.elements[1].y]).toEqual([-10, 60]);
|
expect([h.elements[1].x, h.elements[1].y]).toEqual([30, 20]);
|
||||||
|
|
||||||
h.elements.forEach((element) => expect(element).toMatchSnapshot());
|
h.elements.forEach((element) => expect(element).toMatchSnapshot());
|
||||||
});
|
});
|
||||||
|
|
|
@ -505,12 +505,12 @@ describe("arrow element", () => {
|
||||||
h.state,
|
h.state,
|
||||||
)[0] as ExcalidrawElbowArrowElement;
|
)[0] as ExcalidrawElbowArrowElement;
|
||||||
|
|
||||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1, 0);
|
||||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||||
|
|
||||||
UI.resize(rectangle, "se", [-200, -150]);
|
UI.resize(rectangle, "se", [-200, -150]);
|
||||||
|
|
||||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1, 0);
|
||||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -533,11 +533,11 @@ describe("arrow element", () => {
|
||||||
h.state,
|
h.state,
|
||||||
)[0] as ExcalidrawElbowArrowElement;
|
)[0] as ExcalidrawElbowArrowElement;
|
||||||
|
|
||||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1, 0);
|
||||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||||
|
|
||||||
UI.resize([rectangle, arrow], "nw", [300, 350]);
|
UI.resize([rectangle, arrow], "nw", [300, 350]);
|
||||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(0);
|
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(0, 0);
|
||||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.25);
|
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.25);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue