diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx index 2916345d04..b354c44404 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.tsx @@ -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 { diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index 9d72fdc8d1..ca1ad7b3bb 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -44,7 +44,7 @@ export const actionDuplicateSelection = register({ if (appState.editingLinearElement) { const ret = LinearElementEditor.duplicateSelectedPoints( appState, - app.scene, + app.scene.getNonDeletedElementsMap(), ); if (!ret) { diff --git a/packages/excalidraw/actions/actionFlip.ts b/packages/excalidraw/actions/actionFlip.ts index 128be86c9d..a6dad249fb 100644 --- a/packages/excalidraw/actions/actionFlip.ts +++ b/packages/excalidraw/actions/actionFlip.ts @@ -120,7 +120,6 @@ const flipElements = ( true, flipDirection === "horizontal" ? maxX : minX, flipDirection === "horizontal" ? minY : maxY, - app.scene, ); bindOrUnbindLinearElements( diff --git a/packages/excalidraw/actions/actionHistory.tsx b/packages/excalidraw/actions/actionHistory.tsx index 26a4a60618..2f322b7ad2 100644 --- a/packages/excalidraw/actions/actionHistory.tsx +++ b/packages/excalidraw/actions/actionHistory.tsx @@ -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) => diff --git a/packages/excalidraw/actions/actionProperties.tsx b/packages/excalidraw/actions/actionProperties.tsx index add2e34e3b..f9c66e96f8 100644 --- a/packages/excalidraw/actions/actionProperties.tsx +++ b/packages/excalidraw/actions/actionProperties.tsx @@ -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, diff --git a/packages/excalidraw/change.ts b/packages/excalidraw/change.ts index 0d07157c7c..dc2964b235 100644 --- a/packages/excalidraw/change.ts +++ b/packages/excalidraw/change.ts @@ -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 { public applyTo( elements: SceneElementsMap, snapshot: Map, - scene: Scene, ): [SceneElementsMap, boolean] { let nextElements = toBrandedType(new Map(elements)); let changedElements: Map; @@ -1102,7 +1100,6 @@ export class ElementsChange implements Change { 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 { 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 { private static redrawBoundArrows( elements: SceneElementsMap, changed: Map, - scene: Scene, ) { for (const element of changed.values()) { if (!element.isDeleted && isBindableElement(element)) { - updateBoundElements(element, elements, scene, { + updateBoundElements(element, elements, { changedElements: changed, }); } diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index e1c361f255..324931d3c8 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -4005,14 +4005,9 @@ class App extends React.Component { 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 { 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 { scenePointerX, scenePointerY, this.state, - this.scene, + this.scene.getNonDeletedElementsMap(), ); if ( @@ -5395,7 +5390,7 @@ class App extends React.Component { if (isElbowArrow(multiElement)) { mutateElbowArrow( multiElement, - this.scene, + this.scene.getNonDeletedElementsMap(), [ ...points.slice(0, -1), [ @@ -7771,7 +7766,7 @@ class App extends React.Component { } 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 { resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y, - this.scene, ) ) { const suggestedBindings = getSuggestedBindingsForArrows( diff --git a/packages/excalidraw/components/Stats/MultiDimension.tsx b/packages/excalidraw/components/Stats/MultiDimension.tsx index 2d7b483086..516b3aaf6a 100644 --- a/packages/excalidraw/components/Stats/MultiDimension.tsx +++ b/packages/excalidraw/components/Stats/MultiDimension.tsx @@ -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; diff --git a/packages/excalidraw/components/Stats/utils.ts b/packages/excalidraw/components/Stats/utils.ts index c703202d9b..5ea1f24fde 100644 --- a/packages/excalidraw/components/Stats/utils.ts +++ b/packages/excalidraw/components/Stats/utils.ts @@ -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); } }; diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index 177278b353..b750bffa4f 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -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, 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 } diff --git a/packages/excalidraw/element/dragElements.ts b/packages/excalidraw/element/dragElements.ts index e0b5365a9d..f456118b73 100644 --- a/packages/excalidraw/element/dragElements.ts +++ b/packages/excalidraw/element/dragElements.ts @@ -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), + }); }); }; diff --git a/packages/excalidraw/element/linearElementEditor.ts b/packages/excalidraw/element/linearElementEditor.ts index afc05cfbb0..bab2074b7b 100644 --- a/packages/excalidraw/element/linearElementEditor.ts +++ b/packages/excalidraw/element/linearElementEditor.ts @@ -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, 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, - 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, 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, diff --git a/packages/excalidraw/element/resizeElements.ts b/packages/excalidraw/element/resizeElements.ts index 16213614f2..947e4ed825 100644 --- a/packages/excalidraw/element/resizeElements.ts +++ b/packages/excalidraw/element/resizeElements.ts @@ -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, }); diff --git a/packages/excalidraw/element/routing.test.tsx b/packages/excalidraw/element/routing.test.tsx index 8d95c3489a..68a8aa7279 100644 --- a/packages/excalidraw/element/routing.test.tsx +++ b/packages/excalidraw/element/routing.test.tsx @@ -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], ]); diff --git a/packages/excalidraw/element/routing.ts b/packages/excalidraw/element/routing.ts index d4745a691c..bba7f5a9fd 100644 --- a/packages/excalidraw/element/routing.ts +++ b/packages/excalidraw/element/routing.ts @@ -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; 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, -): NonDeletedSceneElementsMap => - changedElements - ? toBrandedType( - new Map([...scene.getNonDeletedElementsMap(), ...changedElements]), - ) - : scene.getNonDeletedElementsMap(); - -const getAllElements = ( - scene: Scene, - changedElements?: Map, -): 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( + 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, + ), + ]; +}; diff --git a/packages/excalidraw/history.ts b/packages/excalidraw/history.ts index ea76df9b1f..daed2a3940 100644 --- a/packages/excalidraw/history.ts +++ b/packages/excalidraw/history.ts @@ -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, - 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, - 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, 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, - 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); diff --git a/packages/excalidraw/tests/history.test.tsx b/packages/excalidraw/tests/history.test.tsx index dcbfb34fdf..366f848bd9 100644 --- a/packages/excalidraw/tests/history.test.tsx +++ b/packages/excalidraw/tests/history.test.tsx @@ -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) { diff --git a/packages/excalidraw/tests/linearElementEditor.test.tsx b/packages/excalidraw/tests/linearElementEditor.test.tsx index 2d896a1b81..4bf88f0e45 100644 --- a/packages/excalidraw/tests/linearElementEditor.test.tsx +++ b/packages/excalidraw/tests/linearElementEditor.test.tsx @@ -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);