Browse Source

feat: add container to multiple text elements (#6428)

Co-authored-by: dwelle <[email protected]>
Chinmay Mhatre 2 years ago
parent
commit
68692b9d4c
1 changed files with 88 additions and 84 deletions
  1. 88 84
      src/actions/actionBoundText.tsx

+ 88 - 84
src/actions/actionBoundText.tsx

@@ -23,6 +23,7 @@ import {
   ExcalidrawTextElement,
 } from "../element/types";
 import { getSelectedElements } from "../scene";
+import { AppState } from "../types";
 import { getFontString } from "../utils";
 import { register } from "./register";
 
@@ -185,107 +186,110 @@ export const actionCreateContainerFromText = register({
   trackEvent: { category: "element" },
   predicate: (elements, appState) => {
     const selectedElements = getSelectedElements(elements, appState);
-    return selectedElements.length === 1 && isTextElement(selectedElements[0]);
+    const areTextElements = selectedElements.every((el) => isTextElement(el));
+    return selectedElements.length > 0 && areTextElements;
   },
   perform: (elements, appState) => {
     const selectedElements = getSelectedElements(
       getNonDeletedElements(elements),
       appState,
     );
-    const updatedElements = elements.slice();
-    if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
-      const textElement = selectedElements[0];
-      const container = newElement({
-        type: "rectangle",
-        backgroundColor: appState.currentItemBackgroundColor,
-        boundElements: [
-          ...(textElement.boundElements || []),
-          { id: textElement.id, type: "text" },
-        ],
-        angle: textElement.angle,
-        fillStyle: appState.currentItemFillStyle,
-        strokeColor: appState.currentItemStrokeColor,
-        roughness: appState.currentItemRoughness,
-        strokeWidth: appState.currentItemStrokeWidth,
-        strokeStyle: appState.currentItemStrokeStyle,
-        roundness:
-          appState.currentItemRoundness === "round"
-            ? {
-                type: isUsingAdaptiveRadius("rectangle")
-                  ? ROUNDNESS.ADAPTIVE_RADIUS
-                  : ROUNDNESS.PROPORTIONAL_RADIUS,
-              }
-            : null,
-        opacity: 100,
-        locked: false,
-        x: textElement.x - BOUND_TEXT_PADDING,
-        y: textElement.y - BOUND_TEXT_PADDING,
-        width: computeContainerDimensionForBoundText(
-          textElement.width,
-          "rectangle",
-        ),
-        height: computeContainerDimensionForBoundText(
-          textElement.height,
-          "rectangle",
-        ),
-        groupIds: textElement.groupIds,
-      });
+    let updatedElements: readonly ExcalidrawElement[] = elements.slice();
+    const containerIds: AppState["selectedElementIds"] = {};
 
-      // update bindings
-      if (textElement.boundElements?.length) {
-        const linearElementIds = textElement.boundElements
-          .filter((ele) => ele.type === "arrow")
-          .map((el) => el.id);
-        const linearElements = updatedElements.filter((ele) =>
-          linearElementIds.includes(ele.id),
-        ) as ExcalidrawLinearElement[];
-        linearElements.forEach((ele) => {
-          let startBinding = ele.startBinding;
-          let endBinding = ele.endBinding;
+    for (const textElement of selectedElements) {
+      if (isTextElement(textElement)) {
+        const container = newElement({
+          type: "rectangle",
+          backgroundColor: appState.currentItemBackgroundColor,
+          boundElements: [
+            ...(textElement.boundElements || []),
+            { id: textElement.id, type: "text" },
+          ],
+          angle: textElement.angle,
+          fillStyle: appState.currentItemFillStyle,
+          strokeColor: appState.currentItemStrokeColor,
+          roughness: appState.currentItemRoughness,
+          strokeWidth: appState.currentItemStrokeWidth,
+          strokeStyle: appState.currentItemStrokeStyle,
+          roundness:
+            appState.currentItemRoundness === "round"
+              ? {
+                  type: isUsingAdaptiveRadius("rectangle")
+                    ? ROUNDNESS.ADAPTIVE_RADIUS
+                    : ROUNDNESS.PROPORTIONAL_RADIUS,
+                }
+              : null,
+          opacity: 100,
+          locked: false,
+          x: textElement.x - BOUND_TEXT_PADDING,
+          y: textElement.y - BOUND_TEXT_PADDING,
+          width: computeContainerDimensionForBoundText(
+            textElement.width,
+            "rectangle",
+          ),
+          height: computeContainerDimensionForBoundText(
+            textElement.height,
+            "rectangle",
+          ),
+          groupIds: textElement.groupIds,
+        });
 
-          if (startBinding?.elementId === textElement.id) {
-            startBinding = {
-              ...startBinding,
-              elementId: container.id,
-            };
-          }
+        // update bindings
+        if (textElement.boundElements?.length) {
+          const linearElementIds = textElement.boundElements
+            .filter((ele) => ele.type === "arrow")
+            .map((el) => el.id);
+          const linearElements = updatedElements.filter((ele) =>
+            linearElementIds.includes(ele.id),
+          ) as ExcalidrawLinearElement[];
+          linearElements.forEach((ele) => {
+            let startBinding = ele.startBinding;
+            let endBinding = ele.endBinding;
 
-          if (endBinding?.elementId === textElement.id) {
-            endBinding = { ...endBinding, elementId: container.id };
-          }
+            if (startBinding?.elementId === textElement.id) {
+              startBinding = {
+                ...startBinding,
+                elementId: container.id,
+              };
+            }
 
-          if (startBinding || endBinding) {
-            mutateElement(ele, { startBinding, endBinding });
-          }
-        });
-      }
+            if (endBinding?.elementId === textElement.id) {
+              endBinding = { ...endBinding, elementId: container.id };
+            }
 
-      mutateElement(textElement, {
-        containerId: container.id,
-        verticalAlign: VERTICAL_ALIGN.MIDDLE,
-        boundElements: null,
-      });
-      redrawTextBoundingBox(textElement, container);
+            if (startBinding || endBinding) {
+              mutateElement(ele, { startBinding, endBinding }, false);
+            }
+          });
+        }
 
-      return {
-        elements: pushContainerBelowText(
-          [...elements, container],
-          container,
+        mutateElement(
           textElement,
-        ),
-        appState: {
-          ...appState,
-          selectedElementIds: {
-            [container.id]: true,
-            [textElement.id]: false,
+          {
+            containerId: container.id,
+            verticalAlign: VERTICAL_ALIGN.MIDDLE,
+            boundElements: null,
           },
-        },
-        commitToHistory: true,
-      };
+          false,
+        );
+        redrawTextBoundingBox(textElement, container);
+
+        updatedElements = pushContainerBelowText(
+          [...updatedElements, container],
+          container,
+          textElement,
+        );
+        containerIds[container.id] = true;
+      }
     }
+
     return {
       elements: updatedElements,
-      appState,
+      appState: {
+        ...appState,
+        selectedElementIds: containerIds,
+      },
       commitToHistory: true,
     };
   },