From 424e94a403b0c819c3624afb8aad4ae18e32e1dc Mon Sep 17 00:00:00 2001
From: David Luzar <5153846+dwelle@users.noreply.github.com>
Date: Tue, 4 Feb 2025 19:23:47 +0100
Subject: [PATCH] fix: duplicating/removing frame while children selected
(#9079)
---
.../actions/actionDeleteSelected.test.tsx | 211 +
.../actions/actionDeleteSelected.tsx | 50 +-
.../actions/actionDuplicateSelection.test.tsx | 530 +++
.../actions/actionDuplicateSelection.tsx | 291 +-
packages/excalidraw/constants.ts | 3 +
packages/excalidraw/element/newElement.ts | 44 +-
packages/excalidraw/element/sortElements.ts | 5 +-
packages/excalidraw/frame.test.tsx | 11 +-
packages/excalidraw/package.json | 1 +
.../__snapshots__/contextmenu.test.tsx.snap | 8 +-
.../tests/__snapshots__/history.test.tsx.snap | 3654 ++++++++---------
.../tests/__snapshots__/move.test.tsx.snap | 2 +-
.../regressionTests.test.tsx.snap | 14 +-
packages/excalidraw/tests/helpers/api.ts | 99 +-
packages/excalidraw/tests/history.test.tsx | 19 +-
packages/excalidraw/tests/library.test.tsx | 51 +-
packages/excalidraw/tests/test-utils.ts | 151 +
packages/excalidraw/tests/zindex.test.tsx | 70 +-
packages/excalidraw/utility-types.ts | 3 +
packages/excalidraw/utils.ts | 3 +
yarn.lock | 5 +
21 files changed, 3160 insertions(+), 2065 deletions(-)
create mode 100644 packages/excalidraw/actions/actionDeleteSelected.test.tsx
create mode 100644 packages/excalidraw/actions/actionDuplicateSelection.test.tsx
diff --git a/packages/excalidraw/actions/actionDeleteSelected.test.tsx b/packages/excalidraw/actions/actionDeleteSelected.test.tsx
new file mode 100644
index 0000000000..d48646fac7
--- /dev/null
+++ b/packages/excalidraw/actions/actionDeleteSelected.test.tsx
@@ -0,0 +1,211 @@
+import React from "react";
+import { Excalidraw, mutateElement } from "../index";
+import { act, assertElements, render } from "../tests/test-utils";
+import { API } from "../tests/helpers/api";
+import { actionDeleteSelected } from "./actionDeleteSelected";
+
+const { h } = window;
+
+describe("deleting selected elements when frame selected should keep children + select them", () => {
+ beforeEach(async () => {
+ await render();
+ });
+
+ it("frame only", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+
+ const r1 = API.createElement({
+ type: "rectangle",
+ frameId: f1.id,
+ });
+
+ API.setElements([f1, r1]);
+
+ API.setSelectedElements([f1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: r1.id, isDeleted: false, selected: true },
+ ]);
+ });
+
+ it("frame + text container (text's frameId set)", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+
+ const r1 = API.createElement({
+ type: "rectangle",
+ frameId: f1.id,
+ });
+
+ const t1 = API.createElement({
+ type: "text",
+ width: 200,
+ height: 100,
+ fontSize: 20,
+ containerId: r1.id,
+ frameId: f1.id,
+ });
+
+ mutateElement(r1, {
+ boundElements: [{ type: "text", id: t1.id }],
+ });
+
+ API.setElements([f1, r1, t1]);
+
+ API.setSelectedElements([f1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: r1.id, isDeleted: false, selected: true },
+ { id: t1.id, isDeleted: false },
+ ]);
+ });
+
+ it("frame + text container (text's frameId not set)", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+
+ const r1 = API.createElement({
+ type: "rectangle",
+ frameId: f1.id,
+ });
+
+ const t1 = API.createElement({
+ type: "text",
+ width: 200,
+ height: 100,
+ fontSize: 20,
+ containerId: r1.id,
+ frameId: null,
+ });
+
+ mutateElement(r1, {
+ boundElements: [{ type: "text", id: t1.id }],
+ });
+
+ API.setElements([f1, r1, t1]);
+
+ API.setSelectedElements([f1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: r1.id, isDeleted: false, selected: true },
+ { id: t1.id, isDeleted: false },
+ ]);
+ });
+
+ it("frame + text container (text selected too)", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+
+ const r1 = API.createElement({
+ type: "rectangle",
+ frameId: f1.id,
+ });
+
+ const t1 = API.createElement({
+ type: "text",
+ width: 200,
+ height: 100,
+ fontSize: 20,
+ containerId: r1.id,
+ frameId: null,
+ });
+
+ mutateElement(r1, {
+ boundElements: [{ type: "text", id: t1.id }],
+ });
+
+ API.setElements([f1, r1, t1]);
+
+ API.setSelectedElements([f1, t1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: r1.id, isDeleted: false, selected: true },
+ { id: t1.id, isDeleted: false },
+ ]);
+ });
+
+ it("frame + labeled arrow", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+
+ const a1 = API.createElement({
+ type: "arrow",
+ frameId: f1.id,
+ });
+
+ const t1 = API.createElement({
+ type: "text",
+ width: 200,
+ height: 100,
+ fontSize: 20,
+ containerId: a1.id,
+ frameId: null,
+ });
+
+ mutateElement(a1, {
+ boundElements: [{ type: "text", id: t1.id }],
+ });
+
+ API.setElements([f1, a1, t1]);
+
+ API.setSelectedElements([f1, t1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: a1.id, isDeleted: false, selected: true },
+ { id: t1.id, isDeleted: false },
+ ]);
+ });
+
+ it("frame + children selected", async () => {
+ const f1 = API.createElement({
+ type: "frame",
+ });
+ const r1 = API.createElement({
+ type: "rectangle",
+ frameId: f1.id,
+ });
+ API.setElements([f1, r1]);
+
+ API.setSelectedElements([f1, r1]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDeleteSelected);
+ });
+
+ assertElements(h.elements, [
+ { id: f1.id, isDeleted: true },
+ { id: r1.id, isDeleted: false, selected: true },
+ ]);
+ });
+});
diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx
index 882c8b3f33..c66b2c0248 100644
--- a/packages/excalidraw/actions/actionDeleteSelected.tsx
+++ b/packages/excalidraw/actions/actionDeleteSelected.tsx
@@ -18,6 +18,8 @@ import {
import { updateActiveTool } from "../utils";
import { TrashIcon } from "../components/icons";
import { StoreAction } from "../store";
+import { getContainerElement } from "../element/textElement";
+import { getFrameChildren } from "../frame";
const deleteSelectedElements = (
elements: readonly ExcalidrawElement[],
@@ -33,10 +35,50 @@ const deleteSelectedElements = (
const selectedElementIds: Record = {};
+ const elementsMap = app.scene.getNonDeletedElementsMap();
+
+ const processedElements = new Set();
+
+ for (const frameId of framesToBeDeleted) {
+ const frameChildren = getFrameChildren(elements, frameId);
+ for (const el of frameChildren) {
+ if (processedElements.has(el.id)) {
+ continue;
+ }
+
+ if (isBoundToContainer(el)) {
+ const containerElement = getContainerElement(el, elementsMap);
+ if (containerElement) {
+ selectedElementIds[containerElement.id] = true;
+ }
+ } else {
+ selectedElementIds[el.id] = true;
+ }
+ processedElements.add(el.id);
+ }
+ }
+
let shouldSelectEditingGroup = true;
const nextElements = elements.map((el) => {
if (appState.selectedElementIds[el.id]) {
+ const boundElement = isBoundToContainer(el)
+ ? getContainerElement(el, elementsMap)
+ : null;
+
+ if (el.frameId && framesToBeDeleted.has(el.frameId)) {
+ shouldSelectEditingGroup = false;
+ selectedElementIds[el.id] = true;
+ return el;
+ }
+
+ if (
+ boundElement?.frameId &&
+ framesToBeDeleted.has(boundElement?.frameId)
+ ) {
+ return el;
+ }
+
if (el.boundElements) {
el.boundElements.forEach((candidate) => {
const bound = app.scene.getNonDeletedElementsMap().get(candidate.id);
@@ -59,7 +101,9 @@ const deleteSelectedElements = (
// if deleting a frame, remove the children from it and select them
if (el.frameId && framesToBeDeleted.has(el.frameId)) {
shouldSelectEditingGroup = false;
- selectedElementIds[el.id] = true;
+ if (!isBoundToContainer(el)) {
+ selectedElementIds[el.id] = true;
+ }
return newElementWith(el, { frameId: null });
}
@@ -224,11 +268,13 @@ export const actionDeleteSelected = register({
storeAction: StoreAction.CAPTURE,
};
}
+
let { elements: nextElements, appState: nextAppState } =
deleteSelectedElements(elements, appState, app);
+
fixBindingsAfterDeletion(
nextElements,
- elements.filter(({ id }) => appState.selectedElementIds[id]),
+ nextElements.filter((el) => el.isDeleted),
);
nextAppState = handleGroupEditingState(nextAppState, nextElements);
diff --git a/packages/excalidraw/actions/actionDuplicateSelection.test.tsx b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx
new file mode 100644
index 0000000000..dc471df371
--- /dev/null
+++ b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx
@@ -0,0 +1,530 @@
+import { Excalidraw } from "../index";
+import {
+ act,
+ assertElements,
+ getCloneByOrigId,
+ render,
+} from "../tests/test-utils";
+import { API } from "../tests/helpers/api";
+import { actionDuplicateSelection } from "./actionDuplicateSelection";
+import React from "react";
+import { ORIG_ID } from "../constants";
+
+const { h } = window;
+
+describe("actionDuplicateSelection", () => {
+ beforeEach(async () => {
+ await render();
+ });
+
+ describe("duplicating frames", () => {
+ it("frame selected only", async () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const rectangle = API.createElement({
+ type: "rectangle",
+ frameId: frame.id,
+ });
+
+ API.setElements([frame, rectangle]);
+ API.setSelectedElements([frame]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: getCloneByOrigId(frame.id)?.id },
+ { [ORIG_ID]: frame.id, selected: true },
+ ]);
+ });
+
+ it("frame selected only (with text container)", async () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, rectangle, text]);
+ API.setSelectedElements([frame]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: getCloneByOrigId(frame.id)?.id },
+ {
+ [ORIG_ID]: text.id,
+ containerId: getCloneByOrigId(rectangle.id)?.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ },
+ { [ORIG_ID]: frame.id, selected: true },
+ ]);
+ });
+
+ it("frame + text container selected (order A)", async () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, rectangle, text]);
+ API.setSelectedElements([frame, rectangle]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id, frameId: frame.id },
+ {
+ [ORIG_ID]: rectangle.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ },
+ {
+ [ORIG_ID]: text.id,
+ containerId: getCloneByOrigId(rectangle.id)?.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ },
+ {
+ [ORIG_ID]: frame.id,
+ selected: true,
+ },
+ ]);
+ });
+
+ it("frame + text container selected (order B)", async () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([text, rectangle, frame]);
+ API.setSelectedElements([rectangle, frame]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id, frameId: frame.id },
+ { id: frame.id },
+ {
+ type: "rectangle",
+ [ORIG_ID]: `${rectangle.id}`,
+ },
+ {
+ [ORIG_ID]: `${text.id}`,
+ type: "text",
+ containerId: getCloneByOrigId(rectangle.id)?.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ },
+ { [ORIG_ID]: `${frame.id}`, type: "frame", selected: true },
+ ]);
+ });
+ });
+
+ describe("duplicating frame children", () => {
+ it("frame child selected", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const rectangle = API.createElement({
+ type: "rectangle",
+ frameId: frame.id,
+ });
+
+ API.setElements([frame, rectangle]);
+ API.setSelectedElements([rectangle]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: frame.id, selected: true },
+ ]);
+ });
+
+ it("frame text container selected (rectangle selected)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, rectangle, text]);
+ API.setSelectedElements([rectangle]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: frame.id, selected: true },
+ {
+ [ORIG_ID]: text.id,
+ containerId: getCloneByOrigId(rectangle.id).id,
+ frameId: frame.id,
+ },
+ ]);
+ });
+
+ it("frame bound text selected (container not selected)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, rectangle, text]);
+ API.setSelectedElements([text]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: frame.id, selected: true },
+ {
+ [ORIG_ID]: text.id,
+ containerId: getCloneByOrigId(rectangle.id).id,
+ frameId: frame.id,
+ },
+ ]);
+ });
+
+ it("frame text container selected (text not exists)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, rectangle]);
+ API.setSelectedElements([rectangle]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { [ORIG_ID]: rectangle.id, frameId: frame.id, selected: true },
+ ]);
+ });
+
+ // shouldn't happen
+ it("frame bound text selected (container not exists)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [, text] = API.createTextContainer({ frameId: frame.id });
+
+ API.setElements([frame, text]);
+ API.setSelectedElements([text]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: text.id, frameId: frame.id },
+ { [ORIG_ID]: text.id, frameId: frame.id },
+ ]);
+ });
+
+ it("frame bound container selected (text has no frameId)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ });
+
+ const [rectangle, text] = API.createTextContainer({
+ frameId: frame.id,
+ label: { frameId: null },
+ });
+
+ API.setElements([frame, rectangle, text]);
+ API.setSelectedElements([rectangle]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: frame.id },
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, containerId: rectangle.id },
+ { [ORIG_ID]: rectangle.id, frameId: frame.id, selected: true },
+ {
+ [ORIG_ID]: text.id,
+ containerId: getCloneByOrigId(rectangle.id).id,
+ },
+ ]);
+ });
+ });
+
+ describe("duplicating multiple frames", () => {
+ it("multiple frames selected (no children)", () => {
+ const frame1 = API.createElement({
+ type: "frame",
+ });
+
+ const rect1 = API.createElement({
+ type: "rectangle",
+ frameId: frame1.id,
+ });
+
+ const frame2 = API.createElement({
+ type: "frame",
+ });
+
+ const rect2 = API.createElement({
+ type: "rectangle",
+ frameId: frame2.id,
+ });
+
+ const ellipse = API.createElement({
+ type: "ellipse",
+ });
+
+ API.setElements([rect1, frame1, ellipse, rect2, frame2]);
+ API.setSelectedElements([frame1, frame2]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: rect1.id, frameId: frame1.id },
+ { id: frame1.id },
+ { [ORIG_ID]: rect1.id, frameId: getCloneByOrigId(frame1.id)?.id },
+ { [ORIG_ID]: frame1.id, selected: true },
+ { id: ellipse.id },
+ { id: rect2.id, frameId: frame2.id },
+ { id: frame2.id },
+ { [ORIG_ID]: rect2.id, frameId: getCloneByOrigId(frame2.id)?.id },
+ { [ORIG_ID]: frame2.id, selected: true },
+ ]);
+ });
+
+ it("multiple frames selected (no children) + unrelated element", () => {
+ const frame1 = API.createElement({
+ type: "frame",
+ });
+
+ const rect1 = API.createElement({
+ type: "rectangle",
+ frameId: frame1.id,
+ });
+
+ const frame2 = API.createElement({
+ type: "frame",
+ });
+
+ const rect2 = API.createElement({
+ type: "rectangle",
+ frameId: frame2.id,
+ });
+
+ const ellipse = API.createElement({
+ type: "ellipse",
+ });
+
+ API.setElements([rect1, frame1, ellipse, rect2, frame2]);
+ API.setSelectedElements([frame1, ellipse, frame2]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: rect1.id, frameId: frame1.id },
+ { id: frame1.id },
+ { [ORIG_ID]: rect1.id, frameId: getCloneByOrigId(frame1.id)?.id },
+ { [ORIG_ID]: frame1.id, selected: true },
+ { id: ellipse.id },
+ { [ORIG_ID]: ellipse.id, selected: true },
+ { id: rect2.id, frameId: frame2.id },
+ { id: frame2.id },
+ { [ORIG_ID]: rect2.id, frameId: getCloneByOrigId(frame2.id)?.id },
+ { [ORIG_ID]: frame2.id, selected: true },
+ ]);
+ });
+ });
+
+ describe("duplicating containers/bound elements", () => {
+ it("labeled arrow (arrow selected)", () => {
+ const [arrow, text] = API.createLabeledArrow();
+
+ API.setElements([arrow, text]);
+ API.setSelectedElements([arrow]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: arrow.id },
+ { id: text.id, containerId: arrow.id },
+ { [ORIG_ID]: arrow.id, selected: true },
+ { [ORIG_ID]: text.id, containerId: getCloneByOrigId(arrow.id)?.id },
+ ]);
+ });
+
+ // shouldn't happen
+ it("labeled arrow (text selected)", () => {
+ const [arrow, text] = API.createLabeledArrow();
+
+ API.setElements([arrow, text]);
+ API.setSelectedElements([text]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: arrow.id },
+ { id: text.id, containerId: arrow.id },
+ { [ORIG_ID]: arrow.id, selected: true },
+ { [ORIG_ID]: text.id, containerId: getCloneByOrigId(arrow.id)?.id },
+ ]);
+ });
+ });
+
+ describe("duplicating groups", () => {
+ it("duplicate group containing frame (children don't have groupIds set)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ groupIds: ["A"],
+ });
+
+ const [rectangle, text] = API.createTextContainer({
+ frameId: frame.id,
+ });
+
+ const ellipse = API.createElement({
+ type: "ellipse",
+ groupIds: ["A"],
+ });
+
+ API.setElements([rectangle, text, frame, ellipse]);
+ API.setSelectedElements([frame, ellipse]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, frameId: frame.id },
+ { id: frame.id },
+ { id: ellipse.id },
+ { [ORIG_ID]: rectangle.id, frameId: getCloneByOrigId(frame.id)?.id },
+ { [ORIG_ID]: text.id, frameId: getCloneByOrigId(frame.id)?.id },
+ { [ORIG_ID]: frame.id, selected: true },
+ { [ORIG_ID]: ellipse.id, selected: true },
+ ]);
+ });
+
+ it("duplicate group containing frame (children have groupIds)", () => {
+ const frame = API.createElement({
+ type: "frame",
+ groupIds: ["A"],
+ });
+
+ const [rectangle, text] = API.createTextContainer({
+ frameId: frame.id,
+ groupIds: ["A"],
+ });
+
+ const ellipse = API.createElement({
+ type: "ellipse",
+ groupIds: ["A"],
+ });
+
+ API.setElements([rectangle, text, frame, ellipse]);
+ API.setSelectedElements([frame, ellipse]);
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: rectangle.id, frameId: frame.id },
+ { id: text.id, frameId: frame.id },
+ { id: frame.id },
+ { id: ellipse.id },
+ {
+ [ORIG_ID]: rectangle.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ // FIXME shouldn't be selected (in selectGroupsForSelectedElements)
+ selected: true,
+ },
+ {
+ [ORIG_ID]: text.id,
+ frameId: getCloneByOrigId(frame.id)?.id,
+ // FIXME shouldn't be selected (in selectGroupsForSelectedElements)
+ selected: true,
+ },
+ { [ORIG_ID]: frame.id, selected: true },
+ { [ORIG_ID]: ellipse.id, selected: true },
+ ]);
+ });
+
+ it("duplicating element nested in group", () => {
+ const ellipse = API.createElement({
+ type: "ellipse",
+ groupIds: ["B"],
+ });
+ const rect1 = API.createElement({
+ type: "rectangle",
+ groupIds: ["A", "B"],
+ });
+ const rect2 = API.createElement({
+ type: "rectangle",
+ groupIds: ["A", "B"],
+ });
+
+ API.setElements([ellipse, rect1, rect2]);
+ API.setSelectedElements([ellipse], "B");
+
+ act(() => {
+ h.app.actionManager.executeAction(actionDuplicateSelection);
+ });
+
+ assertElements(h.elements, [
+ { id: ellipse.id },
+ { [ORIG_ID]: ellipse.id, groupIds: ["B"], selected: true },
+ { id: rect1.id, groupIds: ["A", "B"] },
+ { id: rect2.id, groupIds: ["A", "B"] },
+ ]);
+ });
+ });
+});
diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx
index d19bfa59d7..735c5cf233 100644
--- a/packages/excalidraw/actions/actionDuplicateSelection.tsx
+++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx
@@ -5,7 +5,13 @@ import { duplicateElement, getNonDeletedElements } from "../element";
import { isSomeElementSelected } from "../scene";
import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n";
-import { arrayToMap, getShortcutKey } from "../utils";
+import {
+ arrayToMap,
+ castArray,
+ findLastIndex,
+ getShortcutKey,
+ invariant,
+} from "../utils";
import { LinearElementEditor } from "../element/linearElementEditor";
import {
selectGroupsForSelectedElements,
@@ -19,8 +25,13 @@ import { DEFAULT_GRID_SIZE } from "../constants";
import {
bindTextToShapeAfterDuplication,
getBoundTextElement,
+ getContainerElement,
} from "../element/textElement";
-import { isBoundToContainer, isFrameLikeElement } from "../element/typeChecks";
+import {
+ hasBoundTextElement,
+ isBoundToContainer,
+ isFrameLikeElement,
+} from "../element/typeChecks";
import { normalizeElementOrder } from "../element/sortElements";
import { DuplicateIcon } from "../components/icons";
import {
@@ -31,7 +42,6 @@ import {
excludeElementsInFramesFromSelection,
getSelectedElements,
} from "../scene/selection";
-import { syncMovedIndices } from "../fractionalIndex";
import { StoreAction } from "../store";
export const actionDuplicateSelection = register({
@@ -85,34 +95,66 @@ const duplicateElements = (
): Partial => {
// ---------------------------------------------------------------------------
- // step (1)
-
- const sortedElements = normalizeElementOrder(elements);
const groupIdMap = new Map();
const newElements: ExcalidrawElement[] = [];
const oldElements: ExcalidrawElement[] = [];
const oldIdToDuplicatedId = new Map();
const duplicatedElementsMap = new Map();
- const duplicateAndOffsetElement = (element: ExcalidrawElement) => {
- const newElement = duplicateElement(
- appState.editingGroupId,
- groupIdMap,
- element,
- {
- x: element.x + DEFAULT_GRID_SIZE / 2,
- y: element.y + DEFAULT_GRID_SIZE / 2,
+ const elementsMap = arrayToMap(elements);
+
+ const duplicateAndOffsetElement = <
+ T extends ExcalidrawElement | ExcalidrawElement[],
+ >(
+ element: T,
+ ): T extends ExcalidrawElement[]
+ ? ExcalidrawElement[]
+ : ExcalidrawElement | null => {
+ const elements = castArray(element);
+
+ const _newElements = elements.reduce(
+ (acc: ExcalidrawElement[], element) => {
+ if (processedIds.has(element.id)) {
+ return acc;
+ }
+
+ processedIds.set(element.id, true);
+
+ const newElement = duplicateElement(
+ appState.editingGroupId,
+ groupIdMap,
+ element,
+ {
+ x: element.x + DEFAULT_GRID_SIZE / 2,
+ y: element.y + DEFAULT_GRID_SIZE / 2,
+ },
+ );
+
+ processedIds.set(newElement.id, true);
+
+ duplicatedElementsMap.set(newElement.id, newElement);
+ oldIdToDuplicatedId.set(element.id, newElement.id);
+
+ oldElements.push(element);
+ newElements.push(newElement);
+
+ acc.push(newElement);
+ return acc;
},
+ [],
);
- duplicatedElementsMap.set(newElement.id, newElement);
- oldIdToDuplicatedId.set(element.id, newElement.id);
- oldElements.push(element);
- newElements.push(newElement);
- return newElement;
+
+ return (
+ Array.isArray(element) ? _newElements : _newElements[0] || null
+ ) as T extends ExcalidrawElement[]
+ ? ExcalidrawElement[]
+ : ExcalidrawElement | null;
};
+ elements = normalizeElementOrder(elements);
+
const idsOfElementsToDuplicate = arrayToMap(
- getSelectedElements(sortedElements, appState, {
+ getSelectedElements(elements, appState, {
includeBoundTextElement: true,
includeElementsInFrames: true,
}),
@@ -130,122 +172,133 @@ const duplicateElements = (
// loop over them.
const processedIds = new Map();
- const markAsProcessed = (elements: ExcalidrawElement[]) => {
- for (const element of elements) {
- processedIds.set(element.id, true);
+ const elementsWithClones: ExcalidrawElement[] = elements.slice();
+
+ const insertAfterIndex = (
+ index: number,
+ elements: ExcalidrawElement | null | ExcalidrawElement[],
+ ) => {
+ invariant(index !== -1, "targetIndex === -1 ");
+
+ if (!Array.isArray(elements) && !elements) {
+ return;
}
- return elements;
+
+ elementsWithClones.splice(index + 1, 0, ...castArray(elements));
};
- const elementsWithClones: ExcalidrawElement[] = [];
+ const frameIdsToDuplicate = new Set(
+ elements
+ .filter(
+ (el) => idsOfElementsToDuplicate.has(el.id) && isFrameLikeElement(el),
+ )
+ .map((el) => el.id),
+ );
- let index = -1;
-
- while (++index < sortedElements.length) {
- const element = sortedElements[index];
-
- if (processedIds.get(element.id)) {
+ for (const element of elements) {
+ if (processedIds.has(element.id)) {
continue;
}
- const boundTextElement = getBoundTextElement(element, arrayToMap(elements));
- const isElementAFrameLike = isFrameLikeElement(element);
+ if (!idsOfElementsToDuplicate.has(element.id)) {
+ continue;
+ }
- if (idsOfElementsToDuplicate.get(element.id)) {
- // if a group or a container/bound-text or frame, duplicate atomically
- if (element.groupIds.length || boundTextElement || isElementAFrameLike) {
- const groupId = getSelectedGroupForElement(appState, element);
- if (groupId) {
- // TODO:
- // remove `.flatMap...`
- // if the elements in a frame are grouped when the frame is grouped
- const groupElements = getElementsInGroup(
- sortedElements,
- groupId,
- ).flatMap((element) =>
- isFrameLikeElement(element)
- ? [...getFrameChildren(elements, element.id), element]
- : [element],
- );
+ // groups
+ // -------------------------------------------------------------------------
- elementsWithClones.push(
- ...markAsProcessed([
- ...groupElements,
- ...groupElements.map((element) =>
- duplicateAndOffsetElement(element),
- ),
- ]),
- );
- continue;
- }
- if (boundTextElement) {
- elementsWithClones.push(
- ...markAsProcessed([
- element,
- boundTextElement,
- duplicateAndOffsetElement(element),
- duplicateAndOffsetElement(boundTextElement),
- ]),
- );
- continue;
- }
- if (isElementAFrameLike) {
- const elementsInFrame = getFrameChildren(sortedElements, element.id);
+ const groupId = getSelectedGroupForElement(appState, element);
+ if (groupId) {
+ const groupElements = getElementsInGroup(elements, groupId).flatMap(
+ (element) =>
+ isFrameLikeElement(element)
+ ? [...getFrameChildren(elements, element.id), element]
+ : [element],
+ );
- elementsWithClones.push(
- ...markAsProcessed([
- ...elementsInFrame,
- element,
- ...elementsInFrame.map((e) => duplicateAndOffsetElement(e)),
- duplicateAndOffsetElement(element),
- ]),
- );
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
+ return el.groupIds?.includes(groupId);
+ });
- continue;
- }
- }
- // since elements in frames have a lower z-index than the frame itself,
- // they will be looped first and if their frames are selected as well,
- // they will have been copied along with the frame atomically in the
- // above branch, so we must skip those elements here
- //
- // now, for elements do not belong any frames or elements whose frames
- // are selected (or elements that are left out from the above
- // steps for whatever reason) we (should at least) duplicate them here
- if (!element.frameId || !idsOfElementsToDuplicate.has(element.frameId)) {
- elementsWithClones.push(
- ...markAsProcessed([element, duplicateAndOffsetElement(element)]),
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(groupElements));
+ continue;
+ }
+
+ // frame duplication
+ // -------------------------------------------------------------------------
+
+ if (element.frameId && frameIdsToDuplicate.has(element.frameId)) {
+ continue;
+ }
+
+ if (isFrameLikeElement(element)) {
+ const frameId = element.id;
+
+ const frameChildren = getFrameChildren(elements, frameId);
+
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
+ return el.frameId === frameId || el.id === frameId;
+ });
+
+ insertAfterIndex(
+ targetIndex,
+ duplicateAndOffsetElement([...frameChildren, element]),
+ );
+ continue;
+ }
+
+ // text container
+ // -------------------------------------------------------------------------
+
+ if (hasBoundTextElement(element)) {
+ const boundTextElement = getBoundTextElement(element, elementsMap);
+
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
+ return (
+ el.id === element.id ||
+ ("containerId" in el && el.containerId === element.id)
);
+ });
+
+ if (boundTextElement) {
+ insertAfterIndex(
+ targetIndex,
+ duplicateAndOffsetElement([element, boundTextElement]),
+ );
+ } else {
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(element));
}
- } else {
- elementsWithClones.push(...markAsProcessed([element]));
+
+ continue;
}
- }
- // step (2)
+ if (isBoundToContainer(element)) {
+ const container = getContainerElement(element, elementsMap);
- // second pass to remove duplicates. We loop from the end as it's likelier
- // that the last elements are in the correct order (contiguous or otherwise).
- // Thus we need to reverse as the last step (3).
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
+ return el.id === element.id || el.id === container?.id;
+ });
- const finalElementsReversed: ExcalidrawElement[] = [];
+ if (container) {
+ insertAfterIndex(
+ targetIndex,
+ duplicateAndOffsetElement([container, element]),
+ );
+ } else {
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(element));
+ }
- const finalElementIds = new Map();
- index = elementsWithClones.length;
-
- while (--index >= 0) {
- const element = elementsWithClones[index];
- if (!finalElementIds.get(element.id)) {
- finalElementIds.set(element.id, true);
- finalElementsReversed.push(element);
+ continue;
}
- }
- // step (3)
- const finalElements = syncMovedIndices(
- finalElementsReversed.reverse(),
- arrayToMap(newElements),
- );
+ // default duplication (regular elements)
+ // -------------------------------------------------------------------------
+
+ insertAfterIndex(
+ findLastIndex(elementsWithClones, (el) => el.id === element.id),
+ duplicateAndOffsetElement(element),
+ );
+ }
// ---------------------------------------------------------------------------
@@ -260,7 +313,7 @@ const duplicateElements = (
oldIdToDuplicatedId,
);
bindElementsToFramesAfterDuplication(
- finalElements,
+ elementsWithClones,
oldElements,
oldIdToDuplicatedId,
);
@@ -269,7 +322,7 @@ const duplicateElements = (
excludeElementsInFramesFromSelection(newElements);
return {
- elements: finalElements,
+ elements: elementsWithClones,
appState: {
...appState,
...selectGroupsForSelectedElements(
@@ -285,7 +338,7 @@ const duplicateElements = (
{},
),
},
- getNonDeletedElements(finalElements),
+ getNonDeletedElements(elementsWithClones),
appState,
null,
),
diff --git a/packages/excalidraw/constants.ts b/packages/excalidraw/constants.ts
index b8f7e1b83e..cb32190b22 100644
--- a/packages/excalidraw/constants.ts
+++ b/packages/excalidraw/constants.ts
@@ -458,3 +458,6 @@ export const ARROW_TYPE: { [T in AppState["currentItemArrowType"]]: T } = {
export const DEFAULT_REDUCED_GLOBAL_ALPHA = 0.3;
export const ELEMENT_LINK_KEY = "element";
+
+/** used in tests */
+export const ORIG_ID = Symbol.for("__test__originalId__");
diff --git a/packages/excalidraw/element/newElement.ts b/packages/excalidraw/element/newElement.ts
index a79f077aa6..58b5ec43fb 100644
--- a/packages/excalidraw/element/newElement.ts
+++ b/packages/excalidraw/element/newElement.ts
@@ -45,6 +45,7 @@ import {
DEFAULT_FONT_SIZE,
DEFAULT_TEXT_ALIGN,
DEFAULT_VERTICAL_ALIGN,
+ ORIG_ID,
VERTICAL_ALIGN,
} from "../constants";
import type { MarkOptional, Merge, Mutable } from "../utility-types";
@@ -592,26 +593,18 @@ export const deepCopyElement = (
return _deepCopyElement(val);
};
+const __test__defineOrigId = (clonedObj: object, origId: string) => {
+ Object.defineProperty(clonedObj, ORIG_ID, {
+ value: origId,
+ writable: false,
+ enumerable: false,
+ });
+};
+
/**
- * utility wrapper to generate new id. In test env it reuses the old + postfix
- * for test assertions.
+ * utility wrapper to generate new id.
*/
-export const regenerateId = (
- /** supply null if no previous id exists */
- previousId: string | null,
-) => {
- if (isTestEnv() && previousId) {
- let nextId = `${previousId}_copy`;
- // `window.h` may not be defined in some unit tests
- if (
- window.h?.app
- ?.getSceneElementsIncludingDeleted()
- .find((el: ExcalidrawElement) => el.id === nextId)
- ) {
- nextId += "_copy";
- }
- return nextId;
- }
+const regenerateId = () => {
return randomId();
};
@@ -637,7 +630,11 @@ export const duplicateElement = (
): Readonly => {
let copy = deepCopyElement(element);
- copy.id = regenerateId(copy.id);
+ if (isTestEnv()) {
+ __test__defineOrigId(copy, element.id);
+ }
+
+ copy.id = regenerateId();
copy.boundElements = null;
copy.updated = getUpdatedTimestamp();
copy.seed = randomInteger();
@@ -646,7 +643,7 @@ export const duplicateElement = (
editingGroupId,
(groupId) => {
if (!groupIdMapForOperation.has(groupId)) {
- groupIdMapForOperation.set(groupId, regenerateId(groupId));
+ groupIdMapForOperation.set(groupId, regenerateId());
}
return groupIdMapForOperation.get(groupId)!;
},
@@ -692,7 +689,7 @@ export const duplicateElements = (
// if we haven't migrated the element id, but an old element with the same
// id exists, generate a new id for it and return it
if (origElementsMap.has(id)) {
- const newId = regenerateId(id);
+ const newId = regenerateId();
elementNewIdsMap.set(id, newId);
return newId;
}
@@ -706,6 +703,9 @@ export const duplicateElements = (
const clonedElement: Mutable = _deepCopyElement(element);
clonedElement.id = maybeGetNewId(element.id)!;
+ if (isTestEnv()) {
+ __test__defineOrigId(clonedElement, element.id);
+ }
if (opts?.randomizeSeed) {
clonedElement.seed = randomInteger();
@@ -715,7 +715,7 @@ export const duplicateElements = (
if (clonedElement.groupIds) {
clonedElement.groupIds = clonedElement.groupIds.map((groupId) => {
if (!groupNewIdsMap.has(groupId)) {
- groupNewIdsMap.set(groupId, regenerateId(groupId));
+ groupNewIdsMap.set(groupId, regenerateId());
}
return groupNewIdsMap.get(groupId)!;
});
diff --git a/packages/excalidraw/element/sortElements.ts b/packages/excalidraw/element/sortElements.ts
index bb758370ad..3078a6827a 100644
--- a/packages/excalidraw/element/sortElements.ts
+++ b/packages/excalidraw/element/sortElements.ts
@@ -116,8 +116,5 @@ const normalizeBoundElementsOrder = (
export const normalizeElementOrder = (
elements: readonly ExcalidrawElement[],
) => {
- // console.time();
- const ret = normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
- // console.timeEnd();
- return ret;
+ return normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
};
diff --git a/packages/excalidraw/frame.test.tsx b/packages/excalidraw/frame.test.tsx
index 010ed93434..6a5045b2cb 100644
--- a/packages/excalidraw/frame.test.tsx
+++ b/packages/excalidraw/frame.test.tsx
@@ -1,9 +1,8 @@
-import React from "react";
import type { ExcalidrawElement } from "./element/types";
import { convertToExcalidrawElements, Excalidraw } from "./index";
import { API } from "./tests/helpers/api";
import { Keyboard, Pointer } from "./tests/helpers/ui";
-import { render } from "./tests/test-utils";
+import { getCloneByOrigId, render } from "./tests/test-utils";
const { h } = window;
const mouse = new Pointer("mouse");
@@ -413,10 +412,10 @@ describe("adding elements to frames", () => {
dragElementIntoFrame(frame, rect2);
- const rect2_copy = { ...rect2, id: `${rect2.id}_copy` };
-
selectElementAndDuplicate(rect2);
+ const rect2_copy = getCloneByOrigId(rect2.id);
+
expect(rect2_copy.frameId).toBe(frame.id);
expect(rect2.frameId).toBe(frame.id);
expectEqualIds([rect2_copy, rect2, frame]);
@@ -427,11 +426,11 @@ describe("adding elements to frames", () => {
dragElementIntoFrame(frame, rect2);
- const rect2_copy = { ...rect2, id: `${rect2.id}_copy` };
-
// move the rect2 outside the frame
selectElementAndDuplicate(rect2, [-1000, -1000]);
+ const rect2_copy = getCloneByOrigId(rect2.id);
+
expect(rect2_copy.frameId).toBe(frame.id);
expect(rect2.frameId).toBe(null);
expectEqualIds([rect2_copy, frame, rect2]);
diff --git a/packages/excalidraw/package.json b/packages/excalidraw/package.json
index fdbda46b81..b1f3e691bb 100644
--- a/packages/excalidraw/package.json
+++ b/packages/excalidraw/package.json
@@ -103,6 +103,7 @@
"@types/pako": "1.0.3",
"@types/pica": "5.1.3",
"@types/resize-observer-browser": "0.1.7",
+ "ansicolor": "2.0.3",
"autoprefixer": "10.4.7",
"babel-loader": "8.2.5",
"babel-plugin-transform-class-properties": "6.24.1",
diff --git a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap
index 36666faec8..f77eb8ddbd 100644
--- a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap
+++ b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap
@@ -2517,7 +2517,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"scrolledOutside": false,
"searchMatches": [],
"selectedElementIds": {
- "id0_copy": true,
+ "id1": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -2590,7 +2590,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"frameId": null,
"groupIds": [],
"height": 20,
- "id": "id0_copy",
+ "id": "id1",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -2680,7 +2680,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id0_copy": true,
+ "id1": true,
},
},
"inserted": {
@@ -2693,7 +2693,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id0_copy" => Delta {
+ "id1" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap
index 58bc9303e5..9e353c06fb 100644
--- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap
+++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap
@@ -78,14 +78,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id166": true,
+ "id172": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id166": true,
+ "id172": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -123,7 +123,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id164",
+ "id": "id170",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -155,7 +155,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id165",
+ "id": "id171",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -186,7 +186,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
- "elementId": "id169",
+ "elementId": "id175",
"fixedPoint": [
"0.50000",
1,
@@ -198,7 +198,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 99,
- "id": "id166",
+ "id": "id172",
"index": "a2",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -239,7 +239,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id166",
+ "id": "id172",
"type": "arrow",
},
],
@@ -248,7 +248,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 50,
- "id": "id169",
+ "id": "id175",
"index": "a3",
"isDeleted": false,
"link": null,
@@ -290,10 +290,10 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id166" => Delta {
+ "id172" => Delta {
"deleted": {
"endBinding": {
- "elementId": "id165",
+ "elementId": "id171",
"fixedPoint": null,
"focus": "0.00990",
"gap": 1,
@@ -310,7 +310,7 @@ History {
],
],
"startBinding": {
- "elementId": "id164",
+ "elementId": "id170",
"fixedPoint": null,
"focus": "0.02970",
"gap": 1,
@@ -318,7 +318,7 @@ History {
},
"inserted": {
"endBinding": {
- "elementId": "id165",
+ "elementId": "id171",
"fixedPoint": null,
"focus": "-0.02000",
"gap": 1,
@@ -335,7 +335,7 @@ History {
],
],
"startBinding": {
- "elementId": "id164",
+ "elementId": "id170",
"fixedPoint": null,
"focus": "0.02000",
"gap": 1,
@@ -356,36 +356,36 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id164" => Delta {
+ "id170" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id166",
+ "id": "id172",
"type": "arrow",
},
],
},
},
- "id165" => Delta {
+ "id171" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id166",
+ "id": "id172",
"type": "arrow",
},
],
},
},
- "id166" => Delta {
+ "id172" => Delta {
"deleted": {
"endBinding": {
- "elementId": "id169",
+ "elementId": "id175",
"fixedPoint": [
"0.50000",
1,
@@ -409,7 +409,7 @@ History {
},
"inserted": {
"endBinding": {
- "elementId": "id165",
+ "elementId": "id171",
"fixedPoint": null,
"focus": "0.00990",
"gap": 1,
@@ -426,7 +426,7 @@ History {
],
],
"startBinding": {
- "elementId": "id164",
+ "elementId": "id170",
"fixedPoint": null,
"focus": "0.02970",
"gap": 1,
@@ -434,11 +434,11 @@ History {
"y": "0.99245",
},
},
- "id169" => Delta {
+ "id175" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id166",
+ "id": "id172",
"type": "arrow",
},
],
@@ -462,7 +462,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id164" => Delta {
+ "id170" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -493,7 +493,7 @@ History {
"isDeleted": true,
},
},
- "id165" => Delta {
+ "id171" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -533,9 +533,9 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id166": true,
+ "id172": true,
},
- "selectedLinearElementId": "id166",
+ "selectedLinearElementId": "id172",
},
"inserted": {
"selectedElementIds": {},
@@ -546,7 +546,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id166" => Delta {
+ "id172" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -683,14 +683,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id161": true,
+ "id167": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id161": true,
+ "id167": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -728,7 +728,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id159",
+ "id": "id165",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -760,7 +760,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id160",
+ "id": "id166",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -795,7 +795,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 0,
- "id": "id161",
+ "id": "id167",
"index": "a2",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -850,7 +850,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id161" => Delta {
+ "id167" => Delta {
"deleted": {
"points": [
[
@@ -890,33 +890,33 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id159" => Delta {
+ "id165" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id161",
+ "id": "id167",
"type": "arrow",
},
],
},
},
- "id160" => Delta {
+ "id166" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id161",
+ "id": "id167",
"type": "arrow",
},
],
},
},
- "id161" => Delta {
+ "id167" => Delta {
"deleted": {
"endBinding": null,
"points": [
@@ -933,7 +933,7 @@ History {
},
"inserted": {
"endBinding": {
- "elementId": "id160",
+ "elementId": "id166",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -949,7 +949,7 @@ History {
],
],
"startBinding": {
- "elementId": "id159",
+ "elementId": "id165",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -971,7 +971,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id159" => Delta {
+ "id165" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1002,7 +1002,7 @@ History {
"isDeleted": true,
},
},
- "id160" => Delta {
+ "id166" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1042,9 +1042,9 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id161": true,
+ "id167": true,
},
- "selectedLinearElementId": "id161",
+ "selectedLinearElementId": "id167",
},
"inserted": {
"selectedElementIds": {},
@@ -1055,7 +1055,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id161" => Delta {
+ "id167" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1235,7 +1235,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"elbowed": false,
"endArrowhead": null,
"endBinding": {
- "elementId": "id171",
+ "elementId": "id177",
"fixedPoint": [
"0.50000",
1,
@@ -1247,7 +1247,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": "2.61991",
- "id": "id172",
+ "id": "id178",
"index": "Zz",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -1270,7 +1270,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id170",
+ "elementId": "id176",
"fixedPoint": [
1,
"0.50000",
@@ -1296,7 +1296,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id172",
+ "id": "id178",
"type": "arrow",
},
],
@@ -1305,7 +1305,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id170",
+ "id": "id176",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -1333,7 +1333,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id172",
+ "id": "id178",
"type": "arrow",
},
],
@@ -1342,7 +1342,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id171",
+ "id": "id177",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -1384,7 +1384,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id170" => Delta {
+ "id176" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1415,7 +1415,7 @@ History {
"isDeleted": true,
},
},
- "id171" => Delta {
+ "id177" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1448,10 +1448,10 @@ History {
},
},
"updated": Map {
- "id172" => Delta {
+ "id178" => Delta {
"deleted": {
"endBinding": {
- "elementId": "id171",
+ "elementId": "id177",
"fixedPoint": [
"0.50000",
1,
@@ -1460,7 +1460,7 @@ History {
"gap": 1,
},
"startBinding": {
- "elementId": "id170",
+ "elementId": "id176",
"fixedPoint": [
1,
"0.50000",
@@ -1606,7 +1606,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"elbowed": false,
"endArrowhead": null,
"endBinding": {
- "elementId": "id174",
+ "elementId": "id180",
"fixedPoint": [
1,
"0.50000",
@@ -1618,7 +1618,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": "2.61991",
- "id": "id175",
+ "id": "id181",
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -1641,7 +1641,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id173",
+ "elementId": "id179",
"fixedPoint": [
"0.50000",
1,
@@ -1667,7 +1667,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id175",
+ "id": "id181",
"type": "arrow",
},
],
@@ -1676,7 +1676,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id173",
+ "id": "id179",
"index": "a0V",
"isDeleted": false,
"link": null,
@@ -1704,7 +1704,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id175",
+ "id": "id181",
"type": "arrow",
},
],
@@ -1713,7 +1713,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id174",
+ "id": "id180",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -1755,7 +1755,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id175" => Delta {
+ "id181" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -1764,7 +1764,7 @@ History {
"elbowed": false,
"endArrowhead": null,
"endBinding": {
- "elementId": "id174",
+ "elementId": "id180",
"fixedPoint": [
1,
"0.50000",
@@ -1798,7 +1798,7 @@ History {
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id173",
+ "elementId": "id179",
"fixedPoint": [
"0.50000",
1,
@@ -1820,11 +1820,11 @@ History {
},
},
"updated": Map {
- "id173" => Delta {
+ "id179" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id175",
+ "id": "id181",
"type": "arrow",
},
],
@@ -1833,11 +1833,11 @@ History {
"boundElements": [],
},
},
- "id174" => Delta {
+ "id180" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id175",
+ "id": "id181",
"type": "arrow",
},
],
@@ -1979,7 +1979,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id176",
+ "id": "id182",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -2011,7 +2011,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id177",
+ "id": "id183",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -2053,7 +2053,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id176" => Delta {
+ "id182" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -2084,7 +2084,7 @@ History {
"isDeleted": true,
},
},
- "id177" => Delta {
+ "id183" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -2210,7 +2210,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id180": true,
+ "id186": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -2244,7 +2244,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id180",
+ "id": "id186",
"type": "arrow",
},
],
@@ -2253,7 +2253,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id178",
+ "id": "id184",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -2281,7 +2281,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id180",
+ "id": "id186",
"type": "arrow",
},
],
@@ -2290,7 +2290,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id179",
+ "id": "id185",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -2321,7 +2321,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
- "elementId": "id179",
+ "elementId": "id185",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -2330,7 +2330,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"frameId": null,
"groupIds": [],
"height": "408.19672",
- "id": "id180",
+ "id": "id186",
"index": "a2",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -2353,7 +2353,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id178",
+ "elementId": "id184",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -2390,7 +2390,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id178" => Delta {
+ "id184" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -2421,7 +2421,7 @@ History {
"isDeleted": true,
},
},
- "id179" => Delta {
+ "id185" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -2461,9 +2461,9 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id180": true,
+ "id186": true,
},
- "selectedLinearElementId": "id180",
+ "selectedLinearElementId": "id186",
},
"inserted": {
"selectedElementIds": {},
@@ -2474,7 +2474,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id180" => Delta {
+ "id186" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -2483,7 +2483,7 @@ History {
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
- "elementId": "id179",
+ "elementId": "id185",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -2514,7 +2514,7 @@ History {
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id178",
+ "elementId": "id184",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -2533,11 +2533,11 @@ History {
},
},
"updated": Map {
- "id178" => Delta {
+ "id184" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id180",
+ "id": "id186",
"type": "arrow",
},
],
@@ -2546,11 +2546,11 @@ History {
"boundElements": [],
},
},
- "id179" => Delta {
+ "id185" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id180",
+ "id": "id186",
"type": "arrow",
},
],
@@ -2688,7 +2688,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id147",
+ "id": "id153",
"type": "text",
},
],
@@ -2697,7 +2697,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id145",
+ "id": "id151",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -2733,7 +2733,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id146",
+ "id": "id152",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -2766,7 +2766,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id145",
+ "containerId": "id151",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -2774,7 +2774,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id147",
+ "id": "id153",
"index": "a2",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -2821,7 +2821,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id145" => Delta {
+ "id151" => Delta {
"deleted": {
"isDeleted": false,
},
@@ -2852,7 +2852,7 @@ History {
"y": 10,
},
},
- "id146" => Delta {
+ "id152" => Delta {
"deleted": {
"containerId": null,
},
@@ -2990,7 +2990,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id150",
+ "id": "id156",
"type": "text",
},
],
@@ -2999,7 +2999,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id148",
+ "id": "id154",
"index": "Zz",
"isDeleted": false,
"link": null,
@@ -3027,7 +3027,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id148",
+ "containerId": "id154",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -3035,7 +3035,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id149",
+ "id": "id155",
"index": "a0",
"isDeleted": true,
"lineHeight": "1.25000",
@@ -3068,7 +3068,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id148",
+ "containerId": "id154",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -3076,7 +3076,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id150",
+ "id": "id156",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -3121,9 +3121,9 @@ History {
},
"elementsChange": ElementsChange {
"added": Map {
- "id149" => Delta {
+ "id155" => Delta {
"deleted": {
- "containerId": "id148",
+ "containerId": "id154",
"isDeleted": true,
},
"inserted": {
@@ -3134,14 +3134,14 @@ History {
},
"removed": Map {},
"updated": Map {
- "id148" => Delta {
+ "id154" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id149",
+ "id": "id155",
"type": "text",
},
],
@@ -3277,7 +3277,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id137",
+ "id": "id143",
"type": "text",
},
],
@@ -3286,7 +3286,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id135",
+ "id": "id141",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -3314,7 +3314,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id135",
+ "containerId": "id141",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -3322,7 +3322,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id137",
+ "id": "id143",
"index": "a0V",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -3363,7 +3363,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id136",
+ "id": "id142",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -3410,11 +3410,11 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id135" => Delta {
+ "id141" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id137",
+ "id": "id143",
"type": "text",
},
],
@@ -3422,23 +3422,23 @@ History {
"inserted": {
"boundElements": [
{
- "id": "id136",
+ "id": "id142",
"type": "text",
},
],
},
},
- "id136" => Delta {
+ "id142" => Delta {
"deleted": {
"containerId": null,
},
"inserted": {
- "containerId": "id135",
+ "containerId": "id141",
},
},
- "id137" => Delta {
+ "id143" => Delta {
"deleted": {
- "containerId": "id135",
+ "containerId": "id141",
},
"inserted": {
"containerId": null,
@@ -3578,7 +3578,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id138",
+ "id": "id144",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -3606,7 +3606,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id139",
+ "id": "id145",
"type": "text",
},
],
@@ -3615,7 +3615,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 60,
- "id": "id140",
+ "id": "id146",
"index": "a0V",
"isDeleted": false,
"link": null,
@@ -3643,7 +3643,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id140",
+ "containerId": "id146",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -3651,7 +3651,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 50,
- "id": "id139",
+ "id": "id145",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -3699,32 +3699,32 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id138" => Delta {
+ "id144" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id139",
+ "id": "id145",
"type": "text",
},
],
},
},
- "id139" => Delta {
+ "id145" => Delta {
"deleted": {
- "containerId": "id140",
+ "containerId": "id146",
},
"inserted": {
- "containerId": "id138",
+ "containerId": "id144",
},
},
- "id140" => Delta {
+ "id146" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id139",
+ "id": "id145",
"type": "text",
},
],
@@ -3867,7 +3867,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id133",
+ "id": "id139",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -3903,7 +3903,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id134",
+ "id": "id140",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -3950,25 +3950,25 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id133" => Delta {
+ "id139" => Delta {
"deleted": {
"boundElements": [],
},
"inserted": {
"boundElements": [
{
- "id": "id134",
+ "id": "id140",
"type": "text",
},
],
},
},
- "id134" => Delta {
+ "id140" => Delta {
"deleted": {
"containerId": null,
},
"inserted": {
- "containerId": "id133",
+ "containerId": "id139",
},
},
},
@@ -4101,7 +4101,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id142",
+ "id": "id148",
"type": "text",
},
],
@@ -4110,7 +4110,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id141",
+ "id": "id147",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -4138,7 +4138,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id141",
+ "containerId": "id147",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -4146,7 +4146,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id142",
+ "id": "id148",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -4193,7 +4193,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id141" => Delta {
+ "id147" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -4226,9 +4226,9 @@ History {
},
},
"updated": Map {
- "id142" => Delta {
+ "id148" => Delta {
"deleted": {
- "containerId": "id141",
+ "containerId": "id147",
},
"inserted": {
"containerId": null,
@@ -4363,7 +4363,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id144",
+ "id": "id150",
"type": "text",
},
],
@@ -4372,7 +4372,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id143",
+ "id": "id149",
"index": "Zz",
"isDeleted": false,
"link": null,
@@ -4400,7 +4400,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id143",
+ "containerId": "id149",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -4408,7 +4408,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id144",
+ "id": "id150",
"index": "a0",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -4455,13 +4455,13 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id144" => Delta {
+ "id150" => Delta {
"deleted": {
"angle": 0,
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id143",
+ "containerId": "id149",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -4497,11 +4497,11 @@ History {
},
},
"updated": Map {
- "id143" => Delta {
+ "id149" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id144",
+ "id": "id150",
"type": "text",
},
],
@@ -4639,7 +4639,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id158",
+ "id": "id164",
"type": "text",
},
],
@@ -4648,7 +4648,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id157",
+ "id": "id163",
"index": "Zz",
"isDeleted": false,
"link": null,
@@ -4676,7 +4676,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id157",
+ "containerId": "id163",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -4684,7 +4684,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id158",
+ "id": "id164",
"index": "a0",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -4731,7 +4731,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id158" => Delta {
+ "id164" => Delta {
"deleted": {
"angle": 0,
"x": 15,
@@ -4873,7 +4873,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id156",
+ "id": "id162",
"type": "text",
},
],
@@ -4882,7 +4882,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id155",
+ "id": "id161",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -4910,7 +4910,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id155",
+ "containerId": "id161",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -4918,7 +4918,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id156",
+ "id": "id162",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -4966,7 +4966,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id155" => Delta {
+ "id161" => Delta {
"deleted": {
"angle": 90,
"x": 200,
@@ -5111,7 +5111,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id151",
+ "id": "id157",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -5139,7 +5139,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id151",
+ "containerId": "id157",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -5147,7 +5147,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id152",
+ "id": "id158",
"index": "a1",
"isDeleted": true,
"lineHeight": "1.25000",
@@ -5194,7 +5194,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id151" => Delta {
+ "id157" => Delta {
"deleted": {
"boundElements": [],
"isDeleted": false,
@@ -5202,7 +5202,7 @@ History {
"inserted": {
"boundElements": [
{
- "id": "id152",
+ "id": "id158",
"type": "text",
},
],
@@ -5339,7 +5339,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id154",
+ "id": "id160",
"type": "text",
},
],
@@ -5348,7 +5348,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id153",
+ "id": "id159",
"index": "Zz",
"isDeleted": true,
"link": null,
@@ -5384,7 +5384,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id154",
+ "id": "id160",
"index": "a0",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -5431,13 +5431,13 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id154" => Delta {
+ "id160" => Delta {
"deleted": {
"containerId": null,
"isDeleted": false,
},
"inserted": {
- "containerId": "id153",
+ "containerId": "id159",
"isDeleted": true,
},
},
@@ -5575,7 +5575,7 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id182",
+ "id": "id188",
"index": "Zz",
"isDeleted": false,
"link": null,
@@ -5607,7 +5607,7 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre
"frameId": null,
"groupIds": [],
"height": 500,
- "id": "id181",
+ "id": "id187",
"index": "a0",
"isDeleted": true,
"link": null,
@@ -5650,7 +5650,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id182" => Delta {
+ "id188" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -5696,9 +5696,9 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id182" => Delta {
+ "id188" => Delta {
"deleted": {
- "frameId": "id181",
+ "frameId": "id187",
},
"inserted": {
"frameId": null,
@@ -5793,7 +5793,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id110": true,
+ "id116": true,
},
"resizingElement": null,
"scrollX": 0,
@@ -5838,7 +5838,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"A",
],
"height": 100,
- "id": "id109",
+ "id": "id115",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -5872,7 +5872,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"A",
],
"height": 100,
- "id": "id110",
+ "id": "id116",
"index": "a1",
"isDeleted": true,
"link": null,
@@ -5909,8 +5909,8 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id109": true,
- "id110": true,
+ "id115": true,
+ "id116": true,
},
"selectedGroupIds": {
"A": true,
@@ -5926,7 +5926,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id109" => Delta {
+ "id115" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -5959,7 +5959,7 @@ History {
"isDeleted": true,
},
},
- "id110" => Delta {
+ "id116" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -6006,7 +6006,7 @@ History {
"inserted": {
"editingGroupId": null,
"selectedElementIds": {
- "id109": true,
+ "id115": true,
},
"selectedGroupIds": {
"A": true,
@@ -6030,7 +6030,7 @@ History {
"inserted": {
"editingGroupId": "A",
"selectedElementIds": {
- "id110": true,
+ "id116": true,
},
},
},
@@ -6127,7 +6127,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id97": true,
+ "id103": true,
},
"resizingElement": null,
"scrollX": 0,
@@ -6170,7 +6170,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id95",
+ "id": "id101",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -6202,7 +6202,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id96",
+ "id": "id102",
"index": "a1",
"isDeleted": true,
"link": null,
@@ -6234,7 +6234,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id97",
+ "id": "id103",
"index": "a2",
"isDeleted": true,
"link": null,
@@ -6271,7 +6271,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id95": true,
+ "id101": true,
},
},
"inserted": {
@@ -6282,7 +6282,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id95" => Delta {
+ "id101" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -6322,12 +6322,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id96": true,
+ "id102": true,
},
},
"inserted": {
"selectedElementIds": {
- "id95": true,
+ "id101": true,
},
},
},
@@ -6336,7 +6336,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id96" => Delta {
+ "id102" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "#ffc9c9",
@@ -6381,7 +6381,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id96" => Delta {
+ "id102" => Delta {
"deleted": {
"backgroundColor": "#ffc9c9",
},
@@ -6397,12 +6397,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id97": true,
+ "id103": true,
},
},
"inserted": {
"selectedElementIds": {
- "id96": true,
+ "id102": true,
},
},
},
@@ -6411,7 +6411,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id97" => Delta {
+ "id103" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "#ffc9c9",
@@ -6456,7 +6456,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id97" => Delta {
+ "id103" => Delta {
"deleted": {
"x": 50,
"y": 50,
@@ -6555,15 +6555,15 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id100": true,
+ "id106": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id100": true,
- "id101": true,
+ "id106": true,
+ "id107": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -6601,7 +6601,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id99",
+ "id": "id105",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -6633,7 +6633,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id100",
+ "id": "id106",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -6665,7 +6665,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id101",
+ "id": "id107",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -6702,7 +6702,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id99": true,
+ "id105": true,
},
},
"inserted": {
@@ -6713,7 +6713,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id99" => Delta {
+ "id105" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -6744,7 +6744,7 @@ History {
"isDeleted": true,
},
},
- "id100" => Delta {
+ "id106" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -6775,7 +6775,7 @@ History {
"isDeleted": true,
},
},
- "id101" => Delta {
+ "id107" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -6815,12 +6815,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id100": true,
+ "id106": true,
},
},
"inserted": {
"selectedElementIds": {
- "id99": true,
+ "id105": true,
},
},
},
@@ -6836,7 +6836,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id101": true,
+ "id107": true,
},
},
"inserted": {
@@ -6944,10 +6944,10 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id105": true,
- "id106": true,
- "id107": true,
- "id108": true,
+ "id111": true,
+ "id112": true,
+ "id113": true,
+ "id114": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {
@@ -6990,7 +6990,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"A",
],
"height": 100,
- "id": "id105",
+ "id": "id111",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -7024,7 +7024,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"A",
],
"height": 100,
- "id": "id106",
+ "id": "id112",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -7058,7 +7058,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"B",
],
"height": 100,
- "id": "id107",
+ "id": "id113",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -7092,7 +7092,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"B",
],
"height": 100,
- "id": "id108",
+ "id": "id114",
"index": "a3",
"isDeleted": false,
"link": null,
@@ -7129,8 +7129,8 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id105": true,
- "id106": true,
+ "id111": true,
+ "id112": true,
},
"selectedGroupIds": {
"A": true,
@@ -7153,8 +7153,8 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id107": true,
- "id108": true,
+ "id113": true,
+ "id114": true,
},
"selectedGroupIds": {
"B": true,
@@ -7263,7 +7263,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id113": true,
+ "id119": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -7304,7 +7304,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id113",
+ "id": "id119",
"index": "a0",
"isDeleted": true,
"lastCommittedPoint": [
@@ -7357,7 +7357,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id113": true,
+ "id119": true,
},
},
"inserted": {
@@ -7368,7 +7368,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id113" => Delta {
+ "id119" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -7426,7 +7426,7 @@ History {
"appStateChange": AppStateChange {
"delta": Delta {
"deleted": {
- "selectedLinearElementId": "id113",
+ "selectedLinearElementId": "id119",
},
"inserted": {
"selectedLinearElementId": null,
@@ -7443,7 +7443,7 @@ History {
"appStateChange": AppStateChange {
"delta": Delta {
"deleted": {
- "editingLinearElementId": "id113",
+ "editingLinearElementId": "id119",
},
"inserted": {
"editingLinearElementId": null,
@@ -7463,7 +7463,7 @@ History {
"editingLinearElementId": null,
},
"inserted": {
- "editingLinearElementId": "id113",
+ "editingLinearElementId": "id119",
},
},
},
@@ -7600,7 +7600,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id94",
+ "id": "id100",
"index": "a0",
"isDeleted": true,
"link": null,
@@ -7637,7 +7637,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id94": true,
+ "id100": true,
},
},
"inserted": {
@@ -7649,7 +7649,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id94" => Delta {
+ "id100" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "#ffec99",
@@ -7694,7 +7694,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id94" => Delta {
+ "id100" => Delta {
"deleted": {
"backgroundColor": "#ffec99",
},
@@ -7832,7 +7832,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id119",
+ "id": "id125",
"index": "a1",
"isDeleted": true,
"link": null,
@@ -7864,7 +7864,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id120",
+ "id": "id126",
"index": "a3V",
"isDeleted": true,
"link": null,
@@ -7896,7 +7896,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id118",
+ "id": "id124",
"index": "a4",
"isDeleted": true,
"link": null,
@@ -7938,7 +7938,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id119" => Delta {
+ "id125" => Delta {
"deleted": {
"index": "a1",
},
@@ -7957,14 +7957,14 @@ History {
},
"inserted": {
"selectedElementIds": {
- "id119": true,
+ "id125": true,
},
},
},
},
"elementsChange": ElementsChange {
"added": Map {
- "id118" => Delta {
+ "id124" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -7995,7 +7995,7 @@ History {
"y": 10,
},
},
- "id119" => Delta {
+ "id125" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -8026,7 +8026,7 @@ History {
"y": 20,
},
},
- "id120" => Delta {
+ "id126" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -8190,7 +8190,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id114",
+ "id": "id120",
"index": "Zx",
"isDeleted": true,
"link": null,
@@ -8222,7 +8222,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id116",
+ "id": "id122",
"index": "Zy",
"isDeleted": true,
"link": null,
@@ -8254,7 +8254,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id115",
+ "id": "id121",
"index": "a1",
"isDeleted": true,
"link": null,
@@ -8296,7 +8296,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id115" => Delta {
+ "id121" => Delta {
"deleted": {
"index": "a1",
},
@@ -8315,14 +8315,14 @@ History {
},
"inserted": {
"selectedElementIds": {
- "id115": true,
+ "id121": true,
},
},
},
},
"elementsChange": ElementsChange {
"added": Map {
- "id114" => Delta {
+ "id120" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -8353,7 +8353,7 @@ History {
"y": 10,
},
},
- "id115" => Delta {
+ "id121" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -8384,7 +8384,7 @@ History {
"y": 20,
},
},
- "id116" => Delta {
+ "id122" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -8507,16 +8507,16 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id127": true,
- "id128": true,
+ "id133": true,
+ "id134": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id127": true,
- "id128": true,
+ "id133": true,
+ "id134": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -8554,7 +8554,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id127",
+ "id": "id133",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -8586,7 +8586,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id128",
+ "id": "id134",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -8618,7 +8618,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id132",
+ "id": "id138",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -8655,7 +8655,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id127": true,
+ "id133": true,
},
},
"inserted": {
@@ -8666,7 +8666,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id127" => Delta {
+ "id133" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -8706,12 +8706,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id128": true,
+ "id134": true,
},
},
"inserted": {
"selectedElementIds": {
- "id127": true,
+ "id133": true,
},
},
},
@@ -8719,7 +8719,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id128" => Delta {
+ "id134" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -8759,12 +8759,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id127": true,
+ "id133": true,
},
},
"inserted": {
"selectedElementIds": {
- "id128": true,
+ "id134": true,
},
},
},
@@ -8780,7 +8780,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id128": true,
+ "id134": true,
},
},
"inserted": {
@@ -8805,7 +8805,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id127" => Delta {
+ "id133" => Delta {
"deleted": {
"x": 90,
"y": 90,
@@ -8815,7 +8815,7 @@ History {
"y": 10,
},
},
- "id128" => Delta {
+ "id134" => Delta {
"deleted": {
"x": 110,
"y": 110,
@@ -8955,7 +8955,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 50,
- "id": "id122",
+ "id": "id128",
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": [
@@ -9014,7 +9014,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id123",
+ "id": "id129",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -9056,7 +9056,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id122" => Delta {
+ "id128" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -9209,7 +9209,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id124": true,
+ "id130": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -9247,7 +9247,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 90,
- "id": "id124",
+ "id": "id130",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -9279,7 +9279,7 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id126",
+ "id": "id132",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -9316,7 +9316,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id124": true,
+ "id130": true,
},
},
"inserted": {
@@ -9327,7 +9327,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id124" => Delta {
+ "id130" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -9373,7 +9373,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id124" => Delta {
+ "id130" => Delta {
"deleted": {
"height": 90,
"width": 90,
@@ -9477,7 +9477,7 @@ exports[`history > multiplayer undo/redo > should not override remote changes on
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id81": true,
+ "id87": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -9515,7 +9515,7 @@ exports[`history > multiplayer undo/redo > should not override remote changes on
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id81",
+ "id": "id87",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -9547,7 +9547,7 @@ exports[`history > multiplayer undo/redo > should not override remote changes on
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id82",
+ "id": "id88",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -9589,7 +9589,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id81" => Delta {
+ "id87" => Delta {
"deleted": {
"backgroundColor": "transparent",
},
@@ -9607,7 +9607,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id81": true,
+ "id87": true,
},
},
"inserted": {
@@ -9618,7 +9618,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id81" => Delta {
+ "id87" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -9744,7 +9744,7 @@ exports[`history > multiplayer undo/redo > should not override remote changes on
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id83": true,
+ "id89": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -9782,7 +9782,7 @@ exports[`history > multiplayer undo/redo > should not override remote changes on
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id83",
+ "id": "id89",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -9819,7 +9819,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id83": true,
+ "id89": true,
},
},
"inserted": {
@@ -9830,7 +9830,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id83" => Delta {
+ "id89" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -9876,7 +9876,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id83" => Delta {
+ "id89" => Delta {
"deleted": {
"backgroundColor": "#ffc9c9",
},
@@ -10020,7 +10020,7 @@ exports[`history > multiplayer undo/redo > should override remotely added groups
"B",
],
"height": 100,
- "id": "id88",
+ "id": "id94",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -10055,7 +10055,7 @@ exports[`history > multiplayer undo/redo > should override remotely added groups
"B",
],
"height": 100,
- "id": "id89",
+ "id": "id95",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -10089,7 +10089,7 @@ exports[`history > multiplayer undo/redo > should override remotely added groups
"B",
],
"height": 100,
- "id": "id90",
+ "id": "id96",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -10123,7 +10123,7 @@ exports[`history > multiplayer undo/redo > should override remotely added groups
"B",
],
"height": 100,
- "id": "id91",
+ "id": "id97",
"index": "a3",
"isDeleted": false,
"link": null,
@@ -10166,7 +10166,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id88" => Delta {
+ "id94" => Delta {
"deleted": {
"groupIds": [
"A",
@@ -10177,7 +10177,7 @@ History {
"groupIds": [],
},
},
- "id89" => Delta {
+ "id95" => Delta {
"deleted": {
"groupIds": [
"A",
@@ -10282,7 +10282,7 @@ exports[`history > multiplayer undo/redo > should override remotely added points
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id92": true,
+ "id98": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -10323,7 +10323,7 @@ exports[`history > multiplayer undo/redo > should override remotely added points
"frameId": null,
"groupIds": [],
"height": 30,
- "id": "id92",
+ "id": "id98",
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": [
@@ -10388,7 +10388,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id92": true,
+ "id98": true,
},
},
"inserted": {
@@ -10399,7 +10399,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id92" => Delta {
+ "id98" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -10464,7 +10464,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id92" => Delta {
+ "id98" => Delta {
"deleted": {
"height": 30,
"lastCommittedPoint": [
@@ -10521,7 +10521,7 @@ History {
"appStateChange": AppStateChange {
"delta": Delta {
"deleted": {
- "selectedLinearElementId": "id92",
+ "selectedLinearElementId": "id98",
},
"inserted": {
"selectedLinearElementId": null,
@@ -10661,7 +10661,7 @@ exports[`history > multiplayer undo/redo > should redistribute deltas when eleme
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id93",
+ "id": "id99",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -10698,7 +10698,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id93": true,
+ "id99": true,
},
},
"inserted": {
@@ -10709,7 +10709,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id93" => Delta {
+ "id99" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "#ffec99",
@@ -10752,7 +10752,7 @@ History {
},
"inserted": {
"selectedElementIds": {
- "id93": true,
+ "id99": true,
},
},
},
@@ -10761,7 +10761,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id93" => Delta {
+ "id99" => Delta {
"deleted": {
"isDeleted": false,
},
@@ -11315,7 +11315,7 @@ exports[`history > multiplayer undo/redo > should update history entries after r
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id84": true,
+ "id90": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -11353,7 +11353,7 @@ exports[`history > multiplayer undo/redo > should update history entries after r
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id84",
+ "id": "id90",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -11395,7 +11395,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id84" => Delta {
+ "id90" => Delta {
"deleted": {
"backgroundColor": "#d0bfff",
},
@@ -11417,7 +11417,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id84" => Delta {
+ "id90" => Delta {
"deleted": {
"backgroundColor": "transparent",
},
@@ -11435,7 +11435,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id84": true,
+ "id90": true,
},
},
"inserted": {
@@ -11446,7 +11446,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id84" => Delta {
+ "id90" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -11640,7 +11640,7 @@ exports[`history > singleplayer undo/redo > remounting undo/redo buttons should
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id80",
+ "id": "id86",
"index": "a1",
"isDeleted": true,
"link": null,
@@ -11679,14 +11679,14 @@ History {
},
"inserted": {
"selectedElementIds": {
- "id80": true,
+ "id86": true,
},
},
},
},
"elementsChange": ElementsChange {
"added": Map {
- "id80" => Delta {
+ "id86" => Delta {
"deleted": {
"isDeleted": true,
},
@@ -12712,7 +12712,7 @@ exports[`history > singleplayer undo/redo > should disable undo/redo buttons whe
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id78": true,
+ "id84": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -12782,7 +12782,7 @@ exports[`history > singleplayer undo/redo > should disable undo/redo buttons whe
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id78",
+ "id": "id84",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -12819,7 +12819,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id78": true,
+ "id84": true,
},
},
"inserted": {
@@ -12830,7 +12830,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id78" => Delta {
+ "id84" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -14881,1328 +14881,6 @@ exports[`history > singleplayer undo/redo > should support appstate name or view
exports[`history > singleplayer undo/redo > should support appstate name or viewBackgroundColor change > [end of test] number of renders 1`] = `8`;
exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] appState 1`] = `
-{
- "activeEmbeddable": null,
- "activeTool": {
- "customType": null,
- "lastActiveTool": null,
- "locked": false,
- "type": "selection",
- },
- "collaborators": Map {},
- "contextMenu": null,
- "croppingElementId": null,
- "currentChartType": "bar",
- "currentHoveredFontFamily": null,
- "currentItemArrowType": "round",
- "currentItemBackgroundColor": "transparent",
- "currentItemEndArrowhead": "arrow",
- "currentItemFillStyle": "solid",
- "currentItemFontFamily": 5,
- "currentItemFontSize": 20,
- "currentItemOpacity": 100,
- "currentItemRoughness": 1,
- "currentItemRoundness": "round",
- "currentItemStartArrowhead": null,
- "currentItemStrokeColor": "#1e1e1e",
- "currentItemStrokeStyle": "solid",
- "currentItemStrokeWidth": 2,
- "currentItemTextAlign": "left",
- "cursorButton": "up",
- "defaultSidebarDockedPreference": false,
- "editingFrame": null,
- "editingGroupId": null,
- "editingLinearElement": null,
- "editingTextElement": null,
- "elementsToHighlight": null,
- "errorMessage": null,
- "exportBackground": true,
- "exportEmbedScene": false,
- "exportScale": 1,
- "exportWithDarkMode": false,
- "fileHandle": null,
- "followedBy": Set {},
- "frameRendering": {
- "clip": true,
- "enabled": true,
- "name": true,
- "outline": true,
- },
- "frameToHighlight": null,
- "gridModeEnabled": false,
- "gridSize": 20,
- "gridStep": 5,
- "height": 0,
- "hoveredElementIds": {},
- "isBindingEnabled": true,
- "isCropping": false,
- "isLoading": false,
- "isResizing": false,
- "isRotating": false,
- "lastPointerDownWith": "mouse",
- "multiElement": null,
- "newElement": null,
- "objectsSnapModeEnabled": false,
- "offsetLeft": 0,
- "offsetTop": 0,
- "openDialog": null,
- "openMenu": null,
- "openPopup": null,
- "openSidebar": null,
- "originSnapOffset": null,
- "pasteDialog": {
- "data": null,
- "shown": false,
- },
- "penDetected": false,
- "penMode": false,
- "pendingImageElementId": null,
- "previousSelectedElementIds": {
- "id50": true,
- },
- "resizingElement": null,
- "scrollX": 0,
- "scrollY": 0,
- "searchMatches": [],
- "selectedElementIds": {
- "id55": true,
- },
- "selectedElementsAreBeingDragged": false,
- "selectedGroupIds": {},
- "selectionElement": null,
- "shouldCacheIgnoreZoom": false,
- "showHyperlinkPopup": false,
- "showWelcomeScreen": true,
- "snapLines": [],
- "startBoundElement": null,
- "stats": {
- "open": false,
- "panels": 3,
- },
- "suggestedBindings": [],
- "theme": "light",
- "toast": null,
- "userToFollow": null,
- "viewBackgroundColor": "#ffffff",
- "viewModeEnabled": false,
- "width": 0,
- "zenModeEnabled": false,
- "zoom": {
- "value": 1,
- },
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 0 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": [
- {
- "id": "id51",
- "type": "text",
- },
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "id": "id50",
- "index": "a0",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "updated": 1,
- "version": 8,
- "width": 100,
- "x": -100,
- "y": -50,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 1 1`] = `
-{
- "angle": 0,
- "autoResize": true,
- "backgroundColor": "transparent",
- "boundElements": null,
- "containerId": "id50",
- "customData": undefined,
- "fillStyle": "solid",
- "fontFamily": 5,
- "fontSize": 20,
- "frameId": null,
- "groupIds": [],
- "height": 25,
- "id": "id51",
- "index": "a1",
- "isDeleted": false,
- "lineHeight": "1.25000",
- "link": null,
- "locked": false,
- "opacity": 100,
- "originalText": "ola",
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "text": "ola",
- "textAlign": "center",
- "type": "text",
- "updated": 1,
- "version": 6,
- "verticalAlign": "middle",
- "width": 30,
- "x": -65,
- "y": "-12.50000",
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 2 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": [
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "id": "id52",
- "index": "a2",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "updated": 1,
- "version": 7,
- "width": 100,
- "x": 100,
- "y": -50,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 3 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "elbowed": false,
- "endArrowhead": "arrow",
- "endBinding": {
- "elementId": "id52",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 0,
- "id": "id55",
- "index": "a3",
- "isDeleted": false,
- "lastCommittedPoint": null,
- "link": null,
- "locked": false,
- "opacity": 100,
- "points": [
- [
- 0,
- 0,
- ],
- [
- "98.00000",
- 0,
- ],
- ],
- "roughness": 1,
- "roundness": {
- "type": 2,
- },
- "startArrowhead": null,
- "startBinding": {
- "elementId": "id50",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "arrow",
- "updated": 1,
- "version": 10,
- "width": "98.00000",
- "x": 1,
- "y": 0,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] history 1`] = `
-History {
- "onHistoryChangedEmitter": Emitter {
- "subscribers": [
- [Function],
- [Function],
- ],
- },
- "redoStack": [
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id55": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id55" => Delta {
- "deleted": {
- "isDeleted": false,
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- },
- "inserted": {
- "isDeleted": true,
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- },
- },
- },
- "updated": Map {
- "id50" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- "id52" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- },
- },
- },
- ],
- "undoStack": [
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {},
- "inserted": {},
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id50" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a0",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "width": 100,
- "x": -100,
- "y": -50,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- "id51" => Delta {
- "deleted": {
- "angle": 0,
- "autoResize": true,
- "backgroundColor": "transparent",
- "boundElements": null,
- "containerId": null,
- "customData": undefined,
- "fillStyle": "solid",
- "fontFamily": 5,
- "fontSize": 20,
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a1",
- "isDeleted": false,
- "lineHeight": "1.25000",
- "link": null,
- "locked": false,
- "opacity": 100,
- "originalText": "ola",
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "text": "ola",
- "textAlign": "left",
- "type": "text",
- "verticalAlign": "top",
- "width": 100,
- "x": -200,
- "y": -200,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- "id52" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a2",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "width": 100,
- "x": 100,
- "y": -50,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- },
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id50": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id51": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {},
- },
- "inserted": {
- "selectedElementIds": {
- "id51": true,
- },
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {
- "id50" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id51",
- "type": "text",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- "id51" => Delta {
- "deleted": {
- "containerId": "id50",
- "height": 25,
- "textAlign": "center",
- "verticalAlign": "middle",
- "width": 30,
- "x": -65,
- "y": "-12.50000",
- },
- "inserted": {
- "containerId": null,
- "height": 100,
- "textAlign": "left",
- "verticalAlign": "top",
- "width": 100,
- "x": -200,
- "y": -200,
- },
- },
- },
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id55": true,
- },
- "selectedLinearElementId": "id55",
- },
- "inserted": {
- "selectedElementIds": {
- "id50": true,
- },
- "selectedLinearElementId": null,
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id55" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "elbowed": false,
- "endArrowhead": "arrow",
- "endBinding": {
- "elementId": "id52",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 0,
- "index": "a3",
- "isDeleted": false,
- "lastCommittedPoint": null,
- "link": null,
- "locked": false,
- "opacity": 100,
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- "roughness": 1,
- "roundness": {
- "type": 2,
- },
- "startArrowhead": null,
- "startBinding": {
- "elementId": "id50",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "arrow",
- "width": 100,
- "x": 0,
- "y": 0,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- },
- "updated": Map {
- "id50" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- "id52" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id55",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- },
- },
- },
- ],
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] number of elements 1`] = `4`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] number of renders 1`] = `12`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] appState 1`] = `
-{
- "activeEmbeddable": null,
- "activeTool": {
- "customType": null,
- "lastActiveTool": null,
- "locked": false,
- "type": "selection",
- },
- "collaborators": Map {},
- "contextMenu": null,
- "croppingElementId": null,
- "currentChartType": "bar",
- "currentHoveredFontFamily": null,
- "currentItemArrowType": "round",
- "currentItemBackgroundColor": "transparent",
- "currentItemEndArrowhead": "arrow",
- "currentItemFillStyle": "solid",
- "currentItemFontFamily": 5,
- "currentItemFontSize": 20,
- "currentItemOpacity": 100,
- "currentItemRoughness": 1,
- "currentItemRoundness": "round",
- "currentItemStartArrowhead": null,
- "currentItemStrokeColor": "#1e1e1e",
- "currentItemStrokeStyle": "solid",
- "currentItemStrokeWidth": 2,
- "currentItemTextAlign": "left",
- "cursorButton": "up",
- "defaultSidebarDockedPreference": false,
- "editingFrame": null,
- "editingGroupId": null,
- "editingLinearElement": null,
- "editingTextElement": null,
- "elementsToHighlight": null,
- "errorMessage": null,
- "exportBackground": true,
- "exportEmbedScene": false,
- "exportScale": 1,
- "exportWithDarkMode": false,
- "fileHandle": null,
- "followedBy": Set {},
- "frameRendering": {
- "clip": true,
- "enabled": true,
- "name": true,
- "outline": true,
- },
- "frameToHighlight": null,
- "gridModeEnabled": false,
- "gridSize": 20,
- "gridStep": 5,
- "height": 0,
- "hoveredElementIds": {},
- "isBindingEnabled": true,
- "isCropping": false,
- "isLoading": false,
- "isResizing": false,
- "isRotating": false,
- "lastPointerDownWith": "mouse",
- "multiElement": null,
- "newElement": null,
- "objectsSnapModeEnabled": false,
- "offsetLeft": 0,
- "offsetTop": 0,
- "openDialog": null,
- "openMenu": null,
- "openPopup": null,
- "openSidebar": null,
- "originSnapOffset": null,
- "pasteDialog": {
- "data": null,
- "shown": false,
- },
- "penDetected": false,
- "penMode": false,
- "pendingImageElementId": null,
- "previousSelectedElementIds": {
- "id44": true,
- },
- "resizingElement": null,
- "scrollX": 0,
- "scrollY": 0,
- "searchMatches": [],
- "selectedElementIds": {
- "id49": true,
- },
- "selectedElementsAreBeingDragged": false,
- "selectedGroupIds": {},
- "selectionElement": null,
- "shouldCacheIgnoreZoom": false,
- "showHyperlinkPopup": false,
- "showWelcomeScreen": true,
- "snapLines": [],
- "startBoundElement": null,
- "stats": {
- "open": false,
- "panels": 3,
- },
- "suggestedBindings": [],
- "theme": "light",
- "toast": null,
- "userToFollow": null,
- "viewBackgroundColor": "#ffffff",
- "viewModeEnabled": false,
- "width": 0,
- "zenModeEnabled": false,
- "zoom": {
- "value": 1,
- },
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 0 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": [
- {
- "id": "id45",
- "type": "text",
- },
- {
- "id": "id49",
- "type": "arrow",
- },
- ],
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "id": "id44",
- "index": "a0",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "updated": 1,
- "version": 8,
- "width": 100,
- "x": -100,
- "y": -50,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 1 1`] = `
-{
- "angle": 0,
- "autoResize": true,
- "backgroundColor": "transparent",
- "boundElements": null,
- "containerId": "id44",
- "customData": undefined,
- "fillStyle": "solid",
- "fontFamily": 5,
- "fontSize": 20,
- "frameId": null,
- "groupIds": [],
- "height": 25,
- "id": "id45",
- "index": "a1",
- "isDeleted": false,
- "lineHeight": "1.25000",
- "link": null,
- "locked": false,
- "opacity": 100,
- "originalText": "ola",
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "text": "ola",
- "textAlign": "center",
- "type": "text",
- "updated": 1,
- "version": 8,
- "verticalAlign": "middle",
- "width": 30,
- "x": -65,
- "y": "-12.50000",
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 2 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": [
- {
- "id": "id49",
- "type": "arrow",
- },
- ],
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "id": "id46",
- "index": "a2",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "updated": 1,
- "version": 7,
- "width": 100,
- "x": 100,
- "y": -50,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 3 1`] = `
-{
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "elbowed": false,
- "endArrowhead": "arrow",
- "endBinding": {
- "elementId": "id46",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 0,
- "id": "id49",
- "index": "a3",
- "isDeleted": false,
- "lastCommittedPoint": null,
- "link": null,
- "locked": false,
- "opacity": 100,
- "points": [
- [
- 0,
- 0,
- ],
- [
- "98.00000",
- 0,
- ],
- ],
- "roughness": 1,
- "roundness": {
- "type": 2,
- },
- "startArrowhead": null,
- "startBinding": {
- "elementId": "id44",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "arrow",
- "updated": 1,
- "version": 10,
- "width": "98.00000",
- "x": 1,
- "y": 0,
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] history 1`] = `
-History {
- "onHistoryChangedEmitter": Emitter {
- "subscribers": [
- [Function],
- [Function],
- ],
- },
- "redoStack": [],
- "undoStack": [
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {},
- "inserted": {},
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id44" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a0",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "width": 100,
- "x": -100,
- "y": -50,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- "id45" => Delta {
- "deleted": {
- "angle": 0,
- "autoResize": true,
- "backgroundColor": "transparent",
- "boundElements": null,
- "containerId": null,
- "customData": undefined,
- "fillStyle": "solid",
- "fontFamily": 5,
- "fontSize": 20,
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a1",
- "isDeleted": false,
- "lineHeight": "1.25000",
- "link": null,
- "locked": false,
- "opacity": 100,
- "originalText": "ola",
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "text": "ola",
- "textAlign": "left",
- "type": "text",
- "verticalAlign": "top",
- "width": 100,
- "x": -200,
- "y": -200,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- "id46" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 100,
- "index": "a2",
- "isDeleted": false,
- "link": null,
- "locked": false,
- "opacity": 100,
- "roughness": 1,
- "roundness": {
- "type": 3,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "rectangle",
- "width": 100,
- "x": 100,
- "y": -50,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- },
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id44": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id45": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {},
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {},
- },
- "inserted": {
- "selectedElementIds": {
- "id45": true,
- },
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {},
- "updated": Map {
- "id44" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id45",
- "type": "text",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- "id45" => Delta {
- "deleted": {
- "containerId": "id44",
- "height": 25,
- "textAlign": "center",
- "verticalAlign": "middle",
- "width": 30,
- "x": -65,
- "y": "-12.50000",
- },
- "inserted": {
- "containerId": null,
- "height": 100,
- "textAlign": "left",
- "verticalAlign": "top",
- "width": 100,
- "x": -200,
- "y": -200,
- },
- },
- },
- },
- },
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id49": true,
- },
- "selectedLinearElementId": "id49",
- },
- "inserted": {
- "selectedElementIds": {
- "id44": true,
- },
- "selectedLinearElementId": null,
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id49" => Delta {
- "deleted": {
- "angle": 0,
- "backgroundColor": "transparent",
- "boundElements": null,
- "customData": undefined,
- "elbowed": false,
- "endArrowhead": "arrow",
- "endBinding": {
- "elementId": "id46",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "fillStyle": "solid",
- "frameId": null,
- "groupIds": [],
- "height": 0,
- "index": "a3",
- "isDeleted": false,
- "lastCommittedPoint": null,
- "link": null,
- "locked": false,
- "opacity": 100,
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- "roughness": 1,
- "roundness": {
- "type": 2,
- },
- "startArrowhead": null,
- "startBinding": {
- "elementId": "id44",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- "strokeColor": "#1e1e1e",
- "strokeStyle": "solid",
- "strokeWidth": 2,
- "type": "arrow",
- "width": 100,
- "x": 0,
- "y": 0,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- },
- "updated": Map {
- "id44" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id49",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- "id46" => Delta {
- "deleted": {
- "boundElements": [
- {
- "id": "id49",
- "type": "arrow",
- },
- ],
- },
- "inserted": {
- "boundElements": [],
- },
- },
- },
- },
- },
- ],
-}
-`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] number of elements 1`] = `4`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] number of renders 1`] = `12`;
-
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] appState 1`] = `
{
"activeEmbeddable": null,
"activeTool": {
@@ -16315,7 +14993,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 0 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 0 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
@@ -16349,14 +15027,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"strokeWidth": 2,
"type": "rectangle",
"updated": 1,
- "version": 12,
+ "version": 8,
"width": 100,
"x": -100,
"y": -50,
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 1 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 1 1`] = `
{
"angle": 0,
"autoResize": true,
@@ -16389,7 +15067,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"textAlign": "center",
"type": "text",
"updated": 1,
- "version": 12,
+ "version": 6,
"verticalAlign": "middle",
"width": 30,
"x": -65,
@@ -16397,7 +15075,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 2 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 2 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
@@ -16427,14 +15105,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"strokeWidth": 2,
"type": "rectangle",
"updated": 1,
- "version": 9,
+ "version": 7,
"width": 100,
"x": 100,
"y": -50,
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 3 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] element 3 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
@@ -16492,7 +15170,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] history 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] history 1`] = `
History {
"onHistoryChangedEmitter": Emitter {
"subscribers": [
@@ -16500,7 +15178,83 @@ History {
[Function],
],
},
- "redoStack": [],
+ "redoStack": [
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id61": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id61" => Delta {
+ "deleted": {
+ "isDeleted": false,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ },
+ "inserted": {
+ "isDeleted": true,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ },
+ },
+ },
+ "updated": Map {
+ "id56" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id61",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ "id58" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id61",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ },
+ },
+ },
+ ],
"undoStack": [
HistoryEntry {
"appStateChange": AppStateChange {
@@ -16821,11 +15575,11 @@ History {
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] number of elements 1`] = `4`;
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] number of elements 1`] = `4`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] number of renders 1`] = `20`;
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on deletion and rebind on undo > [end of test] number of renders 1`] = `12`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] appState 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] appState 1`] = `
{
"activeEmbeddable": null,
"activeTool": {
@@ -16902,13 +15656,15 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
- "previousSelectedElementIds": {},
+ "previousSelectedElementIds": {
+ "id50": true,
+ },
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id62": true,
+ "id55": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -16936,19 +15692,642 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 0 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 0 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id67",
+ "id": "id51",
+ "type": "text",
+ },
+ {
+ "id": "id55",
"type": "arrow",
},
+ ],
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "id": "id50",
+ "index": "a0",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "updated": 1,
+ "version": 8,
+ "width": 100,
+ "x": -100,
+ "y": -50,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 1 1`] = `
+{
+ "angle": 0,
+ "autoResize": true,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "containerId": "id50",
+ "customData": undefined,
+ "fillStyle": "solid",
+ "fontFamily": 5,
+ "fontSize": 20,
+ "frameId": null,
+ "groupIds": [],
+ "height": 25,
+ "id": "id51",
+ "index": "a1",
+ "isDeleted": false,
+ "lineHeight": "1.25000",
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "originalText": "ola",
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "text": "ola",
+ "textAlign": "center",
+ "type": "text",
+ "updated": 1,
+ "version": 8,
+ "verticalAlign": "middle",
+ "width": 30,
+ "x": -65,
+ "y": "-12.50000",
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 2 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": [
+ {
+ "id": "id55",
+ "type": "arrow",
+ },
+ ],
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "id": "id52",
+ "index": "a2",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "updated": 1,
+ "version": 7,
+ "width": 100,
+ "x": 100,
+ "y": -50,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] element 3 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "elbowed": false,
+ "endArrowhead": "arrow",
+ "endBinding": {
+ "elementId": "id52",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 0,
+ "id": "id55",
+ "index": "a3",
+ "isDeleted": false,
+ "lastCommittedPoint": null,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ "98.00000",
+ 0,
+ ],
+ ],
+ "roughness": 1,
+ "roundness": {
+ "type": 2,
+ },
+ "startArrowhead": null,
+ "startBinding": {
+ "elementId": "id50",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "arrow",
+ "updated": 1,
+ "version": 10,
+ "width": "98.00000",
+ "x": 1,
+ "y": 0,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] history 1`] = `
+History {
+ "onHistoryChangedEmitter": Emitter {
+ "subscribers": [
+ [Function],
+ [Function],
+ ],
+ },
+ "redoStack": [],
+ "undoStack": [
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {},
+ "inserted": {},
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id50" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a0",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "width": 100,
+ "x": -100,
+ "y": -50,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ "id51" => Delta {
+ "deleted": {
+ "angle": 0,
+ "autoResize": true,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "containerId": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "fontFamily": 5,
+ "fontSize": 20,
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a1",
+ "isDeleted": false,
+ "lineHeight": "1.25000",
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "originalText": "ola",
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "text": "ola",
+ "textAlign": "left",
+ "type": "text",
+ "verticalAlign": "top",
+ "width": 100,
+ "x": -200,
+ "y": -200,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ "id52" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a2",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "width": 100,
+ "x": 100,
+ "y": -50,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ },
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id50": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id51": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {},
+ },
+ "inserted": {
+ "selectedElementIds": {
+ "id51": true,
+ },
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {
+ "id50" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id51",
+ "type": "text",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ "id51" => Delta {
+ "deleted": {
+ "containerId": "id50",
+ "height": 25,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "width": 30,
+ "x": -65,
+ "y": "-12.50000",
+ },
+ "inserted": {
+ "containerId": null,
+ "height": 100,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "width": 100,
+ "x": -200,
+ "y": -200,
+ },
+ },
+ },
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id55": true,
+ },
+ "selectedLinearElementId": "id55",
+ },
+ "inserted": {
+ "selectedElementIds": {
+ "id50": true,
+ },
+ "selectedLinearElementId": null,
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id55" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "elbowed": false,
+ "endArrowhead": "arrow",
+ "endBinding": {
+ "elementId": "id52",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 0,
+ "index": "a3",
+ "isDeleted": false,
+ "lastCommittedPoint": null,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ "roughness": 1,
+ "roundness": {
+ "type": 2,
+ },
+ "startArrowhead": null,
+ "startBinding": {
+ "elementId": "id50",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "arrow",
+ "width": 100,
+ "x": 0,
+ "y": 0,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ },
+ "updated": Map {
+ "id50" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id55",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ "id52" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id55",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ },
+ },
+ },
+ ],
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] number of elements 1`] = `4`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind arrow from non deleted bindable elements on undo and rebind on redo > [end of test] number of renders 1`] = `12`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] appState 1`] = `
+{
+ "activeEmbeddable": null,
+ "activeTool": {
+ "customType": null,
+ "lastActiveTool": null,
+ "locked": false,
+ "type": "selection",
+ },
+ "collaborators": Map {},
+ "contextMenu": null,
+ "croppingElementId": null,
+ "currentChartType": "bar",
+ "currentHoveredFontFamily": null,
+ "currentItemArrowType": "round",
+ "currentItemBackgroundColor": "transparent",
+ "currentItemEndArrowhead": "arrow",
+ "currentItemFillStyle": "solid",
+ "currentItemFontFamily": 5,
+ "currentItemFontSize": 20,
+ "currentItemOpacity": 100,
+ "currentItemRoughness": 1,
+ "currentItemRoundness": "round",
+ "currentItemStartArrowhead": null,
+ "currentItemStrokeColor": "#1e1e1e",
+ "currentItemStrokeStyle": "solid",
+ "currentItemStrokeWidth": 2,
+ "currentItemTextAlign": "left",
+ "cursorButton": "up",
+ "defaultSidebarDockedPreference": false,
+ "editingFrame": null,
+ "editingGroupId": null,
+ "editingLinearElement": null,
+ "editingTextElement": null,
+ "elementsToHighlight": null,
+ "errorMessage": null,
+ "exportBackground": true,
+ "exportEmbedScene": false,
+ "exportScale": 1,
+ "exportWithDarkMode": false,
+ "fileHandle": null,
+ "followedBy": Set {},
+ "frameRendering": {
+ "clip": true,
+ "enabled": true,
+ "name": true,
+ "outline": true,
+ },
+ "frameToHighlight": null,
+ "gridModeEnabled": false,
+ "gridSize": 20,
+ "gridStep": 5,
+ "height": 0,
+ "hoveredElementIds": {},
+ "isBindingEnabled": true,
+ "isCropping": false,
+ "isLoading": false,
+ "isResizing": false,
+ "isRotating": false,
+ "lastPointerDownWith": "mouse",
+ "multiElement": null,
+ "newElement": null,
+ "objectsSnapModeEnabled": false,
+ "offsetLeft": 0,
+ "offsetTop": 0,
+ "openDialog": null,
+ "openMenu": null,
+ "openPopup": null,
+ "openSidebar": null,
+ "originSnapOffset": null,
+ "pasteDialog": {
+ "data": null,
+ "shown": false,
+ },
+ "penDetected": false,
+ "penMode": false,
+ "pendingImageElementId": null,
+ "previousSelectedElementIds": {
+ "id62": true,
+ },
+ "resizingElement": null,
+ "scrollX": 0,
+ "scrollY": 0,
+ "searchMatches": [],
+ "selectedElementIds": {
+ "id67": true,
+ },
+ "selectedElementsAreBeingDragged": false,
+ "selectedGroupIds": {},
+ "selectionElement": null,
+ "shouldCacheIgnoreZoom": false,
+ "showHyperlinkPopup": false,
+ "showWelcomeScreen": true,
+ "snapLines": [],
+ "startBoundElement": null,
+ "stats": {
+ "open": false,
+ "panels": 3,
+ },
+ "suggestedBindings": [],
+ "theme": "light",
+ "toast": null,
+ "userToFollow": null,
+ "viewBackgroundColor": "#ffffff",
+ "viewModeEnabled": false,
+ "width": 0,
+ "zenModeEnabled": false,
+ "zoom": {
+ "value": 1,
+ },
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 0 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": [
{
"id": "id63",
"type": "text",
},
+ {
+ "id": "id67",
+ "type": "arrow",
+ },
],
"customData": undefined,
"fillStyle": "solid",
@@ -16970,14 +16349,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"strokeWidth": 2,
"type": "rectangle",
"updated": 1,
- "version": 8,
+ "version": 12,
"width": 100,
"x": -100,
"y": -50,
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 1 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 1 1`] = `
{
"angle": 0,
"autoResize": true,
@@ -17010,7 +16389,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"textAlign": "center",
"type": "text",
"updated": 1,
- "version": 8,
+ "version": 12,
"verticalAlign": "middle",
"width": 30,
"x": -65,
@@ -17018,7 +16397,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 2 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 2 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
@@ -17048,14 +16427,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"strokeWidth": 2,
"type": "rectangle",
"updated": 1,
- "version": 5,
+ "version": 9,
"width": 100,
"x": 100,
"y": -50,
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 3 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] element 3 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
@@ -17113,7 +16492,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
}
`;
-exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] history 1`] = `
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] history 1`] = `
History {
"onHistoryChangedEmitter": Emitter {
"subscribers": [
@@ -17121,78 +16500,7 @@ History {
[Function],
],
},
- "redoStack": [
- HistoryEntry {
- "appStateChange": AppStateChange {
- "delta": Delta {
- "deleted": {
- "selectedElementIds": {
- "id62": true,
- },
- },
- "inserted": {
- "selectedElementIds": {},
- },
- },
- },
- "elementsChange": ElementsChange {
- "added": Map {},
- "removed": Map {
- "id62" => Delta {
- "deleted": {
- "isDeleted": false,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- "id63" => Delta {
- "deleted": {
- "isDeleted": false,
- },
- "inserted": {
- "isDeleted": true,
- },
- },
- },
- "updated": Map {
- "id67" => Delta {
- "deleted": {
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- "startBinding": {
- "elementId": "id62",
- "fixedPoint": null,
- "focus": 0,
- "gap": 1,
- },
- },
- "inserted": {
- "points": [
- [
- 0,
- 0,
- ],
- [
- 100,
- 0,
- ],
- ],
- "startBinding": null,
- },
- },
- },
- },
- },
- ],
+ "redoStack": [],
"undoStack": [
HistoryEntry {
"appStateChange": AppStateChange {
@@ -17509,20 +16817,712 @@ History {
},
},
},
+ ],
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] number of elements 1`] = `4`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind everything from non deleted elements when iterating through the whole undo stack and vice versa rebind everything on redo > [end of test] number of renders 1`] = `20`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] appState 1`] = `
+{
+ "activeEmbeddable": null,
+ "activeTool": {
+ "customType": null,
+ "lastActiveTool": null,
+ "locked": false,
+ "type": "selection",
+ },
+ "collaborators": Map {},
+ "contextMenu": null,
+ "croppingElementId": null,
+ "currentChartType": "bar",
+ "currentHoveredFontFamily": null,
+ "currentItemArrowType": "round",
+ "currentItemBackgroundColor": "transparent",
+ "currentItemEndArrowhead": "arrow",
+ "currentItemFillStyle": "solid",
+ "currentItemFontFamily": 5,
+ "currentItemFontSize": 20,
+ "currentItemOpacity": 100,
+ "currentItemRoughness": 1,
+ "currentItemRoundness": "round",
+ "currentItemStartArrowhead": null,
+ "currentItemStrokeColor": "#1e1e1e",
+ "currentItemStrokeStyle": "solid",
+ "currentItemStrokeWidth": 2,
+ "currentItemTextAlign": "left",
+ "cursorButton": "up",
+ "defaultSidebarDockedPreference": false,
+ "editingFrame": null,
+ "editingGroupId": null,
+ "editingLinearElement": null,
+ "editingTextElement": null,
+ "elementsToHighlight": null,
+ "errorMessage": null,
+ "exportBackground": true,
+ "exportEmbedScene": false,
+ "exportScale": 1,
+ "exportWithDarkMode": false,
+ "fileHandle": null,
+ "followedBy": Set {},
+ "frameRendering": {
+ "clip": true,
+ "enabled": true,
+ "name": true,
+ "outline": true,
+ },
+ "frameToHighlight": null,
+ "gridModeEnabled": false,
+ "gridSize": 20,
+ "gridStep": 5,
+ "height": 0,
+ "hoveredElementIds": {},
+ "isBindingEnabled": true,
+ "isCropping": false,
+ "isLoading": false,
+ "isResizing": false,
+ "isRotating": false,
+ "lastPointerDownWith": "mouse",
+ "multiElement": null,
+ "newElement": null,
+ "objectsSnapModeEnabled": false,
+ "offsetLeft": 0,
+ "offsetTop": 0,
+ "openDialog": null,
+ "openMenu": null,
+ "openPopup": null,
+ "openSidebar": null,
+ "originSnapOffset": null,
+ "pasteDialog": {
+ "data": null,
+ "shown": false,
+ },
+ "penDetected": false,
+ "penMode": false,
+ "pendingImageElementId": null,
+ "previousSelectedElementIds": {},
+ "resizingElement": null,
+ "scrollX": 0,
+ "scrollY": 0,
+ "searchMatches": [],
+ "selectedElementIds": {
+ "id68": true,
+ },
+ "selectedElementsAreBeingDragged": false,
+ "selectedGroupIds": {},
+ "selectionElement": null,
+ "shouldCacheIgnoreZoom": false,
+ "showHyperlinkPopup": false,
+ "showWelcomeScreen": true,
+ "snapLines": [],
+ "startBoundElement": null,
+ "stats": {
+ "open": false,
+ "panels": 3,
+ },
+ "suggestedBindings": [],
+ "theme": "light",
+ "toast": null,
+ "userToFollow": null,
+ "viewBackgroundColor": "#ffffff",
+ "viewModeEnabled": false,
+ "width": 0,
+ "zenModeEnabled": false,
+ "zoom": {
+ "value": 1,
+ },
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 0 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": [
+ {
+ "id": "id73",
+ "type": "arrow",
+ },
+ {
+ "id": "id69",
+ "type": "text",
+ },
+ ],
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "id": "id68",
+ "index": "a0",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "updated": 1,
+ "version": 8,
+ "width": 100,
+ "x": -100,
+ "y": -50,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 1 1`] = `
+{
+ "angle": 0,
+ "autoResize": true,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "containerId": "id68",
+ "customData": undefined,
+ "fillStyle": "solid",
+ "fontFamily": 5,
+ "fontSize": 20,
+ "frameId": null,
+ "groupIds": [],
+ "height": 25,
+ "id": "id69",
+ "index": "a1",
+ "isDeleted": false,
+ "lineHeight": "1.25000",
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "originalText": "ola",
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "text": "ola",
+ "textAlign": "center",
+ "type": "text",
+ "updated": 1,
+ "version": 8,
+ "verticalAlign": "middle",
+ "width": 30,
+ "x": -65,
+ "y": "-12.50000",
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 2 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": [
+ {
+ "id": "id73",
+ "type": "arrow",
+ },
+ ],
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "id": "id70",
+ "index": "a2",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "updated": 1,
+ "version": 5,
+ "width": 100,
+ "x": 100,
+ "y": -50,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] element 3 1`] = `
+{
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "elbowed": false,
+ "endArrowhead": "arrow",
+ "endBinding": {
+ "elementId": "id70",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 0,
+ "id": "id73",
+ "index": "a3",
+ "isDeleted": false,
+ "lastCommittedPoint": null,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ "98.00000",
+ 0,
+ ],
+ ],
+ "roughness": 1,
+ "roundness": {
+ "type": 2,
+ },
+ "startArrowhead": null,
+ "startBinding": {
+ "elementId": "id68",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "arrow",
+ "updated": 1,
+ "version": 10,
+ "width": "98.00000",
+ "x": 1,
+ "y": 0,
+}
+`;
+
+exports[`history > singleplayer undo/redo > should support bidirectional bindings > should unbind rectangle from arrow on deletion and rebind on undo > [end of test] history 1`] = `
+History {
+ "onHistoryChangedEmitter": Emitter {
+ "subscribers": [
+ [Function],
+ [Function],
+ ],
+ },
+ "redoStack": [
HistoryEntry {
"appStateChange": AppStateChange {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id62": true,
+ "id68": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id68" => Delta {
+ "deleted": {
+ "isDeleted": false,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ "id69" => Delta {
+ "deleted": {
+ "isDeleted": false,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ },
+ "updated": Map {
+ "id73" => Delta {
+ "deleted": {
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ "startBinding": {
+ "elementId": "id68",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ },
+ "inserted": {
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ "startBinding": null,
+ },
+ },
+ },
+ },
+ },
+ ],
+ "undoStack": [
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {},
+ "inserted": {},
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id68" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a0",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "width": 100,
+ "x": -100,
+ "y": -50,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ "id69" => Delta {
+ "deleted": {
+ "angle": 0,
+ "autoResize": true,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "containerId": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "fontFamily": 5,
+ "fontSize": 20,
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a1",
+ "isDeleted": false,
+ "lineHeight": "1.25000",
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "originalText": "ola",
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "text": "ola",
+ "textAlign": "left",
+ "type": "text",
+ "verticalAlign": "top",
+ "width": 100,
+ "x": -200,
+ "y": -200,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ "id70" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 100,
+ "index": "a2",
+ "isDeleted": false,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "roughness": 1,
+ "roundness": {
+ "type": 3,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "rectangle",
+ "width": 100,
+ "x": 100,
+ "y": -50,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ },
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id68": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id69": true,
+ },
+ },
+ "inserted": {
+ "selectedElementIds": {},
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {},
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {},
+ },
+ "inserted": {
+ "selectedElementIds": {
+ "id69": true,
+ },
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {},
+ "updated": Map {
+ "id68" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id69",
+ "type": "text",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ "id69" => Delta {
+ "deleted": {
+ "containerId": "id68",
+ "height": 25,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "width": 30,
+ "x": -65,
+ "y": "-12.50000",
+ },
+ "inserted": {
+ "containerId": null,
+ "height": 100,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "width": 100,
+ "x": -200,
+ "y": -200,
+ },
+ },
+ },
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id73": true,
+ },
+ "selectedLinearElementId": "id73",
+ },
+ "inserted": {
+ "selectedElementIds": {
+ "id68": true,
+ },
+ "selectedLinearElementId": null,
+ },
+ },
+ },
+ "elementsChange": ElementsChange {
+ "added": Map {},
+ "removed": Map {
+ "id73" => Delta {
+ "deleted": {
+ "angle": 0,
+ "backgroundColor": "transparent",
+ "boundElements": null,
+ "customData": undefined,
+ "elbowed": false,
+ "endArrowhead": "arrow",
+ "endBinding": {
+ "elementId": "id70",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "fillStyle": "solid",
+ "frameId": null,
+ "groupIds": [],
+ "height": 0,
+ "index": "a3",
+ "isDeleted": false,
+ "lastCommittedPoint": null,
+ "link": null,
+ "locked": false,
+ "opacity": 100,
+ "points": [
+ [
+ 0,
+ 0,
+ ],
+ [
+ 100,
+ 0,
+ ],
+ ],
+ "roughness": 1,
+ "roundness": {
+ "type": 2,
+ },
+ "startArrowhead": null,
+ "startBinding": {
+ "elementId": "id68",
+ "fixedPoint": null,
+ "focus": 0,
+ "gap": 1,
+ },
+ "strokeColor": "#1e1e1e",
+ "strokeStyle": "solid",
+ "strokeWidth": 2,
+ "type": "arrow",
+ "width": 100,
+ "x": 0,
+ "y": 0,
+ },
+ "inserted": {
+ "isDeleted": true,
+ },
+ },
+ },
+ "updated": Map {
+ "id68" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id73",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ "id70" => Delta {
+ "deleted": {
+ "boundElements": [
+ {
+ "id": "id73",
+ "type": "arrow",
+ },
+ ],
+ },
+ "inserted": {
+ "boundElements": [],
+ },
+ },
+ },
+ },
+ },
+ HistoryEntry {
+ "appStateChange": AppStateChange {
+ "delta": Delta {
+ "deleted": {
+ "selectedElementIds": {
+ "id68": true,
},
"selectedLinearElementId": null,
},
"inserted": {
"selectedElementIds": {
- "id67": true,
+ "id73": true,
},
- "selectedLinearElementId": "id67",
+ "selectedLinearElementId": "id73",
},
},
},
@@ -17618,15 +17618,15 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id69": true,
+ "id75": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id69": true,
- "id71": true,
+ "id75": true,
+ "id77": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -17660,11 +17660,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id74",
+ "id": "id80",
"type": "arrow",
},
{
- "id": "id70",
+ "id": "id76",
"type": "text",
},
],
@@ -17673,7 +17673,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id69",
+ "id": "id75",
"index": "a0",
"isDeleted": false,
"link": null,
@@ -17701,7 +17701,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"autoResize": true,
"backgroundColor": "transparent",
"boundElements": null,
- "containerId": "id69",
+ "containerId": "id75",
"customData": undefined,
"fillStyle": "solid",
"fontFamily": 5,
@@ -17709,7 +17709,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"frameId": null,
"groupIds": [],
"height": 25,
- "id": "id70",
+ "id": "id76",
"index": "a1",
"isDeleted": false,
"lineHeight": "1.25000",
@@ -17742,7 +17742,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"backgroundColor": "transparent",
"boundElements": [
{
- "id": "id74",
+ "id": "id80",
"type": "arrow",
},
],
@@ -17751,7 +17751,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"frameId": null,
"groupIds": [],
"height": 100,
- "id": "id71",
+ "id": "id77",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -17782,7 +17782,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
- "elementId": "id71",
+ "elementId": "id77",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -17791,7 +17791,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
"frameId": null,
"groupIds": [],
"height": 0,
- "id": "id74",
+ "id": "id80",
"index": "a3",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -17814,7 +17814,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id69",
+ "elementId": "id75",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -17845,8 +17845,8 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id69": true,
- "id71": true,
+ "id75": true,
+ "id77": true,
},
},
"inserted": {
@@ -17857,7 +17857,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id69" => Delta {
+ "id75" => Delta {
"deleted": {
"isDeleted": false,
},
@@ -17865,7 +17865,7 @@ History {
"isDeleted": true,
},
},
- "id70" => Delta {
+ "id76" => Delta {
"deleted": {
"isDeleted": false,
},
@@ -17873,7 +17873,7 @@ History {
"isDeleted": true,
},
},
- "id71" => Delta {
+ "id77" => Delta {
"deleted": {
"isDeleted": false,
},
@@ -17883,10 +17883,10 @@ History {
},
},
"updated": Map {
- "id74" => Delta {
+ "id80" => Delta {
"deleted": {
"endBinding": {
- "elementId": "id71",
+ "elementId": "id77",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -17902,7 +17902,7 @@ History {
],
],
"startBinding": {
- "elementId": "id69",
+ "elementId": "id75",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -17938,7 +17938,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id69" => Delta {
+ "id75" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -17969,7 +17969,7 @@ History {
"isDeleted": true,
},
},
- "id70" => Delta {
+ "id76" => Delta {
"deleted": {
"angle": 0,
"autoResize": true,
@@ -18009,7 +18009,7 @@ History {
"isDeleted": true,
},
},
- "id71" => Delta {
+ "id77" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -18049,7 +18049,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id69": true,
+ "id75": true,
},
},
"inserted": {
@@ -18068,7 +18068,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id70": true,
+ "id76": true,
},
},
"inserted": {
@@ -18090,7 +18090,7 @@ History {
},
"inserted": {
"selectedElementIds": {
- "id70": true,
+ "id76": true,
},
},
},
@@ -18099,11 +18099,11 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id69" => Delta {
+ "id75" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id70",
+ "id": "id76",
"type": "text",
},
],
@@ -18112,9 +18112,9 @@ History {
"boundElements": [],
},
},
- "id70" => Delta {
+ "id76" => Delta {
"deleted": {
- "containerId": "id69",
+ "containerId": "id75",
"height": 25,
"textAlign": "center",
"verticalAlign": "middle",
@@ -18140,13 +18140,13 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id74": true,
+ "id80": true,
},
- "selectedLinearElementId": "id74",
+ "selectedLinearElementId": "id80",
},
"inserted": {
"selectedElementIds": {
- "id69": true,
+ "id75": true,
},
"selectedLinearElementId": null,
},
@@ -18155,7 +18155,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id74" => Delta {
+ "id80" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -18164,7 +18164,7 @@ History {
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
- "elementId": "id71",
+ "elementId": "id77",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -18195,7 +18195,7 @@ History {
},
"startArrowhead": null,
"startBinding": {
- "elementId": "id69",
+ "elementId": "id75",
"fixedPoint": null,
"focus": 0,
"gap": 1,
@@ -18214,11 +18214,11 @@ History {
},
},
"updated": Map {
- "id69" => Delta {
+ "id75" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id74",
+ "id": "id80",
"type": "arrow",
},
],
@@ -18227,11 +18227,11 @@ History {
"boundElements": [],
},
},
- "id71" => Delta {
+ "id77" => Delta {
"deleted": {
"boundElements": [
{
- "id": "id74",
+ "id": "id80",
"type": "arrow",
},
],
@@ -18248,15 +18248,15 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id69": true,
+ "id75": true,
},
"selectedLinearElementId": null,
},
"inserted": {
"selectedElementIds": {
- "id74": true,
+ "id80": true,
},
- "selectedLinearElementId": "id74",
+ "selectedLinearElementId": "id80",
},
},
},
@@ -18271,7 +18271,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id71": true,
+ "id77": true,
},
},
"inserted": {
@@ -18371,15 +18371,15 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {
- "id39": true,
+ "id45": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id39": true,
- "id41": true,
+ "id45": true,
+ "id47": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
@@ -18417,7 +18417,7 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id40",
+ "id": "id46",
"index": "a1",
"isDeleted": false,
"link": null,
@@ -18449,7 +18449,7 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id39",
+ "id": "id45",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -18481,7 +18481,7 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
"frameId": null,
"groupIds": [],
"height": 10,
- "id": "id41",
+ "id": "id47",
"index": "a3",
"isDeleted": false,
"link": null,
@@ -18518,7 +18518,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id39": true,
+ "id45": true,
},
},
"inserted": {
@@ -18529,7 +18529,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id39" => Delta {
+ "id45" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -18569,12 +18569,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id40": true,
+ "id46": true,
},
},
"inserted": {
"selectedElementIds": {
- "id39": true,
+ "id45": true,
},
},
},
@@ -18582,7 +18582,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id40" => Delta {
+ "id46" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -18622,12 +18622,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id41": true,
+ "id47": true,
},
},
"inserted": {
"selectedElementIds": {
- "id40": true,
+ "id46": true,
},
},
},
@@ -18635,7 +18635,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id41" => Delta {
+ "id47" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -18681,7 +18681,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id41" => Delta {
+ "id47" => Delta {
"deleted": {
"index": "a0V",
},
@@ -18697,12 +18697,12 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id39": true,
+ "id45": true,
},
},
"inserted": {
"selectedElementIds": {
- "id41": true,
+ "id47": true,
},
},
},
@@ -18718,7 +18718,7 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id41": true,
+ "id47": true,
},
},
"inserted": {
@@ -18743,7 +18743,7 @@ History {
"added": Map {},
"removed": Map {},
"updated": Map {
- "id39" => Delta {
+ "id45" => Delta {
"deleted": {
"index": "a2",
},
@@ -18751,7 +18751,7 @@ History {
"index": "Zz",
},
},
- "id41" => Delta {
+ "id47" => Delta {
"deleted": {
"index": "a3",
},
@@ -18855,12 +18855,12 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
"scrollY": 0,
"searchMatches": [],
"selectedElementIds": {
- "id35_copy_copy": true,
- "id36_copy_copy": true,
+ "id42": true,
+ "id44": true,
},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {
- "A_copy": true,
+ "id43": true,
},
"selectionElement": null,
"shouldCacheIgnoreZoom": false,
@@ -18963,10 +18963,10 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id43",
],
"height": 100,
- "id": "id35_copy_copy",
+ "id": "id42",
"index": "a1G",
"isDeleted": false,
"link": null,
@@ -18997,10 +18997,10 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id43",
],
"height": 100,
- "id": "id36_copy_copy",
+ "id": "id44",
"index": "a1V",
"isDeleted": false,
"link": null,
@@ -19031,10 +19031,10 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id40",
],
"height": 100,
- "id": "id35_copy",
+ "id": "id39",
"index": "a2",
"isDeleted": true,
"link": null,
@@ -19065,10 +19065,10 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id40",
],
"height": 100,
- "id": "id36_copy",
+ "id": "id41",
"index": "a3",
"isDeleted": true,
"link": null,
@@ -19196,11 +19196,11 @@ History {
"delta": Delta {
"deleted": {
"selectedElementIds": {
- "id35_copy_copy": true,
- "id36_copy_copy": true,
+ "id42": true,
+ "id44": true,
},
"selectedGroupIds": {
- "A_copy": true,
+ "id43": true,
},
},
"inserted": {
@@ -19217,7 +19217,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id35_copy_copy" => Delta {
+ "id42" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -19226,7 +19226,7 @@ History {
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id43",
],
"height": 100,
"index": "a1G",
@@ -19250,7 +19250,7 @@ History {
"isDeleted": true,
},
},
- "id36_copy_copy" => Delta {
+ "id44" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -19259,7 +19259,7 @@ History {
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "A_copy",
+ "id43",
],
"height": 100,
"index": "a1V",
diff --git a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap
index 1fc6156fe6..2e34ec6b2d 100644
--- a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap
+++ b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap
@@ -10,7 +10,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 5`] = `
"frameId": null,
"groupIds": [],
"height": 50,
- "id": "id0_copy",
+ "id": "id2",
"index": "a0",
"isDeleted": false,
"link": null,
diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
index 75795b251e..5d48ead6cf 100644
--- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
+++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
@@ -2129,7 +2129,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id0_copy" => Delta {
+ "id2" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -10619,7 +10619,7 @@ History {
"elementsChange": ElementsChange {
"added": Map {},
"removed": Map {
- "id0_copy" => Delta {
+ "id6" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -10628,7 +10628,7 @@ History {
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "id4_copy",
+ "id7",
],
"height": 10,
"index": "a0",
@@ -10652,7 +10652,7 @@ History {
"isDeleted": true,
},
},
- "id1_copy" => Delta {
+ "id8" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -10661,7 +10661,7 @@ History {
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "id4_copy",
+ "id7",
],
"height": 10,
"index": "a1",
@@ -10685,7 +10685,7 @@ History {
"isDeleted": true,
},
},
- "id2_copy" => Delta {
+ "id9" => Delta {
"deleted": {
"angle": 0,
"backgroundColor": "transparent",
@@ -10694,7 +10694,7 @@ History {
"fillStyle": "solid",
"frameId": null,
"groupIds": [
- "id4_copy",
+ "id7",
],
"height": 10,
"index": "a2",
diff --git a/packages/excalidraw/tests/helpers/api.ts b/packages/excalidraw/tests/helpers/api.ts
index f0876611e2..a6432666a5 100644
--- a/packages/excalidraw/tests/helpers/api.ts
+++ b/packages/excalidraw/tests/helpers/api.ts
@@ -40,6 +40,7 @@ import { createTestHook } from "../../components/App";
import type { Action } from "../../actions/types";
import { mutateElement } from "../../element/mutateElement";
import { pointFrom, type LocalPoint, type Radians } from "../../../math";
+import { selectGroupsForSelectedElements } from "../../groups";
const readFile = util.promisify(fs.readFile);
// so that window.h is available when App.tsx is not imported as well.
@@ -68,13 +69,21 @@ export class API {
});
};
- static setSelectedElements = (elements: ExcalidrawElement[]) => {
+ static setSelectedElements = (elements: ExcalidrawElement[], editingGroupId?: string | null) => {
act(() => {
h.setState({
- selectedElementIds: elements.reduce((acc, element) => {
- acc[element.id] = true;
- return acc;
- }, {} as Record),
+ ...selectGroupsForSelectedElements(
+ {
+ editingGroupId: editingGroupId ?? null,
+ selectedElementIds: elements.reduce((acc, element) => {
+ acc[element.id] = true;
+ return acc;
+ }, {} as Record),
+ },
+ elements,
+ h.state,
+ h.app,
+ )
});
});
};
@@ -158,7 +167,7 @@ export class API {
isDeleted?: boolean;
frameId?: ExcalidrawElement["id"] | null;
index?: ExcalidrawElement["index"];
- groupIds?: string[];
+ groupIds?: ExcalidrawElement["groupIds"];
// generic element props
strokeColor?: ExcalidrawGenericElement["strokeColor"];
backgroundColor?: ExcalidrawGenericElement["backgroundColor"];
@@ -369,6 +378,84 @@ export class API {
return element as any;
};
+ static createTextContainer = (opts?: {
+ frameId?: ExcalidrawElement["id"];
+ groupIds?: ExcalidrawElement["groupIds"];
+ label?: {
+ text?: string;
+ frameId?: ExcalidrawElement["id"] | null;
+ groupIds?: ExcalidrawElement["groupIds"];
+ };
+ }) => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ frameId: opts?.frameId || null,
+ groupIds: opts?.groupIds,
+ });
+
+ const text = API.createElement({
+ type: "text",
+ text: opts?.label?.text || "sample-text",
+ width: 50,
+ height: 20,
+ fontSize: 16,
+ containerId: rectangle.id,
+ frameId:
+ opts?.label?.frameId === undefined
+ ? opts?.frameId ?? null
+ : opts?.label?.frameId ?? null,
+ groupIds: opts?.label?.groupIds === undefined
+ ? opts?.groupIds
+ : opts?.label?.groupIds ,
+
+ });
+
+ mutateElement(
+ rectangle,
+ {
+ boundElements: [{ type: "text", id: text.id }],
+ },
+ false,
+ );
+
+ return [rectangle, text];
+ };
+
+ static createLabeledArrow = (opts?: {
+ frameId?: ExcalidrawElement["id"];
+ label?: {
+ text?: string;
+ frameId?: ExcalidrawElement["id"] | null;
+ };
+ }) => {
+ const arrow = API.createElement({
+ type: "arrow",
+ frameId: opts?.frameId || null,
+ });
+
+ const text = API.createElement({
+ type: "text",
+ id: "text2",
+ width: 50,
+ height: 20,
+ containerId: arrow.id,
+ frameId:
+ opts?.label?.frameId === undefined
+ ? opts?.frameId ?? null
+ : opts?.label?.frameId ?? null,
+ });
+
+ mutateElement(
+ arrow,
+ {
+ boundElements: [{ type: "text", id: text.id }],
+ },
+ false,
+ );
+
+ return [arrow, text];
+ };
+
static readFile = async (
filepath: string,
encoding?: T,
diff --git a/packages/excalidraw/tests/history.test.tsx b/packages/excalidraw/tests/history.test.tsx
index 9402095ee7..5000940ca4 100644
--- a/packages/excalidraw/tests/history.test.tsx
+++ b/packages/excalidraw/tests/history.test.tsx
@@ -7,6 +7,7 @@ import {
assertSelectedElements,
render,
togglePopover,
+ getCloneByOrigId,
} from "./test-utils";
import { Excalidraw } from "../index";
import { Keyboard, Pointer, UI } from "./helpers/ui";
@@ -15,7 +16,7 @@ import { getDefaultAppState } from "../appState";
import { fireEvent, queryByTestId, waitFor } from "@testing-library/react";
import { createUndoAction, createRedoAction } from "../actions/actionHistory";
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
-import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
+import { EXPORT_DATA_TYPES, MIME_TYPES, ORIG_ID } from "../constants";
import type { AppState } from "../types";
import { arrayToMap } from "../utils";
import {
@@ -1138,8 +1139,8 @@ describe("history", () => {
expect(h.elements).toEqual([
expect.objectContaining({ id: rect1.id, isDeleted: false }),
expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: true }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: true }),
+ expect.objectContaining({ [ORIG_ID]: rect1.id, isDeleted: true }),
+ expect.objectContaining({ [ORIG_ID]: rect2.id, isDeleted: true }),
]);
expect(h.state.editingGroupId).toBeNull();
expect(h.state.selectedGroupIds).toEqual({ A: true });
@@ -1151,8 +1152,8 @@ describe("history", () => {
expect(h.elements).toEqual([
expect.objectContaining({ id: rect1.id, isDeleted: false }),
expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: false }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: false }),
+ expect.objectContaining({ [ORIG_ID]: rect1.id, isDeleted: false }),
+ expect.objectContaining({ [ORIG_ID]: rect2.id, isDeleted: false }),
]);
expect(h.state.editingGroupId).toBeNull();
expect(h.state.selectedGroupIds).not.toEqual(
@@ -1171,14 +1172,14 @@ describe("history", () => {
expect.arrayContaining([
expect.objectContaining({ id: rect1.id, isDeleted: false }),
expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: true }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: true }),
+ expect.objectContaining({ [ORIG_ID]: rect1.id, isDeleted: true }),
+ expect.objectContaining({ [ORIG_ID]: rect2.id, isDeleted: true }),
expect.objectContaining({
- id: `${rect1.id}_copy_copy`,
+ [ORIG_ID]: getCloneByOrigId(rect1.id)?.id,
isDeleted: false,
}),
expect.objectContaining({
- id: `${rect2.id}_copy_copy`,
+ [ORIG_ID]: getCloneByOrigId(rect2.id)?.id,
isDeleted: false,
}),
]),
diff --git a/packages/excalidraw/tests/library.test.tsx b/packages/excalidraw/tests/library.test.tsx
index 51b139ce37..7b48407b72 100644
--- a/packages/excalidraw/tests/library.test.tsx
+++ b/packages/excalidraw/tests/library.test.tsx
@@ -1,11 +1,11 @@
import React from "react";
import { vi } from "vitest";
-import { fireEvent, render, waitFor } from "./test-utils";
+import { fireEvent, getCloneByOrigId, render, waitFor } from "./test-utils";
import { act, queryByTestId } from "@testing-library/react";
import { Excalidraw } from "../index";
import { API } from "./helpers/api";
-import { MIME_TYPES } from "../constants";
+import { MIME_TYPES, ORIG_ID } from "../constants";
import type { LibraryItem, LibraryItems } from "../types";
import { UI } from "./helpers/ui";
import { serializeLibraryAsJSON } from "../data/json";
@@ -76,7 +76,7 @@ describe("library", () => {
}),
);
await waitFor(() => {
- expect(h.elements).toEqual([expect.objectContaining({ id: "A_copy" })]);
+ expect(h.elements).toEqual([expect.objectContaining({ [ORIG_ID]: "A" })]);
});
});
@@ -125,23 +125,27 @@ describe("library", () => {
);
await waitFor(() => {
- expect(h.elements).toEqual([
- expect.objectContaining({
- id: "rectangle1_copy",
- boundElements: expect.arrayContaining([
- { type: "text", id: "text1_copy" },
- { type: "arrow", id: "arrow1_copy" },
- ]),
- }),
- expect.objectContaining({
- id: "text1_copy",
- containerId: "rectangle1_copy",
- }),
- expect.objectContaining({
- id: "arrow1_copy",
- endBinding: expect.objectContaining({ elementId: "rectangle1_copy" }),
- }),
- ]);
+ expect(h.elements).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ [ORIG_ID]: "rectangle1",
+ boundElements: expect.arrayContaining([
+ { type: "text", id: getCloneByOrigId("text1").id },
+ { type: "arrow", id: getCloneByOrigId("arrow1").id },
+ ]),
+ }),
+ expect.objectContaining({
+ [ORIG_ID]: "text1",
+ containerId: getCloneByOrigId("rectangle1").id,
+ }),
+ expect.objectContaining({
+ [ORIG_ID]: "arrow1",
+ endBinding: expect.objectContaining({
+ elementId: getCloneByOrigId("rectangle1").id,
+ }),
+ }),
+ ]),
+ );
});
});
@@ -170,10 +174,11 @@ describe("library", () => {
await waitFor(() => {
expect(h.elements).toEqual([
expect.objectContaining({
- id: "elem1_copy",
+ [ORIG_ID]: "elem1",
}),
expect.objectContaining({
- id: expect.not.stringMatching(/^(elem1_copy|elem1)$/),
+ id: expect.not.stringMatching(/^elem1$/),
+ [ORIG_ID]: expect.not.stringMatching(/^\w+$/),
}),
]);
});
@@ -189,7 +194,7 @@ describe("library", () => {
}),
);
await waitFor(() => {
- expect(h.elements).toEqual([expect.objectContaining({ id: "A_copy" })]);
+ expect(h.elements).toEqual([expect.objectContaining({ [ORIG_ID]: "A" })]);
});
expect(h.state.activeTool.type).toBe("selection");
});
diff --git a/packages/excalidraw/tests/test-utils.ts b/packages/excalidraw/tests/test-utils.ts
index 4c0eacee68..1c775c2214 100644
--- a/packages/excalidraw/tests/test-utils.ts
+++ b/packages/excalidraw/tests/test-utils.ts
@@ -11,6 +11,10 @@ import { getSelectedElements } from "../scene/selection";
import type { ExcalidrawElement } from "../element/types";
import { UI } from "./helpers/ui";
import { diffStringsUnified } from "jest-diff";
+import ansi from "ansicolor";
+import { ORIG_ID } from "../constants";
+import { arrayToMap } from "../utils";
+import type { AllPossibleKeys } from "../utility-types";
const customQueries = {
...queries,
@@ -295,3 +299,150 @@ expect.addSnapshotSerializer({
);
},
});
+
+export const getCloneByOrigId = (
+ origId: ExcalidrawElement["id"],
+ returnNullIfNotExists: T = false as T,
+): T extends true ? ExcalidrawElement | null : ExcalidrawElement => {
+ const clonedElement = window.h.elements?.find(
+ (el) => (el as any)[ORIG_ID] === origId,
+ );
+ if (clonedElement) {
+ return clonedElement;
+ }
+ if (returnNullIfNotExists !== true) {
+ throw new Error(`cloned element not found for origId: ${origId}`);
+ }
+ return null as T extends true ? ExcalidrawElement | null : ExcalidrawElement;
+};
+
+/**
+ * Assertion helper that strips the actual elements of extra attributes
+ * so that diffs are easier to read in case of failure.
+ *
+ * Asserts element order as well, and selected element ids
+ * (when `selected: true` set for given element).
+ *
+ * If testing cloned elements, you can use { `[ORIG_ID]: origElement.id }
+ * If you need to refer to cloned element properties, you can use
+ * `getCloneByOrigId()`, e.g.: `{ frameId: getCloneByOrigId(origFrame.id)?.id }`
+ */
+export const assertElements = >(
+ actualElements: readonly ExcalidrawElement[],
+ /** array order matters */
+ expectedElements: (Partial> & {
+ /** meta, will be stripped for element attribute checks */
+ selected?: true;
+ } & (
+ | {
+ id: ExcalidrawElement["id"];
+ }
+ | { [ORIG_ID]?: string }
+ ))[],
+) => {
+ const h = window.h;
+
+ const expectedElementsWithIds: (typeof expectedElements[number] & {
+ id: ExcalidrawElement["id"];
+ })[] = expectedElements.map((el) => {
+ if ("id" in el) {
+ return el;
+ }
+ const actualElement = actualElements.find(
+ (act) => (act as any)[ORIG_ID] === el[ORIG_ID],
+ );
+ if (actualElement) {
+ return { ...el, id: actualElement.id };
+ }
+ return {
+ ...el,
+ id: "UNKNOWN_ID",
+ };
+ });
+
+ const map_expectedElements = arrayToMap(expectedElementsWithIds);
+
+ const selectedElementIds = expectedElementsWithIds.reduce(
+ (acc: Record, el) => {
+ if (el.selected) {
+ acc[el.id] = true;
+ }
+ return acc;
+ },
+ {},
+ );
+
+ const mappedActualElements = actualElements.map((el) => {
+ const expectedElement = map_expectedElements.get(el.id);
+ if (expectedElement) {
+ const pickedAttrs: Record = {};
+
+ for (const key of Object.keys(expectedElement)) {
+ if (key === "selected") {
+ delete expectedElement.selected;
+ continue;
+ }
+ pickedAttrs[key] = (el as any)[key];
+ }
+
+ if (ORIG_ID in expectedElement) {
+ // @ts-ignore
+ pickedAttrs[ORIG_ID] = (el as any)[ORIG_ID];
+ }
+
+ return pickedAttrs;
+ }
+ return el;
+ });
+
+ try {
+ // testing order separately for even easier diffs
+ expect(actualElements.map((x) => x.id)).toEqual(
+ expectedElementsWithIds.map((x) => x.id),
+ );
+ } catch (err: any) {
+ let errStr = "\n\nmismatched element order\n\n";
+
+ errStr += `actual: ${ansi.lightGray(
+ `[${err.actual
+ .map((id: string, index: number) => {
+ const act = actualElements[index];
+
+ return `${
+ id === err.expected[index] ? ansi.green(id) : ansi.red(id)
+ } (${act.type.slice(0, 4)}${
+ ORIG_ID in act ? ` ↳ ${(act as any)[ORIG_ID]}` : ""
+ })`;
+ })
+ .join(", ")}]`,
+ )}\n${ansi.lightGray(
+ `expected: [${err.expected
+ .map((exp: string, index: number) => {
+ const expEl = actualElements.find((el) => el.id === exp);
+ const origEl =
+ expEl &&
+ actualElements.find((el) => el.id === (expEl as any)[ORIG_ID]);
+ return expEl
+ ? `${
+ exp === err.actual[index]
+ ? ansi.green(expEl.id)
+ : ansi.red(expEl.id)
+ } (${expEl.type.slice(0, 4)}${origEl ? ` ↳ ${origEl.id}` : ""})`
+ : exp;
+ })
+ .join(", ")}]\n`,
+ )}`;
+
+ const error = new Error(errStr);
+ const stack = err.stack.split("\n");
+ stack.splice(1, 1);
+ error.stack = stack.join("\n");
+ throw error;
+ }
+
+ expect(mappedActualElements).toEqual(
+ expect.arrayContaining(expectedElementsWithIds),
+ );
+
+ expect(h.state.selectedElementIds).toEqual(selectedElementIds);
+};
diff --git a/packages/excalidraw/tests/zindex.test.tsx b/packages/excalidraw/tests/zindex.test.tsx
index 66599ac860..732644eb35 100644
--- a/packages/excalidraw/tests/zindex.test.tsx
+++ b/packages/excalidraw/tests/zindex.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
import ReactDOM from "react-dom";
-import { act, render } from "./test-utils";
+import { act, getCloneByOrigId, render } from "./test-utils";
import { Excalidraw } from "../index";
import { reseed } from "../random";
import {
@@ -916,9 +916,9 @@ describe("z-index manipulation", () => {
API.executeAction(actionDuplicateSelection);
expect(h.elements).toMatchObject([
{ id: "A" },
- { id: "A_copy" },
+ { id: getCloneByOrigId("A").id },
{ id: "B" },
- { id: "B_copy" },
+ { id: getCloneByOrigId("B").id },
]);
populateElements([
@@ -930,12 +930,12 @@ describe("z-index manipulation", () => {
{ id: "A" },
{ id: "B" },
{
- id: "A_copy",
+ id: getCloneByOrigId("A").id,
groupIds: [expect.stringMatching(/.{3,}/)],
},
{
- id: "B_copy",
+ id: getCloneByOrigId("B").id,
groupIds: [expect.stringMatching(/.{3,}/)],
},
@@ -951,12 +951,12 @@ describe("z-index manipulation", () => {
{ id: "A" },
{ id: "B" },
{
- id: "A_copy",
+ id: getCloneByOrigId("A").id,
groupIds: [expect.stringMatching(/.{3,}/)],
},
{
- id: "B_copy",
+ id: getCloneByOrigId("B").id,
groupIds: [expect.stringMatching(/.{3,}/)],
},
@@ -972,10 +972,10 @@ describe("z-index manipulation", () => {
expect(h.elements.map((element) => element.id)).toEqual([
"A",
"B",
- "A_copy",
- "B_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
"C",
- "C_copy",
+ getCloneByOrigId("C").id,
]);
populateElements([
@@ -988,12 +988,12 @@ describe("z-index manipulation", () => {
expect(h.elements.map((element) => element.id)).toEqual([
"A",
"B",
- "A_copy",
- "B_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
"C",
"D",
- "C_copy",
- "D_copy",
+ getCloneByOrigId("C").id,
+ getCloneByOrigId("D").id,
]);
populateElements(
@@ -1010,10 +1010,10 @@ describe("z-index manipulation", () => {
expect(h.elements.map((element) => element.id)).toEqual([
"A",
"B",
- "A_copy",
- "B_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
"C",
- "C_copy",
+ getCloneByOrigId("C").id,
]);
populateElements(
@@ -1031,9 +1031,9 @@ describe("z-index manipulation", () => {
"A",
"B",
"C",
- "A_copy",
- "B_copy",
- "C_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
+ getCloneByOrigId("C").id,
]);
populateElements(
@@ -1054,15 +1054,15 @@ describe("z-index manipulation", () => {
"A",
"B",
"C",
- "A_copy",
- "B_copy",
- "C_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
+ getCloneByOrigId("C").id,
"D",
"E",
"F",
- "D_copy",
- "E_copy",
- "F_copy",
+ getCloneByOrigId("D").id,
+ getCloneByOrigId("E").id,
+ getCloneByOrigId("F").id,
]);
populateElements(
@@ -1076,7 +1076,7 @@ describe("z-index manipulation", () => {
API.executeAction(actionDuplicateSelection);
expect(h.elements.map((element) => element.id)).toEqual([
"A",
- "A_copy",
+ getCloneByOrigId("A").id,
"B",
"C",
]);
@@ -1093,7 +1093,7 @@ describe("z-index manipulation", () => {
expect(h.elements.map((element) => element.id)).toEqual([
"A",
"B",
- "B_copy",
+ getCloneByOrigId("B").id,
"C",
]);
@@ -1108,9 +1108,9 @@ describe("z-index manipulation", () => {
API.executeAction(actionDuplicateSelection);
expect(h.elements.map((element) => element.id)).toEqual([
"A",
- "A_copy",
+ getCloneByOrigId("A").id,
"B",
- "B_copy",
+ getCloneByOrigId("B").id,
"C",
]);
});
@@ -1125,8 +1125,8 @@ describe("z-index manipulation", () => {
expect(h.elements.map((element) => element.id)).toEqual([
"A",
"C",
- "A_copy",
- "C_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("C").id,
"B",
]);
});
@@ -1144,9 +1144,9 @@ describe("z-index manipulation", () => {
"A",
"B",
"C",
- "A_copy",
- "B_copy",
- "C_copy",
+ getCloneByOrigId("A").id,
+ getCloneByOrigId("B").id,
+ getCloneByOrigId("C").id,
"D",
]);
});
diff --git a/packages/excalidraw/utility-types.ts b/packages/excalidraw/utility-types.ts
index f7872393e5..d4804d1957 100644
--- a/packages/excalidraw/utility-types.ts
+++ b/packages/excalidraw/utility-types.ts
@@ -65,3 +65,6 @@ export type MakeBrand = {
/** Maybe just promise or already fulfilled one! */
export type MaybePromise = T | Promise;
+
+// get union of all keys from the union of types
+export type AllPossibleKeys = T extends any ? keyof T : never;
diff --git a/packages/excalidraw/utils.ts b/packages/excalidraw/utils.ts
index e4431ddd1f..6a682e9cfa 100644
--- a/packages/excalidraw/utils.ts
+++ b/packages/excalidraw/utils.ts
@@ -1240,3 +1240,6 @@ export class PromisePool {
export const escapeDoubleQuotes = (str: string) => {
return str.replace(/"/g, """);
};
+
+export const castArray = (value: T | T[]): T[] =>
+ Array.isArray(value) ? value : [value];
diff --git a/yarn.lock b/yarn.lock
index 79ad50f23e..a71ad5738d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3916,6 +3916,11 @@ ansi-styles@^6.0.0, ansi-styles@^6.1.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+ansicolor@2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/ansicolor/-/ansicolor-2.0.3.tgz#ec4448ae5baf8c2d62bf2dad52eac06ba0b5ea21"
+ integrity sha512-pzusTqk9VHrjgMCcTPDTTvfJfx6Q3+L5tQ6yKC8Diexmoit4YROTFIkxFvRTNL9y5s0Q8HrSrgerCD5bIC+Kiw==
+
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"