diff --git a/excalidraw-app/components/DebugCanvas.tsx b/excalidraw-app/components/DebugCanvas.tsx index 385b9b140e..71e3885b12 100644 --- a/excalidraw-app/components/DebugCanvas.tsx +++ b/excalidraw-app/components/DebugCanvas.tsx @@ -9,7 +9,7 @@ import { } from "@excalidraw/excalidraw/renderer/helpers"; import { type AppState } from "@excalidraw/excalidraw/types"; import { throttleRAF } from "@excalidraw/common"; -import { useCallback, useImperativeHandle, useRef } from "react"; +import { useCallback } from "react"; import { isLineSegment, @@ -18,6 +18,8 @@ import { } from "@excalidraw/math"; import { isCurve } from "@excalidraw/math/curve"; +import React from "react"; + import type { Curve } from "@excalidraw/math"; import type { DebugElement } from "@excalidraw/utils/visualdebug"; @@ -113,10 +115,6 @@ const _debugRenderer = ( scale, ); - if (appState.height !== canvas.height || appState.width !== canvas.width) { - refresh(); - } - const context = bootstrapCanvas({ canvas, scale, @@ -314,35 +312,29 @@ export const DebugFooter = ({ onChange }: { onChange: () => void }) => { interface DebugCanvasProps { appState: AppState; scale: number; - ref?: React.Ref; } -const DebugCanvas = ({ appState, scale, ref }: DebugCanvasProps) => { - const { width, height } = appState; +const DebugCanvas = React.forwardRef( + ({ appState, scale }, ref) => { + const { width, height } = appState; - const canvasRef = useRef(null); - useImperativeHandle( - ref, - () => canvasRef.current, - [canvasRef], - ); - - return ( - - Debug Canvas - - ); -}; + return ( + + Debug Canvas + + ); + }, +); export default DebugCanvas; diff --git a/packages/element/src/delta.ts b/packages/element/src/delta.ts index 9504237b51..bd428d8560 100644 --- a/packages/element/src/delta.ts +++ b/packages/element/src/delta.ts @@ -2,6 +2,7 @@ import { arrayToMap, arrayToObject, assertNever, + invariant, isDevEnv, isShallowEqual, isTestEnv, @@ -548,7 +549,7 @@ export class AppStateDelta implements DeltaContainer { selectedElementIds: addedSelectedElementIds = {}, selectedGroupIds: addedSelectedGroupIds = {}, selectedLinearElementId, - editingLinearElementId, + selectedLinearElementIsEditing, ...directlyApplicablePartial } = this.delta.inserted; @@ -564,39 +565,46 @@ export class AppStateDelta implements DeltaContainer { removedSelectedGroupIds, ); - const selectedLinearElement = - selectedLinearElementId && nextElements.has(selectedLinearElementId) - ? new LinearElementEditor( - nextElements.get( - selectedLinearElementId, - ) as NonDeleted, - nextElements, - ) - : null; + let selectedLinearElement = appState.selectedLinearElement; - const editingLinearElement = - editingLinearElementId && nextElements.has(editingLinearElementId) - ? new LinearElementEditor( - nextElements.get( - editingLinearElementId, - ) as NonDeleted, - nextElements, - ) - : null; + if (selectedLinearElementId === null) { + // Unselect linear element (visible change) + selectedLinearElement = null; + } else if ( + selectedLinearElementId && + nextElements.has(selectedLinearElementId) + ) { + selectedLinearElement = new LinearElementEditor( + nextElements.get( + selectedLinearElementId, + ) as NonDeleted, + nextElements, + selectedLinearElementIsEditing === true, // Can be unknown which is defaulted to false + ); + } + + if ( + // Value being 'null' is equivaluent to unknown in this case because it only gets set + // to null when 'selectedLinearElementId' is set to null + selectedLinearElementIsEditing != null + ) { + invariant( + selectedLinearElement, + `selectedLinearElement is null when selectedLinearElementIsEditing is set to ${selectedLinearElementIsEditing}`, + ); + + selectedLinearElement = { + ...selectedLinearElement, + isEditing: selectedLinearElementIsEditing, + }; + } const nextAppState = { ...appState, ...directlyApplicablePartial, selectedElementIds: mergedSelectedElementIds, selectedGroupIds: mergedSelectedGroupIds, - selectedLinearElement: - typeof selectedLinearElementId !== "undefined" - ? selectedLinearElement // element was either inserted or deleted - : appState.selectedLinearElement, // otherwise assign what we had before - editingLinearElement: - typeof editingLinearElementId !== "undefined" - ? editingLinearElement // element was either inserted or deleted - : appState.editingLinearElement, // otherwise assign what we had before + selectedLinearElement, }; const constainsVisibleChanges = this.filterInvisibleChanges( @@ -725,8 +733,7 @@ export class AppStateDelta implements DeltaContainer { } break; - case "selectedLinearElementId": - case "editingLinearElementId": + case "selectedLinearElementId": { const appStateKey = AppStateDelta.convertToAppStateKey(key); const linearElement = nextAppState[appStateKey]; @@ -746,6 +753,19 @@ export class AppStateDelta implements DeltaContainer { } break; + } + case "selectedLinearElementIsEditing": { + // Changes in editing state are always visible + const prevIsEditing = + prevAppState.selectedLinearElement?.isEditing ?? false; + const nextIsEditing = + nextAppState.selectedLinearElement?.isEditing ?? false; + + if (prevIsEditing !== nextIsEditing) { + visibleDifferenceFlag.value = true; + } + break; + } case "lockedMultiSelections": { const prevLockedUnits = prevAppState[key] || {}; const nextLockedUnits = nextAppState[key] || {}; @@ -779,16 +799,11 @@ export class AppStateDelta implements DeltaContainer { } private static convertToAppStateKey( - key: keyof Pick< - ObservedElementsAppState, - "selectedLinearElementId" | "editingLinearElementId" - >, - ): keyof Pick { + key: keyof Pick, + ): keyof Pick { switch (key) { case "selectedLinearElementId": return "selectedLinearElement"; - case "editingLinearElementId": - return "editingLinearElement"; } } @@ -856,8 +871,8 @@ export class AppStateDelta implements DeltaContainer { editingGroupId, selectedGroupIds, selectedElementIds, - editingLinearElementId, selectedLinearElementId, + selectedLinearElementIsEditing, croppingElementId, lockedMultiSelections, activeLockedId, diff --git a/packages/element/src/linearElementEditor.ts b/packages/element/src/linearElementEditor.ts index 26e59a98ac..995d866b54 100644 --- a/packages/element/src/linearElementEditor.ts +++ b/packages/element/src/linearElementEditor.ts @@ -149,10 +149,12 @@ export class LinearElementEditor { public readonly segmentMidPointHoveredCoords: GlobalPoint | null; public readonly elbowed: boolean; public readonly customLineAngle: number | null; + public readonly isEditing: boolean; constructor( element: NonDeleted, elementsMap: ElementsMap, + isEditing: boolean = false, ) { this.elementId = element.id as string & { _brand: "excalidrawLinearElementId"; @@ -187,6 +189,7 @@ export class LinearElementEditor { this.segmentMidPointHoveredCoords = null; this.elbowed = isElbowArrow(element) && element.elbowed; this.customLineAngle = null; + this.isEditing = isEditing; } // --------------------------------------------------------------------------- @@ -194,6 +197,7 @@ export class LinearElementEditor { // --------------------------------------------------------------------------- static POINT_HANDLE_SIZE = 10; + /** * @param id the `elementId` from the instance of this class (so that we can * statically guarantee this method returns an ExcalidrawLinearElement) @@ -215,11 +219,14 @@ export class LinearElementEditor { setState: React.Component["setState"], elementsMap: NonDeletedSceneElementsMap, ) { - if (!appState.editingLinearElement || !appState.selectionElement) { + if ( + !appState.selectedLinearElement?.isEditing || + !appState.selectionElement + ) { return false; } - const { editingLinearElement } = appState; - const { selectedPointsIndices, elementId } = editingLinearElement; + const { selectedLinearElement } = appState; + const { selectedPointsIndices, elementId } = selectedLinearElement; const element = LinearElementEditor.getElement(elementId, elementsMap); if (!element) { @@ -260,8 +267,8 @@ export class LinearElementEditor { }); setState({ - editingLinearElement: { - ...editingLinearElement, + selectedLinearElement: { + ...selectedLinearElement, selectedPointsIndices: nextSelectedPoints.length ? nextSelectedPoints : null, @@ -479,9 +486,6 @@ export class LinearElementEditor { return { ...app.state, - editingLinearElement: app.state.editingLinearElement - ? newLinearElementEditor - : null, selectedLinearElement: newLinearElementEditor, suggestedBindings, }; @@ -618,7 +622,7 @@ export class LinearElementEditor { // Since its not needed outside editor unless 2 pointer lines or bound text if ( !isElbowArrow(element) && - !appState.editingLinearElement && + !appState.selectedLinearElement?.isEditing && element.points.length > 2 && !boundText ) { @@ -684,7 +688,7 @@ export class LinearElementEditor { ); if ( points.length >= 3 && - !appState.editingLinearElement && + !appState.selectedLinearElement?.isEditing && !isElbowArrow(element) ) { return null; @@ -881,7 +885,7 @@ export class LinearElementEditor { segmentMidpoint, elementsMap, ); - } else if (event.altKey && appState.editingLinearElement) { + } else if (event.altKey && appState.selectedLinearElement?.isEditing) { if (linearElementEditor.lastUncommittedPoint == null) { scene.mutateElement(element, { points: [ @@ -1023,14 +1027,14 @@ export class LinearElementEditor { app: AppClassProperties, ): LinearElementEditor | null { const appState = app.state; - if (!appState.editingLinearElement) { + if (!appState.selectedLinearElement?.isEditing) { return null; } - const { elementId, lastUncommittedPoint } = appState.editingLinearElement; + const { elementId, lastUncommittedPoint } = appState.selectedLinearElement; const elementsMap = app.scene.getNonDeletedElementsMap(); const element = LinearElementEditor.getElement(elementId, elementsMap); if (!element) { - return appState.editingLinearElement; + return appState.selectedLinearElement; } const { points } = element; @@ -1040,12 +1044,12 @@ export class LinearElementEditor { if (lastPoint === lastUncommittedPoint) { LinearElementEditor.deletePoints(element, app, [points.length - 1]); } - return appState.editingLinearElement.lastUncommittedPoint + return appState.selectedLinearElement?.lastUncommittedPoint ? { - ...appState.editingLinearElement, + ...appState.selectedLinearElement, lastUncommittedPoint: null, } - : appState.editingLinearElement; + : appState.selectedLinearElement; } let newPoint: LocalPoint; @@ -1069,8 +1073,8 @@ export class LinearElementEditor { newPoint = LinearElementEditor.createPointAt( element, elementsMap, - scenePointerX - appState.editingLinearElement.pointerOffset.x, - scenePointerY - appState.editingLinearElement.pointerOffset.y, + scenePointerX - appState.selectedLinearElement.pointerOffset.x, + scenePointerY - appState.selectedLinearElement.pointerOffset.y, event[KEYS.CTRL_OR_CMD] || isElbowArrow(element) ? null : app.getEffectiveGridSize(), @@ -1094,7 +1098,7 @@ export class LinearElementEditor { LinearElementEditor.addPoints(element, app.scene, [newPoint]); } return { - ...appState.editingLinearElement, + ...appState.selectedLinearElement, lastUncommittedPoint: element.points[element.points.length - 1], }; } @@ -1253,12 +1257,12 @@ export class LinearElementEditor { // --------------------------------------------------------------------------- static duplicateSelectedPoints(appState: AppState, scene: Scene): AppState { invariant( - appState.editingLinearElement, + appState.selectedLinearElement?.isEditing, "Not currently editing a linear element", ); const elementsMap = scene.getNonDeletedElementsMap(); - const { selectedPointsIndices, elementId } = appState.editingLinearElement; + const { selectedPointsIndices, elementId } = appState.selectedLinearElement; const element = LinearElementEditor.getElement(elementId, elementsMap); invariant( @@ -1320,8 +1324,8 @@ export class LinearElementEditor { return { ...appState, - editingLinearElement: { - ...appState.editingLinearElement, + selectedLinearElement: { + ...appState.selectedLinearElement, selectedPointsIndices: nextSelectedIndices, }, }; @@ -1333,8 +1337,9 @@ export class LinearElementEditor { pointIndices: readonly number[], ) { const isUncommittedPoint = - app.state.editingLinearElement?.lastUncommittedPoint === - element.points[element.points.length - 1]; + app.state.selectedLinearElement?.isEditing && + app.state.selectedLinearElement?.lastUncommittedPoint === + element.points[element.points.length - 1]; const nextPoints = element.points.filter((_, idx) => { return !pointIndices.includes(idx); @@ -1507,7 +1512,7 @@ export class LinearElementEditor { pointFrom(pointerCoords.x, pointerCoords.y), ); if ( - !appState.editingLinearElement && + !appState.selectedLinearElement?.isEditing && dist < DRAGGING_THRESHOLD / appState.zoom.value ) { return false; diff --git a/packages/element/src/store.ts b/packages/element/src/store.ts index ae0c969e5a..2bf70f5814 100644 --- a/packages/element/src/store.ts +++ b/packages/element/src/store.ts @@ -978,8 +978,8 @@ const getDefaultObservedAppState = (): ObservedAppState => { viewBackgroundColor: COLOR_PALETTE.white, selectedElementIds: {}, selectedGroupIds: {}, - editingLinearElementId: null, selectedLinearElementId: null, + selectedLinearElementIsEditing: null, croppingElementId: null, activeLockedId: null, lockedMultiSelections: {}, @@ -998,14 +998,14 @@ export const getObservedAppState = ( croppingElementId: appState.croppingElementId, activeLockedId: appState.activeLockedId, lockedMultiSelections: appState.lockedMultiSelections, - editingLinearElementId: - (appState as AppState).editingLinearElement?.elementId ?? // prefer app state, as it's likely newer - (appState as ObservedAppState).editingLinearElementId ?? // fallback to observed app state, as it's likely older coming from a previous snapshot - null, selectedLinearElementId: (appState as AppState).selectedLinearElement?.elementId ?? (appState as ObservedAppState).selectedLinearElementId ?? null, + selectedLinearElementIsEditing: + (appState as AppState).selectedLinearElement?.isEditing ?? + (appState as ObservedAppState).selectedLinearElementIsEditing ?? + null, }; Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, { diff --git a/packages/element/src/transformHandles.ts b/packages/element/src/transformHandles.ts index f2b0cd2782..679937d4ae 100644 --- a/packages/element/src/transformHandles.ts +++ b/packages/element/src/transformHandles.ts @@ -330,7 +330,7 @@ export const shouldShowBoundingBox = ( elements: readonly NonDeletedExcalidrawElement[], appState: InteractiveCanvasAppState, ) => { - if (appState.editingLinearElement) { + if (appState.selectedLinearElement?.isEditing) { return false; } if (elements.length > 1) { diff --git a/packages/element/tests/binding.test.tsx b/packages/element/tests/binding.test.tsx index 69f4e6dded..a3da1c66d9 100644 --- a/packages/element/tests/binding.test.tsx +++ b/packages/element/tests/binding.test.tsx @@ -155,10 +155,10 @@ describe("element binding", () => { // NOTE this mouse down/up + await needs to be done in order to repro // the issue, due to https://github.com/excalidraw/excalidraw/blob/46bff3daceb602accf60c40a84610797260fca94/src/components/App.tsx#L740 mouse.reset(); - expect(h.state.editingLinearElement).not.toBe(null); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); mouse.down(0, 0); await new Promise((r) => setTimeout(r, 100)); - expect(h.state.editingLinearElement).toBe(null); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); expect(API.getSelectedElement().type).toBe("rectangle"); mouse.up(); expect(API.getSelectedElement().type).toBe("rectangle"); diff --git a/packages/element/tests/delta.test.tsx b/packages/element/tests/delta.test.tsx index 9c416f6ef4..4d56aac834 100644 --- a/packages/element/tests/delta.test.tsx +++ b/packages/element/tests/delta.test.tsx @@ -16,6 +16,7 @@ describe("AppStateDelta", () => { editingGroupId: null, croppingElementId: null, editingLinearElementId: null, + selectedLinearElementIsEditing: null, lockedMultiSelections: {}, activeLockedId: null, }; @@ -58,6 +59,7 @@ describe("AppStateDelta", () => { editingGroupId: null, croppingElementId: null, selectedLinearElementId: null, + selectedLinearElementIsEditing: null, editingLinearElementId: null, activeLockedId: null, lockedMultiSelections: {}, @@ -105,6 +107,7 @@ describe("AppStateDelta", () => { editingGroupId: null, croppingElementId: null, selectedLinearElementId: null, + selectedLinearElementIsEditing: null, editingLinearElementId: null, activeLockedId: null, lockedMultiSelections: {}, diff --git a/packages/element/tests/linearElementEditor.test.tsx b/packages/element/tests/linearElementEditor.test.tsx index a442afe6b5..f1306b8728 100644 --- a/packages/element/tests/linearElementEditor.test.tsx +++ b/packages/element/tests/linearElementEditor.test.tsx @@ -136,7 +136,8 @@ describe("Test Linear Elements", () => { Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.keyPress(KEYS.ENTER); }); - expect(h.state.editingLinearElement?.elementId).toEqual(line.id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(line.id); }; const drag = (startPoint: GlobalPoint, endPoint: GlobalPoint) => { @@ -253,75 +254,82 @@ describe("Test Linear Elements", () => { }); fireEvent.click(queryByText(contextMenu as HTMLElement, "Edit line")!); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should enter line editor via enter (line)", () => { createTwoPointerLinearElement("line"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); mouse.clickAt(midpoint[0], midpoint[1]); Keyboard.keyPress(KEYS.ENTER); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); // ctrl+enter alias (to align with arrows) it("should enter line editor via ctrl+enter (line)", () => { createTwoPointerLinearElement("line"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); mouse.clickAt(midpoint[0], midpoint[1]); Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.keyPress(KEYS.ENTER); }); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should enter line editor via ctrl+enter (arrow)", () => { createTwoPointerLinearElement("arrow"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); mouse.clickAt(midpoint[0], midpoint[1]); Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.keyPress(KEYS.ENTER); }); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should enter line editor on ctrl+dblclick (simple arrow)", () => { createTwoPointerLinearElement("arrow"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); Keyboard.withModifierKeys({ ctrl: true }, () => { mouse.doubleClick(); }); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should enter line editor on ctrl+dblclick (line)", () => { createTwoPointerLinearElement("line"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); Keyboard.withModifierKeys({ ctrl: true }, () => { mouse.doubleClick(); }); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should enter line editor on dblclick (line)", () => { createTwoPointerLinearElement("line"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); mouse.doubleClick(); - expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); it("should not enter line editor on dblclick (arrow)", async () => { createTwoPointerLinearElement("arrow"); - expect(h.state.editingLinearElement?.elementId).toBeUndefined(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); mouse.doubleClick(); - expect(h.state.editingLinearElement).toEqual(null); + expect(h.state.selectedLinearElement).toBe(null); await getTextEditor(); }); @@ -330,10 +338,12 @@ describe("Test Linear Elements", () => { const arrow = h.elements[0] as ExcalidrawLinearElement; enterLineEditingMode(arrow); - expect(h.state.editingLinearElement?.elementId).toEqual(arrow.id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(arrow.id); mouse.doubleClick(); - expect(h.state.editingLinearElement?.elementId).toEqual(arrow.id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); + expect(h.state.selectedLinearElement?.elementId).toEqual(arrow.id); expect(h.elements.length).toEqual(1); expect(document.querySelector(TEXT_EDITOR_SELECTOR)).toBe(null); diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx index c0e43d3dc1..a9281ce84e 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.tsx @@ -205,16 +205,19 @@ export const actionDeleteSelected = register({ icon: TrashIcon, trackEvent: { category: "element", action: "delete" }, perform: (elements, appState, formData, app) => { - if (appState.editingLinearElement) { + if (appState.selectedLinearElement?.isEditing) { const { elementId, selectedPointsIndices, startBindingElement, endBindingElement, - } = appState.editingLinearElement; + } = appState.selectedLinearElement; const elementsMap = app.scene.getNonDeletedElementsMap(); - const element = LinearElementEditor.getElement(elementId, elementsMap); - if (!element) { + const linearElement = LinearElementEditor.getElement( + elementId, + elementsMap, + ); + if (!linearElement) { return false; } // case: no point selected → do nothing, as deleting the whole element @@ -226,12 +229,9 @@ export const actionDeleteSelected = register({ } // case: deleting all points - if ( - element.points.length < 2 || - selectedPointsIndices.length === element.points.length - ) { + if (selectedPointsIndices.length >= linearElement.points.length) { const nextElements = elements.map((el) => { - if (el.id === element.id) { + if (el.id === linearElement.id) { return newElementWith(el, { isDeleted: true }); } return el; @@ -242,7 +242,7 @@ export const actionDeleteSelected = register({ elements: nextElements, appState: { ...nextAppState, - editingLinearElement: null, + selectedLinearElement: null, }, captureUpdate: CaptureUpdateAction.IMMEDIATELY, }; @@ -255,20 +255,24 @@ export const actionDeleteSelected = register({ ? null : startBindingElement, endBindingElement: selectedPointsIndices?.includes( - element.points.length - 1, + linearElement.points.length - 1, ) ? null : endBindingElement, }; - LinearElementEditor.deletePoints(element, app, selectedPointsIndices); + LinearElementEditor.deletePoints( + linearElement, + app, + selectedPointsIndices, + ); return { elements, appState: { ...appState, - editingLinearElement: { - ...appState.editingLinearElement, + selectedLinearElement: { + ...appState.selectedLinearElement, ...binding, selectedPointsIndices: selectedPointsIndices?.[0] > 0 diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index b6363a7306..c1b2a9da42 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -39,7 +39,7 @@ export const actionDuplicateSelection = register({ } // duplicate selected point(s) if editing a line - if (appState.editingLinearElement) { + if (appState.selectedLinearElement?.isEditing) { // TODO: Invariants should be checked here instead of duplicateSelectedPoints() try { const newAppState = LinearElementEditor.duplicateSelectedPoints( diff --git a/packages/excalidraw/actions/actionFinalize.tsx b/packages/excalidraw/actions/actionFinalize.tsx index b699e9ea64..9baeb0b6f0 100644 --- a/packages/excalidraw/actions/actionFinalize.tsx +++ b/packages/excalidraw/actions/actionFinalize.tsx @@ -94,9 +94,9 @@ export const actionFinalize = register({ } } - if (appState.editingLinearElement) { + if (appState.selectedLinearElement?.isEditing) { const { elementId, startBindingElement, endBindingElement } = - appState.editingLinearElement; + appState.selectedLinearElement; const element = LinearElementEditor.getElement(elementId, elementsMap); if (element) { @@ -122,7 +122,11 @@ export const actionFinalize = register({ appState: { ...appState, cursorButton: "up", - editingLinearElement: null, + selectedLinearElement: new LinearElementEditor( + element, + arrayToMap(elementsMap), + false, // exit editing mode + ), }, captureUpdate: CaptureUpdateAction.IMMEDIATELY, }; @@ -285,7 +289,7 @@ export const actionFinalize = register({ }, keyTest: (event, appState) => (event.key === KEYS.ESCAPE && - (appState.editingLinearElement !== null || + (appState.selectedLinearElement?.isEditing || (!appState.newElement && appState.multiElement === null))) || ((event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) && appState.multiElement !== null), diff --git a/packages/excalidraw/actions/actionLinearEditor.tsx b/packages/excalidraw/actions/actionLinearEditor.tsx index 28295d9395..9b18c64de8 100644 --- a/packages/excalidraw/actions/actionLinearEditor.tsx +++ b/packages/excalidraw/actions/actionLinearEditor.tsx @@ -1,10 +1,9 @@ -import { LinearElementEditor } from "@excalidraw/element"; import { isElbowArrow, isLinearElement, isLineElement, } from "@excalidraw/element"; -import { arrayToMap } from "@excalidraw/common"; +import { arrayToMap, invariant } from "@excalidraw/common"; import { toggleLinePolygonState, @@ -46,7 +45,7 @@ export const actionToggleLinearEditor = register({ predicate: (elements, appState, _, app) => { const selectedElements = app.scene.getSelectedElements(appState); if ( - !appState.editingLinearElement && + !appState.selectedLinearElement?.isEditing && selectedElements.length === 1 && isLinearElement(selectedElements[0]) && !isElbowArrow(selectedElements[0]) @@ -61,14 +60,25 @@ export const actionToggleLinearEditor = register({ includeBoundTextElement: true, })[0] as ExcalidrawLinearElement; - const editingLinearElement = - appState.editingLinearElement?.elementId === selectedElement.id - ? null - : new LinearElementEditor(selectedElement, arrayToMap(elements)); + invariant(selectedElement, "No selected element found"); + invariant( + appState.selectedLinearElement, + "No selected linear element found", + ); + invariant( + selectedElement.id === appState.selectedLinearElement.elementId, + "Selected element ID and linear editor elementId does not match", + ); + + const selectedLinearElement = { + ...appState.selectedLinearElement, + isEditing: !appState.selectedLinearElement.isEditing, + }; + return { appState: { ...appState, - editingLinearElement, + selectedLinearElement, }, captureUpdate: CaptureUpdateAction.IMMEDIATELY, }; diff --git a/packages/excalidraw/actions/actionSelectAll.ts b/packages/excalidraw/actions/actionSelectAll.ts index a580528585..7157d76176 100644 --- a/packages/excalidraw/actions/actionSelectAll.ts +++ b/packages/excalidraw/actions/actionSelectAll.ts @@ -21,7 +21,7 @@ export const actionSelectAll = register({ trackEvent: { category: "canvas" }, viewMode: false, perform: (elements, appState, value, app) => { - if (appState.editingLinearElement) { + if (appState.selectedLinearElement?.isEditing) { return false; } diff --git a/packages/excalidraw/appState.ts b/packages/excalidraw/appState.ts index dcc3fba11b..6c4a971162 100644 --- a/packages/excalidraw/appState.ts +++ b/packages/excalidraw/appState.ts @@ -48,7 +48,6 @@ export const getDefaultAppState = (): Omit< newElement: null, editingTextElement: null, editingGroupId: null, - editingLinearElement: null, activeTool: { type: "selection", customType: null, @@ -175,7 +174,6 @@ const APP_STATE_STORAGE_CONF = (< newElement: { browser: false, export: false, server: false }, editingTextElement: { browser: false, export: false, server: false }, editingGroupId: { browser: true, export: false, server: false }, - editingLinearElement: { browser: false, export: false, server: false }, activeTool: { browser: true, export: false, server: false }, penMode: { browser: true, export: false, server: false }, penDetected: { browser: true, export: false, server: false }, diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx index 1b42e9f402..5c9d59ada3 100644 --- a/packages/excalidraw/components/Actions.tsx +++ b/packages/excalidraw/components/Actions.tsx @@ -140,7 +140,7 @@ export const SelectedShapeActions = ({ targetElements.length === 1 || isSingleElementBoundContainer; const showLineEditorAction = - !appState.editingLinearElement && + !appState.selectedLinearElement?.isEditing && targetElements.length === 1 && isLinearElement(targetElements[0]) && !isElbowArrow(targetElements[0]); diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index a7b6718f59..b381ad0f13 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -2158,9 +2158,14 @@ class App extends React.Component { public dismissLinearEditor = () => { setTimeout(() => { - this.setState({ - editingLinearElement: null, - }); + if (this.state.selectedLinearElement?.isEditing) { + this.setState({ + selectedLinearElement: { + ...this.state.selectedLinearElement, + isEditing: false, + }, + }); + } }); }; @@ -2856,15 +2861,15 @@ class App extends React.Component { ); if ( - this.state.editingLinearElement && - !this.state.selectedElementIds[this.state.editingLinearElement.elementId] + this.state.selectedLinearElement?.isEditing && + !this.state.selectedElementIds[this.state.selectedLinearElement.elementId] ) { // defer so that the scheduleCapture flag isn't reset via current update setTimeout(() => { // execute only if the condition still holds when the deferred callback // executes (it can be scheduled multiple times depending on how // many times the component renders) - this.state.editingLinearElement && + this.state.selectedLinearElement?.isEditing && this.actionManager.executeAction(actionFinalize); }); } @@ -4419,17 +4424,13 @@ class App extends React.Component { if (event[KEYS.CTRL_OR_CMD] || isLineElement(selectedElement)) { if (isLinearElement(selectedElement)) { if ( - !this.state.editingLinearElement || - this.state.editingLinearElement.elementId !== selectedElement.id + !this.state.selectedLinearElement?.isEditing || + this.state.selectedLinearElement.elementId !== + selectedElement.id ) { this.store.scheduleCapture(); if (!isElbowArrow(selectedElement)) { - this.setState({ - editingLinearElement: new LinearElementEditor( - selectedElement, - this.scene.getNonDeletedElementsMap(), - ), - }); + this.actionManager.executeAction(actionToggleLinearEditor); } } } @@ -5432,15 +5433,12 @@ class App extends React.Component { if ( ((event[KEYS.CTRL_OR_CMD] && isSimpleArrow(selectedLinearElement)) || isLineElement(selectedLinearElement)) && - this.state.editingLinearElement?.elementId !== selectedLinearElement.id + (!this.state.selectedLinearElement?.isEditing || + this.state.selectedLinearElement.elementId !== + selectedLinearElement.id) ) { - this.store.scheduleCapture(); - this.setState({ - editingLinearElement: new LinearElementEditor( - selectedLinearElement, - this.scene.getNonDeletedElementsMap(), - ), - }); + // Use the proper action to ensure immediate history capture + this.actionManager.executeAction(actionToggleLinearEditor); return; } else if ( this.state.selectedLinearElement && @@ -5505,8 +5503,8 @@ class App extends React.Component { return; } } else if ( - this.state.editingLinearElement && - this.state.editingLinearElement.elementId === + this.state.selectedLinearElement?.isEditing && + this.state.selectedLinearElement.elementId === selectedLinearElement.id && isLineElement(selectedLinearElement) ) { @@ -5561,7 +5559,7 @@ class App extends React.Component { // shouldn't edit/create text when inside line editor (often false positive) - if (!this.state.editingLinearElement) { + if (!this.state.selectedLinearElement?.isEditing) { const container = this.getTextBindableContainerAtPosition( sceneX, sceneY, @@ -5859,8 +5857,8 @@ class App extends React.Component { } if ( - this.state.editingLinearElement && - !this.state.editingLinearElement.isDragging + this.state.selectedLinearElement?.isEditing && + !this.state.selectedLinearElement.isDragging ) { const editingLinearElement = LinearElementEditor.handlePointerMove( event, @@ -5874,14 +5872,14 @@ class App extends React.Component { if ( editingLinearElement && - editingLinearElement !== this.state.editingLinearElement + editingLinearElement !== this.state.selectedLinearElement ) { // Since we are reading from previous state which is not possible with // automatic batching in React 18 hence using flush sync to synchronously // update the state. Check https://github.com/excalidraw/excalidraw/pull/5508 for more details. flushSync(() => { this.setState({ - editingLinearElement, + selectedLinearElement: editingLinearElement, }); }); } @@ -6041,7 +6039,7 @@ class App extends React.Component { if ( selectedElements.length === 1 && !isOverScrollBar && - !this.state.editingLinearElement + !this.state.selectedLinearElement?.isEditing ) { // for linear elements, we'd like to prioritize point dragging over edge resizing // therefore, we update and check hovered point index first @@ -7045,7 +7043,7 @@ class App extends React.Component { if ( selectedElements.length === 1 && - !this.state.editingLinearElement && + !this.state.selectedLinearElement?.isEditing && !isElbowArrow(selectedElements[0]) && !( this.state.selectedLinearElement && @@ -7116,8 +7114,7 @@ class App extends React.Component { } } else { if (this.state.selectedLinearElement) { - const linearElementEditor = - this.state.editingLinearElement || this.state.selectedLinearElement; + const linearElementEditor = this.state.selectedLinearElement; const ret = LinearElementEditor.handlePointerDown( event, this, @@ -7131,10 +7128,6 @@ class App extends React.Component { } if (ret.linearElementEditor) { this.setState({ selectedLinearElement: ret.linearElementEditor }); - - if (this.state.editingLinearElement) { - this.setState({ editingLinearElement: ret.linearElementEditor }); - } } if (ret.didAddPoint) { return true; @@ -7235,11 +7228,11 @@ class App extends React.Component { this.clearSelection(hitElement); } - if (this.state.editingLinearElement) { + if (this.state.selectedLinearElement?.isEditing) { this.setState({ selectedElementIds: makeNextSelectedElementIds( { - [this.state.editingLinearElement.elementId]: true, + [this.state.selectedLinearElement.elementId]: true, }, this.state, ), @@ -8182,8 +8175,7 @@ class App extends React.Component { const elementsMap = this.scene.getNonDeletedElementsMap(); if (this.state.selectedLinearElement) { - const linearElementEditor = - this.state.editingLinearElement || this.state.selectedLinearElement; + const linearElementEditor = this.state.selectedLinearElement; if ( LinearElementEditor.shouldAddMidpoint( @@ -8219,16 +8211,6 @@ class App extends React.Component { }, }); } - if (this.state.editingLinearElement) { - this.setState({ - editingLinearElement: { - ...this.state.editingLinearElement, - pointerDownState: ret.pointerDownState, - selectedPointsIndices: ret.selectedPointsIndices, - segmentMidPointHoveredCoords: null, - }, - }); - } }); return; @@ -8262,9 +8244,9 @@ class App extends React.Component { ); const isSelectingPointsInLineEditor = - this.state.editingLinearElement && + this.state.selectedLinearElement?.isEditing && event.shiftKey && - this.state.editingLinearElement.elementId === + this.state.selectedLinearElement.elementId === pointerDownState.hit.element?.id; if ( (hasHitASelectedElement || @@ -8751,7 +8733,7 @@ class App extends React.Component { const elements = this.scene.getNonDeletedElements(); // box-select line editor points - if (this.state.editingLinearElement) { + if (this.state.selectedLinearElement?.isEditing) { LinearElementEditor.handleBoxSelection( event, this.state, @@ -8994,23 +8976,23 @@ class App extends React.Component { // Handle end of dragging a point of a linear element, might close a loop // and sets binding element - if (this.state.editingLinearElement) { + if (this.state.selectedLinearElement?.isEditing) { if ( !pointerDownState.boxSelection.hasOccurred && pointerDownState.hit?.element?.id !== - this.state.editingLinearElement.elementId + this.state.selectedLinearElement.elementId ) { this.actionManager.executeAction(actionFinalize); } else { const editingLinearElement = LinearElementEditor.handlePointerUp( childEvent, - this.state.editingLinearElement, + this.state.selectedLinearElement, this.state, this.scene, ); - if (editingLinearElement !== this.state.editingLinearElement) { + if (editingLinearElement !== this.state.selectedLinearElement) { this.setState({ - editingLinearElement, + selectedLinearElement: editingLinearElement, suggestedBindings: [], }); } @@ -9523,14 +9505,17 @@ class App extends React.Component { !pointerDownState.hit.wasAddedToSelection && // if we're editing a line, pointerup shouldn't switch selection if // box selected - (!this.state.editingLinearElement || + (!this.state.selectedLinearElement?.isEditing || !pointerDownState.boxSelection.hasOccurred) && // hitElement can be set when alt + ctrl to toggle lasso and we will // just respect the selected elements from lasso instead this.state.activeTool.type !== "lasso" ) { // when inside line editor, shift selects points instead - if (childEvent.shiftKey && !this.state.editingLinearElement) { + if ( + childEvent.shiftKey && + !this.state.selectedLinearElement?.isEditing + ) { if (this.state.selectedElementIds[hitElement.id]) { if (isSelectedViaGroup(this.state, hitElement)) { this.setState((_prevState) => { @@ -9708,8 +9693,9 @@ class App extends React.Component { (!hitElement && pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements)) ) { - if (this.state.editingLinearElement) { - this.setState({ editingLinearElement: null }); + if (this.state.selectedLinearElement?.isEditing) { + // Exit editing mode but keep the element selected + this.actionManager.executeAction(actionToggleLinearEditor); } else { // Deselect selected elements this.setState({ diff --git a/packages/excalidraw/components/HintViewer.tsx b/packages/excalidraw/components/HintViewer.tsx index 5ab9d7bcea..39870a34df 100644 --- a/packages/excalidraw/components/HintViewer.tsx +++ b/packages/excalidraw/components/HintViewer.tsx @@ -115,7 +115,7 @@ const getHints = ({ appState.selectionElement && !selectedElements.length && !appState.editingTextElement && - !appState.editingLinearElement + !appState.selectedLinearElement?.isEditing ) { return [t("hints.deepBoxSelect")]; } @@ -130,8 +130,8 @@ const getHints = ({ if (selectedElements.length === 1) { if (isLinearElement(selectedElements[0])) { - if (appState.editingLinearElement) { - return appState.editingLinearElement.selectedPointsIndices + if (appState.selectedLinearElement?.isEditing) { + return appState.selectedLinearElement.selectedPointsIndices ? t("hints.lineEditor_pointSelected") : t("hints.lineEditor_nothingSelected"); } diff --git a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx index 4b1cd70605..c375a2b168 100644 --- a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx +++ b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx @@ -192,7 +192,6 @@ const getRelevantAppStateProps = ( viewModeEnabled: appState.viewModeEnabled, openDialog: appState.openDialog, editingGroupId: appState.editingGroupId, - editingLinearElement: appState.editingLinearElement, selectedElementIds: appState.selectedElementIds, frameToHighlight: appState.frameToHighlight, offsetLeft: appState.offsetLeft, diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 1f3e0ff21d..e071d47aaf 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -118,7 +118,8 @@ const renderLinearElementPointHighlight = ( ) => { const { elementId, hoverPointIndex } = appState.selectedLinearElement!; if ( - appState.editingLinearElement?.selectedPointsIndices?.includes( + appState.selectedLinearElement?.isEditing && + appState.selectedLinearElement?.selectedPointsIndices?.includes( hoverPointIndex, ) ) { @@ -180,7 +181,7 @@ const renderSingleLinearPoint = ( point[0], point[1], (isOverlappingPoint - ? radius * (appState.editingLinearElement ? 1.5 : 2) + ? radius * (appState.selectedLinearElement?.isEditing ? 1.5 : 2) : radius) / appState.zoom.value, !isPhantomPoint, !isOverlappingPoint || isSelected, @@ -448,7 +449,7 @@ const renderLinearPointHandles = ( ); const { POINT_HANDLE_SIZE } = LinearElementEditor; - const radius = appState.editingLinearElement + const radius = appState.selectedLinearElement?.isEditing ? POINT_HANDLE_SIZE : POINT_HANDLE_SIZE / 2; @@ -470,7 +471,8 @@ const renderLinearPointHandles = ( ); let isSelected = - !!appState.editingLinearElement?.selectedPointsIndices?.includes(idx); + !!appState.selectedLinearElement?.isEditing && + !!appState.selectedLinearElement?.selectedPointsIndices?.includes(idx); // when element is a polygon, highlight the last point as well if first // point is selected since they overlap and the last point tends to be // rendered on top @@ -479,7 +481,8 @@ const renderLinearPointHandles = ( element.polygon && !isSelected && idx === element.points.length - 1 && - !!appState.editingLinearElement?.selectedPointsIndices?.includes(0) + !!appState.selectedLinearElement?.isEditing && + !!appState.selectedLinearElement?.selectedPointsIndices?.includes(0) ) { isSelected = true; } @@ -535,7 +538,7 @@ const renderLinearPointHandles = ( ); midPoints.forEach((segmentMidPoint) => { - if (appState.editingLinearElement || points.length === 2) { + if (appState.selectedLinearElement?.isEditing || points.length === 2) { renderSingleLinearPoint( context, appState, @@ -760,7 +763,10 @@ const _renderInteractiveScene = ({ // Getting the element using LinearElementEditor during collab mismatches version - being one head of visible elements due to // ShapeCache returns empty hence making sure that we get the // correct element from visible elements - if (appState.editingLinearElement?.elementId === element.id) { + if ( + appState.selectedLinearElement?.isEditing && + appState.selectedLinearElement.elementId === element.id + ) { if (element) { editingLinearElement = element as NonDeleted; } @@ -853,7 +859,8 @@ const _renderInteractiveScene = ({ // correct element from visible elements if ( selectedElements.length === 1 && - appState.editingLinearElement?.elementId === selectedElements[0].id + appState.selectedLinearElement?.isEditing && + appState.selectedLinearElement.elementId === selectedElements[0].id ) { renderLinearPointHandles( context, @@ -884,7 +891,7 @@ const _renderInteractiveScene = ({ } // Paint selected elements - if (!appState.multiElement && !appState.editingLinearElement) { + if (!appState.multiElement && !appState.selectedLinearElement?.isEditing) { const showBoundingBox = shouldShowBoundingBox(selectedElements, appState); const isSingleLinearElementSelected = diff --git a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap index 5f609f1e46..e7c3c68d32 100644 --- a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap @@ -908,7 +908,6 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1106,7 +1105,6 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1319,7 +1317,6 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1649,7 +1646,6 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1979,7 +1975,6 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2192,7 +2187,6 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2432,7 +2426,6 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2729,7 +2722,6 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3100,7 +3092,6 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3592,7 +3583,6 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3914,7 +3904,6 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4236,7 +4225,6 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5520,7 +5508,6 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6736,7 +6723,6 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7670,7 +7656,6 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8669,7 +8654,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9659,7 +9643,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index 077897575f..70269263de 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -34,7 +34,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -545,10 +544,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "id4": true, }, "selectedLinearElementId": "id4", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -646,7 +647,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1027,10 +1027,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "id4": true, }, "selectedLinearElementId": "id4", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -1128,7 +1130,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1491,7 +1492,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1857,7 +1857,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2119,7 +2118,6 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2417,10 +2415,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "id4": true, }, "selectedLinearElementId": "id4", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -2557,7 +2557,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2818,7 +2817,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3083,7 +3081,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3376,7 +3373,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3661,7 +3657,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3895,7 +3890,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4151,7 +4145,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4421,7 +4414,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4649,7 +4641,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4877,7 +4868,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5103,7 +5093,6 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5329,7 +5318,6 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5584,7 +5572,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5845,7 +5832,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6207,7 +6193,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6580,7 +6565,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6891,7 +6875,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7107,9 +7090,11 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "delta": Delta { "deleted": { "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -7118,16 +7103,16 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "removed": {}, "updated": {}, }, - "id": "id12", + "id": "id4", }, { "appState": AppStateDelta { "delta": Delta { "deleted": { - "editingLinearElementId": "id0", + "selectedLinearElementIsEditing": true, }, "inserted": { - "editingLinearElementId": null, + "selectedLinearElementIsEditing": false, }, }, }, @@ -7136,16 +7121,16 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "removed": {}, "updated": {}, }, - "id": "id13", + "id": "id6", }, { "appState": AppStateDelta { "delta": Delta { "deleted": { - "editingLinearElementId": null, + "selectedLinearElementIsEditing": false, }, "inserted": { - "editingLinearElementId": "id0", + "selectedLinearElementIsEditing": true, }, }, }, @@ -7154,7 +7139,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "removed": {}, "updated": {}, }, - "id": "id14", + "id": "id10", }, ] `; @@ -7193,7 +7178,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7390,7 +7374,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7741,7 +7724,6 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8092,7 +8074,6 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8497,7 +8478,6 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8783,7 +8763,6 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9046,7 +9025,6 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9310,7 +9288,6 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9541,7 +9518,6 @@ exports[`history > multiplayer undo/redo > should override remotely added groups "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9837,7 +9813,6 @@ exports[`history > multiplayer undo/redo > should override remotely added points "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10132,9 +10107,11 @@ exports[`history > multiplayer undo/redo > should override remotely added points "delta": Delta { "deleted": { "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -10182,7 +10159,6 @@ exports[`history > multiplayer undo/redo > should redistribute deltas when eleme "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10406,7 +10382,6 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10853,7 +10828,6 @@ exports[`history > multiplayer undo/redo > should update history entries after r "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11112,7 +11086,6 @@ exports[`history > singleplayer undo/redo > remounting undo/redo buttons should "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11346,7 +11319,6 @@ exports[`history > singleplayer undo/redo > should clear the redo stack on eleme "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11582,7 +11554,6 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11984,7 +11955,6 @@ exports[`history > singleplayer undo/redo > should create new history entry on e "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": "Couldn't load invalid file", @@ -12193,7 +12163,6 @@ exports[`history > singleplayer undo/redo > should create new history entry on e "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -12402,7 +12371,6 @@ exports[`history > singleplayer undo/redo > should create new history entry on i "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -12625,7 +12593,6 @@ exports[`history > singleplayer undo/redo > should create new history entry on i "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -12848,7 +12815,6 @@ exports[`history > singleplayer undo/redo > should create new history entry on s "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13092,7 +13058,6 @@ exports[`history > singleplayer undo/redo > should disable undo/redo buttons whe "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13328,7 +13293,6 @@ exports[`history > singleplayer undo/redo > should end up with no history entry "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13564,7 +13528,6 @@ exports[`history > singleplayer undo/redo > should iterate through the history w "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13810,7 +13773,6 @@ exports[`history > singleplayer undo/redo > should not clear the redo stack on s "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14140,7 +14102,6 @@ exports[`history > singleplayer undo/redo > should not collapse when applying co "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14309,7 +14270,6 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14592,7 +14552,6 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14854,7 +14813,6 @@ exports[`history > singleplayer undo/redo > should not modify anything on unrela "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -15006,7 +14964,6 @@ exports[`history > singleplayer undo/redo > should not override appstate changes "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -15287,7 +15244,6 @@ exports[`history > singleplayer undo/redo > should support appstate name or view "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -15448,7 +15404,6 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -15712,10 +15667,12 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -16004,12 +15961,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -16146,7 +16105,6 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -16627,12 +16585,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -16777,7 +16737,6 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -17258,12 +17217,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -17408,7 +17369,6 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -17954,12 +17914,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -18067,12 +18029,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedElementIds": { "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, }, }, @@ -18120,7 +18084,6 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -18678,12 +18641,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -18791,12 +18756,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id0": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedElementIds": { "id13": true, }, "selectedLinearElementId": "id13", + "selectedLinearElementIsEditing": false, }, }, }, @@ -18864,7 +18831,6 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -19343,7 +19309,6 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -19853,7 +19818,6 @@ exports[`history > singleplayer undo/redo > should support element creation, del "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -20311,7 +20275,6 @@ exports[`history > singleplayer undo/redo > should support linear element creati "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -20590,9 +20553,11 @@ exports[`history > singleplayer undo/redo > should support linear element creati "delta": Delta { "deleted": { "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -20607,10 +20572,10 @@ exports[`history > singleplayer undo/redo > should support linear element creati "appState": AppStateDelta { "delta": Delta { "deleted": { - "editingLinearElementId": "id0", + "selectedLinearElementIsEditing": true, }, "inserted": { - "editingLinearElementId": null, + "selectedLinearElementIsEditing": false, }, }, }, @@ -20678,10 +20643,10 @@ exports[`history > singleplayer undo/redo > should support linear element creati "appState": AppStateDelta { "delta": Delta { "deleted": { - "editingLinearElementId": null, + "selectedLinearElementIsEditing": false, }, "inserted": { - "editingLinearElementId": "id0", + "selectedLinearElementIsEditing": true, }, }, }, diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap index 56b9c40fbb..f22cfd28cb 100644 --- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap @@ -34,7 +34,6 @@ exports[`given element A and group of elements B and given both are selected whe "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -459,7 +458,6 @@ exports[`given element A and group of elements B and given both are selected whe "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -874,7 +872,6 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": "id28", - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1439,7 +1436,6 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -1645,7 +1641,6 @@ exports[`regression tests > adjusts z order when grouping > [end of test] appSta "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2028,7 +2023,6 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] appSt "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2272,7 +2266,6 @@ exports[`regression tests > arrow keys > [end of test] appState 1`] = ` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2451,7 +2444,6 @@ exports[`regression tests > can drag element that covers another element, while "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -2775,7 +2767,6 @@ exports[`regression tests > change the properties of a shape > [end of test] app "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3029,7 +3020,6 @@ exports[`regression tests > click on an element and drag it > [dragged] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3269,7 +3259,6 @@ exports[`regression tests > click on an element and drag it > [end of test] appS "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3504,7 +3493,6 @@ exports[`regression tests > click to select a shape > [end of test] appState 1`] "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -3761,7 +3749,6 @@ exports[`regression tests > click-drag to select a group > [end of test] appStat "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4074,7 +4061,6 @@ exports[`regression tests > deleting last but one element in editing group shoul "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4509,7 +4495,6 @@ exports[`regression tests > deselects group of selected elements on pointer down "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -4791,7 +4776,6 @@ exports[`regression tests > deselects group of selected elements on pointer up w "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5066,7 +5050,6 @@ exports[`regression tests > deselects selected element on pointer down when poin "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5273,7 +5256,6 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5472,7 +5454,6 @@ exports[`regression tests > double click to edit a group > [end of test] appStat "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": "id11", - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -5864,7 +5845,6 @@ exports[`regression tests > drags selected elements from point inside common bou "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6160,7 +6140,6 @@ exports[`regression tests > draw every type of shape > [end of test] appState 1` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -6416,12 +6395,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "id9": true, }, "selectedLinearElementId": "id9", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": { "id6": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -6562,12 +6543,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "id15": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedElementIds": { "id12": true, }, "selectedLinearElementId": "id12", + "selectedLinearElementIsEditing": false, }, }, }, @@ -6695,9 +6678,11 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "delta": Delta { "deleted": { "selectedLinearElementId": "id15", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -6716,12 +6701,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "id22": true, }, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedElementIds": { "id15": true, }, "selectedLinearElementId": "id15", + "selectedLinearElementIsEditing": false, }, }, }, @@ -6847,9 +6834,11 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "delta": Delta { "deleted": { "selectedLinearElementId": "id22", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -6885,9 +6874,11 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "delta": Delta { "deleted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedLinearElementId": "id22", + "selectedLinearElementIsEditing": false, }, }, }, @@ -6991,7 +6982,6 @@ exports[`regression tests > given a group of selected elements with an element t "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7324,7 +7314,6 @@ exports[`regression tests > given a selected element A and a not selected elemen "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7602,7 +7591,6 @@ exports[`regression tests > given selected element A with lower z-index than uns "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -7836,7 +7824,6 @@ exports[`regression tests > given selected element A with lower z-index than uns "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8075,7 +8062,6 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] appStat "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8254,7 +8240,6 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8433,7 +8418,6 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8612,7 +8596,6 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8676,6 +8659,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "endBindingElement": "keep", "hoverPointIndex": -1, "isDragging": false, + "isEditing": false, "lastUncommittedPoint": null, "pointerDownState": { "lastClickedIsEndPoint": false, @@ -8736,10 +8720,12 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack "id0": true, }, "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -8837,7 +8823,6 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -8901,6 +8886,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "endBindingElement": "keep", "hoverPointIndex": -1, "isDragging": false, + "isEditing": false, "lastUncommittedPoint": null, "pointerDownState": { "lastClickedIsEndPoint": false, @@ -8961,10 +8947,12 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1 "id0": true, }, "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -9060,7 +9048,6 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9255,7 +9242,6 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9319,6 +9305,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "endBindingElement": "keep", "hoverPointIndex": -1, "isDragging": false, + "isEditing": false, "lastUncommittedPoint": null, "pointerDownState": { "lastClickedIsEndPoint": false, @@ -9379,10 +9366,12 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack "id0": true, }, "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -9480,7 +9469,6 @@ exports[`regression tests > key d selects diamond tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9659,7 +9647,6 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -9723,6 +9710,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "endBindingElement": "keep", "hoverPointIndex": -1, "isDragging": false, + "isEditing": false, "lastUncommittedPoint": null, "pointerDownState": { "lastClickedIsEndPoint": false, @@ -9783,10 +9771,12 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1 "id0": true, }, "selectedLinearElementId": "id0", + "selectedLinearElementIsEditing": false, }, "inserted": { "selectedElementIds": {}, "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, }, }, @@ -9882,7 +9872,6 @@ exports[`regression tests > key o selects ellipse tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10061,7 +10050,6 @@ exports[`regression tests > key p selects freedraw tool > [end of test] appState "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10256,7 +10244,6 @@ exports[`regression tests > key r selects rectangle tool > [end of test] appStat "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10435,7 +10422,6 @@ exports[`regression tests > make a group and duplicate it > [end of test] appSta "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -10965,7 +10951,6 @@ exports[`regression tests > noop interaction after undo shouldn't create history "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11244,7 +11229,6 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11366,7 +11350,6 @@ exports[`regression tests > shift click on selected element should deselect it o "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11565,7 +11548,6 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -11883,7 +11865,6 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -12311,7 +12292,6 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -12950,7 +12930,6 @@ exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] a "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13075,7 +13054,6 @@ exports[`regression tests > supports nested groups > [end of test] appState 1`] "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": "id11", - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -13705,7 +13683,6 @@ exports[`regression tests > switches from group of selected elements to another "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14043,7 +14020,6 @@ exports[`regression tests > switches selected element on pointer down > [end of "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14306,7 +14282,6 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`] "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14428,7 +14403,6 @@ exports[`regression tests > undo/redo drawing an element > [end of test] appStat "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14521,9 +14495,11 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st "delta": Delta { "deleted": { "selectedLinearElementId": null, + "selectedLinearElementIsEditing": null, }, "inserted": { "selectedLinearElementId": "id6", + "selectedLinearElementIsEditing": false, }, }, }, @@ -14816,7 +14792,6 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, @@ -14938,7 +14913,6 @@ exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null, diff --git a/packages/excalidraw/tests/history.test.tsx b/packages/excalidraw/tests/history.test.tsx index ba013e29d8..a0953b8d47 100644 --- a/packages/excalidraw/tests/history.test.tsx +++ b/packages/excalidraw/tests/history.test.tsx @@ -1073,7 +1073,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(6); expect(API.getRedoStack().length).toBe(0); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).not.toBeNull(); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1090,7 +1090,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(5); expect(API.getRedoStack().length).toBe(1); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement?.elementId).toBe(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1114,7 +1114,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(4); expect(API.getRedoStack().length).toBe(2); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement?.elementId).toBe(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1131,7 +1131,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(3); expect(API.getRedoStack().length).toBe(3); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); // undo `open editor` + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); // undo `open editor` expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1148,7 +1148,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(2); expect(API.getRedoStack().length).toBe(4); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).toBeNull(); // undo `actionFinalize` expect(h.elements).toEqual([ expect.objectContaining({ @@ -1165,7 +1165,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(1); expect(API.getRedoStack().length).toBe(5); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).toBeNull(); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1181,7 +1181,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(0); expect(API.getRedoStack().length).toBe(6); expect(API.getSelectedElements().length).toBe(0); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).toBeNull(); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1197,7 +1197,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(1); expect(API.getRedoStack().length).toBe(5); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).toBeNull(); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1213,7 +1213,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(2); expect(API.getRedoStack().length).toBe(4); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).toBeNull(); // undo `actionFinalize` expect(h.elements).toEqual([ expect.objectContaining({ @@ -1230,7 +1230,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(3); expect(API.getRedoStack().length).toBe(3); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); // undo `open editor` + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); // undo `open editor` expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1247,7 +1247,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(4); expect(API.getRedoStack().length).toBe(2); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement?.elementId).toBe(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1264,7 +1264,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(5); expect(API.getRedoStack().length).toBe(1); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement?.elementId).toBe(h.elements[0].id); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); expect(h.state.selectedLinearElement?.elementId).toBe(h.elements[0].id); expect(h.elements).toEqual([ expect.objectContaining({ @@ -1281,7 +1281,7 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(6); expect(API.getRedoStack().length).toBe(0); expect(assertSelectedElements(h.elements[0])); - expect(h.state.editingLinearElement).toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); expect(h.state.selectedLinearElement).not.toBeNull(); expect(h.elements).toEqual([ expect.objectContaining({ @@ -3029,8 +3029,8 @@ describe("history", () => { expect(API.getUndoStack().length).toBe(4); expect(API.getRedoStack().length).toBe(0); - expect(h.state.editingLinearElement).toBeNull(); expect(h.state.selectedLinearElement).not.toBeNull(); + expect(h.state.selectedLinearElement?.isEditing).toBe(false); // Simulate remote update API.updateScene({ @@ -3043,16 +3043,16 @@ describe("history", () => { }); Keyboard.undo(); - expect(API.getUndoStack().length).toBe(1); - expect(API.getRedoStack().length).toBe(3); - expect(h.state.editingLinearElement).toBeNull(); - expect(h.state.selectedLinearElement).toBeNull(); + expect(API.getUndoStack().length).toBe(3); + expect(API.getRedoStack().length).toBe(1); + expect(h.state.selectedLinearElement).not.toBeNull(); + expect(h.state.selectedLinearElement?.isEditing).toBe(true); Keyboard.redo(); expect(API.getUndoStack().length).toBe(4); expect(API.getRedoStack().length).toBe(0); - expect(h.state.editingLinearElement).toBeNull(); - expect(h.state.selectedLinearElement).toBeNull(); + expect(h.state.selectedLinearElement).not.toBeNull(); + expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false); }); it("should iterate through the history when z-index changes do not produce visible change and we synced changed indices", async () => { diff --git a/packages/excalidraw/types.ts b/packages/excalidraw/types.ts index 7981e7b7f4..c265d7ca61 100644 --- a/packages/excalidraw/types.ts +++ b/packages/excalidraw/types.ts @@ -213,7 +213,6 @@ export type InteractiveCanvasAppState = Readonly< _CommonCanvasAppState & { // renderInteractiveScene activeEmbeddable: AppState["activeEmbeddable"]; - editingLinearElement: AppState["editingLinearElement"]; selectionElement: AppState["selectionElement"]; selectedGroupIds: AppState["selectedGroupIds"]; selectedLinearElement: AppState["selectedLinearElement"]; @@ -249,10 +248,8 @@ export type ObservedElementsAppState = { editingGroupId: AppState["editingGroupId"]; selectedElementIds: AppState["selectedElementIds"]; selectedGroupIds: AppState["selectedGroupIds"]; - // Avoiding storing whole instance, as it could lead into state incosistencies, empty undos/redos and etc. - editingLinearElementId: LinearElementEditor["elementId"] | null; - // Right now it's coupled to `editingLinearElement`, ideally it should not be really needed as we already have selectedElementIds & editingLinearElementId selectedLinearElementId: LinearElementEditor["elementId"] | null; + selectedLinearElementIsEditing: boolean | null; croppingElementId: AppState["croppingElementId"]; lockedMultiSelections: AppState["lockedMultiSelections"]; activeLockedId: AppState["activeLockedId"]; @@ -307,7 +304,6 @@ export interface AppState { * set when a new text is created or when an existing text is being edited */ editingTextElement: NonDeletedExcalidrawElement | null; - editingLinearElement: LinearElementEditor | null; activeTool: { /** * indicates a previous tool we should revert back to if we deselect the diff --git a/packages/utils/tests/__snapshots__/export.test.ts.snap b/packages/utils/tests/__snapshots__/export.test.ts.snap index 209ef87579..1c89411dd1 100644 --- a/packages/utils/tests/__snapshots__/export.test.ts.snap +++ b/packages/utils/tests/__snapshots__/export.test.ts.snap @@ -34,7 +34,6 @@ exports[`exportToSvg > with default arguments 1`] = ` "defaultSidebarDockedPreference": false, "editingFrame": null, "editingGroupId": null, - "editingLinearElement": null, "editingTextElement": null, "elementsToHighlight": null, "errorMessage": null,