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

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Chinmay Mhatre 2023-04-07 19:20:36 +05:30 committed by GitHub
parent d61b3cf83d
commit 68692b9d4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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(); let updatedElements: readonly ExcalidrawElement[] = elements.slice();
if (selectedElements.length === 1 && isTextElement(selectedElements[0])) { const containerIds: AppState["selectedElementIds"] = {};
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 for (const textElement of selectedElements) {
if (textElement.boundElements?.length) { if (isTextElement(textElement)) {
const linearElementIds = textElement.boundElements const container = newElement({
.filter((ele) => ele.type === "arrow") type: "rectangle",
.map((el) => el.id); backgroundColor: appState.currentItemBackgroundColor,
const linearElements = updatedElements.filter((ele) => boundElements: [
linearElementIds.includes(ele.id), ...(textElement.boundElements || []),
) as ExcalidrawLinearElement[]; { id: textElement.id, type: "text" },
linearElements.forEach((ele) => { ],
let startBinding = ele.startBinding; angle: textElement.angle,
let endBinding = ele.endBinding; fillStyle: appState.currentItemFillStyle,
strokeColor: appState.currentItemStrokeColor,
if (startBinding?.elementId === textElement.id) { roughness: appState.currentItemRoughness,
startBinding = { strokeWidth: appState.currentItemStrokeWidth,
...startBinding, strokeStyle: appState.currentItemStrokeStyle,
elementId: container.id, roundness:
}; appState.currentItemRoundness === "round"
} ? {
type: isUsingAdaptiveRadius("rectangle")
if (endBinding?.elementId === textElement.id) { ? ROUNDNESS.ADAPTIVE_RADIUS
endBinding = { ...endBinding, elementId: container.id }; : ROUNDNESS.PROPORTIONAL_RADIUS,
} }
: null,
if (startBinding || endBinding) { opacity: 100,
mutateElement(ele, { startBinding, endBinding }); 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,
}); });
}
mutateElement(textElement, { // update bindings
containerId: container.id, if (textElement.boundElements?.length) {
verticalAlign: VERTICAL_ALIGN.MIDDLE, const linearElementIds = textElement.boundElements
boundElements: null, .filter((ele) => ele.type === "arrow")
}); .map((el) => el.id);
redrawTextBoundingBox(textElement, container); const linearElements = updatedElements.filter((ele) =>
linearElementIds.includes(ele.id),
) as ExcalidrawLinearElement[];
linearElements.forEach((ele) => {
let startBinding = ele.startBinding;
let endBinding = ele.endBinding;
return { if (startBinding?.elementId === textElement.id) {
elements: pushContainerBelowText( startBinding = {
[...elements, container], ...startBinding,
elementId: container.id,
};
}
if (endBinding?.elementId === textElement.id) {
endBinding = { ...endBinding, elementId: container.id };
}
if (startBinding || endBinding) {
mutateElement(ele, { startBinding, endBinding }, false);
}
});
}
mutateElement(
textElement,
{
containerId: container.id,
verticalAlign: VERTICAL_ALIGN.MIDDLE,
boundElements: null,
},
false,
);
redrawTextBoundingBox(textElement, container);
updatedElements = pushContainerBelowText(
[...updatedElements, container],
container, container,
textElement, textElement,
), );
appState: { containerIds[container.id] = true;
...appState, }
selectedElementIds: {
[container.id]: true,
[textElement.id]: false,
},
},
commitToHistory: true,
};
} }
return { return {
elements: updatedElements, elements: updatedElements,
appState, appState: {
...appState,
selectedElementIds: containerIds,
},
commitToHistory: true, commitToHistory: true,
}; };
}, },