|
@@ -23,6 +23,7 @@ import {
|
|
ExcalidrawTextElement,
|
|
ExcalidrawTextElement,
|
|
} from "../element/types";
|
|
} from "../element/types";
|
|
import { getSelectedElements } from "../scene";
|
|
import { getSelectedElements } from "../scene";
|
|
|
|
+import { AppState } from "../types";
|
|
import { getFontString } from "../utils";
|
|
import { getFontString } from "../utils";
|
|
import { register } from "./register";
|
|
import { register } from "./register";
|
|
|
|
|
|
@@ -185,107 +186,110 @@ export const actionCreateContainerFromText = register({
|
|
trackEvent: { category: "element" },
|
|
trackEvent: { category: "element" },
|
|
predicate: (elements, appState) => {
|
|
predicate: (elements, appState) => {
|
|
const selectedElements = getSelectedElements(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) => {
|
|
perform: (elements, appState) => {
|
|
const selectedElements = getSelectedElements(
|
|
const selectedElements = getSelectedElements(
|
|
getNonDeletedElements(elements),
|
|
getNonDeletedElements(elements),
|
|
appState,
|
|
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,
|
|
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 {
|
|
return {
|
|
elements: updatedElements,
|
|
elements: updatedElements,
|
|
- appState,
|
|
|
|
|
|
+ appState: {
|
|
|
|
+ ...appState,
|
|
|
|
+ selectedElementIds: containerIds,
|
|
|
|
+ },
|
|
commitToHistory: true,
|
|
commitToHistory: true,
|
|
};
|
|
};
|
|
},
|
|
},
|