mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
Scene passed in everywhere (WIP)
This commit is contained in:
parent
a9c3b2a4d4
commit
9b0a2f86a9
16 changed files with 246 additions and 281 deletions
|
@ -15,13 +15,12 @@ export interface Alignment {
|
|||
|
||||
export const alignElements = (
|
||||
selectedElements: ExcalidrawElement[],
|
||||
elementsMap: ElementsMap,
|
||||
alignment: Alignment,
|
||||
scene: Scene,
|
||||
): ExcalidrawElement[] => {
|
||||
const groups: ExcalidrawElement[][] = getMaximumGroups(
|
||||
selectedElements,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
const selectionBoundingBox = getCommonBoundingBox(selectedElements);
|
||||
|
||||
|
@ -33,12 +32,12 @@ export const alignElements = (
|
|||
);
|
||||
return group.map((element) => {
|
||||
// update element
|
||||
const updatedEle = mutateElement(element, {
|
||||
const updatedEle = scene.mutateElement(element, {
|
||||
x: element.x + translation.x,
|
||||
y: element.y + translation.y,
|
||||
});
|
||||
// update bound elements
|
||||
updateBoundElements(element, scene.getNonDeletedElementsMap(), {
|
||||
updateBoundElements(scene, element, {
|
||||
simultaneouslyUpdated: group,
|
||||
});
|
||||
return updatedEle;
|
||||
|
|
|
@ -84,7 +84,6 @@ import type {
|
|||
OrderedExcalidrawElement,
|
||||
ExcalidrawElbowArrowElement,
|
||||
FixedPoint,
|
||||
SceneElementsMap,
|
||||
FixedPointBinding,
|
||||
} from "./types";
|
||||
|
||||
|
@ -130,7 +129,6 @@ export const bindOrUnbindLinearElement = (
|
|||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
startBindingElement: ExcalidrawBindableElement | null | "keep",
|
||||
endBindingElement: ExcalidrawBindableElement | null | "keep",
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
scene: Scene,
|
||||
): void => {
|
||||
const boundToElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
||||
|
@ -142,7 +140,7 @@ export const bindOrUnbindLinearElement = (
|
|||
"start",
|
||||
boundToElementIds,
|
||||
unboundFromElementIds,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
bindOrUnbindLinearElementEdge(
|
||||
linearElement,
|
||||
|
@ -151,7 +149,7 @@ export const bindOrUnbindLinearElement = (
|
|||
"end",
|
||||
boundToElementIds,
|
||||
unboundFromElementIds,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
|
||||
const onlyUnbound = Array.from(unboundFromElementIds).filter(
|
||||
|
@ -159,7 +157,7 @@ export const bindOrUnbindLinearElement = (
|
|||
);
|
||||
|
||||
getNonDeletedElements(scene, onlyUnbound).forEach((element) => {
|
||||
mutateElement(element, {
|
||||
scene.mutateElement(element, {
|
||||
boundElements: element.boundElements?.filter(
|
||||
(element) =>
|
||||
element.type !== "arrow" || element.id !== linearElement.id,
|
||||
|
@ -177,7 +175,7 @@ const bindOrUnbindLinearElementEdge = (
|
|||
boundToElementIds: Set<ExcalidrawBindableElement["id"]>,
|
||||
// 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") {
|
||||
|
@ -186,7 +184,7 @@ const bindOrUnbindLinearElementEdge = (
|
|||
|
||||
// null means break the bind, so nothing to consider here
|
||||
if (bindableElement === null) {
|
||||
const unbound = unbindLinearElement(linearElement, startOrEnd);
|
||||
const unbound = unbindLinearElement(linearElement, scene, startOrEnd);
|
||||
if (unbound != null) {
|
||||
unboundFromElementIds.add(unbound);
|
||||
}
|
||||
|
@ -209,16 +207,11 @@ const bindOrUnbindLinearElementEdge = (
|
|||
: startOrEnd === "start" ||
|
||||
otherEdgeBindableElement.id !== bindableElement.id)
|
||||
) {
|
||||
bindLinearElement(
|
||||
linearElement,
|
||||
bindableElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
);
|
||||
bindLinearElement(linearElement, bindableElement, startOrEnd, scene);
|
||||
boundToElementIds.add(bindableElement.id);
|
||||
}
|
||||
} else {
|
||||
bindLinearElement(linearElement, bindableElement, startOrEnd, elementsMap);
|
||||
bindLinearElement(linearElement, bindableElement, startOrEnd, scene);
|
||||
boundToElementIds.add(bindableElement.id);
|
||||
}
|
||||
};
|
||||
|
@ -362,7 +355,6 @@ const getBindingStrategyForDraggingArrowOrJoints = (
|
|||
|
||||
export const bindOrUnbindLinearElements = (
|
||||
selectedElements: NonDeleted<ExcalidrawLinearElement>[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
scene: Scene,
|
||||
isBindingEnabled: boolean,
|
||||
|
@ -376,20 +368,20 @@ export const bindOrUnbindLinearElements = (
|
|||
selectedElement,
|
||||
isBindingEnabled,
|
||||
draggingPoints ?? [],
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
elements,
|
||||
zoom,
|
||||
)
|
||||
: // The arrow itself (the shaft) or the inner joins are dragged
|
||||
getBindingStrategyForDraggingArrowOrJoints(
|
||||
selectedElement,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
elements,
|
||||
isBindingEnabled,
|
||||
zoom,
|
||||
);
|
||||
|
||||
bindOrUnbindLinearElement(selectedElement, start, end, elementsMap, scene);
|
||||
bindOrUnbindLinearElement(selectedElement, start, end, scene);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -429,22 +421,21 @@ export const maybeBindLinearElement = (
|
|||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
appState: AppState,
|
||||
pointerCoords: { x: number; y: number },
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
scene: Scene,
|
||||
): void => {
|
||||
if (appState.startBoundElement != null) {
|
||||
bindLinearElement(
|
||||
linearElement,
|
||||
appState.startBoundElement,
|
||||
"start",
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
||||
const hoveredElement = getHoveredElementForBinding(
|
||||
pointerCoords,
|
||||
elements,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElements(),
|
||||
scene.getNonDeletedElementsMap(),
|
||||
appState.zoom,
|
||||
isElbowArrow(linearElement),
|
||||
isElbowArrow(linearElement),
|
||||
|
@ -458,7 +449,7 @@ export const maybeBindLinearElement = (
|
|||
"end",
|
||||
)
|
||||
) {
|
||||
bindLinearElement(linearElement, hoveredElement, "end", elementsMap);
|
||||
bindLinearElement(linearElement, hoveredElement, "end", scene);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -487,7 +478,7 @@ export const bindLinearElement = (
|
|||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
hoveredElement: ExcalidrawBindableElement,
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
scene: Scene,
|
||||
): void => {
|
||||
if (!isArrowElement(linearElement)) {
|
||||
return;
|
||||
|
@ -500,7 +491,7 @@ export const bindLinearElement = (
|
|||
linearElement,
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
),
|
||||
hoveredElement,
|
||||
),
|
||||
|
@ -513,18 +504,17 @@ export const bindLinearElement = (
|
|||
linearElement,
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
mutateElement(linearElement, {
|
||||
scene.mutateElement(linearElement, {
|
||||
[startOrEnd === "start" ? "startBinding" : "endBinding"]: binding,
|
||||
});
|
||||
|
||||
const boundElementsMap = arrayToMap(hoveredElement.boundElements || []);
|
||||
if (!boundElementsMap.has(linearElement.id)) {
|
||||
mutateElement(hoveredElement, {
|
||||
scene.mutateElement(hoveredElement, {
|
||||
boundElements: (hoveredElement.boundElements || []).concat({
|
||||
id: linearElement.id,
|
||||
type: "arrow",
|
||||
|
@ -565,6 +555,7 @@ const isLinearElementSimple = (
|
|||
|
||||
const unbindLinearElement = (
|
||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
scene: Scene,
|
||||
startOrEnd: "start" | "end",
|
||||
): ExcalidrawBindableElement["id"] | null => {
|
||||
const field = startOrEnd === "start" ? "startBinding" : "endBinding";
|
||||
|
@ -572,7 +563,7 @@ const unbindLinearElement = (
|
|||
if (binding == null) {
|
||||
return null;
|
||||
}
|
||||
mutateElement(linearElement, { [field]: null });
|
||||
scene.mutateElement(linearElement, { [field]: null });
|
||||
return binding.elementId;
|
||||
};
|
||||
|
||||
|
@ -739,8 +730,8 @@ const calculateFocusAndGap = (
|
|||
// done before the `changedElement` is updated, and the `newSize` is passed
|
||||
// in explicitly.
|
||||
export const updateBoundElements = (
|
||||
scene: Scene,
|
||||
changedElement: NonDeletedExcalidrawElement,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
options?: {
|
||||
simultaneouslyUpdated?: readonly ExcalidrawElement[];
|
||||
newSize?: { width: number; height: number };
|
||||
|
@ -756,6 +747,7 @@ export const updateBoundElements = (
|
|||
return;
|
||||
}
|
||||
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
boundElementsVisitor(elementsMap, changedElement, (element) => {
|
||||
if (!isLinearElement(element) || element.isDeleted) {
|
||||
return;
|
||||
|
@ -796,20 +788,7 @@ export const updateBoundElements = (
|
|||
|
||||
// `linearElement` is being moved/scaled already, just update the binding
|
||||
if (simultaneouslyUpdatedElementIds.has(element.id)) {
|
||||
if (isElbowArrow(element)) {
|
||||
mutateElbowArrow(
|
||||
element,
|
||||
bindings as {
|
||||
startBinding: FixedPointBinding;
|
||||
endBinding: FixedPointBinding;
|
||||
},
|
||||
true,
|
||||
elementsMap,
|
||||
);
|
||||
} else {
|
||||
mutateElement(element, bindings, true);
|
||||
}
|
||||
|
||||
scene.mutateElement(element, bindings);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -898,7 +877,6 @@ export const getHeadingForElbowArrowSnap = (
|
|||
otherPoint: Readonly<GlobalPoint>,
|
||||
bindableElement: ExcalidrawBindableElement | undefined | null,
|
||||
aabb: Bounds | undefined | null,
|
||||
elementsMap: ElementsMap,
|
||||
origPoint: GlobalPoint,
|
||||
zoom?: AppState["zoom"],
|
||||
): Heading => {
|
||||
|
@ -908,12 +886,7 @@ export const getHeadingForElbowArrowSnap = (
|
|||
return otherPointHeading;
|
||||
}
|
||||
|
||||
const distance = getDistanceForBinding(
|
||||
origPoint,
|
||||
bindableElement,
|
||||
elementsMap,
|
||||
zoom,
|
||||
);
|
||||
const distance = getDistanceForBinding(origPoint, bindableElement, zoom);
|
||||
|
||||
if (!distance) {
|
||||
return vectorToHeading(
|
||||
|
@ -933,7 +906,6 @@ export const getHeadingForElbowArrowSnap = (
|
|||
const getDistanceForBinding = (
|
||||
point: Readonly<GlobalPoint>,
|
||||
bindableElement: ExcalidrawBindableElement,
|
||||
elementsMap: ElementsMap,
|
||||
zoom?: AppState["zoom"],
|
||||
) => {
|
||||
const distance = distanceToBindableElement(bindableElement, point);
|
||||
|
@ -1239,7 +1211,6 @@ const updateBoundPoint = (
|
|||
linearElement,
|
||||
bindableElement,
|
||||
startOrEnd === "startBinding" ? "start" : "end",
|
||||
elementsMap,
|
||||
).fixedPoint;
|
||||
const globalMidPoint = pointFrom<GlobalPoint>(
|
||||
bindableElement.x + bindableElement.width / 2,
|
||||
|
@ -1349,7 +1320,6 @@ export const calculateFixedPointForElbowArrowBinding = (
|
|||
linearElement: NonDeleted<ExcalidrawElbowArrowElement>,
|
||||
hoveredElement: ExcalidrawBindableElement,
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: ElementsMap,
|
||||
): { fixedPoint: FixedPoint } => {
|
||||
const bounds = [
|
||||
hoveredElement.x,
|
||||
|
@ -2166,6 +2136,7 @@ export class BoundElement {
|
|||
* NOTE: rebind expects that affected elements were previously unbound with `BoundElement.unbindAffected`
|
||||
*/
|
||||
public static rebindAffected = (
|
||||
scene: Scene,
|
||||
elements: ElementsMap,
|
||||
boundElement: ExcalidrawElement | undefined,
|
||||
updateElementWith: (
|
||||
|
|
|
@ -104,7 +104,7 @@ export const dragSelectedElements = (
|
|||
);
|
||||
|
||||
elementsToUpdate.forEach((element) => {
|
||||
updateElementCoords(pointerDownState, element, adjustedOffset);
|
||||
updateElementCoords(pointerDownState, element, scene, adjustedOffset);
|
||||
if (!isArrowElement(element)) {
|
||||
// skip arrow labels since we calculate its position during render
|
||||
const textElement = getBoundTextElement(
|
||||
|
@ -112,9 +112,14 @@ export const dragSelectedElements = (
|
|||
scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
if (textElement) {
|
||||
updateElementCoords(pointerDownState, textElement, adjustedOffset);
|
||||
updateElementCoords(
|
||||
pointerDownState,
|
||||
textElement,
|
||||
scene,
|
||||
adjustedOffset,
|
||||
);
|
||||
}
|
||||
updateBoundElements(element, scene.getElementsMapIncludingDeleted(), {
|
||||
updateBoundElements(scene, element, {
|
||||
simultaneouslyUpdated: Array.from(elementsToUpdate),
|
||||
});
|
||||
}
|
||||
|
@ -155,6 +160,7 @@ const calculateOffset = (
|
|||
const updateElementCoords = (
|
||||
pointerDownState: PointerDownState,
|
||||
element: NonDeletedExcalidrawElement,
|
||||
scene: Scene,
|
||||
dragOffset: { x: number; y: number },
|
||||
) => {
|
||||
const originalElement =
|
||||
|
@ -163,7 +169,7 @@ const updateElementCoords = (
|
|||
const nextX = originalElement.x + dragOffset.x;
|
||||
const nextY = originalElement.y + dragOffset.y;
|
||||
|
||||
mutateElement(element, {
|
||||
scene.mutateElement(element, {
|
||||
x: nextX,
|
||||
y: nextY,
|
||||
});
|
||||
|
@ -190,6 +196,7 @@ export const dragNewElement = ({
|
|||
shouldMaintainAspectRatio,
|
||||
shouldResizeFromCenter,
|
||||
zoom,
|
||||
scene,
|
||||
widthAspectRatio = null,
|
||||
originOffset = null,
|
||||
informMutation = true,
|
||||
|
@ -205,6 +212,7 @@ export const dragNewElement = ({
|
|||
shouldMaintainAspectRatio: boolean;
|
||||
shouldResizeFromCenter: boolean;
|
||||
zoom: NormalizedZoomValue;
|
||||
scene: Scene;
|
||||
/** whether to keep given aspect ratio when `isResizeWithSidesSameLength` is
|
||||
true */
|
||||
widthAspectRatio?: number | null;
|
||||
|
@ -285,7 +293,7 @@ export const dragNewElement = ({
|
|||
};
|
||||
}
|
||||
|
||||
mutateElement(
|
||||
scene.mutateElement(
|
||||
newElement,
|
||||
{
|
||||
x: newX + (originOffset?.x ?? 0),
|
||||
|
@ -295,7 +303,7 @@ export const dragNewElement = ({
|
|||
...textAutoResize,
|
||||
...imageInitialDimension,
|
||||
},
|
||||
informMutation,
|
||||
{ informMutation },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1329,14 +1329,12 @@ const getElbowArrowData = (
|
|||
const startHeading = getBindPointHeading(
|
||||
startGlobalPoint,
|
||||
endGlobalPoint,
|
||||
elementsMap,
|
||||
hoveredStartElement,
|
||||
origStartGlobalPoint,
|
||||
);
|
||||
const endHeading = getBindPointHeading(
|
||||
endGlobalPoint,
|
||||
startGlobalPoint,
|
||||
elementsMap,
|
||||
hoveredEndElement,
|
||||
origEndGlobalPoint,
|
||||
);
|
||||
|
@ -2306,7 +2304,6 @@ const getGlobalPoint = (
|
|||
const getBindPointHeading = (
|
||||
p: GlobalPoint,
|
||||
otherPoint: GlobalPoint,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
hoveredElement: ExcalidrawBindableElement | null | undefined,
|
||||
origPoint: GlobalPoint,
|
||||
): Heading =>
|
||||
|
@ -2324,7 +2321,6 @@ const getBindPointHeading = (
|
|||
number,
|
||||
],
|
||||
),
|
||||
elementsMap,
|
||||
origPoint,
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import type {
|
|||
PendingExcalidrawElements,
|
||||
} from "@excalidraw/excalidraw/types";
|
||||
|
||||
import type Scene from "@excalidraw/excalidraw/scene/Scene";
|
||||
|
||||
import { bindLinearElement } from "./binding";
|
||||
import { updateElbowArrowPoints } from "./elbowArrow";
|
||||
import {
|
||||
|
@ -239,6 +241,7 @@ const addNewNode = (
|
|||
elementsMap: ElementsMap,
|
||||
appState: AppState,
|
||||
direction: LinkDirection,
|
||||
scene: Scene,
|
||||
) => {
|
||||
const successors = getSuccessors(element, elementsMap, direction);
|
||||
const predeccessors = getPredecessors(element, elementsMap, direction);
|
||||
|
@ -277,6 +280,7 @@ const addNewNode = (
|
|||
elementsMap,
|
||||
direction,
|
||||
appState,
|
||||
scene,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -290,6 +294,7 @@ export const addNewNodes = (
|
|||
elementsMap: ElementsMap,
|
||||
appState: AppState,
|
||||
direction: LinkDirection,
|
||||
scene: Scene,
|
||||
numberOfNodes: number,
|
||||
) => {
|
||||
// always start from 0 and distribute evenly
|
||||
|
@ -355,6 +360,7 @@ export const addNewNodes = (
|
|||
elementsMap,
|
||||
direction,
|
||||
appState,
|
||||
scene,
|
||||
);
|
||||
|
||||
newNodes.push(nextNode);
|
||||
|
@ -370,6 +376,7 @@ const createBindingArrow = (
|
|||
elementsMap: ElementsMap,
|
||||
direction: LinkDirection,
|
||||
appState: AppState,
|
||||
scene: Scene,
|
||||
) => {
|
||||
let startX: number;
|
||||
let startY: number;
|
||||
|
@ -440,18 +447,8 @@ const createBindingArrow = (
|
|||
elbowed: true,
|
||||
});
|
||||
|
||||
bindLinearElement(
|
||||
bindingArrow,
|
||||
startBindingElement,
|
||||
"start",
|
||||
elementsMap as NonDeletedSceneElementsMap,
|
||||
);
|
||||
bindLinearElement(
|
||||
bindingArrow,
|
||||
endBindingElement,
|
||||
"end",
|
||||
elementsMap as NonDeletedSceneElementsMap,
|
||||
);
|
||||
bindLinearElement(bindingArrow, startBindingElement, "start", scene);
|
||||
bindLinearElement(bindingArrow, endBindingElement, "end", scene);
|
||||
|
||||
const changedElements = new Map<string, OrderedExcalidrawElement>();
|
||||
changedElements.set(
|
||||
|
@ -635,6 +632,7 @@ export class FlowChartCreator {
|
|||
elementsMap: ElementsMap,
|
||||
appState: AppState,
|
||||
direction: LinkDirection,
|
||||
scene: Scene,
|
||||
) {
|
||||
if (direction !== this.direction) {
|
||||
const { nextNode, bindingArrow } = addNewNode(
|
||||
|
@ -642,6 +640,7 @@ export class FlowChartCreator {
|
|||
elementsMap,
|
||||
appState,
|
||||
direction,
|
||||
scene,
|
||||
);
|
||||
|
||||
this.numberOfNodes = 1;
|
||||
|
@ -655,6 +654,7 @@ export class FlowChartCreator {
|
|||
elementsMap,
|
||||
appState,
|
||||
direction,
|
||||
scene,
|
||||
this.numberOfNodes,
|
||||
);
|
||||
|
||||
|
@ -682,13 +682,9 @@ export class FlowChartCreator {
|
|||
)
|
||||
) {
|
||||
this.pendingNodes = this.pendingNodes.map((node) =>
|
||||
mutateElement(
|
||||
node,
|
||||
{
|
||||
frameId: startNode.frameId,
|
||||
},
|
||||
false,
|
||||
),
|
||||
mutateElement(node, {
|
||||
frameId: startNode.frameId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,13 +125,13 @@ export class LinearElementEditor {
|
|||
public readonly segmentMidPointHoveredCoords: GlobalPoint | null;
|
||||
public readonly elbowed: boolean;
|
||||
|
||||
constructor(element: NonDeleted<ExcalidrawLinearElement>) {
|
||||
constructor(element: NonDeleted<ExcalidrawLinearElement>, scene: Scene) {
|
||||
this.elementId = element.id as string & {
|
||||
_brand: "excalidrawLinearElementId";
|
||||
};
|
||||
if (!pointsEqual(element.points[0], pointFrom(0, 0))) {
|
||||
console.error("Linear element is not normalized", Error().stack);
|
||||
LinearElementEditor.normalizePoints(element);
|
||||
LinearElementEditor.normalizePoints(element, scene);
|
||||
}
|
||||
|
||||
this.selectedPointsIndices = null;
|
||||
|
@ -796,7 +796,7 @@ export class LinearElementEditor {
|
|||
linearElementEditor.lastUncommittedPoint == null &&
|
||||
!isElbowArrow(element)
|
||||
) {
|
||||
mutateElement(element, {
|
||||
scene.mutateElement(element, {
|
||||
points: [
|
||||
...element.points,
|
||||
LinearElementEditor.createPointAt(
|
||||
|
@ -862,7 +862,6 @@ export class LinearElementEditor {
|
|||
element,
|
||||
startBindingElement,
|
||||
endBindingElement,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
@ -1165,19 +1164,23 @@ export class LinearElementEditor {
|
|||
// element-mutating methods
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static normalizePoints(element: NonDeleted<ExcalidrawLinearElement>) {
|
||||
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
|
||||
static normalizePoints(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
scene: Scene,
|
||||
) {
|
||||
scene.mutateElement(
|
||||
element,
|
||||
LinearElementEditor.getNormalizedPoints(element),
|
||||
);
|
||||
}
|
||||
|
||||
static duplicateSelectedPoints(
|
||||
appState: AppState,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
): AppState {
|
||||
static duplicateSelectedPoints(appState: AppState, scene: Scene): AppState {
|
||||
invariant(
|
||||
appState.editingLinearElement,
|
||||
"Not currently editing a linear element",
|
||||
);
|
||||
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const { selectedPointsIndices, elementId } = appState.editingLinearElement;
|
||||
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||
|
||||
|
@ -1220,12 +1223,7 @@ export class LinearElementEditor {
|
|||
return acc;
|
||||
}, []);
|
||||
|
||||
const updates = { points: nextPoints };
|
||||
if (isElbowArrow(element)) {
|
||||
mutateElbowArrow(element, updates, true, elementsMap);
|
||||
} else {
|
||||
mutateElement(element, updates);
|
||||
}
|
||||
scene.mutateElement(element, { points: nextPoints });
|
||||
|
||||
// temp hack to ensure the line doesn't move when adding point to the end,
|
||||
// potentially expanding the bounding box
|
||||
|
@ -1400,8 +1398,9 @@ export class LinearElementEditor {
|
|||
pointerCoords: PointerCoords,
|
||||
app: AppClassProperties,
|
||||
snapToGrid: boolean,
|
||||
elementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
) {
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const element = LinearElementEditor.getElement(
|
||||
linearElementEditor.elementId,
|
||||
elementsMap,
|
||||
|
@ -1431,12 +1430,7 @@ export class LinearElementEditor {
|
|||
...element.points.slice(segmentMidpoint.index!),
|
||||
];
|
||||
|
||||
const updates = { points };
|
||||
if (isElbowArrow(element)) {
|
||||
mutateElbowArrow(element, updates, true, elementsMap);
|
||||
} else {
|
||||
mutateElement(element, updates);
|
||||
}
|
||||
scene.mutateElement(element, { points });
|
||||
|
||||
ret.pointerDownState = {
|
||||
...linearElementEditor.pointerDownState,
|
||||
|
@ -1489,7 +1483,7 @@ export class LinearElementEditor {
|
|||
updates.points = Array.from(nextPoints);
|
||||
|
||||
if (!options?.sceneElementsMap) {
|
||||
mutateElbowArrow(element, updates, true, options?.sceneElementsMap!, {
|
||||
mutateElbowArrow(element, updates, options?.sceneElementsMap!, {
|
||||
isDragging: options?.isDragging,
|
||||
});
|
||||
} else {
|
||||
|
@ -1790,8 +1784,9 @@ export class LinearElementEditor {
|
|||
index: number,
|
||||
x: number,
|
||||
y: number,
|
||||
elementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
): LinearElementEditor {
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const element = LinearElementEditor.getElement(
|
||||
linearElement.elementId,
|
||||
elementsMap,
|
||||
|
@ -1834,14 +1829,9 @@ export class LinearElementEditor {
|
|||
.map((segment) => segment.index)
|
||||
.reduce((count, idx) => (idx < index ? count + 1 : count), 0);
|
||||
|
||||
mutateElbowArrow(
|
||||
element,
|
||||
{
|
||||
fixedSegments: nextFixedSegments,
|
||||
},
|
||||
true,
|
||||
elementsMap,
|
||||
);
|
||||
scene.mutateElement(element, {
|
||||
fixedSegments: nextFixedSegments,
|
||||
});
|
||||
|
||||
const point = pointFrom<GlobalPoint>(
|
||||
element.x +
|
||||
|
@ -1873,19 +1863,14 @@ export class LinearElementEditor {
|
|||
|
||||
static deleteFixedSegment(
|
||||
element: ExcalidrawElbowArrowElement,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
scene: Scene,
|
||||
index: number,
|
||||
): void {
|
||||
mutateElbowArrow(
|
||||
element,
|
||||
{
|
||||
fixedSegments: element.fixedSegments?.filter(
|
||||
(segment) => segment.index !== index,
|
||||
),
|
||||
},
|
||||
true,
|
||||
elementsMap,
|
||||
);
|
||||
scene.mutateElement(element, {
|
||||
fixedSegments: element.fixedSegments?.filter(
|
||||
(segment) => segment.index !== index,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ export const transformElements = (
|
|||
originalElements: PointerDownState["originalElements"],
|
||||
transformHandleType: MaybeTransformHandleType,
|
||||
selectedElements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: SceneElementsMap,
|
||||
scene: Scene,
|
||||
shouldRotateWithDiscreteAngle: boolean,
|
||||
shouldResizeFromCenter: boolean,
|
||||
|
@ -95,35 +94,35 @@ export const transformElements = (
|
|||
centerX: number,
|
||||
centerY: number,
|
||||
): boolean => {
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
if (selectedElements.length === 1) {
|
||||
const [element] = selectedElements;
|
||||
if (transformHandleType === "rotation") {
|
||||
if (!isElbowArrow(element)) {
|
||||
rotateSingleElement(
|
||||
element,
|
||||
elementsMap,
|
||||
scene,
|
||||
pointerX,
|
||||
pointerY,
|
||||
shouldRotateWithDiscreteAngle,
|
||||
);
|
||||
updateBoundElements(element, elementsMap);
|
||||
updateBoundElements(scene, element);
|
||||
}
|
||||
} else if (isTextElement(element) && transformHandleType) {
|
||||
resizeSingleTextElement(
|
||||
originalElements,
|
||||
element,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
transformHandleType,
|
||||
shouldResizeFromCenter,
|
||||
pointerX,
|
||||
pointerY,
|
||||
);
|
||||
updateBoundElements(element, elementsMap);
|
||||
updateBoundElements(scene, element);
|
||||
return true;
|
||||
} else if (transformHandleType) {
|
||||
const elementId = selectedElements[0].id;
|
||||
const latestElement = elementsMap.get(elementId);
|
||||
const latestElement = scene.getNonDeletedElementsMap().get(elementId);
|
||||
const origElement = originalElements.get(elementId);
|
||||
|
||||
if (latestElement && origElement) {
|
||||
|
@ -131,7 +130,7 @@ export const transformElements = (
|
|||
getNextSingleWidthAndHeightFromPointer(
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
originalElements,
|
||||
transformHandleType,
|
||||
pointerX,
|
||||
|
@ -147,8 +146,8 @@ export const transformElements = (
|
|||
nextHeight,
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
originalElements,
|
||||
scene,
|
||||
transformHandleType,
|
||||
{
|
||||
shouldMaintainAspectRatio,
|
||||
|
@ -163,7 +162,6 @@ export const transformElements = (
|
|||
rotateMultipleElements(
|
||||
originalElements,
|
||||
selectedElements,
|
||||
elementsMap,
|
||||
scene,
|
||||
pointerX,
|
||||
pointerY,
|
||||
|
@ -212,13 +210,15 @@ export const transformElements = (
|
|||
|
||||
const rotateSingleElement = (
|
||||
element: NonDeletedExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
shouldRotateWithDiscreteAngle: boolean,
|
||||
) => {
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(
|
||||
element,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
let angle: Radians;
|
||||
|
@ -235,13 +235,13 @@ const rotateSingleElement = (
|
|||
}
|
||||
const boundTextElementId = getBoundTextElementId(element);
|
||||
|
||||
mutateElement(element, { angle });
|
||||
scene.mutateElement(element, { angle });
|
||||
if (boundTextElementId) {
|
||||
const textElement =
|
||||
scene.getElement<ExcalidrawTextElementWithContainer>(boundTextElementId);
|
||||
|
||||
if (textElement && !isArrowElement(element)) {
|
||||
mutateElement(textElement, { angle });
|
||||
scene.mutateElement(textElement, { angle });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -517,7 +517,6 @@ const resizeSingleTextElement = (
|
|||
const rotateMultipleElements = (
|
||||
originalElements: PointerDownState["originalElements"],
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: SceneElementsMap,
|
||||
scene: Scene,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
|
@ -525,6 +524,7 @@ const rotateMultipleElements = (
|
|||
centerX: number,
|
||||
centerY: number,
|
||||
) => {
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
let centerAngle =
|
||||
(5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
|
||||
if (shouldRotateWithDiscreteAngle) {
|
||||
|
@ -552,36 +552,27 @@ const rotateMultipleElements = (
|
|||
{
|
||||
points: getArrowLocalFixedPoints(element, elementsMap),
|
||||
},
|
||||
false,
|
||||
elementsMap,
|
||||
);
|
||||
} else {
|
||||
mutateElement(
|
||||
element,
|
||||
{
|
||||
x: element.x + (rotatedCX - cx),
|
||||
y: element.y + (rotatedCY - cy),
|
||||
angle: normalizeRadians((centerAngle + origAngle) as Radians),
|
||||
},
|
||||
false,
|
||||
);
|
||||
mutateElement(element, {
|
||||
x: element.x + (rotatedCX - cx),
|
||||
y: element.y + (rotatedCY - cy),
|
||||
angle: normalizeRadians((centerAngle + origAngle) as Radians),
|
||||
});
|
||||
}
|
||||
|
||||
updateBoundElements(element, elementsMap, {
|
||||
updateBoundElements(scene, element, {
|
||||
simultaneouslyUpdated: elements,
|
||||
});
|
||||
|
||||
const boundText = getBoundTextElement(element, elementsMap);
|
||||
if (boundText && !isArrowElement(element)) {
|
||||
mutateElement(
|
||||
boundText,
|
||||
{
|
||||
x: boundText.x + (rotatedCX - cx),
|
||||
y: boundText.y + (rotatedCY - cy),
|
||||
angle: normalizeRadians((centerAngle + origAngle) as Radians),
|
||||
},
|
||||
false,
|
||||
);
|
||||
mutateElement(boundText, {
|
||||
x: boundText.x + (rotatedCX - cx),
|
||||
y: boundText.y + (rotatedCY - cy),
|
||||
angle: normalizeRadians((centerAngle + origAngle) as Radians),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -826,8 +817,8 @@ export const resizeSingleElement = (
|
|||
nextHeight: number,
|
||||
latestElement: ExcalidrawElement,
|
||||
origElement: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
originalElementsMap: ElementsMap,
|
||||
scene: Scene,
|
||||
handleDirection: TransformHandleDirection,
|
||||
{
|
||||
shouldInformMutation = true,
|
||||
|
@ -840,7 +831,10 @@ export const resizeSingleElement = (
|
|||
} = {},
|
||||
) => {
|
||||
let boundTextFont: { fontSize?: number } = {};
|
||||
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
|
||||
const boundTextElement = getBoundTextElement(
|
||||
latestElement,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
|
||||
if (boundTextElement) {
|
||||
const stateOfBoundTextElementAtResize = originalElementsMap.get(
|
||||
|
@ -860,7 +854,7 @@ export const resizeSingleElement = (
|
|||
|
||||
const nextFont = measureFontSizeFromWidth(
|
||||
boundTextElement,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
getBoundTextMaxWidth(updatedElement, boundTextElement),
|
||||
);
|
||||
if (nextFont === null) {
|
||||
|
@ -939,7 +933,7 @@ export const resizeSingleElement = (
|
|||
}
|
||||
|
||||
if ("scale" in latestElement && "scale" in origElement) {
|
||||
mutateElement(latestElement, {
|
||||
scene.mutateElement(latestElement, {
|
||||
scale: [
|
||||
// defaulting because scaleX/Y can be 0/-0
|
||||
(Math.sign(nextWidth) || origElement.scale[0]) * origElement.scale[0],
|
||||
|
@ -974,21 +968,23 @@ export const resizeSingleElement = (
|
|||
...rescaledPoints,
|
||||
};
|
||||
|
||||
mutateElement(latestElement, updates, shouldInformMutation);
|
||||
scene.mutateElement(latestElement, updates, {
|
||||
informMutation: shouldInformMutation,
|
||||
});
|
||||
|
||||
updateBoundElements(latestElement, elementsMap as SceneElementsMap, {
|
||||
updateBoundElements(scene, latestElement, {
|
||||
// TODO: confirm with MARK if this actually makes sense
|
||||
newSize: { width: nextWidth, height: nextHeight },
|
||||
});
|
||||
|
||||
if (boundTextElement && boundTextFont != null) {
|
||||
mutateElement(boundTextElement, {
|
||||
scene.mutateElement(boundTextElement, {
|
||||
fontSize: boundTextFont.fontSize,
|
||||
});
|
||||
}
|
||||
handleBindTextResize(
|
||||
latestElement,
|
||||
elementsMap,
|
||||
scene.getNonDeletedElementsMap(),
|
||||
handleDirection,
|
||||
shouldMaintainAspectRatio,
|
||||
);
|
||||
|
@ -1534,26 +1530,22 @@ export const resizeMultipleElements = (
|
|||
} of elementsAndUpdates) {
|
||||
const { width, height, angle } = update;
|
||||
|
||||
scene.mutateElement(element, update, false, {
|
||||
scene.mutateElement(element, update, {
|
||||
// needed for the fixed binding point udpate to take effect
|
||||
isDragging: true,
|
||||
});
|
||||
|
||||
updateBoundElements(element, elementsMap as SceneElementsMap, {
|
||||
updateBoundElements(scene, element, {
|
||||
simultaneouslyUpdated: elementsToUpdate,
|
||||
newSize: { width, height },
|
||||
});
|
||||
|
||||
const boundTextElement = getBoundTextElement(element, elementsMap);
|
||||
if (boundTextElement && boundTextFontSize) {
|
||||
scene.mutateElement(
|
||||
boundTextElement,
|
||||
{
|
||||
fontSize: boundTextFontSize,
|
||||
angle: isLinearElement(element) ? undefined : angle,
|
||||
},
|
||||
false,
|
||||
);
|
||||
scene.mutateElement(boundTextElement, {
|
||||
fontSize: boundTextFontSize,
|
||||
angle: isLinearElement(element) ? undefined : angle,
|
||||
});
|
||||
handleBindTextResize(element, elementsMap, handleDirection, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,14 +50,8 @@ const alignSelectedElements = (
|
|||
alignment: Alignment,
|
||||
) => {
|
||||
const selectedElements = app.scene.getSelectedElements(appState);
|
||||
const elementsMap = arrayToMap(elements);
|
||||
|
||||
const updatedElements = alignElements(
|
||||
selectedElements,
|
||||
elementsMap,
|
||||
alignment,
|
||||
app.scene,
|
||||
);
|
||||
const updatedElements = alignElements(selectedElements, alignment, app.scene);
|
||||
|
||||
const updatedElementsMap = arrayToMap(updatedElements);
|
||||
|
||||
|
|
|
@ -289,6 +289,7 @@ export const actionDeleteSelected = register({
|
|||
deleteSelectedElements(elements, appState, app);
|
||||
|
||||
fixBindingsAfterDeletion(
|
||||
app.scene,
|
||||
nextElements,
|
||||
nextElements.filter((el) => el.isDeleted),
|
||||
);
|
||||
|
|
|
@ -36,6 +36,8 @@ import { CaptureUpdateAction } from "../store";
|
|||
|
||||
import { register } from "./register";
|
||||
|
||||
import type Scene from "../scene/Scene";
|
||||
|
||||
export const actionDuplicateSelection = register({
|
||||
name: "duplicateSelection",
|
||||
label: "labels.duplicateSelection",
|
||||
|
@ -52,7 +54,7 @@ export const actionDuplicateSelection = register({
|
|||
try {
|
||||
const newAppState = LinearElementEditor.duplicateSelectedPoints(
|
||||
appState,
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
app.scene,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -95,7 +97,7 @@ export const actionDuplicateSelection = register({
|
|||
elements: syncMovedIndices(nextElements, arrayToMap(duplicatedElements)),
|
||||
appState: {
|
||||
...appState,
|
||||
...updateLinearElementEditors(duplicatedElements),
|
||||
...updateLinearElementEditors(duplicatedElements, app.scene),
|
||||
...selectGroupsForSelectedElements(
|
||||
{
|
||||
editingGroupId: appState.editingGroupId,
|
||||
|
@ -131,7 +133,10 @@ export const actionDuplicateSelection = register({
|
|||
),
|
||||
});
|
||||
|
||||
const updateLinearElementEditors = (clonedElements: ExcalidrawElement[]) => {
|
||||
const updateLinearElementEditors = (
|
||||
clonedElements: ExcalidrawElement[],
|
||||
scene: Scene,
|
||||
) => {
|
||||
const linears = clonedElements.filter(isLinearElement);
|
||||
if (linears.length === 1) {
|
||||
const linear = linears[0];
|
||||
|
@ -142,7 +147,7 @@ const updateLinearElementEditors = (clonedElements: ExcalidrawElement[]) => {
|
|||
|
||||
if (onlySingleLinearSelected) {
|
||||
return {
|
||||
selectedLinearElement: new LinearElementEditor(linear),
|
||||
selectedLinearElement: new LinearElementEditor(linear, scene),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ export const actionFinalize = register({
|
|||
element,
|
||||
startBindingElement,
|
||||
endBindingElement,
|
||||
elementsMap,
|
||||
scene,
|
||||
);
|
||||
}
|
||||
|
@ -82,7 +81,11 @@ export const actionFinalize = register({
|
|||
scene.getElement(appState.pendingImageElementId);
|
||||
|
||||
if (pendingImageElement) {
|
||||
mutateElement(pendingImageElement, { isDeleted: true }, false);
|
||||
scene.mutateElement(
|
||||
pendingImageElement,
|
||||
{ isDeleted: true },
|
||||
{ informMutation: false },
|
||||
);
|
||||
}
|
||||
|
||||
if (window.document.activeElement instanceof HTMLElement) {
|
||||
|
@ -95,16 +98,6 @@ export const actionFinalize = register({
|
|||
? appState.newElement
|
||||
: null;
|
||||
|
||||
const mutate = (updates: ElementUpdate<ExcalidrawElbowArrowElement>) =>
|
||||
isElbowArrow(multiPointElement as ExcalidrawElbowArrowElement)
|
||||
? mutateElbowArrow(
|
||||
multiPointElement as ExcalidrawElbowArrowElement,
|
||||
updates,
|
||||
true,
|
||||
elementsMap,
|
||||
)
|
||||
: mutateElement(multiPointElement as ExcalidrawElement, updates);
|
||||
|
||||
if (multiPointElement) {
|
||||
// pen and mouse have hover
|
||||
if (
|
||||
|
@ -116,7 +109,7 @@ export const actionFinalize = register({
|
|||
!lastCommittedPoint ||
|
||||
points[points.length - 1] !== lastCommittedPoint
|
||||
) {
|
||||
mutate({
|
||||
scene.mutateElement(multiPointElement, {
|
||||
points: multiPointElement.points.slice(0, -1),
|
||||
});
|
||||
}
|
||||
|
@ -140,7 +133,7 @@ export const actionFinalize = register({
|
|||
if (isLoop) {
|
||||
const linePoints = multiPointElement.points;
|
||||
const firstPoint = linePoints[0];
|
||||
mutate({
|
||||
scene.mutateElement(multiPointElement, {
|
||||
points: linePoints.map((p, index) =>
|
||||
index === linePoints.length - 1
|
||||
? pointFrom(firstPoint[0], firstPoint[1])
|
||||
|
@ -160,13 +153,7 @@ export const actionFinalize = register({
|
|||
-1,
|
||||
arrayToMap(elements),
|
||||
);
|
||||
maybeBindLinearElement(
|
||||
multiPointElement,
|
||||
appState,
|
||||
{ x, y },
|
||||
elementsMap,
|
||||
elements,
|
||||
);
|
||||
maybeBindLinearElement(multiPointElement, appState, { x, y }, scene);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +209,7 @@ export const actionFinalize = register({
|
|||
// To select the linear element when user has finished mutipoint editing
|
||||
selectedLinearElement:
|
||||
multiPointElement && isLinearElement(multiPointElement)
|
||||
? new LinearElementEditor(multiPointElement)
|
||||
? new LinearElementEditor(multiPointElement, scene)
|
||||
: appState.selectedLinearElement,
|
||||
pendingImageElementId: null,
|
||||
},
|
||||
|
|
|
@ -1670,10 +1670,10 @@ export const actionChangeArrowType = register({
|
|||
newElement,
|
||||
startHoveredElement,
|
||||
"start",
|
||||
elementsMap,
|
||||
app.scene,
|
||||
);
|
||||
endHoveredElement &&
|
||||
bindLinearElement(newElement, endHoveredElement, "end", elementsMap);
|
||||
bindLinearElement(newElement, endHoveredElement, "end", app.scene);
|
||||
|
||||
const startBinding =
|
||||
startElement && newElement.startBinding
|
||||
|
@ -1684,7 +1684,6 @@ export const actionChangeArrowType = register({
|
|||
newElement,
|
||||
startElement,
|
||||
"start",
|
||||
elementsMap,
|
||||
),
|
||||
}
|
||||
: null;
|
||||
|
@ -1697,7 +1696,6 @@ export const actionChangeArrowType = register({
|
|||
newElement,
|
||||
endElement,
|
||||
"end",
|
||||
elementsMap,
|
||||
),
|
||||
}
|
||||
: null;
|
||||
|
@ -1729,7 +1727,7 @@ export const actionChangeArrowType = register({
|
|||
newElement.startBinding.elementId,
|
||||
) as ExcalidrawBindableElement;
|
||||
if (startElement) {
|
||||
bindLinearElement(newElement, startElement, "start", elementsMap);
|
||||
bindLinearElement(newElement, startElement, "start", app.scene);
|
||||
}
|
||||
}
|
||||
if (newElement.endBinding) {
|
||||
|
@ -1737,7 +1735,7 @@ export const actionChangeArrowType = register({
|
|||
newElement.endBinding.elementId,
|
||||
) as ExcalidrawBindableElement;
|
||||
if (endElement) {
|
||||
bindLinearElement(newElement, endElement, "end", elementsMap);
|
||||
bindLinearElement(newElement, endElement, "end", app.scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.mutateElement(
|
||||
frameElement,
|
||||
{
|
||||
customData: { generationData: undefined },
|
||||
},
|
||||
{ informMutation: false },
|
||||
);
|
||||
} else {
|
||||
this.scene.mutateElement(frameElement, {
|
||||
customData: { generationData: data },
|
||||
}, { informMutation: false });
|
||||
this.scene.mutateElement(
|
||||
frameElement,
|
||||
{
|
||||
customData: { generationData: data },
|
||||
},
|
||||
{ informMutation: false },
|
||||
);
|
||||
}
|
||||
this.magicGenerations.set(frameElement.id, data);
|
||||
this.triggerRender();
|
||||
|
@ -2926,8 +2934,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
nonDeletedElementsMap,
|
||||
),
|
||||
),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3452,7 +3459,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.mutateElement(
|
||||
initializedImageElement,
|
||||
{ y },
|
||||
{ informMutation: false },
|
||||
);
|
||||
|
||||
y = imageElement.y + imageElement.height + 25;
|
||||
|
||||
|
@ -4177,6 +4188,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.scene.getNonDeletedElementsMap(),
|
||||
this.state,
|
||||
getLinkDirectionFromKey(event.key),
|
||||
this.scene,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4418,12 +4430,16 @@ 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.mutateElement(
|
||||
element,
|
||||
{
|
||||
x: element.x + offsetX,
|
||||
y: element.y + offsetY,
|
||||
},
|
||||
{ informMutation: false },
|
||||
);
|
||||
|
||||
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
|
||||
updateBoundElements(this.scene, element, {
|
||||
simultaneouslyUpdated: selectedElements,
|
||||
});
|
||||
});
|
||||
|
@ -4457,6 +4473,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.setState({
|
||||
editingLinearElement: new LinearElementEditor(
|
||||
selectedElement,
|
||||
this.scene,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
@ -4650,7 +4667,6 @@ 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),
|
||||
|
@ -4961,7 +4977,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
onChange: withBatchedUpdates((nextOriginalText) => {
|
||||
updateElement(nextOriginalText, false);
|
||||
if (isNonDeletedElement(element)) {
|
||||
updateBoundElements(element, this.scene.getNonDeletedElementsMap());
|
||||
updateBoundElements(this.scene, element);
|
||||
}
|
||||
}),
|
||||
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
||||
|
@ -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,
|
||||
),
|
||||
});
|
||||
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,
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
)
|
||||
: null,
|
||||
showHyperlinkPopup:
|
||||
elementsWithinSelection.length === 1 &&
|
||||
|
@ -8968,7 +8990,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
element,
|
||||
startBindingElement,
|
||||
endBindingElement,
|
||||
elementsMap,
|
||||
this.scene,
|
||||
);
|
||||
}
|
||||
|
@ -9109,8 +9130,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 +9148,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||
},
|
||||
prevState,
|
||||
),
|
||||
selectedLinearElement: new LinearElementEditor(newElement),
|
||||
selectedLinearElement: new LinearElementEditor(
|
||||
newElement,
|
||||
this.scene,
|
||||
),
|
||||
}));
|
||||
} else {
|
||||
this.setState((prevState) => ({
|
||||
|
@ -9268,9 +9291,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.mutateElement(
|
||||
element,
|
||||
{
|
||||
groupIds: element.groupIds.slice(0, index),
|
||||
},
|
||||
{ informMutation: false },
|
||||
);
|
||||
}
|
||||
|
||||
nextElements.forEach((element) => {
|
||||
|
@ -9281,9 +9308,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.mutateElement(
|
||||
element,
|
||||
{
|
||||
groupIds: [],
|
||||
},
|
||||
{ informMutation: false },
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9392,7 +9423,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,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -9517,7 +9551,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,
|
||||
)
|
||||
: prevState.selectedLinearElement,
|
||||
};
|
||||
});
|
||||
|
@ -9591,7 +9628,7 @@ 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)
|
||||
: prevState.selectedLinearElement,
|
||||
}));
|
||||
}
|
||||
|
@ -9684,7 +9721,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
bindOrUnbindLinearElements(
|
||||
linearElements,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene,
|
||||
isBindingEnabled(this.state),
|
||||
|
@ -9845,9 +9881,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.mutateElement(
|
||||
_imageElement,
|
||||
{
|
||||
fileId,
|
||||
},
|
||||
{ informMutation: false },
|
||||
) as NonDeleted<InitializedExcalidrawImageElement>;
|
||||
|
||||
return new Promise<NonDeleted<InitializedExcalidrawImageElement>>(
|
||||
async (resolve, reject) => {
|
||||
|
@ -10523,7 +10563,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this,
|
||||
),
|
||||
selectedLinearElement: isLinearElement(element)
|
||||
? new LinearElementEditor(element)
|
||||
? new LinearElementEditor(element, this.scene)
|
||||
: null,
|
||||
}
|
||||
: this.state),
|
||||
|
@ -10556,6 +10596,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 +10662,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,
|
||||
|
@ -10723,16 +10765,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||
),
|
||||
);
|
||||
|
||||
updateBoundElements(
|
||||
croppingElement,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
{
|
||||
newSize: {
|
||||
width: croppingElement.width,
|
||||
height: croppingElement.height,
|
||||
},
|
||||
updateBoundElements(this.scene, croppingElement, {
|
||||
newSize: {
|
||||
width: croppingElement.width,
|
||||
height: croppingElement.height,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isCropping: transformHandleType && transformHandleType !== "rotation",
|
||||
|
@ -10846,7 +10884,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
pointerDownState.originalElements,
|
||||
transformHandleType,
|
||||
selectedElements,
|
||||
this.scene.getElementsMapIncludingDeleted(),
|
||||
this.scene,
|
||||
shouldRotateWithDiscreteAngle(event),
|
||||
shouldResizeFromCenter(event),
|
||||
|
|
|
@ -113,7 +113,7 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
};
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
scene.mutateElement(element, {
|
||||
crop: nextCrop,
|
||||
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
|
||||
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
|
||||
|
@ -144,7 +144,7 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
height: nextCropHeight,
|
||||
};
|
||||
|
||||
mutateElement(element, {
|
||||
scene.mutateElement(element, {
|
||||
crop: nextCrop,
|
||||
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
|
||||
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
|
||||
|
@ -176,8 +176,8 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
nextHeight,
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
scene,
|
||||
property === "width" ? "e" : "s",
|
||||
{
|
||||
shouldMaintainAspectRatio: keepAspectRatio,
|
||||
|
@ -223,8 +223,8 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
nextHeight,
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
scene,
|
||||
property === "width" ? "e" : "s",
|
||||
{
|
||||
shouldMaintainAspectRatio: keepAspectRatio,
|
||||
|
|
|
@ -244,7 +244,6 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
nextHeight,
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
property === "width" ? "e" : "s",
|
||||
{
|
||||
|
@ -347,7 +346,6 @@ const handleDimensionChange: DragInputCallbackType<
|
|||
nextHeight,
|
||||
latestElement,
|
||||
origElement,
|
||||
elementsMap,
|
||||
originalElementsMap,
|
||||
property === "width" ? "e" : "s",
|
||||
{
|
||||
|
|
|
@ -85,15 +85,13 @@ describe("move element", () => {
|
|||
const rectA = UI.createElement("rectangle", { size: 100 });
|
||||
const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
|
||||
const arrow = UI.createElement("arrow", { x: 110, y: 50, size: 80 });
|
||||
const elementsMap = h.app.scene.getNonDeletedElementsMap();
|
||||
act(() => {
|
||||
// bind line to two rectangles
|
||||
bindOrUnbindLinearElement(
|
||||
arrow.get() as NonDeleted<ExcalidrawLinearElement>,
|
||||
rectA.get() as ExcalidrawRectangleElement,
|
||||
rectB.get() as ExcalidrawRectangleElement,
|
||||
elementsMap,
|
||||
{} as Scene,
|
||||
h.app.scene,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue