|
@@ -0,0 +1,134 @@
|
|
|
+import { VERTICAL_ALIGN } from "../constants";
|
|
|
+import { getNonDeletedElements, isTextElement } from "../element";
|
|
|
+import { mutateElement } from "../element/mutateElement";
|
|
|
+import {
|
|
|
+ getBoundTextElement,
|
|
|
+ measureText,
|
|
|
+ redrawTextBoundingBox,
|
|
|
+} from "../element/textElement";
|
|
|
+import {
|
|
|
+ hasBoundTextElement,
|
|
|
+ isTextBindableContainer,
|
|
|
+} from "../element/typeChecks";
|
|
|
+import {
|
|
|
+ ExcalidrawTextContainer,
|
|
|
+ ExcalidrawTextElement,
|
|
|
+} from "../element/types";
|
|
|
+import { getSelectedElements } from "../scene";
|
|
|
+import { getFontString } from "../utils";
|
|
|
+import { register } from "./register";
|
|
|
+
|
|
|
+export const actionUnbindText = register({
|
|
|
+ name: "unbindText",
|
|
|
+ contextItemLabel: "labels.unbindText",
|
|
|
+ contextItemPredicate: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(elements, appState);
|
|
|
+ return selectedElements.some((element) => hasBoundTextElement(element));
|
|
|
+ },
|
|
|
+ perform: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(
|
|
|
+ getNonDeletedElements(elements),
|
|
|
+ appState,
|
|
|
+ );
|
|
|
+ selectedElements.forEach((element) => {
|
|
|
+ const boundTextElement = getBoundTextElement(element);
|
|
|
+ if (boundTextElement) {
|
|
|
+ const { width, height, baseline } = measureText(
|
|
|
+ boundTextElement.originalText,
|
|
|
+ getFontString(boundTextElement),
|
|
|
+ );
|
|
|
+ mutateElement(boundTextElement as ExcalidrawTextElement, {
|
|
|
+ containerId: null,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ baseline,
|
|
|
+ text: boundTextElement.originalText,
|
|
|
+ });
|
|
|
+ mutateElement(element, {
|
|
|
+ boundElements: element.boundElements?.filter(
|
|
|
+ (ele) => ele.id !== boundTextElement.id,
|
|
|
+ ),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ elements,
|
|
|
+ appState,
|
|
|
+ commitToHistory: true,
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+export const actionBindText = register({
|
|
|
+ name: "bindText",
|
|
|
+ contextItemLabel: "labels.bindText",
|
|
|
+ contextItemPredicate: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(elements, appState);
|
|
|
+
|
|
|
+ if (selectedElements.length === 2) {
|
|
|
+ const textElement =
|
|
|
+ isTextElement(selectedElements[0]) ||
|
|
|
+ isTextElement(selectedElements[1]);
|
|
|
+
|
|
|
+ let bindingContainer;
|
|
|
+ if (isTextBindableContainer(selectedElements[0])) {
|
|
|
+ bindingContainer = selectedElements[0];
|
|
|
+ } else if (isTextBindableContainer(selectedElements[1])) {
|
|
|
+ bindingContainer = selectedElements[1];
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ textElement &&
|
|
|
+ bindingContainer &&
|
|
|
+ getBoundTextElement(bindingContainer) === null
|
|
|
+ ) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ perform: (elements, appState) => {
|
|
|
+ const selectedElements = getSelectedElements(
|
|
|
+ getNonDeletedElements(elements),
|
|
|
+ appState,
|
|
|
+ );
|
|
|
+
|
|
|
+ let textElement: ExcalidrawTextElement;
|
|
|
+ let container: ExcalidrawTextContainer;
|
|
|
+
|
|
|
+ if (
|
|
|
+ isTextElement(selectedElements[0]) &&
|
|
|
+ isTextBindableContainer(selectedElements[1])
|
|
|
+ ) {
|
|
|
+ textElement = selectedElements[0];
|
|
|
+ container = selectedElements[1];
|
|
|
+ } else {
|
|
|
+ textElement = selectedElements[1] as ExcalidrawTextElement;
|
|
|
+ container = selectedElements[0] as ExcalidrawTextContainer;
|
|
|
+ }
|
|
|
+ mutateElement(textElement, {
|
|
|
+ containerId: container.id,
|
|
|
+ verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
|
|
+ });
|
|
|
+ mutateElement(container, {
|
|
|
+ boundElements: (container.boundElements || []).concat({
|
|
|
+ type: "text",
|
|
|
+ id: textElement.id,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ 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,
|
|
|
+ );
|
|
|
+ updatedElements.splice(containerIndex + 1, 0, textElement);
|
|
|
+ return {
|
|
|
+ elements: updatedElements,
|
|
|
+ appState: { ...appState, selectedElementIds: { [container.id]: true } },
|
|
|
+ commitToHistory: true,
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|