mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Duplicate fixes
This commit is contained in:
parent
f05470736b
commit
d05c163aec
9 changed files with 61 additions and 252 deletions
|
@ -52,7 +52,8 @@ export const actionDuplicateSelection = register({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const duplicatedElements = duplicateElements(elements, {
|
let { newElements: duplicatedElements, elementsWithClones: nextElements } =
|
||||||
|
duplicateElements(elements, {
|
||||||
idsOfElementsToDuplicate: arrayToMap(
|
idsOfElementsToDuplicate: arrayToMap(
|
||||||
getSelectedElements(elements, appState, {
|
getSelectedElements(elements, appState, {
|
||||||
includeBoundTextElement: true,
|
includeBoundTextElement: true,
|
||||||
|
@ -67,10 +68,6 @@ export const actionDuplicateSelection = register({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
let nextElements = (elements as ExcalidrawElement[])
|
|
||||||
.slice()
|
|
||||||
.concat(duplicatedElements);
|
|
||||||
|
|
||||||
if (app.props.onDuplicate && nextElements) {
|
if (app.props.onDuplicate && nextElements) {
|
||||||
const mappedElements = app.props.onDuplicate(nextElements, elements);
|
const mappedElements = app.props.onDuplicate(nextElements, elements);
|
||||||
if (mappedElements) {
|
if (mappedElements) {
|
||||||
|
|
|
@ -3219,7 +3219,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
const [gridX, gridY] = getGridPoint(dx, dy, this.getEffectiveGridSize());
|
const [gridX, gridY] = getGridPoint(dx, dy, this.getEffectiveGridSize());
|
||||||
|
|
||||||
const newElements = duplicateElements(
|
const { newElements } = duplicateElements(
|
||||||
elements.map((element) => {
|
elements.map((element) => {
|
||||||
return newElementWith(element, {
|
return newElementWith(element, {
|
||||||
x: element.x + gridX - minX,
|
x: element.x + gridX - minX,
|
||||||
|
@ -8419,6 +8419,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
pointerDownState.hit.hasBeenDuplicated = true;
|
pointerDownState.hit.hasBeenDuplicated = true;
|
||||||
|
|
||||||
|
const elements = this.scene.getElementsIncludingDeleted();
|
||||||
const hitElement = pointerDownState.hit.element;
|
const hitElement = pointerDownState.hit.element;
|
||||||
const selectedElements = this.scene.getSelectedElements({
|
const selectedElements = this.scene.getSelectedElements({
|
||||||
selectedElementIds: this.state.selectedElementIds,
|
selectedElementIds: this.state.selectedElementIds,
|
||||||
|
@ -8431,13 +8432,18 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
) {
|
) {
|
||||||
selectedElements.push(hitElement);
|
selectedElements.push(hitElement);
|
||||||
}
|
}
|
||||||
const clonedElements = duplicateElements(selectedElements, {
|
|
||||||
|
const { newElements: clonedElements, elementsWithClones } =
|
||||||
|
duplicateElements(elements, {
|
||||||
appState: this.state,
|
appState: this.state,
|
||||||
randomizeSeed: true,
|
randomizeSeed: true,
|
||||||
|
idsOfElementsToDuplicate: new Map(
|
||||||
|
selectedElements.map((el) => [el.id, el]),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const nextSceneElements = syncMovedIndices(
|
const nextSceneElements = syncMovedIndices(
|
||||||
[...clonedElements, ...this.scene.getElementsIncludingDeleted()],
|
elementsWithClones,
|
||||||
arrayToMap(clonedElements),
|
arrayToMap(clonedElements),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,8 @@ export default function LibraryMenuItems({
|
||||||
...item,
|
...item,
|
||||||
// duplicate each library item before inserting on canvas to confine
|
// duplicate each library item before inserting on canvas to confine
|
||||||
// ids and bindings to each library item. See #6465
|
// ids and bindings to each library item. See #6465
|
||||||
elements: duplicateElements(item.elements, { randomizeSeed: true }),
|
elements: duplicateElements(item.elements, { randomizeSeed: true })
|
||||||
|
.newElements,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,7 +46,7 @@ describe("duplicating single elements", () => {
|
||||||
points: [pointFrom<LocalPoint>(1, 2), pointFrom<LocalPoint>(3, 4)],
|
points: [pointFrom<LocalPoint>(1, 2), pointFrom<LocalPoint>(3, 4)],
|
||||||
});
|
});
|
||||||
|
|
||||||
const copy = duplicateElement(null, new Map(), element);
|
const copy = duplicateElement(null, new Map(), element, undefined, true);
|
||||||
|
|
||||||
assertCloneObjects(element, copy);
|
assertCloneObjects(element, copy);
|
||||||
|
|
||||||
|
@ -65,6 +65,8 @@ describe("duplicating single elements", () => {
|
||||||
...element,
|
...element,
|
||||||
id: copy.id,
|
id: copy.id,
|
||||||
seed: copy.seed,
|
seed: copy.seed,
|
||||||
|
version: copy.version,
|
||||||
|
versionNonce: copy.versionNonce,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,7 +152,7 @@ describe("duplicating multiple elements", () => {
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
const origElements = [rectangle1, text1, arrow1, arrow2, text2] as const;
|
const origElements = [rectangle1, text1, arrow1, arrow2, text2] as const;
|
||||||
const clonedElements = duplicateElements(origElements);
|
const { newElements: clonedElements } = duplicateElements(origElements);
|
||||||
|
|
||||||
// generic id in-equality checks
|
// generic id in-equality checks
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
@ -369,7 +371,9 @@ describe("duplicating multiple elements", () => {
|
||||||
groupIds: ["g1"],
|
groupIds: ["g1"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const [clonedRectangle1] = duplicateElements([rectangle1]);
|
const {
|
||||||
|
newElements: [clonedRectangle1],
|
||||||
|
} = duplicateElements([rectangle1]);
|
||||||
|
|
||||||
expect(typeof clonedRectangle1.groupIds[0]).toBe("string");
|
expect(typeof clonedRectangle1.groupIds[0]).toBe("string");
|
||||||
expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]);
|
expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]);
|
||||||
|
|
|
@ -130,7 +130,8 @@ export const duplicateElements = (
|
||||||
const duplicatedElementsMap = new Map<string, ExcalidrawElement>();
|
const duplicatedElementsMap = new Map<string, ExcalidrawElement>();
|
||||||
const elementsMap = arrayToMap(elements) as ElementsMap;
|
const elementsMap = arrayToMap(elements) as ElementsMap;
|
||||||
const _idsOfElementsToDuplicate =
|
const _idsOfElementsToDuplicate =
|
||||||
opts?.idsOfElementsToDuplicate ?? new Set(elements.map((el) => el.id));
|
opts?.idsOfElementsToDuplicate ??
|
||||||
|
new Map(elements.map((el) => [el.id, el]));
|
||||||
|
|
||||||
elements = normalizeElementOrder(elements);
|
elements = normalizeElementOrder(elements);
|
||||||
|
|
||||||
|
@ -397,138 +398,12 @@ export const duplicateElements = (
|
||||||
oldIdToDuplicatedId,
|
oldIdToDuplicatedId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return newElements;
|
return {
|
||||||
|
newElements,
|
||||||
|
elementsWithClones,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones elements, regenerating their ids (including bindings) and group ids.
|
|
||||||
*
|
|
||||||
* If bindings don't exist in the elements array, they are removed. Therefore,
|
|
||||||
* it's advised to supply the whole elements array, or sets of elements that
|
|
||||||
* are encapsulated (such as library items), if the purpose is to retain
|
|
||||||
* bindings to the cloned elements intact.
|
|
||||||
*
|
|
||||||
* NOTE by default does not randomize or regenerate anything except the id.
|
|
||||||
*/
|
|
||||||
// export const duplicateElements = (
|
|
||||||
// elements: readonly ExcalidrawElement[],
|
|
||||||
// opts?: {
|
|
||||||
// /** NOTE also updates version flags and `updated` */
|
|
||||||
// randomizeSeed?: boolean;
|
|
||||||
// overrides?: (element: ExcalidrawElement) => Partial<ExcalidrawElement>;
|
|
||||||
// },
|
|
||||||
// ) => {
|
|
||||||
// const clonedElements: ExcalidrawElement[] = [];
|
|
||||||
|
|
||||||
// const origElementsMap = arrayToMap(elements);
|
|
||||||
|
|
||||||
// // used for for migrating old ids to new ids
|
|
||||||
// const elementNewIdsMap = new Map<
|
|
||||||
// /* orig */ ExcalidrawElement["id"],
|
|
||||||
// /* new */ ExcalidrawElement["id"]
|
|
||||||
// >();
|
|
||||||
|
|
||||||
// const maybeGetNewIdFor = (id: ExcalidrawElement["id"]) => {
|
|
||||||
// // if we've already migrated the element id, return the new one directly
|
|
||||||
// if (elementNewIdsMap.has(id)) {
|
|
||||||
// return elementNewIdsMap.get(id)!;
|
|
||||||
// }
|
|
||||||
// // 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 = randomId();
|
|
||||||
// elementNewIdsMap.set(id, newId);
|
|
||||||
// return newId;
|
|
||||||
// }
|
|
||||||
// // if old element doesn't exist, return null to mark it for removal
|
|
||||||
// return null;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const groupNewIdsMap = new Map</* orig */ GroupId, /* new */ GroupId>();
|
|
||||||
|
|
||||||
// for (const element of elements) {
|
|
||||||
// let clonedElement: Mutable<ExcalidrawElement> = _deepCopyElement(element);
|
|
||||||
|
|
||||||
// if (opts?.overrides) {
|
|
||||||
// clonedElement = Object.assign(
|
|
||||||
// clonedElement,
|
|
||||||
// opts.overrides(clonedElement),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// clonedElement.id = maybeGetNewIdFor(element.id)!;
|
|
||||||
// if (isTestEnv()) {
|
|
||||||
// __test__defineOrigId(clonedElement, element.id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (opts?.randomizeSeed) {
|
|
||||||
// clonedElement.seed = randomInteger();
|
|
||||||
// bumpVersion(clonedElement);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (clonedElement.groupIds) {
|
|
||||||
// clonedElement.groupIds = clonedElement.groupIds.map((groupId) => {
|
|
||||||
// if (!groupNewIdsMap.has(groupId)) {
|
|
||||||
// groupNewIdsMap.set(groupId, randomId());
|
|
||||||
// }
|
|
||||||
// return groupNewIdsMap.get(groupId)!;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if ("containerId" in clonedElement && clonedElement.containerId) {
|
|
||||||
// const newContainerId = maybeGetNewIdFor(clonedElement.containerId);
|
|
||||||
// clonedElement.containerId = newContainerId;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if ("boundElements" in clonedElement && clonedElement.boundElements) {
|
|
||||||
// clonedElement.boundElements = clonedElement.boundElements.reduce(
|
|
||||||
// (
|
|
||||||
// acc: Mutable<NonNullable<ExcalidrawElement["boundElements"]>>,
|
|
||||||
// binding,
|
|
||||||
// ) => {
|
|
||||||
// const newBindingId = maybeGetNewIdFor(binding.id);
|
|
||||||
// if (newBindingId) {
|
|
||||||
// acc.push({ ...binding, id: newBindingId });
|
|
||||||
// }
|
|
||||||
// return acc;
|
|
||||||
// },
|
|
||||||
// [],
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if ("endBinding" in clonedElement && clonedElement.endBinding) {
|
|
||||||
// const newEndBindingId = maybeGetNewIdFor(
|
|
||||||
// clonedElement.endBinding.elementId,
|
|
||||||
// );
|
|
||||||
// clonedElement.endBinding = newEndBindingId
|
|
||||||
// ? {
|
|
||||||
// ...clonedElement.endBinding,
|
|
||||||
// elementId: newEndBindingId,
|
|
||||||
// }
|
|
||||||
// : null;
|
|
||||||
// }
|
|
||||||
// if ("startBinding" in clonedElement && clonedElement.startBinding) {
|
|
||||||
// const newEndBindingId = maybeGetNewIdFor(
|
|
||||||
// clonedElement.startBinding.elementId,
|
|
||||||
// );
|
|
||||||
// clonedElement.startBinding = newEndBindingId
|
|
||||||
// ? {
|
|
||||||
// ...clonedElement.startBinding,
|
|
||||||
// elementId: newEndBindingId,
|
|
||||||
// }
|
|
||||||
// : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (clonedElement.frameId) {
|
|
||||||
// clonedElement.frameId = maybeGetNewIdFor(clonedElement.frameId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// clonedElements.push(clonedElement);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return clonedElements;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Simplified deep clone for the purpose of cloning ExcalidrawElement.
|
// Simplified deep clone for the purpose of cloning ExcalidrawElement.
|
||||||
//
|
//
|
||||||
// Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set,
|
// Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set,
|
||||||
|
|
|
@ -2606,8 +2606,8 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 4,
|
"version": 5,
|
||||||
"versionNonce": 238820263,
|
"versionNonce": 400692809,
|
||||||
"width": 20,
|
"width": 20,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
|
|
|
@ -15172,9 +15172,11 @@ History {
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id61": true,
|
"id61": true,
|
||||||
},
|
},
|
||||||
|
"selectedLinearElementId": "id61",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"selectedElementIds": {},
|
"selectedElementIds": {},
|
||||||
|
"selectedLinearElementId": null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -18946,7 +18948,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 3,
|
"version": 4,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
|
@ -18980,7 +18982,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 3,
|
"version": 4,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 110,
|
"x": 110,
|
||||||
"y": 110,
|
"y": 110,
|
||||||
|
@ -19014,7 +19016,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 6,
|
"version": 7,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
|
@ -19048,7 +19050,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 6,
|
"version": 7,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 110,
|
"x": 110,
|
||||||
"y": 110,
|
"y": 110,
|
||||||
|
|
|
@ -1,73 +1,5 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`duplicate element on move when ALT is clicked > rectangle 5`] = `
|
|
||||||
{
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [],
|
|
||||||
"height": 50,
|
|
||||||
"id": "id2",
|
|
||||||
"index": "a0",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": {
|
|
||||||
"type": 3,
|
|
||||||
},
|
|
||||||
"seed": 238820263,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"updated": 1,
|
|
||||||
"version": 5,
|
|
||||||
"versionNonce": 400692809,
|
|
||||||
"width": 30,
|
|
||||||
"x": 30,
|
|
||||||
"y": 20,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`duplicate element on move when ALT is clicked > rectangle 6`] = `
|
|
||||||
{
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [],
|
|
||||||
"height": 50,
|
|
||||||
"id": "id0",
|
|
||||||
"index": "a1",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": {
|
|
||||||
"type": 3,
|
|
||||||
},
|
|
||||||
"seed": 1278240551,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"updated": 1,
|
|
||||||
"version": 6,
|
|
||||||
"versionNonce": 23633383,
|
|
||||||
"width": 30,
|
|
||||||
"x": -10,
|
|
||||||
"y": 60,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`move element > rectangle 5`] = `
|
exports[`move element > rectangle 5`] = `
|
||||||
{
|
{
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
|
|
|
@ -2139,7 +2139,7 @@ History {
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 10,
|
"height": 10,
|
||||||
"index": "a0",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
|
@ -2153,8 +2153,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 10,
|
"x": 20,
|
||||||
"y": 10,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -2164,12 +2164,10 @@ History {
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id0" => Delta {
|
"id0" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"index": "a1",
|
|
||||||
"x": 20,
|
"x": 20,
|
||||||
"y": 20,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"index": "a0",
|
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
},
|
},
|
||||||
|
@ -10631,7 +10629,7 @@ History {
|
||||||
"id7",
|
"id7",
|
||||||
],
|
],
|
||||||
"height": 10,
|
"height": 10,
|
||||||
"index": "a0",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
|
@ -10645,8 +10643,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 10,
|
"x": 20,
|
||||||
"y": 10,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10664,7 +10662,7 @@ History {
|
||||||
"id7",
|
"id7",
|
||||||
],
|
],
|
||||||
"height": 10,
|
"height": 10,
|
||||||
"index": "a1",
|
"index": "a4",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
|
@ -10678,8 +10676,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 30,
|
"x": 40,
|
||||||
"y": 10,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10697,7 +10695,7 @@ History {
|
||||||
"id7",
|
"id7",
|
||||||
],
|
],
|
||||||
"height": 10,
|
"height": 10,
|
||||||
"index": "a2",
|
"index": "a5",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
|
@ -10711,8 +10709,8 @@ History {
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 50,
|
"x": 60,
|
||||||
"y": 10,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
|
@ -10722,36 +10720,30 @@ History {
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id0" => Delta {
|
"id0" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"index": "a3",
|
|
||||||
"x": 20,
|
"x": 20,
|
||||||
"y": 20,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"index": "a0",
|
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id1" => Delta {
|
"id1" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"index": "a4",
|
|
||||||
"x": 40,
|
"x": 40,
|
||||||
"y": 20,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"index": "a1",
|
|
||||||
"x": 30,
|
"x": 30,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id2" => Delta {
|
"id2" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"index": "a5",
|
|
||||||
"x": 60,
|
"x": 60,
|
||||||
"y": 20,
|
"y": 20,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"index": "a2",
|
|
||||||
"x": 50,
|
"x": 50,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue