Browse Source

feat: add pasted elements to frame under cursor (#7590)

David Luzar 1 year ago
parent
commit
1e7df58b5b

+ 8 - 2
packages/excalidraw/components/App.tsx

@@ -3099,12 +3099,18 @@ class App extends React.Component<AppProps, AppState> {
       },
     );
 
-    const nextElements = [
+    const allElements = [
       ...this.scene.getElementsIncludingDeleted(),
       ...newElements,
     ];
 
-    this.scene.replaceAllElements(nextElements);
+    const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
+
+    if (topLayerFrame) {
+      addElementsToFrame(allElements, newElements, topLayerFrame);
+    }
+
+    this.scene.replaceAllElements(allElements);
 
     newElements.forEach((newElement) => {
       if (isTextElement(newElement) && isBoundToContainer(newElement)) {

+ 16 - 0
packages/excalidraw/frame.ts

@@ -398,12 +398,28 @@ export const addElementsToFrame = (
 
   const finalElementsToAdd: ExcalidrawElement[] = [];
 
+  const otherFrames = new Set<ExcalidrawFrameLikeElement["id"]>();
+
+  for (const element of elementsToAdd) {
+    if (isFrameLikeElement(element) && element.id !== frame.id) {
+      otherFrames.add(element.id);
+    }
+  }
+
   // - add bound text elements if not already in the array
   // - filter out elements that are already in the frame
   for (const element of omitGroupsContainingFrameLikes(
     allElements,
     elementsToAdd,
   )) {
+    // don't add frames or their children
+    if (
+      isFrameLikeElement(element) ||
+      (element.frameId && otherFrames.has(element.frameId))
+    ) {
+      continue;
+    }
+
     if (!currTargetFrameChildrenMap.has(element.id)) {
       finalElementsToAdd.push(element);
     }

+ 30 - 0
packages/excalidraw/tests/clipboard.test.tsx

@@ -263,3 +263,33 @@ describe("Paste bound text container", () => {
     });
   });
 });
+
+describe("pasting & frames", () => {
+  it("should add pasted elements to frame under cursor", async () => {
+    const frame = API.createElement({
+      type: "frame",
+      width: 100,
+      height: 100,
+      x: 0,
+      y: 0,
+    });
+    const rect = API.createElement({ type: "rectangle" });
+
+    h.elements = [frame];
+
+    const clipboardJSON = await serializeAsClipboardJSON({
+      elements: [rect],
+      files: null,
+    });
+
+    mouse.moveTo(50, 50);
+
+    pasteWithCtrlCmdV(clipboardJSON);
+
+    await waitFor(() => {
+      expect(h.elements.length).toBe(2);
+      expect(h.elements[1].type).toBe(rect.type);
+      expect(h.elements[1].frameId).toBe(frame.id);
+    });
+  });
+});

+ 2 - 0
packages/excalidraw/tests/helpers/ui.ts

@@ -206,6 +206,8 @@ export class Pointer {
   moveTo(x: number = this.clientX, y: number = this.clientY) {
     this.clientX = x;
     this.clientY = y;
+    // fire "mousemove" to update editor cursor position
+    fireEvent.mouseMove(document, this.getEvent());
     fireEvent.pointerMove(GlobalTestState.interactiveCanvas, this.getEvent());
   }