One set of actionFinalize

This commit is contained in:
Mark Tolmacs 2025-04-16 17:29:08 +02:00
parent b63cd86cd6
commit 64fa1c6948
No known key found for this signature in database
3 changed files with 36 additions and 34 deletions

View file

@ -3,10 +3,12 @@ import {
viewportCoordsToSceneCoords, viewportCoordsToSceneCoords,
} from "@excalidraw/common"; } from "@excalidraw/common";
import { pointsEqual } from "@excalidraw/math";
import type { AppState, Offsets, Zoom } from "@excalidraw/excalidraw/types"; import type { AppState, Offsets, Zoom } from "@excalidraw/excalidraw/types";
import { getCommonBounds, getElementBounds } from "./bounds"; import { getCommonBounds, getElementBounds } from "./bounds";
import { isFreeDrawElement, isLinearElement } from "./typeChecks"; import { isElbowArrow, isFreeDrawElement, isLinearElement } from "./typeChecks";
import type { ElementsMap, ExcalidrawElement } from "./types"; import type { ElementsMap, ExcalidrawElement } from "./types";
@ -16,6 +18,12 @@ import type { ElementsMap, ExcalidrawElement } from "./types";
export const isInvisiblySmallElement = ( export const isInvisiblySmallElement = (
element: ExcalidrawElement, element: ExcalidrawElement,
): boolean => { ): boolean => {
if (isElbowArrow(element)) {
return (
element.points.length < 2 ||
pointsEqual(element.points[0], element.points[element.points.length - 1])
);
}
if (isLinearElement(element) || isFreeDrawElement(element)) { if (isLinearElement(element) || isFreeDrawElement(element)) {
return element.points.length < 2; return element.points.length < 2;
} }

View file

@ -7,6 +7,7 @@ import {
import { LinearElementEditor } from "@excalidraw/element/linearElementEditor"; import { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
import { import {
isArrowElement,
isBindingElement, isBindingElement,
isLinearElement, isLinearElement,
} from "@excalidraw/element/typeChecks"; } from "@excalidraw/element/typeChecks";
@ -62,6 +63,7 @@ export const actionFinalize = register({
captureUpdate: CaptureUpdateAction.IMMEDIATELY, captureUpdate: CaptureUpdateAction.IMMEDIATELY,
}; };
} }
} else if (isArrowElement(appState.newElement)) {
} }
let newElements = elements; let newElements = elements;
@ -82,48 +84,46 @@ export const actionFinalize = register({
focusContainer(); focusContainer();
} }
const multiPointElement = appState.multiElement const element = appState.multiElement
? appState.multiElement ? appState.multiElement
: appState.newElement?.type === "freedraw" : appState.newElement?.type === "freedraw"
? appState.newElement ? appState.newElement
: isBindingElement(appState.newElement)
? appState.newElement
: null; : null;
if (multiPointElement) { if (element) {
// pen and mouse have hover // pen and mouse have hover
if ( if (
multiPointElement.type !== "freedraw" && appState.multiElement &&
element.type !== "freedraw" &&
appState.lastPointerDownWith !== "touch" appState.lastPointerDownWith !== "touch"
) { ) {
const { points, lastCommittedPoint } = multiPointElement; const { points, lastCommittedPoint } = element;
if ( if (
!lastCommittedPoint || !lastCommittedPoint ||
points[points.length - 1] !== lastCommittedPoint points[points.length - 1] !== lastCommittedPoint
) { ) {
scene.mutateElement(multiPointElement, { scene.mutateElement(element, {
points: multiPointElement.points.slice(0, -1), points: element.points.slice(0, -1),
}); });
} }
} }
if (isInvisiblySmallElement(multiPointElement)) { if (isInvisiblySmallElement(element)) {
// TODO: #7348 in theory this gets recorded by the store, so the invisible elements could be restored by the undo/redo, which might be not what we would want // TODO: #7348 in theory this gets recorded by the store, so the invisible elements could be restored by the undo/redo, which might be not what we would want
newElements = newElements.filter( newElements = newElements.filter((el) => el.id !== element.id);
(el) => el.id !== multiPointElement.id,
);
} }
// If the multi point line closes the loop, // If the multi point line closes the loop,
// set the last point to first point. // set the last point to first point.
// This ensures that loop remains closed at different scales. // This ensures that loop remains closed at different scales.
const isLoop = isPathALoop(multiPointElement.points, appState.zoom.value); const isLoop = isPathALoop(element.points, appState.zoom.value);
if ( if (element.type === "line" || element.type === "freedraw") {
multiPointElement.type === "line" ||
multiPointElement.type === "freedraw"
) {
if (isLoop) { if (isLoop) {
const linePoints = multiPointElement.points; const linePoints = element.points;
const firstPoint = linePoints[0]; const firstPoint = linePoints[0];
scene.mutateElement(multiPointElement, { scene.mutateElement(element, {
points: linePoints.map((p, index) => points: linePoints.map((p, index) =>
index === linePoints.length - 1 index === linePoints.length - 1
? pointFrom(firstPoint[0], firstPoint[1]) ? pointFrom(firstPoint[0], firstPoint[1])
@ -133,24 +133,20 @@ export const actionFinalize = register({
} }
} }
if ( if (isBindingElement(element) && !isLoop && element.points.length > 1) {
isBindingElement(multiPointElement) &&
!isLoop &&
multiPointElement.points.length > 1
) {
const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates( const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates(
multiPointElement, element,
-1, -1,
arrayToMap(elements), arrayToMap(elements),
); );
maybeBindLinearElement(multiPointElement, appState, { x, y }, scene); maybeBindLinearElement(element, appState, { x, y }, scene);
} }
} }
if ( if (
(!appState.activeTool.locked && (!appState.activeTool.locked &&
appState.activeTool.type !== "freedraw") || appState.activeTool.type !== "freedraw") ||
!multiPointElement !element
) { ) {
resetCursor(interactiveCanvas); resetCursor(interactiveCanvas);
} }
@ -177,7 +173,7 @@ export const actionFinalize = register({
activeTool: activeTool:
(appState.activeTool.locked || (appState.activeTool.locked ||
appState.activeTool.type === "freedraw") && appState.activeTool.type === "freedraw") &&
multiPointElement element
? appState.activeTool ? appState.activeTool
: activeTool, : activeTool,
activeEmbeddable: null, activeEmbeddable: null,
@ -188,21 +184,18 @@ export const actionFinalize = register({
startBoundElement: null, startBoundElement: null,
suggestedBindings: [], suggestedBindings: [],
selectedElementIds: selectedElementIds:
multiPointElement && element &&
!appState.activeTool.locked && !appState.activeTool.locked &&
appState.activeTool.type !== "freedraw" appState.activeTool.type !== "freedraw"
? { ? {
...appState.selectedElementIds, ...appState.selectedElementIds,
[multiPointElement.id]: true, [element.id]: true,
} }
: appState.selectedElementIds, : appState.selectedElementIds,
// To select the linear element when user has finished mutipoint editing // To select the linear element when user has finished mutipoint editing
selectedLinearElement: selectedLinearElement:
multiPointElement && isLinearElement(multiPointElement) element && isLinearElement(element)
? new LinearElementEditor( ? new LinearElementEditor(element, arrayToMap(newElements))
multiPointElement,
arrayToMap(newElements),
)
: appState.selectedLinearElement, : appState.selectedLinearElement,
pendingImageElementId: null, pendingImageElementId: null,
}, },

View file

@ -9022,6 +9022,7 @@ class App extends React.Component<AppProps, AppState> {
linearElementEditor; linearElementEditor;
const element = this.scene.getElement(linearElementEditor.elementId); const element = this.scene.getElement(linearElementEditor.elementId);
if (isBindingElement(element)) { if (isBindingElement(element)) {
this.actionManager.executeAction(actionFinalize);
bindOrUnbindLinearElement( bindOrUnbindLinearElement(
element, element,
startBindingElement, startBindingElement,