From 8e9e0e27098f666bd1c0639d5b05de7498fa206b Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Wed, 16 Apr 2025 18:04:16 +1000 Subject: [PATCH] limit which linear elements can be switched --- packages/element/src/typeChecks.ts | 23 ++++++++++- .../excalidraw/components/ShapeSwitch.tsx | 39 ++++++++++++------- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/packages/element/src/typeChecks.ts b/packages/element/src/typeChecks.ts index 15931dced..7a0c6a3e4 100644 --- a/packages/element/src/typeChecks.ts +++ b/packages/element/src/typeChecks.ts @@ -379,8 +379,29 @@ export const getSwitchableTypeFromElements = ( } if (onlyLinear) { + // check at least some linear element is switchable + // for a linear to be swtichable: + // - no labels + // - not bound to anything + + let linear = true; + + for (const element of elements) { + if ( + isArrowElement(element) && + (element.startBinding !== null || element.endBinding !== null) + ) { + linear = false; + } else if (element.boundElements && element.boundElements.length > 0) { + linear = false; + } else { + linear = true; + break; + } + } + return { - linear: true, + linear, generic: false, }; } diff --git a/packages/excalidraw/components/ShapeSwitch.tsx b/packages/excalidraw/components/ShapeSwitch.tsx index f94c5b70a..938195d48 100644 --- a/packages/excalidraw/components/ShapeSwitch.tsx +++ b/packages/excalidraw/components/ShapeSwitch.tsx @@ -6,6 +6,7 @@ import { pointFrom, pointRotateRads } from "@excalidraw/math"; import { getSwitchableTypeFromElements, + isArrowElement, isUsingAdaptiveRadius, } from "@excalidraw/element/typeChecks"; import { @@ -223,13 +224,14 @@ const Panel = ({ const { generic, linear } = getSwitchableTypeFromElements(elements); const genericElements = generic ? getGenericSwitchableElements(elements) : []; + const linearElements = linear ? getLinearSwitchableElements(elements) : []; const sameType = generic ? genericElements.every( (element) => element.type === genericElements[0].type, ) : linear - ? elements.every((element) => element.type === elements[0].type) + ? linearElements.every((element) => element.type === linearElements[0].type) : false; if (elements.length === 1) { @@ -289,7 +291,7 @@ const Panel = ({ const isSelected = sameType && ((generic && genericElements[0].type === type) || - (linear && elements[0].type === type)); + (linear && linearElements[0].type === type)); return ( { - if (!app.state.penDetected && pointerType === "pen") { - app.togglePenMode(true); - } - }} onChange={() => { if (app.state.activeTool.type !== type) { trackEvent("shape-switch", type, "ui"); @@ -487,11 +484,16 @@ export const switchShapes = ( } if (linear) { - const sameType = selectedElements.every( - (element) => element.type === selectedElements[0].type, + const selectedLinearSwitchableElements = + getLinearSwitchableElements(selectedElements); + + const sameType = selectedLinearSwitchableElements.every( + (element) => element.type === selectedLinearSwitchableElements[0].type, ); const index = sameType - ? LINEAR_SWITCHABLE_SHAPES.indexOf(selectedElements[0].type) + ? LINEAR_SWITCHABLE_SHAPES.indexOf( + selectedLinearSwitchableElements[0].type, + ) : -1; nextType = nextType ?? @@ -500,7 +502,7 @@ export const switchShapes = ( LINEAR_SWITCHABLE_SHAPES.length ] as LinearSwitchableToolType); - selectedElements.forEach((element) => { + selectedLinearSwitchableElements.forEach((element) => { ShapeCache.delete(element); mutateElement( @@ -513,12 +515,12 @@ export const switchShapes = ( false, ); }); - const firstElement = selectedElements[0]; + const firstElement = selectedLinearSwitchableElements[0]; app.setState((prevState) => ({ selectedElementIds, selectedLinearElement: - selectedElements.length === 1 + selectedLinearSwitchableElements.length === 1 ? new LinearElementEditor(firstElement as ExcalidrawLinearElement) : null, activeTool: updateActiveTool(prevState, { @@ -535,4 +537,15 @@ const getGenericSwitchableElements = (elements: ExcalidrawElement[]) => GENERIC_SWITCHABLE_SHAPES.includes(element.type), ); +const getLinearSwitchableElements = (elements: ExcalidrawElement[]) => + elements.filter( + (element) => + LINEAR_SWITCHABLE_SHAPES.includes(element.type) && + !( + isArrowElement(element) && + (element.startBinding !== null || element.endBinding !== null) + ) && + (!element.boundElements || element.boundElements.length === 0), + ); + export default ShapeSwitch;