瀏覽代碼

normalize before replacing

Ryan Di 1 年之前
父節點
當前提交
1e132e33ae
共有 3 個文件被更改,包括 93 次插入108 次删除
  1. 1 4
      src/data/restore.ts
  2. 5 2
      src/scene/Scene.ts
  3. 87 102
      src/zindex.ts

+ 1 - 4
src/data/restore.ts

@@ -43,7 +43,6 @@ import {
   measureBaseline,
 } from "../element/textElement";
 import { normalizeLink } from "./url";
-import { normalizeFractionalIndexing } from "../zindex";
 
 type RestoredAppState = Omit<
   AppState,
@@ -583,9 +582,7 @@ export const restore = (
   elementsConfig?: { refreshDimensions?: boolean; repairBindings?: boolean },
 ): RestoredDataState => {
   return {
-    elements: normalizeFractionalIndexing(
-      restoreElements(data?.elements, localElements, elementsConfig),
-    ),
+    elements: restoreElements(data?.elements, localElements, elementsConfig),
     appState: restoreAppState(data?.appState, localAppState || null),
     files: data?.files || {},
   };

+ 5 - 2
src/scene/Scene.ts

@@ -11,6 +11,7 @@ import { getSelectedElements } from "./selection";
 import { AppState } from "../types";
 import { Assert, SameType } from "../utility-types";
 import { randomInteger } from "../random";
+import { normalizeFractionalIndexing } from "../zindex";
 
 type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
 type ElementKey = ExcalidrawElement | ElementIdKey;
@@ -231,10 +232,12 @@ class Scene {
     nextElements: readonly ExcalidrawElement[],
     mapElementIds = true,
   ) {
-    this.elements = nextElements;
+    const _nextElements = normalizeFractionalIndexing(nextElements);
+
+    this.elements = _nextElements;
     const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
     this.elementsMap.clear();
-    nextElements.forEach((element) => {
+    _nextElements.forEach((element) => {
       if (isFrameLikeElement(element)) {
         nextFrameLikes.push(element);
       }

+ 87 - 102
src/zindex.ts

@@ -1,4 +1,4 @@
-import { bumpVersion } from "./element/mutateElement";
+import { bumpVersion, mutateElement } from "./element/mutateElement";
 import { isFrameLikeElement } from "./element/typeChecks";
 import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types";
 import { getElementsInGroup } from "./groups";
@@ -490,74 +490,75 @@ function shiftElementsAccountingForFrames(
 // -----------------------------------------------------------------------------
 type FractionalIndex = ExcalidrawElement["fractionalIndex"];
 
-const fractionalIndexCompare = {
-  isSmallerThan(indexA: string, indexB: string) {
-    return indexA < indexB;
-  },
-
-  isGreaterThan(indexA: string, indexB: string) {
-    return indexA > indexB;
-  },
-};
-
 const isValidFractionalIndex = (
   index: FractionalIndex,
-  predecessorFractionalIndex: FractionalIndex,
-  successorFractionalIndex: FractionalIndex,
+  predecessor: FractionalIndex,
+  successor: FractionalIndex,
 ) => {
   if (index) {
-    if (predecessorFractionalIndex) {
-      if (successorFractionalIndex) {
-        return (
-          fractionalIndexCompare.isGreaterThan(
-            index,
-            predecessorFractionalIndex,
-          ) &&
-          fractionalIndexCompare.isSmallerThan(index, successorFractionalIndex)
-        );
-      }
-      return fractionalIndexCompare.isGreaterThan(
-        index,
-        predecessorFractionalIndex,
-      );
+    if (!predecessor && !successor) {
+      return index.length > 0;
     }
 
-    if (successorFractionalIndex) {
-      return fractionalIndexCompare.isSmallerThan(
-        index,
-        successorFractionalIndex,
-      );
+    if (!predecessor) {
+      // first element
+      return index < successor!;
     }
 
-    return index.length > 0;
+    if (!successor) {
+      // last element
+      return predecessor! < index;
+    }
   }
 
   return false;
 };
 
 const generateFractionalIndex = (
-  predecessorFractionalIndex: string | null,
-  successorFractionalIndex: string | null,
+  index: FractionalIndex,
+  predecessor: FractionalIndex,
+  successor: FractionalIndex,
 ) => {
-  if (predecessorFractionalIndex && successorFractionalIndex) {
-    if (predecessorFractionalIndex < successorFractionalIndex) {
-      return generateKeyBetween(
-        predecessorFractionalIndex,
-        successorFractionalIndex,
-      );
-    } else if (predecessorFractionalIndex > successorFractionalIndex) {
-      return generateKeyBetween(
-        successorFractionalIndex,
-        predecessorFractionalIndex,
-      );
+  if (index) {
+    if (!predecessor && !successor) {
+      return index;
     }
-    return generateKeyBetween(predecessorFractionalIndex, null);
+
+    if (!predecessor) {
+      // first element in the array
+      return generateKeyBetween(null, successor);
+    }
+
+    if (!successor) {
+      // last element in the array
+      return generateKeyBetween(predecessor, null);
+    }
+
+    // both predecessor and successor exist
+    // insert after predecessor
+    return generateKeyBetween(predecessor, null);
   }
 
-  return generateKeyBetween(
-    predecessorFractionalIndex,
-    successorFractionalIndex,
-  );
+  return generateKeyBetween(null, null);
+};
+
+const compareStrings = (a: string, b: string) => {
+  return a < b ? -1 : 1;
+};
+
+export const orderByFractionalIndex = (allElements: ExcalidrawElement[]) => {
+  return allElements.sort((a, b) => {
+    if (a.fractionalIndex && b.fractionalIndex) {
+      if (a.fractionalIndex < b.fractionalIndex) {
+        return -1;
+      } else if (a.fractionalIndex > b.fractionalIndex) {
+        return 1;
+      }
+      return compareStrings(a.id, b.id);
+    }
+
+    return 0;
+  });
 };
 
 /**
@@ -568,47 +569,39 @@ const generateFractionalIndex = (
 export const normalizeFractionalIndexing = (
   allElements: readonly ExcalidrawElement[],
 ) => {
-  let predecessor = -1;
-  let successor = 1;
-
-  const normalizedElementsMap = arrayToMap(allElements);
+  let pre = -1;
+  let suc = 1;
 
   for (const element of allElements) {
-    const predecessorFractionalIndex =
-      normalizedElementsMap.get(allElements[predecessor]?.id)
-        ?.fractionalIndex || null;
-
-    const successorFractionalIndex =
-      normalizedElementsMap.get(allElements[successor]?.id)?.fractionalIndex ||
-      null;
+    const predecessor = allElements[pre]?.fractionalIndex || null;
+    const successor = allElements[suc]?.fractionalIndex || null;
 
-    try {
-      if (
-        !isValidFractionalIndex(
-          element.fractionalIndex,
-          predecessorFractionalIndex,
-          successorFractionalIndex,
-        )
-      ) {
+    if (
+      !isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
+    ) {
+      try {
         const nextFractionalIndex = generateFractionalIndex(
-          predecessorFractionalIndex,
-          successorFractionalIndex,
+          element.fractionalIndex,
+          predecessor,
+          successor,
         );
 
-        normalizedElementsMap.set(element.id, {
-          ...element,
-          fractionalIndex: nextFractionalIndex,
-        });
+        mutateElement(
+          element,
+          {
+            fractionalIndex: nextFractionalIndex,
+          },
+          false,
+        );
+      } catch (e) {
+        console.error(e);
       }
-    } catch (e) {
-      console.error(e);
     }
-
-    predecessor++;
-    successor++;
+    pre++;
+    suc++;
   }
 
-  return [...normalizedElementsMap.values()];
+  return allElements;
 };
 
 // public API
@@ -618,31 +611,25 @@ export const moveOneLeft = (
   allElements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return normalizeFractionalIndexing(
-    shiftElementsByOne(allElements, appState, "left"),
-  );
+  return shiftElementsByOne(allElements, appState, "left");
 };
 
 export const moveOneRight = (
   allElements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return normalizeFractionalIndexing(
-    shiftElementsByOne(allElements, appState, "right"),
-  );
+  return shiftElementsByOne(allElements, appState, "right");
 };
 
 export const moveAllLeft = (
   allElements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return normalizeFractionalIndexing(
-    shiftElementsAccountingForFrames(
-      allElements,
-      appState,
-      "left",
-      shiftElementsToEnd,
-    ),
+  return shiftElementsAccountingForFrames(
+    allElements,
+    appState,
+    "left",
+    shiftElementsToEnd,
   );
 };
 
@@ -650,12 +637,10 @@ export const moveAllRight = (
   allElements: readonly ExcalidrawElement[],
   appState: AppState,
 ) => {
-  return normalizeFractionalIndexing(
-    shiftElementsAccountingForFrames(
-      allElements,
-      appState,
-      "right",
-      shiftElementsToEnd,
-    ),
+  return shiftElementsAccountingForFrames(
+    allElements,
+    appState,
+    "right",
+    shiftElementsToEnd,
   );
 };