Further reverse refactor

This commit is contained in:
Mark Tolmacs 2025-05-02 14:46:57 +02:00
parent 1f97420475
commit b3946aa3b4
No known key found for this signature in database
3 changed files with 256 additions and 101 deletions

View file

@ -8,9 +8,18 @@ import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
import type Scene from "@excalidraw/element/Scene";
import StatsDragInput from "./DragInput";
import { handlePositionChange } from "./utils";
import {
getAtomicUnits,
getStepSizedValue,
isPropertyEditable,
moveElements,
moveGroupTo,
STEP_SIZE,
type AtomicUnit,
} from "./utils";
import { getElementsInAtomicUnit, moveElement } from "./utils";
import type { AtomicUnit } from "./utils";
import type { DragInputCallbackType } from "./DragInput";
import type { AppState } from "../../types";
interface MultiPositionProps {
@ -22,6 +31,100 @@ interface MultiPositionProps {
appState: AppState;
}
const handlePositionChange: DragInputCallbackType<
MultiPositionProps["property"]
> = ({
accumulatedChange,
originalElements,
originalElementsMap,
shouldChangeByStepSize,
nextValue,
property,
scene,
originalAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
if (nextValue !== undefined) {
for (const atomicUnit of getAtomicUnits(
originalElements,
originalAppState,
)) {
const elementsInUnit = getElementsInAtomicUnit(
atomicUnit,
elementsMap,
originalElementsMap,
);
if (elementsInUnit.length > 1) {
const [x1, y1, ,] = getCommonBounds(
elementsInUnit.map((el) => el.latest!),
);
const newTopLeftX = property === "x" ? nextValue : x1;
const newTopLeftY = property === "y" ? nextValue : y1;
moveGroupTo(
newTopLeftX,
newTopLeftY,
elementsInUnit.map((el) => el.original),
originalElementsMap,
scene,
);
} else {
const origElement = elementsInUnit[0]?.original;
const latestElement = elementsInUnit[0]?.latest;
if (
origElement &&
latestElement &&
isPropertyEditable(latestElement, property)
) {
const [cx, cy] = [
origElement.x + origElement.width / 2,
origElement.y + origElement.height / 2,
];
const [topLeftX, topLeftY] = pointRotateRads(
pointFrom(origElement.x, origElement.y),
pointFrom(cx, cy),
origElement.angle,
);
const newTopLeftX = property === "x" ? nextValue : topLeftX;
const newTopLeftY = property === "y" ? nextValue : topLeftY;
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
false,
);
}
}
}
scene.triggerUpdate();
return;
}
const change = shouldChangeByStepSize
? getStepSizedValue(accumulatedChange, STEP_SIZE)
: accumulatedChange;
const changeInTopX = property === "x" ? change : 0;
const changeInTopY = property === "y" ? change : 0;
moveElements(
property,
changeInTopX,
changeInTopY,
originalElements,
originalElementsMap,
scene,
);
scene.triggerUpdate();
};
const MultiPosition = ({
property,
elements,

View file

@ -1,6 +1,9 @@
import { pointFrom, pointRotateRads, round } from "@excalidraw/math";
import { clamp, pointFrom, pointRotateRads, round } from "@excalidraw/math";
import { getFlipAdjustedCropPosition } from "@excalidraw/element/cropElement";
import {
getFlipAdjustedCropPosition,
getUncroppedWidthAndHeight,
} from "@excalidraw/element/cropElement";
import { isImageElement } from "@excalidraw/element/typeChecks";
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
@ -8,8 +11,9 @@ import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
import type Scene from "@excalidraw/element/Scene";
import StatsDragInput from "./DragInput";
import { handlePositionChange } from "./utils";
import { getStepSizedValue, moveElement, STEP_SIZE } from "./utils";
import type { DragInputCallbackType } from "./DragInput";
import type { AppState } from "../../types";
interface PositionProps {
@ -20,6 +24,148 @@ interface PositionProps {
appState: AppState;
}
const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
accumulatedChange,
instantChange,
originalElements,
originalElementsMap,
shouldChangeByStepSize,
nextValue,
property,
scene,
originalAppState,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const origElement = originalElements[0];
const [cx, cy] = [
origElement.x + origElement.width / 2,
origElement.y + origElement.height / 2,
];
const [topLeftX, topLeftY] = pointRotateRads(
pointFrom(origElement.x, origElement.y),
pointFrom(cx, cy),
origElement.angle,
);
if (originalAppState.croppingElementId === origElement.id) {
const element = elementsMap.get(origElement.id);
if (!element || !isImageElement(element) || !element.crop) {
return;
}
const crop = element.crop;
let nextCrop = crop;
const isFlippedByX = element.scale[0] === -1;
const isFlippedByY = element.scale[1] === -1;
const { width: uncroppedWidth, height: uncroppedHeight } =
getUncroppedWidthAndHeight(element);
if (nextValue !== undefined) {
if (property === "x") {
const nextValueInNatural =
nextValue * (crop.naturalWidth / uncroppedWidth);
if (isFlippedByX) {
nextCrop = {
...crop,
x: clamp(
crop.naturalWidth - nextValueInNatural - crop.width,
0,
crop.naturalWidth - crop.width,
),
};
} else {
nextCrop = {
...crop,
x: clamp(
nextValue * (crop.naturalWidth / uncroppedWidth),
0,
crop.naturalWidth - crop.width,
),
};
}
}
if (property === "y") {
nextCrop = {
...crop,
y: clamp(
nextValue * (crop.naturalHeight / uncroppedHeight),
0,
crop.naturalHeight - crop.height,
),
};
}
scene.mutateElement(element, {
crop: nextCrop,
});
return;
}
const changeInX =
(property === "x" ? instantChange : 0) * (isFlippedByX ? -1 : 1);
const changeInY =
(property === "y" ? instantChange : 0) * (isFlippedByY ? -1 : 1);
nextCrop = {
...crop,
x: clamp(crop.x + changeInX, 0, crop.naturalWidth - crop.width),
y: clamp(crop.y + changeInY, 0, crop.naturalHeight - crop.height),
};
scene.mutateElement(element, {
crop: nextCrop,
});
return;
}
if (nextValue !== undefined) {
const newTopLeftX = property === "x" ? nextValue : topLeftX;
const newTopLeftY = property === "y" ? nextValue : topLeftY;
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
);
return;
}
const changeInTopX = property === "x" ? accumulatedChange : 0;
const changeInTopY = property === "y" ? accumulatedChange : 0;
const newTopLeftX =
property === "x"
? Math.round(
shouldChangeByStepSize
? getStepSizedValue(origElement.x + changeInTopX, STEP_SIZE)
: topLeftX + changeInTopX,
)
: topLeftX;
const newTopLeftY =
property === "y"
? Math.round(
shouldChangeByStepSize
? getStepSizedValue(origElement.y + changeInTopY, STEP_SIZE)
: topLeftY + changeInTopY,
)
: topLeftY;
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
);
};
const Position = ({
property,
element,

View file

@ -31,7 +31,6 @@ import type {
import type Scene from "@excalidraw/element/Scene";
import type { AppState } from "../../types";
import type { DragInputCallbackType } from "./DragInput";
export type StatsInputProperty =
| "x"
@ -43,6 +42,7 @@ export type StatsInputProperty =
| "gridStep";
export const SMALLEST_DELTA = 0.01;
export const STEP_SIZE = 10;
export const isPropertyEditable = (
element: ExcalidrawElement,
@ -274,7 +274,7 @@ export const moveElements = (
}
};
export const moveGroup = (
export const moveGroupTo = (
nextX: number,
nextY: number,
originalElements: ExcalidrawElement[],
@ -355,97 +355,3 @@ export const updateBindings = (
updateBoundElements(latestElement, scene, options);
}
};
export const handlePositionChange: DragInputCallbackType<"x" | "y"> = ({
accumulatedChange,
originalElements,
originalElementsMap,
shouldChangeByStepSize,
nextValue,
property,
scene,
originalAppState,
}) => {
const STEP_SIZE = 10;
const elementsMap = scene.getNonDeletedElementsMap();
if (nextValue !== undefined) {
for (const atomicUnit of getAtomicUnits(
originalElements,
originalAppState,
)) {
const elementsInUnit = getElementsInAtomicUnit(
atomicUnit,
elementsMap,
originalElementsMap,
);
if (elementsInUnit.length > 1) {
const [x1, y1, ,] = getCommonBounds(
elementsInUnit.map((el) => el.latest!),
);
const newTopLeftX = property === "x" ? nextValue : x1;
const newTopLeftY = property === "y" ? nextValue : y1;
moveGroup(
newTopLeftX,
newTopLeftY,
elementsInUnit.map((el) => el.original),
originalElementsMap,
scene,
);
} else {
const origElement = elementsInUnit[0]?.original;
const latestElement = elementsInUnit[0]?.latest;
if (
origElement &&
latestElement &&
isPropertyEditable(latestElement, property)
) {
const [cx, cy] = [
origElement.x + origElement.width / 2,
origElement.y + origElement.height / 2,
];
const [topLeftX, topLeftY] = pointRotateRads(
pointFrom(origElement.x, origElement.y),
pointFrom(cx, cy),
origElement.angle,
);
const newTopLeftX = property === "x" ? nextValue : topLeftX;
const newTopLeftY = property === "y" ? nextValue : topLeftY;
moveElement(
newTopLeftX,
newTopLeftY,
origElement,
scene,
originalElementsMap,
false,
);
}
}
}
scene.triggerUpdate();
return;
}
const change = shouldChangeByStepSize
? getStepSizedValue(accumulatedChange, STEP_SIZE)
: accumulatedChange;
const changeInTopX = property === "x" ? change : 0;
const changeInTopY = property === "y" ? change : 0;
moveElements(
property,
changeInTopX,
changeInTopY,
originalElements,
originalElementsMap,
scene,
);
scene.triggerUpdate();
};