mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
chore: Refactor and remove scene from elbow arrow generation (#8342)
* Refactor and remove scene from elbow arrow generation
This commit is contained in:
parent
72d6ee48fc
commit
dd1370381d
18 changed files with 115 additions and 156 deletions
|
@ -25,6 +25,7 @@ const deleteSelectedElements = (
|
|||
appState: AppState,
|
||||
app: AppClassProperties,
|
||||
) => {
|
||||
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||
const framesToBeDeleted = new Set(
|
||||
getSelectedElements(
|
||||
elements.filter((el) => isFrameLikeElement(el)),
|
||||
|
@ -51,7 +52,7 @@ const deleteSelectedElements = (
|
|||
? null
|
||||
: bound.endBinding,
|
||||
});
|
||||
mutateElbowArrow(bound, app.scene, bound.points);
|
||||
mutateElbowArrow(bound, elementsMap, bound.points);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ export const actionDeleteSelected = register({
|
|||
LinearElementEditor.deletePoints(
|
||||
element,
|
||||
selectedPointsIndices,
|
||||
app.scene,
|
||||
elementsMap,
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -44,7 +44,7 @@ export const actionDuplicateSelection = register({
|
|||
if (appState.editingLinearElement) {
|
||||
const ret = LinearElementEditor.duplicateSelectedPoints(
|
||||
appState,
|
||||
app.scene,
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
|
||||
if (!ret) {
|
||||
|
|
|
@ -120,7 +120,6 @@ const flipElements = (
|
|||
true,
|
||||
flipDirection === "horizontal" ? maxX : minX,
|
||||
flipDirection === "horizontal" ? minY : maxY,
|
||||
app.scene,
|
||||
);
|
||||
|
||||
bindOrUnbindLinearElements(
|
||||
|
|
|
@ -58,7 +58,6 @@ export const createUndoAction: ActionCreator = (history, store) => ({
|
|||
arrayToMap(elements) as SceneElementsMap, // TODO: #7348 refactor action manager to already include `SceneElementsMap`
|
||||
appState,
|
||||
store.snapshot,
|
||||
app.scene,
|
||||
),
|
||||
),
|
||||
keyTest: (event) =>
|
||||
|
@ -100,7 +99,6 @@ export const createRedoAction: ActionCreator = (history, store) => ({
|
|||
arrayToMap(elements) as SceneElementsMap, // TODO: #7348 refactor action manager to already include `SceneElementsMap`
|
||||
appState,
|
||||
store.snapshot,
|
||||
app.scene,
|
||||
),
|
||||
),
|
||||
keyTest: (event) =>
|
||||
|
|
|
@ -1648,7 +1648,7 @@ export const actionChangeArrowType = register({
|
|||
|
||||
mutateElbowArrow(
|
||||
newElement,
|
||||
app.scene,
|
||||
elementsMap,
|
||||
[finalStartPoint, finalEndPoint].map(
|
||||
(point) =>
|
||||
[point[0] - newElement.x, point[1] - newElement.y] as Point,
|
||||
|
|
|
@ -29,7 +29,6 @@ import type {
|
|||
} from "./element/types";
|
||||
import { orderByFractionalIndex, syncMovedIndices } from "./fractionalIndex";
|
||||
import { getNonDeletedGroupIds } from "./groups";
|
||||
import type Scene from "./scene/Scene";
|
||||
import { getObservedAppState } from "./store";
|
||||
import type {
|
||||
AppState,
|
||||
|
@ -1054,7 +1053,6 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
|||
public applyTo(
|
||||
elements: SceneElementsMap,
|
||||
snapshot: Map<string, OrderedExcalidrawElement>,
|
||||
scene: Scene,
|
||||
): [SceneElementsMap, boolean] {
|
||||
let nextElements = toBrandedType<SceneElementsMap>(new Map(elements));
|
||||
let changedElements: Map<string, OrderedExcalidrawElement>;
|
||||
|
@ -1102,7 +1100,6 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
|||
try {
|
||||
// TODO: #7348 refactor away mutations below, so that we couldn't end up in an incosistent state
|
||||
ElementsChange.redrawTextBoundingBoxes(nextElements, changedElements);
|
||||
ElementsChange.redrawBoundArrows(nextElements, changedElements, scene);
|
||||
|
||||
// the following reorder performs also mutations, but only on new instances of changed elements
|
||||
// (unless something goes really bad and it fallbacks to fixing all invalid indices)
|
||||
|
@ -1111,6 +1108,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
|||
changedElements,
|
||||
flags,
|
||||
);
|
||||
|
||||
// Need ordered nextElements to avoid z-index binding issues
|
||||
ElementsChange.redrawBoundArrows(nextElements, changedElements);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Couldn't mutate elements after applying elements change`,
|
||||
|
@ -1459,11 +1459,10 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
|||
private static redrawBoundArrows(
|
||||
elements: SceneElementsMap,
|
||||
changed: Map<string, OrderedExcalidrawElement>,
|
||||
scene: Scene,
|
||||
) {
|
||||
for (const element of changed.values()) {
|
||||
if (!element.isDeleted && isBindableElement(element)) {
|
||||
updateBoundElements(element, elements, scene, {
|
||||
updateBoundElements(element, elements, {
|
||||
changedElements: changed,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4005,14 +4005,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
y: element.y + offsetY,
|
||||
});
|
||||
|
||||
updateBoundElements(
|
||||
element,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.scene,
|
||||
{
|
||||
simultaneouslyUpdated: selectedElements,
|
||||
},
|
||||
);
|
||||
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
|
||||
simultaneouslyUpdated: selectedElements,
|
||||
});
|
||||
});
|
||||
|
||||
this.setState({
|
||||
|
@ -4469,7 +4464,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
onChange: withBatchedUpdates((nextOriginalText) => {
|
||||
updateElement(nextOriginalText, false);
|
||||
if (isNonDeletedElement(element)) {
|
||||
updateBoundElements(element, elementsMap, this.scene);
|
||||
updateBoundElements(element, this.scene.getNonDeletedElementsMap());
|
||||
}
|
||||
}),
|
||||
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
||||
|
@ -5279,7 +5274,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
scenePointerX,
|
||||
scenePointerY,
|
||||
this.state,
|
||||
this.scene,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
|
||||
if (
|
||||
|
@ -5395,7 +5390,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
if (isElbowArrow(multiElement)) {
|
||||
mutateElbowArrow(
|
||||
multiElement,
|
||||
this.scene,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
[
|
||||
...points.slice(0, -1),
|
||||
[
|
||||
|
@ -7771,7 +7766,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
} else if (points.length > 1 && isElbowArrow(newElement)) {
|
||||
mutateElbowArrow(
|
||||
newElement,
|
||||
this.scene,
|
||||
elementsMap,
|
||||
[...points.slice(0, -1), [dx, dy]],
|
||||
[0, 0],
|
||||
undefined,
|
||||
|
@ -9756,7 +9751,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
resizeY,
|
||||
pointerDownState.resize.center.x,
|
||||
pointerDownState.resize.center.y,
|
||||
this.scene,
|
||||
)
|
||||
) {
|
||||
const suggestedBindings = getSuggestedBindingsForArrows(
|
||||
|
|
|
@ -66,7 +66,6 @@ const resizeElementInGroup = (
|
|||
origElement: ExcalidrawElement,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
originalElementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
) => {
|
||||
const updates = getResizedUpdates(anchorX, anchorY, scale, origElement);
|
||||
const { width: oldWidth, height: oldHeight } = latestElement;
|
||||
|
@ -78,7 +77,7 @@ const resizeElementInGroup = (
|
|||
);
|
||||
if (boundTextElement) {
|
||||
const newFontSize = boundTextElement.fontSize * scale;
|
||||
updateBoundElements(latestElement, elementsMap, scene, {
|
||||
updateBoundElements(latestElement, elementsMap, {
|
||||
oldSize: { width: oldWidth, height: oldHeight },
|
||||
});
|
||||
const latestBoundTextElement = elementsMap.get(boundTextElement.id);
|
||||
|
@ -111,7 +110,6 @@ const resizeGroup = (
|
|||
originalElements: ExcalidrawElement[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
originalElementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
) => {
|
||||
// keep aspect ratio for groups
|
||||
if (property === "width") {
|
||||
|
@ -135,7 +133,6 @@ const resizeGroup = (
|
|||
origElement,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
scene,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -190,7 +187,6 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
originalElements,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
scene,
|
||||
);
|
||||
} else {
|
||||
const [el] = elementsInUnit;
|
||||
|
@ -296,7 +292,6 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
originalElements,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
scene,
|
||||
);
|
||||
} else {
|
||||
const [el] = elementsInUnit;
|
||||
|
|
|
@ -198,7 +198,7 @@ export const resizeElement = (
|
|||
}
|
||||
}
|
||||
|
||||
updateBoundElements(latestElement, elementsMap, scene, {
|
||||
updateBoundElements(latestElement, elementsMap, {
|
||||
oldSize: { width: oldWidth, height: oldHeight },
|
||||
});
|
||||
|
||||
|
@ -316,6 +316,6 @@ export const updateBindings = (
|
|||
[],
|
||||
);
|
||||
} else {
|
||||
updateBoundElements(latestElement, elementsMap, scene, options);
|
||||
updateBoundElements(latestElement, elementsMap, options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ import type {
|
|||
OrderedExcalidrawElement,
|
||||
ExcalidrawElbowArrowElement,
|
||||
FixedPoint,
|
||||
SceneElementsMap,
|
||||
} from "./types";
|
||||
|
||||
import type { Bounds } from "./bounds";
|
||||
|
@ -124,7 +125,6 @@ export const bindOrUnbindLinearElement = (
|
|||
boundToElementIds,
|
||||
unboundFromElementIds,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
bindOrUnbindLinearElementEdge(
|
||||
linearElement,
|
||||
|
@ -134,7 +134,6 @@ export const bindOrUnbindLinearElement = (
|
|||
boundToElementIds,
|
||||
unboundFromElementIds,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
|
||||
const onlyUnbound = Array.from(unboundFromElementIds).filter(
|
||||
|
@ -161,7 +160,6 @@ const bindOrUnbindLinearElementEdge = (
|
|||
// Is mutated
|
||||
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
scene: Scene,
|
||||
): void => {
|
||||
// "keep" is for method chaining convenience, a "no-op", so just bail out
|
||||
if (bindableElement === "keep") {
|
||||
|
@ -571,8 +569,7 @@ const calculateFocusAndGap = (
|
|||
// in explicitly.
|
||||
export const updateBoundElements = (
|
||||
changedElement: NonDeletedExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
options?: {
|
||||
simultaneouslyUpdated?: readonly ExcalidrawElement[];
|
||||
oldSize?: { width: number; height: number };
|
||||
|
@ -658,7 +655,7 @@ export const updateBoundElements = (
|
|||
LinearElementEditor.movePoints(
|
||||
element,
|
||||
updates,
|
||||
scene,
|
||||
elementsMap,
|
||||
{
|
||||
...(changedElement.id === element.startBinding?.elementId
|
||||
? { startBinding: bindings.startBinding }
|
||||
|
|
|
@ -91,14 +91,9 @@ export const dragSelectedElements = (
|
|||
updateElementCoords(pointerDownState, textElement, adjustedOffset);
|
||||
}
|
||||
}
|
||||
updateBoundElements(
|
||||
element,
|
||||
scene.getElementsMapIncludingDeleted(),
|
||||
scene,
|
||||
{
|
||||
simultaneouslyUpdated: Array.from(elementsToUpdate),
|
||||
},
|
||||
);
|
||||
updateBoundElements(element, scene.getElementsMapIncludingDeleted(), {
|
||||
simultaneouslyUpdated: Array.from(elementsToUpdate),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
|||
NonDeletedSceneElementsMap,
|
||||
OrderedExcalidrawElement,
|
||||
FixedPointBinding,
|
||||
SceneElementsMap,
|
||||
} from "./types";
|
||||
import {
|
||||
distance2d,
|
||||
|
@ -290,7 +291,7 @@ export class LinearElementEditor {
|
|||
isDragging: selectedIndex === lastClickedPoint,
|
||||
},
|
||||
],
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
} else {
|
||||
const newDraggingPointPosition = LinearElementEditor.createPointAt(
|
||||
|
@ -326,7 +327,7 @@ export class LinearElementEditor {
|
|||
isDragging: pointIndex === lastClickedPoint,
|
||||
};
|
||||
}),
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -420,7 +421,7 @@ export class LinearElementEditor {
|
|||
: element.points[0],
|
||||
},
|
||||
],
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -876,13 +877,12 @@ export class LinearElementEditor {
|
|||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
appState: AppState,
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
): LinearElementEditor | null {
|
||||
if (!appState.editingLinearElement) {
|
||||
return null;
|
||||
}
|
||||
const { elementId, lastUncommittedPoint } = appState.editingLinearElement;
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||
if (!element) {
|
||||
return appState.editingLinearElement;
|
||||
|
@ -893,7 +893,11 @@ export class LinearElementEditor {
|
|||
|
||||
if (!event.altKey) {
|
||||
if (lastPoint === lastUncommittedPoint) {
|
||||
LinearElementEditor.deletePoints(element, [points.length - 1], scene);
|
||||
LinearElementEditor.deletePoints(
|
||||
element,
|
||||
[points.length - 1],
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
return {
|
||||
...appState.editingLinearElement,
|
||||
|
@ -939,14 +943,13 @@ export class LinearElementEditor {
|
|||
point: newPoint,
|
||||
},
|
||||
],
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
} else {
|
||||
LinearElementEditor.addPoints(
|
||||
element,
|
||||
appState,
|
||||
[{ point: newPoint }],
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
@ -1091,7 +1094,7 @@ export class LinearElementEditor {
|
|||
const offsetY = points[0][1];
|
||||
|
||||
return {
|
||||
points: points.map((point, _idx) => {
|
||||
points: points.map((point) => {
|
||||
return [point[0] - offsetX, point[1] - offsetY] as const;
|
||||
}),
|
||||
x: element.x + offsetX,
|
||||
|
@ -1106,13 +1109,15 @@ export class LinearElementEditor {
|
|||
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
|
||||
}
|
||||
|
||||
static duplicateSelectedPoints(appState: AppState, scene: Scene) {
|
||||
static duplicateSelectedPoints(
|
||||
appState: AppState,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
) {
|
||||
if (!appState.editingLinearElement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { selectedPointsIndices, elementId } = appState.editingLinearElement;
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||
|
||||
if (!element || selectedPointsIndices === null) {
|
||||
|
@ -1163,7 +1168,7 @@ export class LinearElementEditor {
|
|||
point: [lastPoint[0] + 30, lastPoint[1] + 30],
|
||||
},
|
||||
],
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1181,7 +1186,7 @@ export class LinearElementEditor {
|
|||
static deletePoints(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
pointIndices: readonly number[],
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
) {
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
@ -1214,15 +1219,14 @@ export class LinearElementEditor {
|
|||
nextPoints,
|
||||
offsetX,
|
||||
offsetY,
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
static addPoints(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
appState: AppState,
|
||||
targetPoints: { point: Point }[],
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
) {
|
||||
const offsetX = 0;
|
||||
const offsetY = 0;
|
||||
|
@ -1233,14 +1237,14 @@ export class LinearElementEditor {
|
|||
nextPoints,
|
||||
offsetX,
|
||||
offsetY,
|
||||
scene,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
static movePoints(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
targetPoints: { index: number; point: Point; isDragging?: boolean }[],
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
otherUpdates?: {
|
||||
startBinding?: PointBinding | null;
|
||||
endBinding?: PointBinding | null;
|
||||
|
@ -1296,7 +1300,7 @@ export class LinearElementEditor {
|
|||
nextPoints,
|
||||
offsetX,
|
||||
offsetY,
|
||||
scene,
|
||||
elementsMap,
|
||||
otherUpdates,
|
||||
{
|
||||
isDragging: targetPoints.reduce(
|
||||
|
@ -1413,7 +1417,7 @@ export class LinearElementEditor {
|
|||
nextPoints: readonly Point[],
|
||||
offsetX: number,
|
||||
offsetY: number,
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
otherUpdates?: {
|
||||
startBinding?: PointBinding | null;
|
||||
endBinding?: PointBinding | null;
|
||||
|
@ -1445,7 +1449,7 @@ export class LinearElementEditor {
|
|||
|
||||
mutateElbowArrow(
|
||||
element,
|
||||
scene,
|
||||
elementsMap,
|
||||
nextPoints,
|
||||
[offsetX, offsetY],
|
||||
bindings,
|
||||
|
|
|
@ -11,6 +11,8 @@ import type {
|
|||
ExcalidrawTextElementWithContainer,
|
||||
ExcalidrawImageElement,
|
||||
ElementsMap,
|
||||
NonDeletedSceneElementsMap,
|
||||
SceneElementsMap,
|
||||
} from "./types";
|
||||
import type { Mutable } from "../utility-types";
|
||||
import {
|
||||
|
@ -69,7 +71,7 @@ export const transformElements = (
|
|||
originalElements: PointerDownState["originalElements"],
|
||||
transformHandleType: MaybeTransformHandleType,
|
||||
selectedElements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: ElementsMap,
|
||||
elementsMap: SceneElementsMap,
|
||||
shouldRotateWithDiscreteAngle: boolean,
|
||||
shouldResizeFromCenter: boolean,
|
||||
shouldMaintainAspectRatio: boolean,
|
||||
|
@ -77,7 +79,6 @@ export const transformElements = (
|
|||
pointerY: number,
|
||||
centerX: number,
|
||||
centerY: number,
|
||||
scene: Scene,
|
||||
) => {
|
||||
if (selectedElements.length === 1) {
|
||||
const [element] = selectedElements;
|
||||
|
@ -90,7 +91,7 @@ export const transformElements = (
|
|||
pointerY,
|
||||
shouldRotateWithDiscreteAngle,
|
||||
);
|
||||
updateBoundElements(element, elementsMap, scene);
|
||||
updateBoundElements(element, elementsMap);
|
||||
}
|
||||
} else if (isTextElement(element) && transformHandleType) {
|
||||
resizeSingleTextElement(
|
||||
|
@ -102,7 +103,7 @@ export const transformElements = (
|
|||
pointerX,
|
||||
pointerY,
|
||||
);
|
||||
updateBoundElements(element, elementsMap, scene);
|
||||
updateBoundElements(element, elementsMap);
|
||||
} else if (transformHandleType) {
|
||||
resizeSingleElement(
|
||||
originalElements,
|
||||
|
@ -113,7 +114,6 @@ export const transformElements = (
|
|||
shouldResizeFromCenter,
|
||||
pointerX,
|
||||
pointerY,
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,6 @@ export const transformElements = (
|
|||
shouldRotateWithDiscreteAngle,
|
||||
centerX,
|
||||
centerY,
|
||||
scene,
|
||||
);
|
||||
return true;
|
||||
} else if (transformHandleType) {
|
||||
|
@ -142,7 +141,6 @@ export const transformElements = (
|
|||
shouldMaintainAspectRatio,
|
||||
pointerX,
|
||||
pointerY,
|
||||
scene,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
@ -434,12 +432,11 @@ export const resizeSingleElement = (
|
|||
originalElements: PointerDownState["originalElements"],
|
||||
shouldMaintainAspectRatio: boolean,
|
||||
element: NonDeletedExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
elementsMap: SceneElementsMap,
|
||||
transformHandleDirection: TransformHandleDirection,
|
||||
shouldResizeFromCenter: boolean,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
scene: Scene,
|
||||
) => {
|
||||
const stateAtResizeStart = originalElements.get(element.id)!;
|
||||
// Gets bounds corners
|
||||
|
@ -710,7 +707,7 @@ export const resizeSingleElement = (
|
|||
) {
|
||||
mutateElement(element, resizedElement);
|
||||
|
||||
updateBoundElements(element, elementsMap, scene, {
|
||||
updateBoundElements(element, elementsMap, {
|
||||
oldSize: {
|
||||
width: stateAtResizeStart.width,
|
||||
height: stateAtResizeStart.height,
|
||||
|
@ -734,13 +731,12 @@ export const resizeSingleElement = (
|
|||
export const resizeMultipleElements = (
|
||||
originalElements: PointerDownState["originalElements"],
|
||||
selectedElements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: ElementsMap,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
transformHandleType: TransformHandleDirection,
|
||||
shouldResizeFromCenter: boolean,
|
||||
shouldMaintainAspectRatio: boolean,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
scene: Scene,
|
||||
) => {
|
||||
// map selected elements to the original elements. While it never should
|
||||
// happen that pointerDownState.originalElements won't contain the selected
|
||||
|
@ -974,12 +970,19 @@ export const resizeMultipleElements = (
|
|||
mutateElement(element, update, false);
|
||||
|
||||
if (isArrowElement(element) && isElbowArrow(element)) {
|
||||
mutateElbowArrow(element, scene, element.points, undefined, undefined, {
|
||||
informMutation: false,
|
||||
});
|
||||
mutateElbowArrow(
|
||||
element,
|
||||
elementsMap,
|
||||
element.points,
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
informMutation: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
updateBoundElements(element, elementsMap, scene, {
|
||||
updateBoundElements(element, elementsMap, {
|
||||
simultaneouslyUpdated: elementsToUpdate,
|
||||
oldSize: { width: oldWidth, height: oldHeight },
|
||||
});
|
||||
|
@ -1004,13 +1007,12 @@ export const resizeMultipleElements = (
|
|||
const rotateMultipleElements = (
|
||||
originalElements: PointerDownState["originalElements"],
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: ElementsMap,
|
||||
elementsMap: SceneElementsMap,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
shouldRotateWithDiscreteAngle: boolean,
|
||||
centerX: number,
|
||||
centerY: number,
|
||||
scene: Scene,
|
||||
) => {
|
||||
let centerAngle =
|
||||
(5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
|
||||
|
@ -1037,7 +1039,7 @@ const rotateMultipleElements = (
|
|||
|
||||
if (isArrowElement(element) && isElbowArrow(element)) {
|
||||
const points = getArrowLocalFixedPoints(element, elementsMap);
|
||||
mutateElbowArrow(element, scene, points);
|
||||
mutateElbowArrow(element, elementsMap, points);
|
||||
} else {
|
||||
mutateElement(
|
||||
element,
|
||||
|
@ -1050,7 +1052,7 @@ const rotateMultipleElements = (
|
|||
);
|
||||
}
|
||||
|
||||
updateBoundElements(element, elementsMap, scene, {
|
||||
updateBoundElements(element, elementsMap, {
|
||||
simultaneouslyUpdated: elements,
|
||||
});
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ describe("elbow arrow routing", () => {
|
|||
elbowed: true,
|
||||
}) as ExcalidrawElbowArrowElement;
|
||||
scene.insertElement(arrow);
|
||||
mutateElbowArrow(arrow, scene, [
|
||||
mutateElbowArrow(arrow, scene.getNonDeletedElementsMap(), [
|
||||
[-45 - arrow.x, -100.1 - arrow.y],
|
||||
[45 - arrow.x, 99.9 - arrow.y],
|
||||
]);
|
||||
|
@ -98,7 +98,7 @@ describe("elbow arrow routing", () => {
|
|||
expect(arrow.startBinding).not.toBe(null);
|
||||
expect(arrow.endBinding).not.toBe(null);
|
||||
|
||||
mutateElbowArrow(arrow, scene, [
|
||||
mutateElbowArrow(arrow, elementsMap, [
|
||||
[0, 0],
|
||||
[90, 200],
|
||||
]);
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
translatePoint,
|
||||
} from "../math";
|
||||
import { getSizeFromPoints } from "../points";
|
||||
import type Scene from "../scene/Scene";
|
||||
import type { Point } from "../types";
|
||||
import { isAnyTrue, toBrandedType, tupleToCoors } from "../utils";
|
||||
import {
|
||||
|
@ -37,14 +36,10 @@ import { isBindableElement, isRectanguloidElement } from "./typeChecks";
|
|||
import type {
|
||||
ExcalidrawElbowArrowElement,
|
||||
FixedPointBinding,
|
||||
NonDeletedExcalidrawElement,
|
||||
NonDeletedSceneElementsMap,
|
||||
SceneElementsMap,
|
||||
} from "./types";
|
||||
import type {
|
||||
ElementsMap,
|
||||
ExcalidrawBindableElement,
|
||||
OrderedExcalidrawElement,
|
||||
} from "./types";
|
||||
import type { ElementsMap, ExcalidrawBindableElement } from "./types";
|
||||
|
||||
type Node = {
|
||||
f: number;
|
||||
|
@ -67,7 +62,7 @@ const BASE_PADDING = 40;
|
|||
|
||||
export const mutateElbowArrow = (
|
||||
arrow: ExcalidrawElbowArrowElement,
|
||||
scene: Scene,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
nextPoints: readonly Point[],
|
||||
offset?: Point,
|
||||
otherUpdates?: {
|
||||
|
@ -75,15 +70,11 @@ export const mutateElbowArrow = (
|
|||
endBinding?: FixedPointBinding | null;
|
||||
},
|
||||
options?: {
|
||||
changedElements?: Map<string, OrderedExcalidrawElement>;
|
||||
isDragging?: boolean;
|
||||
disableBinding?: boolean;
|
||||
informMutation?: boolean;
|
||||
},
|
||||
) => {
|
||||
const elements = getAllElements(scene, options?.changedElements);
|
||||
const elementsMap = getAllElementsMap(scene, options?.changedElements);
|
||||
|
||||
const origStartGlobalPoint = translatePoint(nextPoints[0], [
|
||||
arrow.x + (offset ? offset[0] : 0),
|
||||
arrow.y + (offset ? offset[1] : 0),
|
||||
|
@ -99,22 +90,9 @@ export const mutateElbowArrow = (
|
|||
const endElement =
|
||||
arrow.endBinding &&
|
||||
getBindableElementForId(arrow.endBinding.elementId, elementsMap);
|
||||
const hoveredStartElement = options?.isDragging
|
||||
? getHoveredElementForBinding(
|
||||
tupleToCoors(origStartGlobalPoint),
|
||||
elements,
|
||||
elementsMap,
|
||||
true,
|
||||
)
|
||||
: startElement;
|
||||
const hoveredEndElement = options?.isDragging
|
||||
? getHoveredElementForBinding(
|
||||
tupleToCoors(origEndGlobalPoint),
|
||||
elements,
|
||||
elementsMap,
|
||||
true,
|
||||
)
|
||||
: endElement;
|
||||
const [hoveredStartElement, hoveredEndElement] = options?.isDragging
|
||||
? getHoveredElements(origStartGlobalPoint, origEndGlobalPoint, elementsMap)
|
||||
: [startElement, endElement];
|
||||
const startGlobalPoint = getGlobalPoint(
|
||||
arrow.startBinding?.fixedPoint,
|
||||
origStartGlobalPoint,
|
||||
|
@ -895,7 +873,7 @@ const normalizedArrowElementUpdate = (
|
|||
const offsetY = global[0][1];
|
||||
|
||||
const points = global.map(
|
||||
(point, _idx) => [point[0] - offsetX, point[1] - offsetY] as const,
|
||||
(point) => [point[0] - offsetX, point[1] - offsetY] as const,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -935,32 +913,11 @@ const neighborIndexToHeading = (idx: number): Heading => {
|
|||
return HEADING_LEFT;
|
||||
};
|
||||
|
||||
const getAllElementsMap = (
|
||||
scene: Scene,
|
||||
changedElements?: Map<string, OrderedExcalidrawElement>,
|
||||
): NonDeletedSceneElementsMap =>
|
||||
changedElements
|
||||
? toBrandedType<NonDeletedSceneElementsMap>(
|
||||
new Map([...scene.getNonDeletedElementsMap(), ...changedElements]),
|
||||
)
|
||||
: scene.getNonDeletedElementsMap();
|
||||
|
||||
const getAllElements = (
|
||||
scene: Scene,
|
||||
changedElements?: Map<string, OrderedExcalidrawElement>,
|
||||
): readonly NonDeletedExcalidrawElement[] =>
|
||||
changedElements
|
||||
? ([
|
||||
...scene.getNonDeletedElements(),
|
||||
...[...changedElements].map(([_, value]) => value),
|
||||
] as NonDeletedExcalidrawElement[])
|
||||
: scene.getNonDeletedElements();
|
||||
|
||||
const getGlobalPoint = (
|
||||
fixedPointRatio: [number, number] | undefined | null,
|
||||
initialPoint: Point,
|
||||
otherPoint: Point,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
boundElement?: ExcalidrawBindableElement | null,
|
||||
hoveredElement?: ExcalidrawBindableElement | null,
|
||||
isDragging?: boolean,
|
||||
|
@ -1016,7 +973,7 @@ const getSnapPoint = (
|
|||
const getBindPointHeading = (
|
||||
point: Point,
|
||||
otherPoint: Point,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
hoveredElement: ExcalidrawBindableElement | null | undefined,
|
||||
origPoint: Point,
|
||||
) =>
|
||||
|
@ -1034,3 +991,30 @@ const getBindPointHeading = (
|
|||
elementsMap,
|
||||
origPoint,
|
||||
);
|
||||
|
||||
const getHoveredElements = (
|
||||
origStartGlobalPoint: Point,
|
||||
origEndGlobalPoint: Point,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
) => {
|
||||
// TODO: Might be a performance bottleneck and the Map type
|
||||
// remembers the insertion order anyway...
|
||||
const nonDeletedSceneElementsMap = toBrandedType<NonDeletedSceneElementsMap>(
|
||||
new Map([...elementsMap].filter((el) => !el[1].isDeleted)),
|
||||
);
|
||||
const elements = Array.from(elementsMap.values());
|
||||
return [
|
||||
getHoveredElementForBinding(
|
||||
tupleToCoors(origStartGlobalPoint),
|
||||
elements,
|
||||
nonDeletedSceneElementsMap,
|
||||
true,
|
||||
),
|
||||
getHoveredElementForBinding(
|
||||
tupleToCoors(origEndGlobalPoint),
|
||||
elements,
|
||||
nonDeletedSceneElementsMap,
|
||||
true,
|
||||
),
|
||||
];
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type { AppStateChange, ElementsChange } from "./change";
|
||||
import type { SceneElementsMap } from "./element/types";
|
||||
import { Emitter } from "./emitter";
|
||||
import type Scene from "./scene/Scene";
|
||||
import type { Snapshot } from "./store";
|
||||
import type { AppState } from "./types";
|
||||
|
||||
|
@ -65,7 +64,6 @@ export class History {
|
|||
elements: SceneElementsMap,
|
||||
appState: AppState,
|
||||
snapshot: Readonly<Snapshot>,
|
||||
scene: Scene,
|
||||
) {
|
||||
return this.perform(
|
||||
elements,
|
||||
|
@ -73,7 +71,6 @@ export class History {
|
|||
snapshot,
|
||||
() => History.pop(this.undoStack),
|
||||
(entry: HistoryEntry) => History.push(this.redoStack, entry, elements),
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -81,7 +78,6 @@ export class History {
|
|||
elements: SceneElementsMap,
|
||||
appState: AppState,
|
||||
snapshot: Readonly<Snapshot>,
|
||||
scene: Scene,
|
||||
) {
|
||||
return this.perform(
|
||||
elements,
|
||||
|
@ -89,7 +85,6 @@ export class History {
|
|||
snapshot,
|
||||
() => History.pop(this.redoStack),
|
||||
(entry: HistoryEntry) => History.push(this.undoStack, entry, elements),
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -99,7 +94,6 @@ export class History {
|
|||
snapshot: Readonly<Snapshot>,
|
||||
pop: () => HistoryEntry | null,
|
||||
push: (entry: HistoryEntry) => void,
|
||||
scene: Scene,
|
||||
): [SceneElementsMap, AppState] | void {
|
||||
try {
|
||||
let historyEntry = pop();
|
||||
|
@ -116,7 +110,7 @@ export class History {
|
|||
while (historyEntry) {
|
||||
try {
|
||||
[nextElements, nextAppState, containsVisibleChange] =
|
||||
historyEntry.applyTo(nextElements, nextAppState, snapshot, scene);
|
||||
historyEntry.applyTo(nextElements, nextAppState, snapshot);
|
||||
} finally {
|
||||
// make sure to always push / pop, even if the increment is corrupted
|
||||
push(historyEntry);
|
||||
|
@ -187,10 +181,9 @@ export class HistoryEntry {
|
|||
elements: SceneElementsMap,
|
||||
appState: AppState,
|
||||
snapshot: Readonly<Snapshot>,
|
||||
scene: Scene,
|
||||
): [SceneElementsMap, AppState, boolean] {
|
||||
const [nextElements, elementsContainVisibleChange] =
|
||||
this.elementsChange.applyTo(elements, snapshot.elements, scene);
|
||||
this.elementsChange.applyTo(elements, snapshot.elements);
|
||||
|
||||
const [nextAppState, appStateContainsVisibleChange] =
|
||||
this.appStateChange.applyTo(appState, nextElements);
|
||||
|
|
|
@ -44,7 +44,6 @@ import { queryByText } from "@testing-library/react";
|
|||
import { HistoryEntry } from "../history";
|
||||
import { AppStateChange, ElementsChange } from "../change";
|
||||
import { Snapshot, StoreAction } from "../store";
|
||||
import type Scene from "../scene/Scene";
|
||||
|
||||
const { h } = window;
|
||||
|
||||
|
@ -118,7 +117,6 @@ describe("history", () => {
|
|||
arrayToMap(h.elements) as SceneElementsMap,
|
||||
appState,
|
||||
Snapshot.empty(),
|
||||
{} as Scene,
|
||||
) as any,
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -140,7 +138,6 @@ describe("history", () => {
|
|||
arrayToMap(h.elements) as SceneElementsMap,
|
||||
appState,
|
||||
Snapshot.empty(),
|
||||
{} as Scene,
|
||||
) as any,
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import type {
|
|||
ExcalidrawLinearElement,
|
||||
ExcalidrawTextElementWithContainer,
|
||||
FontString,
|
||||
SceneElementsMap,
|
||||
} from "../element/types";
|
||||
import { Excalidraw, mutateElement } from "../index";
|
||||
import { centerPoint } from "../math";
|
||||
|
@ -1344,7 +1345,7 @@ describe("Test Linear Elements", () => {
|
|||
],
|
||||
},
|
||||
],
|
||||
h.scene,
|
||||
new Map() as SceneElementsMap,
|
||||
);
|
||||
});
|
||||
expect(line.x).toBe(origStartX + 10);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue