semi-fix bindings on generic type conversion

This commit is contained in:
dwelle 2025-04-30 18:00:21 +02:00
parent 8cf726b8f9
commit f2f62a3df9
4 changed files with 42 additions and 33 deletions

View file

@ -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<string, OrderedExcalidrawElement>;
changedElements?: Map<string, ExcalidrawElement>;
},
) => {
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<ExcalidrawLinearElement>,
changedElement: ExcalidrawBindableElement,

View file

@ -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)) {

View file

@ -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";

View file

@ -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);
}
};