Position revert

This commit is contained in:
Mark Tolmacs 2025-03-30 09:10:25 +02:00
parent 9a599cfc05
commit 5cc5c626df
4 changed files with 87 additions and 45 deletions

View file

@ -18,10 +18,7 @@ import type { AppState } from "@excalidraw/excalidraw/types";
import type { Degrees, Radians } from "@excalidraw/math";
import type {
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "@excalidraw/element/types";
import type { ExcalidrawElement } from "@excalidraw/element/types";
import { angleIcon } from "../icons";
@ -75,14 +72,6 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
mutateElement(boundTextElement, { angle: nextAngle });
}
setAppState({
suggestedBindings: getSuggestedBindingsForArrows(
[latestElement] as NonDeletedExcalidrawElement[],
elementsMap,
originalAppState.zoom,
),
});
return;
}
@ -114,7 +103,7 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
setAppState({
suggestedBindings: getSuggestedBindingsForArrows(
[latestElement] as NonDeletedExcalidrawElement[],
[latestElement],
elementsMap,
originalAppState.zoom,
),
@ -122,7 +111,7 @@ const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
}
};
const handleFinished: DragFinishedCallbackType = ({
const handleFinished: DragFinishedCallbackType<AngleProps["property"]> = ({
originalElements,
originalAppState,
scene,
@ -137,25 +126,15 @@ const handleFinished: DragFinishedCallbackType = ({
if (latestElement) {
updateBindings(latestElement, elementsMap, originalAppState.zoom, () => {
const revertAngle = (latestElement.angle -
degreesToRadians(accumulatedChange as Degrees)) as Radians;
const change = degreesToRadians(accumulatedChange as Degrees);
mutateElement(latestElement, {
angle: revertAngle,
angle: (latestElement.angle - change) as Radians,
});
});
const boundTextElement = getBoundTextElement(
latestElement,
elementsMap,
);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: revertAngle });
}
setAppState({
suggestedBindings: [],
});
setAppState({
suggestedBindings: [],
});
}
}

View file

@ -37,10 +37,14 @@ export type DragInputCallbackType<
setAppState: React.Component<any, AppState>["setState"];
}) => void;
export type DragFinishedCallbackType<E = ExcalidrawElement> = (props: {
export type DragFinishedCallbackType<
P extends StatsInputProperty,
E = ExcalidrawElement,
> = (props: {
originalElements: readonly E[];
originalElementsMap: ElementsMap;
scene: Scene;
property: P;
originalAppState: AppState;
accumulatedChange: number;
setAppState: React.Component<any, AppState>["setState"];
@ -57,7 +61,7 @@ interface StatsDragInputProps<
editable?: boolean;
shouldKeepAspectRatio?: boolean;
dragInputCallback: DragInputCallbackType<T, E>;
dragFinishedCallback?: DragFinishedCallbackType<E>;
dragFinishedCallback?: DragFinishedCallbackType<T, E>;
property: T;
scene: Scene;
appState: AppState;
@ -136,6 +140,7 @@ const StatsDragInput = <
// reason: idempotent to avoid unnecessary
if (isNaN(original) || Math.abs(rounded - original) >= SMALLEST_DELTA) {
stateRef.current.lastUpdatedValue = updatedValue;
const originalElementsMap = app.scene.getNonDeletedElementsMap();
dragInputCallback({
accumulatedChange: 0,
instantChange: 0,
@ -150,6 +155,15 @@ const StatsDragInput = <
setInputValue: (value) => setInputValue(String(value)),
setAppState,
});
dragFinishedCallback?.({
originalElements: elements,
originalElementsMap,
scene,
originalAppState: appState,
accumulatedChange: rounded,
property,
setAppState,
});
app.syncActionResult({
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
});
@ -303,6 +317,7 @@ const StatsDragInput = <
originalElementsMap,
scene,
originalAppState,
property,
accumulatedChange,
setAppState,
});

View file

@ -7,12 +7,17 @@ import {
import { mutateElement } from "@excalidraw/element/mutateElement";
import { isImageElement } from "@excalidraw/element/typeChecks";
import { getSuggestedBindingsForArrows } from "@excalidraw/element/binding";
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
import StatsDragInput from "./DragInput";
import { getStepSizedValue, moveElement } from "./utils";
import { getStepSizedValue, moveElement, updateBindings } from "./utils";
import type { DragInputCallbackType } from "./DragInput";
import type {
DragFinishedCallbackType,
DragInputCallbackType,
} from "./DragInput";
import type Scene from "../../scene/Scene";
import type { AppState } from "../../types";
@ -36,6 +41,7 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
property,
scene,
originalAppState,
setAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const origElement = originalElements[0];
@ -122,6 +128,14 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
crop: nextCrop,
});
setAppState({
suggestedBindings: getSuggestedBindingsForArrows(
[origElement],
elementsMap,
originalAppState.zoom,
),
});
return;
}
@ -135,6 +149,7 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
elementsMap,
originalElementsMap,
);
return;
}
@ -166,15 +181,51 @@ const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
elementsMap,
originalElementsMap,
);
if (origElement) {
const latestElement = elementsMap.get(origElement.id);
if (latestElement) {
setAppState({
suggestedBindings: getSuggestedBindingsForArrows(
[latestElement],
elementsMap,
originalAppState.zoom,
),
});
}
}
};
const Position = ({
property,
element,
elementsMap,
const handleFinished: DragFinishedCallbackType<"x" | "y"> = ({
originalElements,
originalAppState,
scene,
appState,
}: PositionProps) => {
accumulatedChange,
property,
setAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const origElement = originalElements[0];
if (origElement) {
const latestElement = elementsMap.get(origElement.id);
if (latestElement) {
updateBindings(latestElement, elementsMap, originalAppState.zoom, () => {
mutateElement(latestElement, {
[property]: latestElement[property] - accumulatedChange,
});
});
setAppState({
suggestedBindings: [],
});
}
}
};
const Position = ({ property, element, scene, appState }: PositionProps) => {
const [topLeftX, topLeftY] = pointRotateRads(
pointFrom(element.x, element.y),
pointFrom(element.x + element.width / 2, element.y + element.height / 2),
@ -202,6 +253,7 @@ const Position = ({
label={property === "x" ? "X" : "Y"}
elements={[element]}
dragInputCallback={handlePositionChange}
dragFinishedCallback={handleFinished}
scene={scene}
value={value}
property={property}

View file

@ -204,7 +204,7 @@ export const updateBindings = (
latestElement: ExcalidrawElement,
elementsMap: NonDeletedSceneElementsMap,
zoom?: AppState["zoom"],
remainedBound?: () => void,
bindingElementKeptOriginalBinding?: () => void,
) => {
if (isBindingElement(latestElement)) {
const [start, end] = getOriginalBindingsIfStillCloseToArrowEnds(
@ -217,7 +217,7 @@ export const updateBindings = (
(latestElement.startBinding && start) ||
(latestElement.endBinding && end)
) {
remainedBound?.();
bindingElementKeptOriginalBinding?.();
} else {
if (latestElement.startBinding && !start) {
unbindLinearElement(latestElement, "start");
@ -227,9 +227,5 @@ export const updateBindings = (
unbindLinearElement(latestElement, "end");
}
}
// else if (end) {
// updateBoundElements(end, elementsMap);
// }
}
};