fix: Move a frame along with its children

This commit is contained in:
Hazem Krimi 2025-04-25 00:38:22 +01:00
parent 7343285694
commit 820a8540b1
3 changed files with 67 additions and 35 deletions

View file

@ -58,17 +58,13 @@ const MultiPosition = ({
}),
[atomicUnits, elementsMap, property],
);
const elementsWithFramesChildren = elements.reduce((accumulator: ExcalidrawElement[], element: ExcalidrawElement) => {
if (!isFrameLikeElement(element)) return [...accumulator, element];
return [...accumulator, element, ...getFrameChildren(elementsMap, element.id)];
}, []);
const value = new Set(positions).size === 1 ? positions[0] : "Mixed";
return (
<StatsDragInput
label={property === "x" ? "X" : "Y"}
elements={elementsWithFramesChildren}
elements={elements}
dragInputCallback={handlePositionChange}
value={value}
property={property}

View file

@ -35,8 +35,6 @@ const Position = ({
pointFrom(element.x + element.width / 2, element.y + element.height / 2),
element.angle,
);
const children = isFrameLikeElement(element) ? getFrameChildren(elementsMap, element.id) : [];
const elements = children.length > 0 ? [element, ...children] : [element];
let value = round(property === "x" ? topLeftX : topLeftY, 2);
if (
@ -57,7 +55,7 @@ const Position = ({
return (
<StatsDragInput
label={property === "x" ? "X" : "Y"}
elements={elements}
elements={[element]}
dragInputCallback={handlePositionChange}
scene={scene}
value={value}

View file

@ -30,6 +30,7 @@ import type {
import type Scene from "@excalidraw/element/Scene";
import type { AppState } from "../../types";
import { getFrameChildren } from "@excalidraw/element/frame";
export type StatsInputProperty =
| "x"
@ -191,6 +192,60 @@ export const moveElement = (
{ informMutation: shouldInformMutation, isDragging: false },
);
}
if (isFrameLikeElement(originalElement)) {
getFrameChildren(originalElementsMap, originalElement.id).forEach(child => {
const latestChildElement = elementsMap.get(child.id);
if (!latestChildElement) return;
const [childCX, childCY] = [
child.x + child.width / 2,
child.y + child.height / 2,
];
const [childTopLeftX, childTopLeftY] = pointRotateRads(
pointFrom(child.x, child.y),
pointFrom(childCX, childCY),
child.angle,
);
const childNewTopLeftX = Math.round(childTopLeftX + changeInX);
const childNewTopLeftY = Math.round(childTopLeftY + changeInY);
const [childX, childY] = pointRotateRads(
pointFrom(childNewTopLeftX, childNewTopLeftY),
pointFrom(childCX + changeInX, childCY + changeInY),
-child.angle as Radians,
);
scene.mutateElement(
latestChildElement,
{
x: childX,
y: childY,
},
{ informMutation: shouldInformMutation, isDragging: false },
);
updateBindings(latestChildElement, scene);
const boundTextElement = getBoundTextElement(
latestChildElement,
originalElementsMap,
);
if (boundTextElement) {
const latestBoundTextElement = elementsMap.get(boundTextElement.id);
latestBoundTextElement &&
scene.mutateElement(
latestBoundTextElement,
{
x: boundTextElement.x + changeInX,
y: boundTextElement.y + changeInY,
},
{ informMutation: shouldInformMutation, isDragging: false },
);
}
})
}
};
export const moveElements = (
@ -371,34 +426,17 @@ export const handlePositionChange: DragInputCallbackType<
origElement.angle,
);
if (isFrameChildElement(origElement)) {
const childNewTopLeftX = property === "x" ? nextValue + Math.abs(topLeftX) : topLeftX;
const childNewTopLeftY = property === "y" ? nextValue + Math.abs(topLeftY) : topLeftY;
const newTopLeftX = property === "x" ? nextValue : topLeftX;
const newTopLeftY = property === "y" ? nextValue : topLeftY;
moveElement(
childNewTopLeftX,
childNewTopLeftY,
origElement,
scene,
originalElementsMap,
false,
);
scene.triggerUpdate();
return;
} else {
const newTopLeftX = property === "x" ? nextValue : topLeftX;
const newTopLeftY = property === "y" ? nextValue : topLeftY;
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
false,
);
}
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
false,
);
}
}
}