|
@@ -1,7 +1,13 @@
|
|
|
-import { VERTICAL_ALIGN } from "../constants";
|
|
|
-import { getNonDeletedElements, isTextElement } from "../element";
|
|
|
+import {
|
|
|
+ BOUND_TEXT_PADDING,
|
|
|
+ ROUNDNESS,
|
|
|
+ TEXT_ALIGN,
|
|
|
+ VERTICAL_ALIGN,
|
|
|
+} from "../constants";
|
|
|
+import { getNonDeletedElements, isTextElement, newElement } from "../element";
|
|
|
import { mutateElement } from "../element/mutateElement";
|
|
|
import {
|
|
|
+ computeContainerDimensionForBoundText,
|
|
|
getBoundTextElement,
|
|
|
measureText,
|
|
|
redrawTextBoundingBox,
|
|
@@ -13,8 +19,11 @@ import {
|
|
|
import {
|
|
|
hasBoundTextElement,
|
|
|
isTextBindableContainer,
|
|
|
+ isUsingAdaptiveRadius,
|
|
|
} from "../element/typeChecks";
|
|
|
import {
|
|
|
+ ExcalidrawElement,
|
|
|
+ ExcalidrawLinearElement,
|
|
|
ExcalidrawTextContainer,
|
|
|
ExcalidrawTextElement,
|
|
|
} from "../element/types";
|
|
@@ -129,18 +138,151 @@ export const actionBindText = register({
|
|
|
}),
|
|
|
});
|
|
|
redrawTextBoundingBox(textElement, container);
|
|
|
- const updatedElements = elements.slice();
|
|
|
- const textElementIndex = updatedElements.findIndex(
|
|
|
- (ele) => ele.id === textElement.id,
|
|
|
- );
|
|
|
- updatedElements.splice(textElementIndex, 1);
|
|
|
- const containerIndex = updatedElements.findIndex(
|
|
|
- (ele) => ele.id === container.id,
|
|
|
+
|
|
|
+ return {
|
|
|
+ elements: pushTextAboveContainer(elements, container, textElement),
|
|
|
+ appState: { ...appState, selectedElementIds: { [container.id]: true } },
|
|
|
+ commitToHistory: true,
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+const pushTextAboveContainer = (
|
|
|
+ elements: readonly ExcalidrawElement[],
|
|
|
+ container: ExcalidrawElement,
|
|
|
+ textElement: ExcalidrawTextElement,
|
|
|
+) => {
|
|
|
+ const updatedElements = elements.slice();
|
|
|
+ const textElementIndex = updatedElements.findIndex(
|
|
|
+ (ele) => ele.id === textElement.id,
|
|
|
+ );
|
|
|
+ updatedElements.splice(textElementIndex, 1);
|
|
|
+
|
|
|
+ const containerIndex = updatedElements.findIndex(
|
|
|
+ (ele) => ele.id === container.id,
|
|
|
+ );
|
|
|
+ updatedElements.splice(containerIndex + 1, 0, textElement);
|
|
|
+ return updatedElements;
|
|
|
+};
|
|
|
+
|
|
|
+const pushContainerBelowText = (
|
|
|
+ elements: readonly ExcalidrawElement[],
|
|
|
+ container: ExcalidrawElement,
|
|
|
+ textElement: ExcalidrawTextElement,
|
|
|
+) => {
|
|
|
+ const updatedElements = elements.slice();
|
|
|
+ const containerIndex = updatedElements.findIndex(
|
|
|
+ (ele) => ele.id === container.id,
|
|
|
+ );
|
|
|
+ updatedElements.splice(containerIndex, 1);
|
|
|
+
|
|
|
+ const textElementIndex = updatedElements.findIndex(
|
|
|
+ (ele) => ele.id === textElement.id,
|
|
|
+ );
|
|
|
+ updatedElements.splice(textElementIndex, 0, container);
|
|
|
+ return updatedElements;
|
|
|
+};
|
|
|
+
|
|
|
+export const actionCreateContainerFromText = register({
|
|
|
+ name: "createContainerFromText",
|
|
|
+ contextItemLabel: "labels.createContainerFromText",
|
|
|
+ trackEvent: { category: "element" },
|
|
|
+ predicate: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(elements, appState);
|
|
|
+ return selectedElements.length === 1 && isTextElement(selectedElements[0]);
|
|
|
+ },
|
|
|
+ perform: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(
|
|
|
+ getNonDeletedElements(elements),
|
|
|
+ appState,
|
|
|
);
|
|
|
- updatedElements.splice(containerIndex + 1, 0, textElement);
|
|
|
+ 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,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 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 = null;
|
|
|
+ let endBinding = null;
|
|
|
+ if (ele.startBinding) {
|
|
|
+ startBinding = { ...ele.startBinding, elementId: container.id };
|
|
|
+ }
|
|
|
+ if (ele.endBinding) {
|
|
|
+ endBinding = { ...ele.endBinding, elementId: container.id };
|
|
|
+ }
|
|
|
+ mutateElement(ele, { startBinding, endBinding });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ mutateElement(textElement, {
|
|
|
+ containerId: container.id,
|
|
|
+ verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
|
|
+ textAlign: TEXT_ALIGN.CENTER,
|
|
|
+ boundElements: null,
|
|
|
+ });
|
|
|
+ redrawTextBoundingBox(textElement, container);
|
|
|
+
|
|
|
+ return {
|
|
|
+ elements: pushContainerBelowText(
|
|
|
+ [...elements, container],
|
|
|
+ container,
|
|
|
+ textElement,
|
|
|
+ ),
|
|
|
+ appState: {
|
|
|
+ ...appState,
|
|
|
+ selectedElementIds: {
|
|
|
+ [container.id]: true,
|
|
|
+ [textElement.id]: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ commitToHistory: true,
|
|
|
+ };
|
|
|
+ }
|
|
|
return {
|
|
|
elements: updatedElements,
|
|
|
- appState: { ...appState, selectedElementIds: { [container.id]: true } },
|
|
|
+ appState,
|
|
|
commitToHistory: true,
|
|
|
};
|
|
|
},
|