diff --git a/packages/excalidraw/actions/actionFinalize.tsx b/packages/excalidraw/actions/actionFinalize.tsx index 22638ee917..89acad6826 100644 --- a/packages/excalidraw/actions/actionFinalize.tsx +++ b/packages/excalidraw/actions/actionFinalize.tsx @@ -16,6 +16,12 @@ import { isPathALoop } from "@excalidraw/element/shapes"; import { isInvisiblySmallElement } from "@excalidraw/element/sizeHelpers"; +import type { + ExcalidrawElement, + ExcalidrawLinearElement, + NonDeleted, +} from "@excalidraw/element/types"; + import { t } from "../i18n"; import { resetCursor } from "../cursor"; import { done } from "../components/icons"; @@ -82,48 +88,55 @@ export const actionFinalize = register({ focusContainer(); } - const multiPointElement = appState.multiElement - ? appState.multiElement - : appState.newElement?.type === "freedraw" - ? appState.newElement - : null; + let element: NonDeleted | null = null; + if (appState.multiElement) { + element = appState.multiElement; + } else if ( + appState.newElement?.type === "freedraw" || + isBindingElement(appState.newElement) + ) { + element = appState.newElement; + } else if (Object.keys(appState.selectedElementIds).length === 1) { + const candidate = elementsMap.get( + Object.keys(appState.selectedElementIds)[0], + ) as NonDeleted | undefined; + if (candidate) { + element = candidate; + } + } - if (multiPointElement) { + if (element) { // pen and mouse have hover if ( - multiPointElement.type !== "freedraw" && + appState.multiElement && + element.type !== "freedraw" && appState.lastPointerDownWith !== "touch" ) { - const { points, lastCommittedPoint } = multiPointElement; + const { points, lastCommittedPoint } = element; if ( !lastCommittedPoint || points[points.length - 1] !== lastCommittedPoint ) { - scene.mutateElement(multiPointElement, { - points: multiPointElement.points.slice(0, -1), + scene.mutateElement(element, { + 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 - newElements = newElements.filter( - (el) => el.id !== multiPointElement.id, - ); + newElements = newElements.filter((el) => el.id !== element.id); } // If the multi point line closes the loop, // set the last point to first point. // This ensures that loop remains closed at different scales. - const isLoop = isPathALoop(multiPointElement.points, appState.zoom.value); - if ( - multiPointElement.type === "line" || - multiPointElement.type === "freedraw" - ) { + const isLoop = isPathALoop(element.points, appState.zoom.value); + if (element.type === "line" || element.type === "freedraw") { if (isLoop) { - const linePoints = multiPointElement.points; + const linePoints = element.points; const firstPoint = linePoints[0]; - scene.mutateElement(multiPointElement, { + scene.mutateElement(element, { points: linePoints.map((p, index) => index === linePoints.length - 1 ? pointFrom(firstPoint[0], firstPoint[1]) @@ -134,23 +147,24 @@ export const actionFinalize = register({ } if ( - isBindingElement(multiPointElement) && + isBindingElement(element) && !isLoop && - multiPointElement.points.length > 1 + element.points.length > 1 && + !appState.selectedElementIds[element.id] ) { const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates( - multiPointElement, + element, -1, arrayToMap(elements), ); - maybeBindLinearElement(multiPointElement, appState, { x, y }, scene); + maybeBindLinearElement(element, appState, { x, y }, scene); } } if ( (!appState.activeTool.locked && appState.activeTool.type !== "freedraw") || - !multiPointElement + !element ) { resetCursor(interactiveCanvas); } @@ -177,7 +191,7 @@ export const actionFinalize = register({ activeTool: (appState.activeTool.locked || appState.activeTool.type === "freedraw") && - multiPointElement + element ? appState.activeTool : activeTool, activeEmbeddable: null, @@ -188,21 +202,18 @@ export const actionFinalize = register({ startBoundElement: null, suggestedBindings: [], selectedElementIds: - multiPointElement && + element && !appState.activeTool.locked && appState.activeTool.type !== "freedraw" ? { ...appState.selectedElementIds, - [multiPointElement.id]: true, + [element.id]: true, } : appState.selectedElementIds, // To select the linear element when user has finished mutipoint editing selectedLinearElement: - multiPointElement && isLinearElement(multiPointElement) - ? new LinearElementEditor( - multiPointElement, - arrayToMap(newElements), - ) + element && isLinearElement(element) + ? new LinearElementEditor(element, arrayToMap(newElements)) : appState.selectedLinearElement, pendingImageElementId: null, }, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index ddb071981f..5ce8ee3762 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -9022,12 +9022,13 @@ class App extends React.Component { linearElementEditor; const element = this.scene.getElement(linearElementEditor.elementId); if (isBindingElement(element)) { - bindOrUnbindLinearElement( - element, - startBindingElement, - endBindingElement, - this.scene, - ); + this.actionManager.executeAction(actionFinalize); + // bindOrUnbindLinearElement( + // element, + // startBindingElement, + // endBindingElement, + // this.scene, + // ); } if (linearElementEditor !== this.state.selectedLinearElement) { @@ -9162,12 +9163,7 @@ class App extends React.Component { isBindingEnabled(this.state) && isBindingElement(newElement, false) ) { - maybeBindLinearElement( - newElement, - this.state, - pointerCoords, - this.scene, - ); + this.actionManager.executeAction(actionFinalize); } this.setState({ suggestedBindings: [], startBoundElement: null }); if (!activeTool.locked) {