Browse Source

revert: remove bound-arrows from frames (#7190)

David Luzar 1 năm trước cách đây
mục cha
commit
104f64f1dc

+ 0 - 10
dev-docs/docs/codebase/frames.mdx

@@ -20,13 +20,3 @@ Frames should be ordered where frame children come first, followed by the frame
 ```
 ```
 
 
 If not oredered correctly, the editor will still function, but the elements may not be rendered and clipped correctly. Further, the renderer relies on this ordering for performance optimizations.
 If not oredered correctly, the editor will still function, but the elements may not be rendered and clipped correctly. Further, the renderer relies on this ordering for performance optimizations.
-
-# Arrows
-
-An arrow can be a child of a frame only if it has no binding (either start or end) to any other element, regardless of whether the bound element is inside the frame or not.
-
-This ensures that when an arrow is bound to an element outside the frame, it's rendered and behaves correctly.
-
-Therefore, when an arrow (that's a child of a frame) gets bound to an element, it's automatically removed from the frame.
-
-Bound-arrow is duplicated alongside a frame only if the arrow start is bound to an element within that frame.

+ 2 - 9
src/actions/actionDuplicateSelection.tsx

@@ -155,12 +155,7 @@ const duplicateElements = (
             groupId,
             groupId,
           ).flatMap((element) =>
           ).flatMap((element) =>
             isFrameElement(element)
             isFrameElement(element)
-              ? [
-                  ...getFrameElements(elements, element.id, {
-                    includeBoundArrows: true,
-                  }),
-                  element,
-                ]
+              ? [...getFrameElements(elements, element.id), element]
               : [element],
               : [element],
           );
           );
 
 
@@ -186,9 +181,7 @@ const duplicateElements = (
           continue;
           continue;
         }
         }
         if (isElementAFrame) {
         if (isElementAFrame) {
-          const elementsInFrame = getFrameElements(sortedElements, element.id, {
-            includeBoundArrows: true,
-          });
+          const elementsInFrame = getFrameElements(sortedElements, element.id);
 
 
           elementsWithClones.push(
           elementsWithClones.push(
             ...markAsProcessed([
             ...markAsProcessed([

+ 6 - 5
src/components/App.tsx

@@ -2554,12 +2554,18 @@ class App extends React.Component<AppProps, AppState> {
 
 
         const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
         const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
         if (text.length) {
         if (text.length) {
+          const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
+            x,
+            y: currentY,
+          });
+
           const element = newTextElement({
           const element = newTextElement({
             ...textElementProps,
             ...textElementProps,
             x,
             x,
             y: currentY,
             y: currentY,
             text,
             text,
             lineHeight,
             lineHeight,
+            frameId: topLayerFrame ? topLayerFrame.id : null,
           });
           });
           acc.push(element);
           acc.push(element);
           currentY += element.height + LINE_GAP;
           currentY += element.height + LINE_GAP;
@@ -3574,11 +3580,6 @@ class App extends React.Component<AppProps, AppState> {
     return getElementsAtPosition(elements, (element) =>
     return getElementsAtPosition(elements, (element) =>
       hitTest(element, this.state, this.frameNameBoundsCache, x, y),
       hitTest(element, this.state, this.frameNameBoundsCache, x, y),
     ).filter((element) => {
     ).filter((element) => {
-      // arrows don't clip even if they're children of frames,
-      // so always allow hitbox regardless of beinging contained in frame
-      if (isArrowElement(element)) {
-        return true;
-      }
       // hitting a frame's element from outside the frame is not considered a hit
       // hitting a frame's element from outside the frame is not considered a hit
       const containingFrame = getContainingFrame(element);
       const containingFrame = getContainingFrame(element);
       return containingFrame &&
       return containingFrame &&

+ 6 - 13
src/data/restore.ts

@@ -43,7 +43,6 @@ import {
   measureBaseline,
   measureBaseline,
 } from "../element/textElement";
 } from "../element/textElement";
 import { normalizeLink } from "./url";
 import { normalizeLink } from "./url";
-import { isValidFrameChild } from "../frame";
 
 
 type RestoredAppState = Omit<
 type RestoredAppState = Omit<
   AppState,
   AppState,
@@ -397,7 +396,7 @@ const repairBoundElement = (
 };
 };
 
 
 /**
 /**
- * resets `frameId` if no longer applicable.
+ * Remove an element's frameId if its containing frame is non-existent
  *
  *
  * NOTE mutates elements.
  * NOTE mutates elements.
  */
  */
@@ -405,16 +404,12 @@ const repairFrameMembership = (
   element: Mutable<ExcalidrawElement>,
   element: Mutable<ExcalidrawElement>,
   elementsMap: Map<string, Mutable<ExcalidrawElement>>,
   elementsMap: Map<string, Mutable<ExcalidrawElement>>,
 ) => {
 ) => {
-  if (!element.frameId) {
-    return;
-  }
+  if (element.frameId) {
+    const containingFrame = elementsMap.get(element.frameId);
 
 
-  if (
-    !isValidFrameChild(element) ||
-    // target frame not exists
-    !elementsMap.get(element.frameId)
-  ) {
-    element.frameId = null;
+    if (!containingFrame) {
+      element.frameId = null;
+    }
   }
   }
 };
 };
 
 
@@ -458,8 +453,6 @@ export const restoreElements = (
   // repair binding. Mutates elements.
   // repair binding. Mutates elements.
   const restoredElementsMap = arrayToMap(restoredElements);
   const restoredElementsMap = arrayToMap(restoredElements);
   for (const element of restoredElements) {
   for (const element of restoredElements) {
-    // repair frame membership *after* bindings we do in restoreElement()
-    // since we rely on bindings to be correct
     if (element.frameId) {
     if (element.frameId) {
       repairFrameMembership(element, restoredElementsMap);
       repairFrameMembership(element, restoredElementsMap);
     }
     }

+ 0 - 10
src/element/binding.ts

@@ -27,7 +27,6 @@ import { LinearElementEditor } from "./linearElementEditor";
 import { arrayToMap, tupleToCoors } from "../utils";
 import { arrayToMap, tupleToCoors } from "../utils";
 import { KEYS } from "../keys";
 import { KEYS } from "../keys";
 import { getBoundTextElement, handleBindTextResize } from "./textElement";
 import { getBoundTextElement, handleBindTextResize } from "./textElement";
-import { isValidFrameChild } from "../frame";
 
 
 export type SuggestedBinding =
 export type SuggestedBinding =
   | NonDeleted<ExcalidrawBindableElement>
   | NonDeleted<ExcalidrawBindableElement>
@@ -212,15 +211,6 @@ export const bindLinearElement = (
       }),
       }),
     });
     });
   }
   }
-  if (linearElement.frameId && !isValidFrameChild(linearElement)) {
-    mutateElement(
-      linearElement,
-      {
-        frameId: null,
-      },
-      false,
-    );
-  }
 };
 };
 
 
 // Don't bind both ends of a simple segment
 // Don't bind both ends of a simple segment

+ 1 - 29
src/frame.ts

@@ -323,24 +323,7 @@ export const groupByFrames = (elements: readonly ExcalidrawElement[]) => {
 export const getFrameElements = (
 export const getFrameElements = (
   allElements: ExcalidrawElementsIncludingDeleted,
   allElements: ExcalidrawElementsIncludingDeleted,
   frameId: string,
   frameId: string,
-  opts?: { includeBoundArrows?: boolean },
-) => {
-  return allElements.filter((element) => {
-    if (element.frameId === frameId) {
-      return true;
-    }
-    if (opts?.includeBoundArrows && element.type === "arrow") {
-      const bindingId = element.startBinding?.elementId;
-      if (bindingId) {
-        const boundElement = Scene.getScene(element)?.getElement(bindingId);
-        if (boundElement?.frameId === frameId) {
-          return true;
-        }
-      }
-    }
-    return false;
-  });
-};
+) => allElements.filter((element) => element.frameId === frameId);
 
 
 export const getElementsInResizingFrame = (
 export const getElementsInResizingFrame = (
   allElements: ExcalidrawElementsIncludingDeleted,
   allElements: ExcalidrawElementsIncludingDeleted,
@@ -468,14 +451,6 @@ export const getContainingFrame = (
   return null;
   return null;
 };
 };
 
 
-export const isValidFrameChild = (element: ExcalidrawElement) => {
-  return (
-    element.type !== "frame" &&
-    // arrows that are bound to elements cannot be frame children
-    (element.type !== "arrow" || (!element.startBinding && !element.endBinding))
-  );
-};
-
 // --------------------------- Frame Operations -------------------------------
 // --------------------------- Frame Operations -------------------------------
 
 
 /**
 /**
@@ -514,9 +489,6 @@ export const addElementsToFrame = (
     elementsToAdd,
     elementsToAdd,
   )) {
   )) {
     if (!currTargetFrameChildrenMap.has(element.id)) {
     if (!currTargetFrameChildrenMap.has(element.id)) {
-      if (!isValidFrameChild(element)) {
-        continue;
-      }
       finalElementsToAdd.push(element);
       finalElementsToAdd.push(element);
     }
     }
 
 

+ 1 - 5
src/renderer/renderScene.ts

@@ -69,7 +69,6 @@ import {
 } from "../element/Hyperlink";
 } from "../element/Hyperlink";
 import { renderSnaps } from "./renderSnaps";
 import { renderSnaps } from "./renderSnaps";
 import {
 import {
-  isArrowElement,
   isEmbeddableElement,
   isEmbeddableElement,
   isFrameElement,
   isFrameElement,
   isLinearElement,
   isLinearElement,
@@ -985,10 +984,7 @@ const _renderStaticScene = ({
 
 
           // TODO do we need to check isElementInFrame here?
           // TODO do we need to check isElementInFrame here?
           if (frame && isElementInFrame(element, elements, appState)) {
           if (frame && isElementInFrame(element, elements, appState)) {
-            // do not clip arrows
-            if (!isArrowElement(element)) {
-              frameClip(frame, context, renderConfig, appState);
-            }
+            frameClip(frame, context, renderConfig, appState);
           }
           }
           renderElement(element, rc, context, renderConfig, appState);
           renderElement(element, rc, context, renderConfig, appState);
           context.restore();
           context.restore();