Browse Source

feat: do not delete frame children on frame delete (#9011)

David Luzar 6 months ago
parent
commit
ae6bee3403

+ 87 - 36
packages/excalidraw/actions/actionDeleteSelected.tsx

@@ -7,7 +7,7 @@ import { getNonDeletedElements } from "../element";
 import type { ExcalidrawElement } from "../element/types";
 import type { AppClassProperties, AppState } from "../types";
 import { mutateElement, newElementWith } from "../element/mutateElement";
-import { getElementsInGroup } from "../groups";
+import { getElementsInGroup, selectGroupsForSelectedElements } from "../groups";
 import { LinearElementEditor } from "../element/linearElementEditor";
 import { fixBindingsAfterDeletion } from "../element/binding";
 import {
@@ -33,48 +33,99 @@ const deleteSelectedElements = (
     ).map((el) => el.id),
   );
 
-  return {
-    elements: elements.map((el) => {
-      if (appState.selectedElementIds[el.id]) {
-        if (el.boundElements) {
-          el.boundElements.forEach((candidate) => {
-            const bound = app.scene
-              .getNonDeletedElementsMap()
-              .get(candidate.id);
-            if (bound && isElbowArrow(bound)) {
-              mutateElement(bound, {
-                startBinding:
-                  el.id === bound.startBinding?.elementId
-                    ? null
-                    : bound.startBinding,
-                endBinding:
-                  el.id === bound.endBinding?.elementId
-                    ? null
-                    : bound.endBinding,
-              });
-              mutateElbowArrow(bound, elementsMap, bound.points);
-            }
-          });
-        }
-        return newElementWith(el, { isDeleted: true });
+  const selectedElementIds: Record<ExcalidrawElement["id"], true> = {};
+
+  let shouldSelectEditingGroup = true;
+
+  const nextElements = elements.map((el) => {
+    if (appState.selectedElementIds[el.id]) {
+      if (el.boundElements) {
+        el.boundElements.forEach((candidate) => {
+          const bound = app.scene.getNonDeletedElementsMap().get(candidate.id);
+          if (bound && isElbowArrow(bound)) {
+            mutateElement(bound, {
+              startBinding:
+                el.id === bound.startBinding?.elementId
+                  ? null
+                  : bound.startBinding,
+              endBinding:
+                el.id === bound.endBinding?.elementId ? null : bound.endBinding,
+            });
+            mutateElbowArrow(bound, elementsMap, bound.points);
+          }
+        });
       }
+      return newElementWith(el, { isDeleted: true });
+    }
+
+    // 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;
+      return newElementWith(el, { frameId: null });
+    }
+
+    if (isBoundToContainer(el) && appState.selectedElementIds[el.containerId]) {
+      return newElementWith(el, { isDeleted: true });
+    }
+    return el;
+  });
+
+  let nextEditingGroupId = appState.editingGroupId;
 
-      if (el.frameId && framesToBeDeleted.has(el.frameId)) {
-        return newElementWith(el, { isDeleted: true });
+  // select next eligible element in currently editing group or supergroup
+  if (shouldSelectEditingGroup && appState.editingGroupId) {
+    const elems = getElementsInGroup(
+      nextElements,
+      appState.editingGroupId,
+    ).filter((el) => !el.isDeleted);
+    if (elems.length > 1) {
+      if (elems[0]) {
+        selectedElementIds[elems[0].id] = true;
+      }
+    } else {
+      nextEditingGroupId = null;
+      if (elems[0]) {
+        selectedElementIds[elems[0].id] = true;
       }
 
-      if (
-        isBoundToContainer(el) &&
-        appState.selectedElementIds[el.containerId]
-      ) {
-        return newElementWith(el, { isDeleted: true });
+      const lastElementInGroup = elems[0];
+      if (lastElementInGroup) {
+        const editingGroupIdx = lastElementInGroup.groupIds.findIndex(
+          (groupId) => {
+            return groupId === appState.editingGroupId;
+          },
+        );
+        const superGroupId = lastElementInGroup.groupIds[editingGroupIdx + 1];
+        if (superGroupId) {
+          const elems = getElementsInGroup(nextElements, superGroupId).filter(
+            (el) => !el.isDeleted,
+          );
+          if (elems.length > 1) {
+            nextEditingGroupId = superGroupId;
+
+            elems.forEach((el) => {
+              selectedElementIds[el.id] = true;
+            });
+          }
+        }
       }
-      return el;
-    }),
+    }
+  }
+
+  return {
+    elements: nextElements,
     appState: {
       ...appState,
-      selectedElementIds: {},
-      selectedGroupIds: {},
+      ...selectGroupsForSelectedElements(
+        {
+          selectedElementIds,
+          editingGroupId: nextEditingGroupId,
+        },
+        nextElements,
+        appState,
+        null,
+      ),
     },
   };
 };

+ 4 - 0
packages/excalidraw/groups.ts

@@ -105,6 +105,10 @@ export const selectGroupsForSelectedElements = (function () {
     const groupElementsIndex: Record<GroupId, string[]> = {};
     const selectedElementIdsInGroups = elements.reduce(
       (acc: Record<string, true>, element) => {
+        if (element.isDeleted) {
+          return acc;
+        }
+
         const groupId = element.groupIds.find((id) => selectedGroupIds[id]);
 
         if (groupId) {

+ 10 - 2
packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap

@@ -4307,14 +4307,20 @@ History {
       "appStateChange": AppStateChange {
         "delta": Delta {
           "deleted": {
+            "editingGroupId": null,
             "selectedElementIds": {
               "id1": true,
             },
+            "selectedGroupIds": {
+              "id4": false,
+            },
           },
           "inserted": {
+            "editingGroupId": "id4",
             "selectedElementIds": {
               "id0": true,
             },
+            "selectedGroupIds": {},
           },
         },
       },
@@ -4337,14 +4343,16 @@ History {
       "appStateChange": AppStateChange {
         "delta": Delta {
           "deleted": {
-            "editingGroupId": null,
             "selectedElementIds": {},
+            "selectedGroupIds": {},
           },
           "inserted": {
-            "editingGroupId": "id4",
             "selectedElementIds": {
               "id1": true,
             },
+            "selectedGroupIds": {
+              "id4": false,
+            },
           },
         },
       },