Jelajahi Sumber

feat: bump element version on z-index change (#3483)

* feat: bump element version on z-index change

* update snaps

* update changelog
David Luzar 4 tahun lalu
induk
melakukan
4ef7cb7365

+ 11 - 0
src/element/mutateElement.ts

@@ -114,3 +114,14 @@ export const newElementWith = <TElement extends ExcalidrawElement>(
     versionNonce: randomInteger(),
   };
 };
+
+/**
+ * Mutates element and updates `version` & `versionNonce`.
+ *
+ * NOTE: does not trigger re-render.
+ */
+export const bumpVersion = (element: Mutable<ExcalidrawElement>) => {
+  element.version = element.version + 1;
+  element.versionNonce = randomInteger();
+  return element;
+};

+ 1 - 1
src/packages/excalidraw/CHANGELOG.md

@@ -44,9 +44,9 @@ Please add the latest change on the top under the correct section.
 
 ### Fixes
 
+- Changing z-index of elements (sorting them below/above other elements) now updates their `version` and `versionNonce` (only applies to the selected elements). This fix will allow you account for z-index changes if you're syncing just the elements that changed (and not the whole scene) [#3483](https://github.com/excalidraw/excalidraw/pull/3483).
 - Only handle cut/paste events inside excalidraw [#3484](https://github.com/excalidraw/excalidraw/pull/3484).
 - Make history local to a given Excalidraw instance. This fixes a case where history was getting shared when you have multiple Excalidraw components on the same page [#3481](https://github.com/excalidraw/excalidraw/pull/3481).
-
 - Use active Excalidraw component when editing text. This fixes a case where text editing was not working when you have multiple Excalidraw components on the same page [#3478](https://github.com/excalidraw/excalidraw/pull/3478).
 - When switching theme, apply it only to the active Excalidraw component. This fixes a case where the theme was getting applied to the first Excalidraw component if you had multiple Excalidraw components on the same page [#3446](https://github.com/excalidraw/excalidraw/pull/3446)
 

+ 16 - 16
src/tests/__snapshots__/contextmenu.test.tsx.snap

@@ -287,8 +287,8 @@ Object {
   "strokeStyle": "solid",
   "strokeWidth": 1,
   "type": "rectangle",
-  "version": 2,
-  "versionNonce": 449462985,
+  "version": 3,
+  "versionNonce": 2019559783,
   "width": 20,
   "x": -10,
   "y": 0,
@@ -456,8 +456,8 @@ Object {
           "strokeStyle": "solid",
           "strokeWidth": 1,
           "type": "rectangle",
-          "version": 2,
-          "versionNonce": 449462985,
+          "version": 3,
+          "versionNonce": 2019559783,
           "width": 20,
           "x": -10,
           "y": 0,
@@ -595,8 +595,8 @@ Object {
   "strokeStyle": "solid",
   "strokeWidth": 1,
   "type": "rectangle",
-  "version": 2,
-  "versionNonce": 449462985,
+  "version": 3,
+  "versionNonce": 2019559783,
   "width": 20,
   "x": -10,
   "y": 0,
@@ -764,8 +764,8 @@ Object {
           "strokeStyle": "solid",
           "strokeWidth": 1,
           "type": "rectangle",
-          "version": 2,
-          "versionNonce": 449462985,
+          "version": 3,
+          "versionNonce": 2019559783,
           "width": 20,
           "x": -10,
           "y": 0,
@@ -2531,8 +2531,8 @@ Object {
   "strokeStyle": "solid",
   "strokeWidth": 1,
   "type": "rectangle",
-  "version": 2,
-  "versionNonce": 401146281,
+  "version": 3,
+  "versionNonce": 2019559783,
   "width": 20,
   "x": 20,
   "y": 30,
@@ -2703,8 +2703,8 @@ Object {
           "strokeStyle": "solid",
           "strokeWidth": 1,
           "type": "rectangle",
-          "version": 2,
-          "versionNonce": 401146281,
+          "version": 3,
+          "versionNonce": 2019559783,
           "width": 20,
           "x": 20,
           "y": 30,
@@ -2839,8 +2839,8 @@ Object {
   "strokeStyle": "solid",
   "strokeWidth": 1,
   "type": "rectangle",
-  "version": 2,
-  "versionNonce": 401146281,
+  "version": 3,
+  "versionNonce": 2019559783,
   "width": 20,
   "x": 20,
   "y": 30,
@@ -3011,8 +3011,8 @@ Object {
           "strokeStyle": "solid",
           "strokeWidth": 1,
           "type": "rectangle",
-          "version": 2,
-          "versionNonce": 401146281,
+          "version": 3,
+          "versionNonce": 2019559783,
           "width": 20,
           "x": 20,
           "y": 30,

+ 29 - 9
src/zindex.ts

@@ -1,3 +1,4 @@
+import { bumpVersion } from "./element/mutateElement";
 import { ExcalidrawElement } from "./element/types";
 import { getElementsInGroup } from "./groups";
 import { AppState } from "./types";
@@ -51,7 +52,7 @@ const toContiguousGroups = (array: number[]) => {
  */
 const getTargetIndex = (
   appState: AppState,
-  elements: ExcalidrawElement[],
+  elements: readonly ExcalidrawElement[],
   boundaryIndex: number,
   direction: "left" | "right",
 ) => {
@@ -117,12 +118,24 @@ const getTargetIndex = (
   return candidateIndex;
 };
 
+const getTargetElementsMap = (
+  elements: readonly ExcalidrawElement[],
+  indices: number[],
+) => {
+  return indices.reduce((acc, index) => {
+    const element = elements[index];
+    acc[element.id] = element;
+    return acc;
+  }, {} as Record<string, ExcalidrawElement>);
+};
+
 const shiftElements = (
   appState: AppState,
-  elements: ExcalidrawElement[],
+  elements: readonly ExcalidrawElement[],
   direction: "left" | "right",
 ) => {
   const indicesToMove = getIndicesToMove(elements, appState);
+  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
   let groupedIndices = toContiguousGroups(indicesToMove);
 
   if (direction === "right") {
@@ -175,7 +188,12 @@ const shiftElements = (
           ];
   });
 
-  return elements;
+  return elements.map((element) => {
+    if (targetElementsMap[element.id]) {
+      return bumpVersion(element);
+    }
+    return element;
+  });
 };
 
 const shiftElementsToEnd = (
@@ -184,7 +202,7 @@ const shiftElementsToEnd = (
   direction: "left" | "right",
 ) => {
   const indicesToMove = getIndicesToMove(elements, appState);
-  const targetElements: ExcalidrawElement[] = [];
+  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
   const displacedElements: ExcalidrawElement[] = [];
 
   let leadingIndex: number;
@@ -222,13 +240,15 @@ const shiftElementsToEnd = (
   }
 
   for (let index = leadingIndex; index < trailingIndex + 1; index++) {
-    if (indicesToMove.includes(index)) {
-      targetElements.push(elements[index]);
-    } else {
+    if (!indicesToMove.includes(index)) {
       displacedElements.push(elements[index]);
     }
   }
 
+  const targetElements = Object.values(targetElementsMap).map((element) => {
+    return bumpVersion(element);
+  });
+
   const leadingElements = elements.slice(0, leadingIndex);
   const trailingElements = elements.slice(trailingIndex + 1);
 
@@ -254,14 +274,14 @@ export const moveOneLeft = (
   elements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return shiftElements(appState, elements.slice(), "left");
+  return shiftElements(appState, elements, "left");
 };
 
 export const moveOneRight = (
   elements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return shiftElements(appState, elements.slice(), "right");
+  return shiftElements(appState, elements, "right");
 };
 
 export const moveAllLeft = (