Pārlūkot izejas kodu

fix: adding partial group to frame (#9014)

* prevent new frame from including partial groups

* separate wrapped partial group
Ryan Di 6 mēneši atpakaļ
vecāks
revīzija
0bf234fcc9

+ 22 - 0
packages/excalidraw/actions/actionFrame.ts

@@ -12,6 +12,8 @@ import { frameToolIcon } from "../components/icons";
 import { StoreAction } from "../store";
 import { getSelectedElements } from "../scene";
 import { newFrameElement } from "../element/newElement";
+import { getElementsInGroup } from "../groups";
+import { mutateElement } from "../element/mutateElement";
 
 const isSingleFrameSelected = (
   appState: UIAppState,
@@ -174,6 +176,26 @@ export const actionWrapSelectionInFrame = register({
       height: y2 - y1 + PADDING * 2,
     });
 
+    // for a selected partial group, we want to remove it from the remainder of the group
+    if (appState.editingGroupId) {
+      const elementsInGroup = getElementsInGroup(
+        selectedElements,
+        appState.editingGroupId,
+      );
+
+      for (const elementInGroup of elementsInGroup) {
+        const index = elementInGroup.groupIds.indexOf(appState.editingGroupId);
+
+        mutateElement(
+          elementInGroup,
+          {
+            groupIds: elementInGroup.groupIds.slice(0, index),
+          },
+          false,
+        );
+      }
+    }
+
     const nextElements = addElementsToFrame(
       [...app.scene.getElementsIncludingDeleted(), frame],
       selectedElements,

+ 48 - 3
packages/excalidraw/frame.ts

@@ -369,12 +369,57 @@ export const getElementsInNewFrame = (
   frame: ExcalidrawFrameLikeElement,
   elementsMap: ElementsMap,
 ) => {
-  return omitGroupsContainingFrameLikes(
-    elements,
-    getElementsCompletelyInFrame(elements, frame, elementsMap),
+  return omitPartialGroups(
+    omitGroupsContainingFrameLikes(
+      elements,
+      getElementsCompletelyInFrame(elements, frame, elementsMap),
+    ),
+    frame,
+    elementsMap,
   );
 };
 
+export const omitPartialGroups = (
+  elements: ExcalidrawElement[],
+  frame: ExcalidrawFrameLikeElement,
+  allElementsMap: ElementsMap,
+) => {
+  const elementsToReturn = [];
+  const checkedGroups = new Map<string, boolean>();
+
+  for (const element of elements) {
+    let shouldOmit = false;
+    if (element.groupIds.length > 0) {
+      // if some partial group should be omitted, then all elements in that group should be omitted
+      if (element.groupIds.some((gid) => checkedGroups.get(gid))) {
+        shouldOmit = true;
+      } else {
+        const allElementsInGroup = new Set(
+          element.groupIds.flatMap((gid) =>
+            getElementsInGroup(allElementsMap, gid),
+          ),
+        );
+
+        shouldOmit = !elementsAreInFrameBounds(
+          Array.from(allElementsInGroup),
+          frame,
+          allElementsMap,
+        );
+      }
+
+      element.groupIds.forEach((gid) => {
+        checkedGroups.set(gid, shouldOmit);
+      });
+    }
+
+    if (!shouldOmit) {
+      elementsToReturn.push(element);
+    }
+  }
+
+  return elementsToReturn;
+};
+
 export const getContainingFrame = (
   element: ExcalidrawElement,
   elementsMap: ElementsMap,