From 689179f428300b4097a0ff110dd57e6692f2c12d Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Sat, 15 Mar 2025 11:33:45 +0100 Subject: [PATCH] Fix single selected arrow highlight after delete, undo, redo --- .../actions/actionDuplicateSelection.tsx | 22 ++++++++ .../excalidraw/renderer/interactiveScene.ts | 52 ++++++++++++------- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index 2d61d1c0b..194980f2c 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -96,6 +96,7 @@ export const actionDuplicateSelection = register({ elements: nextElements, appState: { ...appState, + ...updateLinearElementEditors(nextElements), ...selectGroupsForSelectedElements( { editingGroupId: appState.editingGroupId, @@ -131,3 +132,24 @@ export const actionDuplicateSelection = register({ /> ), }); + +const updateLinearElementEditors = (clonedElements: ExcalidrawElement[]) => { + const linears = clonedElements.filter(isLinearElement); + if (linears.length === 1) { + const linear = linears[0]; + const boundElements = linear.boundElements?.map((def) => def.id) ?? []; + const onlySingleLinearSelected = clonedElements.every( + (el) => el.id === linear.id || boundElements.includes(el.id), + ); + + if (onlySingleLinearSelected) { + return { + selectedLinearElement: new LinearElementEditor(linear), + }; + } + } + + return { + selectedLinearElement: null, + }; +}; diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 257decd62..0bbfc92b1 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -51,7 +51,13 @@ import { } from "../scene/scrollbars"; import { getCornerRadius } from "../shapes"; import { type InteractiveCanvasAppState } from "../types"; -import { arrayToMap, invariant, throttleRAF } from "../utils"; +import { + arrayToMap, + invariant, + isDevEnv, + isTestEnv, + throttleRAF, +} from "../utils"; import { bootstrapCanvas, @@ -886,23 +892,33 @@ const _renderInteractiveScene = ({ ); } - if ( - isElbowArrow(selectedElements[0]) && - appState.selectedLinearElement && - appState.selectedLinearElement.segmentMidPointHoveredCoords - ) { - renderElbowArrowMidPointHighlight(context, appState); - } else if ( - appState.selectedLinearElement && - appState.selectedLinearElement.hoverPointIndex >= 0 && - !( - isElbowArrow(selectedElements[0]) && - appState.selectedLinearElement.hoverPointIndex > 0 && - appState.selectedLinearElement.hoverPointIndex < - selectedElements[0].points.length - 1 - ) - ) { - renderLinearElementPointHighlight(context, appState, elementsMap); + // Arrows have a different highlight behavior when + // they are the only selected element + if (appState.selectedLinearElement) { + if (isTestEnv() || isDevEnv()) { + invariant( + selectedElements.length <= 1, + `There is an active selectedLinearElement on app state but the selectedElements length is ${selectedElements?.length} not 1`, + ); + } + + const editor = appState.selectedLinearElement; + const firstSelectedLinear = selectedElements.find( + (el) => el.id === editor.elementId, // Don't forget bound text elements! + ); + + if (isElbowArrow(firstSelectedLinear)) { + if (editor.segmentMidPointHoveredCoords) { + renderElbowArrowMidPointHighlight(context, appState); + } else if ( + editor.hoverPointIndex !== 0 && + editor.hoverPointIndex !== firstSelectedLinear.points.length - 1 + ) { + renderLinearElementPointHighlight(context, appState, elementsMap); + } + } else if (editor.hoverPointIndex >= 0) { + renderLinearElementPointHighlight(context, appState, elementsMap); + } } // Paint selected elements