diff --git a/packages/element/src/binding.ts b/packages/element/src/binding.ts index 1e9daee18..ee5d037a8 100644 --- a/packages/element/src/binding.ts +++ b/packages/element/src/binding.ts @@ -81,7 +81,6 @@ import type { NonDeletedSceneElementsMap, ExcalidrawTextElement, ExcalidrawArrowElement, - OrderedExcalidrawElement, ExcalidrawElbowArrowElement, FixedPoint, FixedPointBinding, @@ -710,29 +709,32 @@ const calculateFocusAndGap = ( // Supports translating, rotating and scaling `changedElement` with bound // linear elements. -// Because scaling involves moving the focus points as well, it is -// done before the `changedElement` is updated, and the `newSize` is passed -// in explicitly. export const updateBoundElements = ( changedElement: NonDeletedExcalidrawElement, scene: Scene, options?: { simultaneouslyUpdated?: readonly ExcalidrawElement[]; newSize?: { width: number; height: number }; - changedElements?: Map; + changedElements?: Map; }, ) => { + if (!isBindableElement(changedElement)) { + return; + } + const { newSize, simultaneouslyUpdated } = options ?? {}; const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds( simultaneouslyUpdated, ); - if (!isBindableElement(changedElement)) { - return; + let elementsMap: ElementsMap = scene.getNonDeletedElementsMap(); + if (options?.changedElements) { + elementsMap = new Map(elementsMap) as typeof elementsMap; + options.changedElements.forEach((element) => { + elementsMap.set(element.id, element); + }); } - const elementsMap = scene.getNonDeletedElementsMap(); - boundElementsVisitor(elementsMap, changedElement, (element) => { if (!isLinearElement(element) || element.isDeleted) { return; @@ -836,6 +838,25 @@ export const updateBoundElements = ( }); }; +export const updateBindings = ( + latestElement: ExcalidrawElement, + scene: Scene, + options?: { + simultaneouslyUpdated?: readonly ExcalidrawElement[]; + newSize?: { width: number; height: number }; + zoom?: AppState["zoom"]; + }, +) => { + if (isLinearElement(latestElement)) { + bindOrUnbindLinearElements([latestElement], true, [], scene, options?.zoom); + } else { + updateBoundElements(latestElement, scene, { + ...options, + changedElements: new Map([[latestElement.id, latestElement]]), + }); + } +}; + const doesNeedUpdate = ( boundElement: NonDeleted, changedElement: ExcalidrawBindableElement, diff --git a/packages/excalidraw/components/ConvertElementTypePopup.tsx b/packages/excalidraw/components/ConvertElementTypePopup.tsx index 1646d46cc..9ff171988 100644 --- a/packages/excalidraw/components/ConvertElementTypePopup.tsx +++ b/packages/excalidraw/components/ConvertElementTypePopup.tsx @@ -76,6 +76,7 @@ import { } from ".."; import { trackEvent } from "../analytics"; import { atom, editorJotaiStore, useSetAtom } from "../editor-jotai"; +import { updateBindings } from "../../element/src/binding"; import "./ConvertElementTypePopup.scss"; import { ToolButton } from "./ToolButton"; @@ -945,7 +946,7 @@ const convertElementType = < ShapeCache.delete(element); if (isConvertibleGenericType(targetType)) { - return bumpVersion( + const nextElement = bumpVersion( newElement({ ...element, type: targetType, @@ -958,7 +959,11 @@ const convertElementType = < } : element.roundness, }), - ); + ) as typeof element; + + updateBindings(nextElement, app.scene); + + return nextElement; } if (isConvertibleLinearType(targetType)) { diff --git a/packages/excalidraw/components/Stats/Angle.tsx b/packages/excalidraw/components/Stats/Angle.tsx index d0cb187da..76cb4876d 100644 --- a/packages/excalidraw/components/Stats/Angle.tsx +++ b/packages/excalidraw/components/Stats/Angle.tsx @@ -11,8 +11,10 @@ import type Scene from "@excalidraw/element/Scene"; import { angleIcon } from "../icons"; +import { updateBindings } from "../../../element/src/binding"; + import DragInput from "./DragInput"; -import { getStepSizedValue, isPropertyEditable, updateBindings } from "./utils"; +import { getStepSizedValue, isPropertyEditable } from "./utils"; import type { DragInputCallbackType } from "./DragInput"; import type { AppState } from "../../types"; diff --git a/packages/excalidraw/components/Stats/utils.ts b/packages/excalidraw/components/Stats/utils.ts index 79e7ed18b..9aefa5544 100644 --- a/packages/excalidraw/components/Stats/utils.ts +++ b/packages/excalidraw/components/Stats/utils.ts @@ -1,13 +1,8 @@ import { pointFrom, pointRotateRads } from "@excalidraw/math"; -import { - bindOrUnbindLinearElements, - updateBoundElements, -} from "@excalidraw/element/binding"; import { getBoundTextElement } from "@excalidraw/element/textElement"; import { isFrameLikeElement, - isLinearElement, isTextElement, } from "@excalidraw/element/typeChecks"; @@ -27,6 +22,8 @@ import type { import type Scene from "@excalidraw/element/Scene"; +import { updateBindings } from "../../../element/src/binding"; + import type { AppState } from "../../types"; export type StatsInputProperty = @@ -194,19 +191,3 @@ export const getAtomicUnits = ( }); return _atomicUnits; }; - -export const updateBindings = ( - latestElement: ExcalidrawElement, - scene: Scene, - options?: { - simultaneouslyUpdated?: readonly ExcalidrawElement[]; - newSize?: { width: number; height: number }; - zoom?: AppState["zoom"]; - }, -) => { - if (isLinearElement(latestElement)) { - bindOrUnbindLinearElements([latestElement], true, [], scene, options?.zoom); - } else { - updateBoundElements(latestElement, scene, options); - } -};