Deprecate mutateElement, use scene.mutateElement or mutateElementWIth instead

This commit is contained in:
Marcel Mraz 2025-04-11 22:07:42 +00:00
parent a9c3b2a4d4
commit 94c773a990
No known key found for this signature in database
GPG key ID: 4EBD6E62DC830CD2
45 changed files with 593 additions and 704 deletions

View file

@ -1402,7 +1402,7 @@ class App extends React.Component<AppProps, AppState> {
private resetEditingFrame = (frame: ExcalidrawFrameLikeElement | null) => {
if (frame) {
this.scene.mutateElement(frame, { name: frame.name?.trim() || null });
this.scene.mutate(frame, { name: frame.name?.trim() || null });
}
this.setState({ editingFrame: null });
};
@ -1459,7 +1459,7 @@ class App extends React.Component<AppProps, AppState> {
autoFocus
value={frameNameInEdit}
onChange={(e) => {
this.scene.mutateElement(f, {
this.scene.mutate(f, {
name: e.target.value,
});
}}
@ -1950,13 +1950,21 @@ class App extends React.Component<AppProps, AppState> {
// state only.
// Thus reset so that we prefer local cache (if there was some
// generationData set previously)
this.scene.mutateElement(frameElement, {
customData: { generationData: undefined },
}, { informMutation: false });
this.scene.mutate(
frameElement,
{
customData: { generationData: undefined },
},
{ informMutation: false },
);
} else {
this.scene.mutateElement(frameElement, {
customData: { generationData: data },
}, { informMutation: false });
this.scene.mutate(
frameElement,
{
customData: { generationData: data },
},
{ informMutation: false },
);
}
this.magicGenerations.set(frameElement.id, data);
this.triggerRender();
@ -2127,7 +2135,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.insertElement(frame);
for (const child of selectedElements) {
this.scene.mutateElement(child, { frameId: frame.id });
this.scene.mutate(child, { frameId: frame.id });
}
this.setState({
@ -2926,8 +2934,7 @@ class App extends React.Component<AppProps, AppState> {
nonDeletedElementsMap,
),
),
this.scene.getNonDeletedElementsMap(),
this.scene.getNonDeletedElements(),
this.scene,
);
}
@ -3329,6 +3336,7 @@ class App extends React.Component<AppProps, AppState> {
newElement,
container,
this.scene.getElementsMapIncludingDeleted(),
(...args) => this.scene.mutate(...args),
);
}
});
@ -3452,7 +3460,11 @@ class App extends React.Component<AppProps, AppState> {
}
// hack to reset the `y` coord because we vertically center during
// insertImageElement
this.scene.mutateElement(initializedImageElement, { y }, { informMutation: false });
this.scene.mutate(
initializedImageElement,
{ y },
{ informMutation: false },
);
y = imageElement.y + imageElement.height + 25;
@ -4177,6 +4189,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElementsMap(),
this.state,
getLinkDirectionFromKey(event.key),
this.scene,
);
}
@ -4418,10 +4431,14 @@ class App extends React.Component<AppProps, AppState> {
}
selectedElements.forEach((element) => {
this.scene.mutateElement(element, {
x: element.x + offsetX,
y: element.y + offsetY,
}, { informMutation: false });
this.scene.mutate(
element,
{
x: element.x + offsetX,
y: element.y + offsetY,
},
{ informMutation: false },
);
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
simultaneouslyUpdated: selectedElements,
@ -4457,6 +4474,7 @@ class App extends React.Component<AppProps, AppState> {
this.setState({
editingLinearElement: new LinearElementEditor(
selectedElement,
this.scene.getNonDeletedElementsMap(),
),
});
}
@ -4650,11 +4668,9 @@ class App extends React.Component<AppProps, AppState> {
if (isArrowKey(event.key)) {
bindOrUnbindLinearElements(
this.scene.getSelectedElements(this.state).filter(isLinearElement),
this.scene.getNonDeletedElementsMap(),
this.scene.getNonDeletedElements(),
this.scene,
isBindingEnabled(this.state),
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
this.scene,
this.state.zoom,
);
this.setState({ suggestedBindings: [] });
@ -5324,7 +5340,7 @@ class App extends React.Component<AppProps, AppState> {
const minHeight = getApproxMinLineHeight(fontSize, lineHeight);
const newHeight = Math.max(container.height, minHeight);
const newWidth = Math.max(container.width, minWidth);
this.scene.mutateElement(container, {
this.scene.mutate(container, {
height: newHeight,
width: newWidth,
});
@ -5378,7 +5394,7 @@ class App extends React.Component<AppProps, AppState> {
});
if (!existingTextElement && shouldBindToContainer && container) {
this.scene.mutateElement(container, {
this.scene.mutate(container, {
boundElements: (container.boundElements || []).concat({
type: "text",
id: element.id,
@ -5454,7 +5470,10 @@ class App extends React.Component<AppProps, AppState> {
) {
this.store.shouldCaptureIncrement();
this.setState({
editingLinearElement: new LinearElementEditor(selectedElements[0]),
editingLinearElement: new LinearElementEditor(
selectedElements[0],
this.scene.getNonDeletedElementsMap(),
),
});
return;
} else if (
@ -5480,7 +5499,7 @@ class App extends React.Component<AppProps, AppState> {
this.store.shouldCaptureIncrement();
LinearElementEditor.deleteFixedSegment(
selectedElements[0],
this.scene.getNonDeletedElementsMap(),
this.scene,
midPoint,
);
@ -5927,7 +5946,7 @@ class App extends React.Component<AppProps, AppState> {
lastPoint,
) >= LINE_CONFIRM_THRESHOLD
) {
this.scene.mutateElement(
this.scene.mutate(
multiElement,
{
points: [
@ -5951,7 +5970,7 @@ class App extends React.Component<AppProps, AppState> {
) < LINE_CONFIRM_THRESHOLD
) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
this.scene.mutateElement(
this.scene.mutate(
multiElement,
{
points: points.slice(0, -1),
@ -5990,7 +6009,7 @@ class App extends React.Component<AppProps, AppState> {
}
// update last uncommitted point
this.scene.mutateElement(
this.scene.mutate(
multiElement,
{
points: [
@ -6675,7 +6694,7 @@ class App extends React.Component<AppProps, AppState> {
const frame = this.getTopLayerFrameAtSceneCoords({ x, y });
this.scene.mutateElement(pendingImageElement, {
this.scene.mutate(pendingImageElement, {
x,
y,
frameId: frame ? frame.id : null,
@ -7729,7 +7748,7 @@ class App extends React.Component<AppProps, AppState> {
multiElement.type === "line" &&
isPathALoop(multiElement.points, this.state.zoom.value)
) {
this.scene.mutateElement(multiElement, {
this.scene.mutate(multiElement, {
lastCommittedPoint:
multiElement.points[multiElement.points.length - 1],
});
@ -7740,7 +7759,7 @@ class App extends React.Component<AppProps, AppState> {
// Elbow arrows cannot be created by putting down points
// only the start and end points can be defined
if (isElbowArrow(multiElement) && multiElement.points.length > 1) {
this.scene.mutateElement(multiElement, {
this.scene.mutate(multiElement, {
lastCommittedPoint:
multiElement.points[multiElement.points.length - 1],
});
@ -7777,7 +7796,7 @@ class App extends React.Component<AppProps, AppState> {
}));
// clicking outside commit zone → update reference for last committed
// point
this.scene.mutateElement(multiElement, {
this.scene.mutate(multiElement, {
lastCommittedPoint: multiElement.points[multiElement.points.length - 1],
});
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
@ -7863,7 +7882,7 @@ class App extends React.Component<AppProps, AppState> {
),
};
});
this.scene.mutateElement(element, {
this.scene.mutate(element, {
points: [...element.points, pointFrom<LocalPoint>(0, 0)],
});
const boundElement = getHoveredElementForBinding(
@ -8113,7 +8132,7 @@ class App extends React.Component<AppProps, AppState> {
index,
gridX,
gridY,
this.scene.getNonDeletedElementsMap(),
this.scene,
);
flushSync(() => {
@ -8218,7 +8237,7 @@ class App extends React.Component<AppProps, AppState> {
pointerCoords,
this,
!event[KEYS.CTRL_OR_CMD],
elementsMap,
this.scene,
);
if (!ret) {
return;
@ -8445,7 +8464,7 @@ class App extends React.Component<AppProps, AppState> {
),
};
this.scene.mutateElement(croppingElement, {
this.scene.mutate(croppingElement, {
crop: nextCrop,
});
@ -8642,7 +8661,7 @@ class App extends React.Component<AppProps, AppState> {
? newElement.pressures
: [...newElement.pressures, event.pressure];
this.scene.mutateElement(
this.scene.mutate(
newElement,
{
points: [...points, pointFrom<LocalPoint>(dx, dy)],
@ -8673,7 +8692,7 @@ class App extends React.Component<AppProps, AppState> {
}
if (points.length === 1) {
this.scene.mutateElement(
this.scene.mutate(
newElement,
{
points: [...points, pointFrom<LocalPoint>(dx, dy)],
@ -8684,7 +8703,7 @@ class App extends React.Component<AppProps, AppState> {
points.length === 2 ||
(points.length > 1 && isElbowArrow(newElement))
) {
this.scene.mutateElement(
this.scene.mutate(
newElement,
{
points: [...points.slice(0, -1), pointFrom<LocalPoint>(dx, dy)],
@ -8800,7 +8819,10 @@ class App extends React.Component<AppProps, AppState> {
selectedLinearElement:
elementsWithinSelection.length === 1 &&
isLinearElement(elementsWithinSelection[0])
? new LinearElementEditor(elementsWithinSelection[0])
? new LinearElementEditor(
elementsWithinSelection[0],
this.scene.getNonDeletedElementsMap(),
)
: null,
showHyperlinkPopup:
elementsWithinSelection.length === 1 &&
@ -8900,7 +8922,7 @@ class App extends React.Component<AppProps, AppState> {
.map((e) => elementsMap.get(e.id))
.filter((e) => isElbowArrow(e))
.forEach((e) => {
!!e && this.scene.mutateElement(e, {});
!!e && this.scene.mutate(e, {});
});
}
}
@ -8936,10 +8958,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElementsMap(),
);
if (element) {
this.scene.mutateElement(
element as ExcalidrawElbowArrowElement,
{},
);
this.scene.mutate(element as ExcalidrawElbowArrowElement, {});
}
}
@ -8968,7 +8987,6 @@ class App extends React.Component<AppProps, AppState> {
element,
startBindingElement,
endBindingElement,
elementsMap,
this.scene,
);
}
@ -9035,7 +9053,7 @@ class App extends React.Component<AppProps, AppState> {
? []
: [...newElement.pressures, childEvent.pressure];
this.scene.mutateElement(newElement, {
this.scene.mutate(newElement, {
points: [...points, pointFrom<LocalPoint>(dx, dy)],
pressures,
lastCommittedPoint: pointFrom<LocalPoint>(dx, dy),
@ -9082,7 +9100,7 @@ class App extends React.Component<AppProps, AppState> {
);
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
this.scene.mutateElement(
this.scene.mutate(
newElement,
{
points: [
@ -9109,8 +9127,7 @@ class App extends React.Component<AppProps, AppState> {
newElement,
this.state,
pointerCoords,
this.scene.getNonDeletedElementsMap(),
this.scene.getNonDeletedElements(),
this.scene,
);
}
this.setState({ suggestedBindings: [], startBoundElement: null });
@ -9128,7 +9145,10 @@ class App extends React.Component<AppProps, AppState> {
},
prevState,
),
selectedLinearElement: new LinearElementEditor(newElement),
selectedLinearElement: new LinearElementEditor(
newElement,
this.scene.getNonDeletedElementsMap(),
),
}));
} else {
this.setState((prevState) => ({
@ -9151,7 +9171,7 @@ class App extends React.Component<AppProps, AppState> {
);
if (newElement.width < minWidth) {
this.scene.mutateElement(newElement, {
this.scene.mutate(newElement, {
autoResize: true,
});
}
@ -9201,13 +9221,9 @@ class App extends React.Component<AppProps, AppState> {
}
if (newElement) {
this.scene.mutateElement(
newElement,
getNormalizedDimensions(newElement),
{
informMutation: false,
},
);
this.scene.mutate(newElement, getNormalizedDimensions(newElement), {
informMutation: false,
});
// the above does not guarantee the scene to be rendered again, hence the trigger below
this.scene.triggerUpdate();
}
@ -9239,7 +9255,7 @@ class App extends React.Component<AppProps, AppState> {
) {
// remove the linear element from all groups
// before removing it from the frame as well
this.scene.mutateElement(linearElement, {
this.scene.mutate(linearElement, {
groupIds: [],
});
@ -9268,9 +9284,13 @@ class App extends React.Component<AppProps, AppState> {
this.state.editingGroupId!,
);
this.scene.mutateElement(element, {
groupIds: element.groupIds.slice(0, index),
}, { informMutation: false });
this.scene.mutate(
element,
{
groupIds: element.groupIds.slice(0, index),
},
{ informMutation: false },
);
}
nextElements.forEach((element) => {
@ -9281,9 +9301,13 @@ class App extends React.Component<AppProps, AppState> {
element.groupIds[element.groupIds.length - 1],
).length < 2
) {
this.scene.mutateElement(element, {
groupIds: [],
}, { informMutation: false });
this.scene.mutate(
element,
{
groupIds: [],
},
{ informMutation: false },
);
}
});
@ -9392,7 +9416,10 @@ class App extends React.Component<AppProps, AppState> {
// the one we've hit
if (selectedElements.length === 1) {
this.setState({
selectedLinearElement: new LinearElementEditor(hitElement),
selectedLinearElement: new LinearElementEditor(
hitElement,
this.scene.getNonDeletedElementsMap(),
),
});
}
}
@ -9517,7 +9544,10 @@ class App extends React.Component<AppProps, AppState> {
selectedLinearElement:
newSelectedElements.length === 1 &&
isLinearElement(newSelectedElements[0])
? new LinearElementEditor(newSelectedElements[0])
? new LinearElementEditor(
newSelectedElements[0],
this.scene.getNonDeletedElementsMap(),
)
: prevState.selectedLinearElement,
};
});
@ -9591,7 +9621,10 @@ class App extends React.Component<AppProps, AppState> {
// Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
// Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
prevState.selectedLinearElement?.elementId !== hitElement.id
? new LinearElementEditor(hitElement)
? new LinearElementEditor(
hitElement,
this.scene.getNonDeletedElementsMap(),
)
: prevState.selectedLinearElement,
}));
}
@ -9684,11 +9717,9 @@ class App extends React.Component<AppProps, AppState> {
bindOrUnbindLinearElements(
linearElements,
this.scene.getNonDeletedElementsMap(),
this.scene.getNonDeletedElements(),
this.scene,
isBindingEnabled(this.state),
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
this.scene,
this.state.zoom,
);
}
@ -9845,9 +9876,13 @@ class App extends React.Component<AppProps, AppState> {
const dataURL =
this.files[fileId]?.dataURL || (await getDataURL(imageFile));
const imageElement = this.scene.mutateElement(_imageElement, {
fileId,
}, { informMutation: false }) as NonDeleted<InitializedExcalidrawImageElement>;
const imageElement = this.scene.mutate(
_imageElement,
{
fileId,
},
{ informMutation: false },
) as NonDeleted<InitializedExcalidrawImageElement>;
return new Promise<NonDeleted<InitializedExcalidrawImageElement>>(
async (resolve, reject) => {
@ -9912,7 +9947,7 @@ class App extends React.Component<AppProps, AppState> {
showCursorImagePreview,
});
} catch (error: any) {
this.scene.mutateElement(imageElement, {
this.scene.mutate(imageElement, {
isDeleted: true,
});
this.actionManager.executeAction(actionFinalize);
@ -10058,7 +10093,7 @@ class App extends React.Component<AppProps, AppState> {
imageElement.height < DRAGGING_THRESHOLD / this.state.zoom.value
) {
const placeholderSize = 100 / this.state.zoom.value;
this.scene.mutateElement(imageElement, {
this.scene.mutate(imageElement, {
x: imageElement.x - placeholderSize / 2,
y: imageElement.y - placeholderSize / 2,
width: placeholderSize,
@ -10092,7 +10127,7 @@ class App extends React.Component<AppProps, AppState> {
const x = imageElement.x + imageElement.width / 2 - width / 2;
const y = imageElement.y + imageElement.height / 2 - height / 2;
this.scene.mutateElement(imageElement, {
this.scene.mutate(imageElement, {
x,
y,
width,
@ -10523,7 +10558,10 @@ class App extends React.Component<AppProps, AppState> {
this,
),
selectedLinearElement: isLinearElement(element)
? new LinearElementEditor(element)
? new LinearElementEditor(
element,
this.scene.getNonDeletedElementsMap(),
)
: null,
}
: this.state),
@ -10556,6 +10594,7 @@ class App extends React.Component<AppProps, AppState> {
height: distance(pointerDownState.origin.y, pointerCoords.y),
shouldMaintainAspectRatio: shouldMaintainAspectRatio(event),
shouldResizeFromCenter: false,
scene: this.scene,
zoom: this.state.zoom.value,
informMutation,
});
@ -10621,6 +10660,7 @@ class App extends React.Component<AppProps, AppState> {
: shouldMaintainAspectRatio(event),
shouldResizeFromCenter: shouldResizeFromCenter(event),
zoom: this.state.zoom.value,
scene: this.scene,
widthAspectRatio: aspectRatio,
originOffset: this.state.originSnapOffset,
informMutation,
@ -10708,7 +10748,7 @@ class App extends React.Component<AppProps, AppState> {
transformHandleType,
);
this.scene.mutateElement(
this.scene.mutate(
croppingElement,
cropElement(
croppingElement,
@ -10846,7 +10886,6 @@ class App extends React.Component<AppProps, AppState> {
pointerDownState.originalElements,
transformHandleType,
selectedElements,
this.scene.getElementsMapIncludingDeleted(),
this.scene,
shouldRotateWithDiscreteAngle(event),
shouldResizeFromCenter(event),

View file

@ -5,11 +5,12 @@ import {
CLASSES,
DEFAULT_SIDEBAR,
TOOL_TYPE,
arrayToMap,
capitalizeString,
isShallowEqual,
} from "@excalidraw/common";
import { mutateElement } from "@excalidraw/element/mutateElement";
import { mutateElementWith } from "@excalidraw/element/mutateElement";
import { showSelectedShapeActions } from "@excalidraw/element/showSelectedShapeActions";
@ -17,7 +18,6 @@ import { ShapeCache } from "@excalidraw/element/ShapeCache";
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
import Scene from "../scene/Scene";
import { actionToggleStats } from "../actions";
import { trackEvent } from "../analytics";
import { isHandToolActive } from "../appState";
@ -446,22 +446,18 @@ const LayerUI = ({
if (selectedElements.length) {
for (const element of selectedElements) {
mutateElement(
element,
{
[altKey && eyeDropperState.swapPreviewOnAlt
? colorPickerType === "elementBackground"
? "strokeColor"
: "backgroundColor"
: colorPickerType === "elementBackground"
? "backgroundColor"
: "strokeColor"]: color,
},
false,
);
mutateElementWith(element, arrayToMap(elements), {
[altKey && eyeDropperState.swapPreviewOnAlt
? colorPickerType === "elementBackground"
? "strokeColor"
: "backgroundColor"
: colorPickerType === "elementBackground"
? "backgroundColor"
: "strokeColor"]: color,
});
ShapeCache.delete(element);
}
Scene.getScene(selectedElements[0])?.triggerUpdate();
app.scene.triggerUpdate();
} else if (colorPickerType === "elementBackground") {
setAppState({
currentItemBackgroundColor: color,

View file

@ -1,7 +1,5 @@
import { degreesToRadians, radiansToDegrees } from "@excalidraw/math";
import { mutateElement } from "@excalidraw/element/mutateElement";
import { getBoundTextElement } from "@excalidraw/element/textElement";
import { isArrowElement, isElbowArrow } from "@excalidraw/element/typeChecks";
@ -35,7 +33,6 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
scene,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const elements = scene.getNonDeletedElements();
const origElement = originalElements[0];
if (origElement && !isElbowArrow(origElement)) {
const latestElement = elementsMap.get(origElement.id);
@ -45,14 +42,14 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
if (nextValue !== undefined) {
const nextAngle = degreesToRadians(nextValue as Degrees);
mutateElement(latestElement, {
scene.mutate(latestElement, {
angle: nextAngle,
});
updateBindings(latestElement, elementsMap, elements, scene);
updateBindings(latestElement, scene);
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: nextAngle });
scene.mutate(boundTextElement, { angle: nextAngle });
}
return;
@ -71,14 +68,14 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
const nextAngle = degreesToRadians(nextAngleInDegrees as Degrees);
mutateElement(latestElement, {
scene.mutate(latestElement, {
angle: nextAngle,
});
updateBindings(latestElement, elementsMap, elements, scene);
updateBindings(latestElement, scene);
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: nextAngle });
scene.mutate(boundTextElement, { angle: nextAngle });
}
}
};

View file

@ -5,7 +5,6 @@ import {
MINIMAL_CROP_SIZE,
getUncroppedWidthAndHeight,
} from "@excalidraw/element/cropElement";
import { mutateElement } from "@excalidraw/element/mutateElement";
import { resizeSingleElement } from "@excalidraw/element/resizeElements";
import { isImageElement } from "@excalidraw/element/typeChecks";
@ -113,7 +112,7 @@ const handleDimensionChange: DragInputCallbackType<
};
}
mutateElement(element, {
scene.mutate(element, {
crop: nextCrop,
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
@ -144,7 +143,7 @@ const handleDimensionChange: DragInputCallbackType<
height: nextCropHeight,
};
mutateElement(element, {
scene.mutate(element, {
crop: nextCrop,
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
@ -176,8 +175,8 @@ const handleDimensionChange: DragInputCallbackType<
nextHeight,
latestElement,
origElement,
elementsMap,
originalElementsMap,
scene,
property === "width" ? "e" : "s",
{
shouldMaintainAspectRatio: keepAspectRatio,
@ -223,8 +222,8 @@ const handleDimensionChange: DragInputCallbackType<
nextHeight,
latestElement,
origElement,
elementsMap,
originalElementsMap,
scene,
property === "width" ? "e" : "s",
{
shouldMaintainAspectRatio: keepAspectRatio,

View file

@ -75,6 +75,7 @@ const handleFontSizeChange: DragInputCallbackType<
latestElement,
scene.getContainerElement(latestElement),
scene.getNonDeletedElementsMap(),
(...args) => scene.mutate(...args),
);
}
}

View file

@ -54,17 +54,13 @@ const handleDegreeChange: DragInputCallbackType<
if (!element) {
continue;
}
mutateElement(
element,
{
angle: nextAngle,
},
false,
);
mutateElement(element, {
angle: nextAngle,
});
const boundTextElement = getBoundTextElement(element, elementsMap);
if (boundTextElement && !isArrowElement(element)) {
mutateElement(boundTextElement, { angle: nextAngle }, false);
mutateElement(boundTextElement, { angle: nextAngle });
}
}
@ -92,17 +88,13 @@ const handleDegreeChange: DragInputCallbackType<
const nextAngle = degreesToRadians(nextAngleInDegrees as Degrees);
mutateElement(
latestElement,
{
angle: nextAngle,
},
false,
);
mutateElement(latestElement, {
angle: nextAngle,
});
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: nextAngle }, false);
mutateElement(boundTextElement, { angle: nextAngle });
}
}
scene.triggerUpdate();

View file

@ -3,7 +3,6 @@ import { useMemo } from "react";
import { MIN_WIDTH_OR_HEIGHT } from "@excalidraw/common";
import { updateBoundElements } from "@excalidraw/element/binding";
import { mutateElement } from "@excalidraw/element/mutateElement";
import {
rescalePointsInElement,
resizeSingleElement,
@ -13,11 +12,14 @@ import {
handleBindTextResize,
} from "@excalidraw/element/textElement";
import { isElbowArrow, isTextElement } from "@excalidraw/element/typeChecks";
import { isTextElement } from "@excalidraw/element/typeChecks";
import { getCommonBounds } from "@excalidraw/utils";
import { mutateElbowArrow } from "@excalidraw/element/elbowArrow";
import {
mutateElement,
mutateElementWith,
} from "@excalidraw/element/mutateElement";
import type {
ElementsMap,
@ -82,11 +84,7 @@ const resizeElementInGroup = (
) => {
const updates = getResizedUpdates(anchorX, anchorY, scale, origElement);
if (isElbowArrow(latestElement)) {
mutateElbowArrow(latestElement, updates, false, elementsMap);
} else {
mutateElement(latestElement, updates, false);
}
mutateElementWith(latestElement, elementsMap, updates);
const boundTextElement = getBoundTextElement(
origElement,
@ -99,13 +97,9 @@ const resizeElementInGroup = (
});
const latestBoundTextElement = elementsMap.get(boundTextElement.id);
if (latestBoundTextElement && isTextElement(latestBoundTextElement)) {
mutateElement(
latestBoundTextElement,
{
fontSize: newFontSize,
},
false,
);
mutateElement(latestBoundTextElement, {
fontSize: newFontSize,
});
handleBindTextResize(
latestElement,
elementsMap,
@ -244,8 +238,8 @@ const handleDimensionChange: DragInputCallbackType<
nextHeight,
latestElement,
origElement,
elementsMap,
originalElementsMap,
scene,
property === "width" ? "e" : "s",
{
shouldInformMutation: false,
@ -347,8 +341,8 @@ const handleDimensionChange: DragInputCallbackType<
nextHeight,
latestElement,
origElement,
elementsMap,
originalElementsMap,
scene,
property === "width" ? "e" : "s",
{
shouldInformMutation: false,

View file

@ -84,19 +84,15 @@ const handleFontSizeChange: DragInputCallbackType<
nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE);
for (const textElement of latestTextElements) {
mutateElement(
textElement,
{
fontSize: nextFontSize,
},
false,
);
mutateElement(textElement, {
fontSize: nextFontSize,
});
redrawTextBoundingBox(
textElement,
scene.getContainerElement(textElement),
elementsMap,
false,
(...args) => scene.mutate(...args),
);
}
@ -117,19 +113,15 @@ const handleFontSizeChange: DragInputCallbackType<
if (shouldChangeByStepSize) {
nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE);
}
mutateElement(
latestElement,
{
fontSize: nextFontSize,
},
false,
);
mutateElement(latestElement, {
fontSize: nextFontSize,
});
redrawTextBoundingBox(
latestElement,
scene.getContainerElement(latestElement),
elementsMap,
false,
(...args) => scene.mutate(...args),
);
}

View file

@ -5,12 +5,7 @@ import { isTextElement } from "@excalidraw/element/typeChecks";
import { getCommonBounds } from "@excalidraw/element/bounds";
import type {
ElementsMap,
ExcalidrawElement,
NonDeletedExcalidrawElement,
NonDeletedSceneElementsMap,
} from "@excalidraw/element/types";
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
import StatsDragInput from "./DragInput";
import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils";
@ -36,13 +31,11 @@ const moveElements = (
property: MultiPositionProps["property"],
changeInTopX: number,
changeInTopY: number,
elements: readonly ExcalidrawElement[],
originalElements: readonly ExcalidrawElement[],
elementsMap: NonDeletedSceneElementsMap,
originalElementsMap: ElementsMap,
scene: Scene,
) => {
for (let i = 0; i < elements.length; i++) {
for (let i = 0; i < originalElements.length; i++) {
const origElement = originalElements[i];
const [cx, cy] = [
@ -65,8 +58,6 @@ const moveElements = (
newTopLeftX,
newTopLeftY,
origElement,
elementsMap,
elements,
scene,
originalElementsMap,
false,
@ -78,11 +69,10 @@ const moveGroupTo = (
nextX: number,
nextY: number,
originalElements: ExcalidrawElement[],
elementsMap: NonDeletedSceneElementsMap,
elements: readonly NonDeletedExcalidrawElement[],
originalElementsMap: ElementsMap,
scene: Scene,
) => {
const elementsMap = scene.getNonDeletedElementsMap();
const [x1, y1, ,] = getCommonBounds(originalElements);
const offsetX = nextX - x1;
const offsetY = nextY - y1;
@ -112,8 +102,6 @@ const moveGroupTo = (
topLeftX + offsetX,
topLeftY + offsetY,
origElement,
elementsMap,
elements,
scene,
originalElementsMap,
false,
@ -135,7 +123,6 @@ const handlePositionChange: DragInputCallbackType<
originalAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const elements = scene.getNonDeletedElements();
if (nextValue !== undefined) {
for (const atomicUnit of getAtomicUnits(
@ -159,8 +146,6 @@ const handlePositionChange: DragInputCallbackType<
newTopLeftX,
newTopLeftY,
elementsInUnit.map((el) => el.original),
elementsMap,
elements,
originalElementsMap,
scene,
);
@ -188,8 +173,6 @@ const handlePositionChange: DragInputCallbackType<
newTopLeftX,
newTopLeftY,
origElement,
elementsMap,
elements,
scene,
originalElementsMap,
false,
@ -214,8 +197,6 @@ const handlePositionChange: DragInputCallbackType<
changeInTopX,
changeInTopY,
originalElements,
originalElements,
elementsMap,
originalElementsMap,
scene,
);

View file

@ -38,7 +38,6 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
originalAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const elements = scene.getNonDeletedElements();
const origElement = originalElements[0];
const [cx, cy] = [
origElement.x + origElement.width / 2,
@ -133,8 +132,6 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
newTopLeftX,
newTopLeftY,
origElement,
elementsMap,
elements,
scene,
originalElementsMap,
);
@ -166,8 +163,6 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
newTopLeftX,
newTopLeftY,
origElement,
elementsMap,
elements,
scene,
originalElementsMap,
);

View file

@ -4,7 +4,6 @@ import {
bindOrUnbindLinearElements,
updateBoundElements,
} from "@excalidraw/element/binding";
import { mutateElement } from "@excalidraw/element/mutateElement";
import { getBoundTextElement } from "@excalidraw/element/textElement";
import {
isFrameLikeElement,
@ -24,7 +23,6 @@ import type {
ElementsMap,
ExcalidrawElement,
NonDeletedExcalidrawElement,
NonDeletedSceneElementsMap,
} from "@excalidraw/element/types";
import type Scene from "../../scene/Scene";
@ -119,12 +117,11 @@ export const moveElement = (
newTopLeftX: number,
newTopLeftY: number,
originalElement: ExcalidrawElement,
elementsMap: NonDeletedSceneElementsMap,
elements: readonly NonDeletedExcalidrawElement[],
scene: Scene,
originalElementsMap: ElementsMap,
shouldInformMutation = true,
) => {
const elementsMap = scene.getNonDeletedElementsMap();
const latestElement = elementsMap.get(originalElement.id);
if (!latestElement) {
return;
@ -148,15 +145,15 @@ export const moveElement = (
-originalElement.angle as Radians,
);
mutateElement(
scene.mutate(
latestElement,
{
x,
y,
},
shouldInformMutation,
{ informMutation: shouldInformMutation },
);
updateBindings(latestElement, elementsMap, elements, scene);
updateBindings(latestElement, scene);
const boundTextElement = getBoundTextElement(
originalElement,
@ -165,13 +162,13 @@ export const moveElement = (
if (boundTextElement) {
const latestBoundTextElement = elementsMap.get(boundTextElement.id);
latestBoundTextElement &&
mutateElement(
scene.mutate(
latestBoundTextElement,
{
x: boundTextElement.x + changeInX,
y: boundTextElement.y + changeInY,
},
shouldInformMutation,
{ informMutation: shouldInformMutation },
);
}
};
@ -199,8 +196,6 @@ export const getAtomicUnits = (
export const updateBindings = (
latestElement: ExcalidrawElement,
elementsMap: NonDeletedSceneElementsMap,
elements: readonly NonDeletedExcalidrawElement[],
scene: Scene,
options?: {
simultaneouslyUpdated?: readonly ExcalidrawElement[];
@ -209,16 +204,12 @@ export const updateBindings = (
},
) => {
if (isLinearElement(latestElement)) {
bindOrUnbindLinearElements(
[latestElement],
elementsMap,
elements,
scene,
true,
[],
options?.zoom,
);
bindOrUnbindLinearElements([latestElement], true, [], scene, options?.zoom);
} else {
updateBoundElements(latestElement, elementsMap, options);
updateBoundElements(
latestElement,
scene.getNonDeletedElementsMap(),
options,
);
}
};