From b7350f97070edd33d093e6195d2ed17b925ba1da Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Fri, 23 Jun 2023 06:10:08 +0800 Subject: [PATCH] fix: elements in non-existing frame getting removed (#6708) Co-authored-by: dwelle --- src/data/restore.ts | 22 ++++++++++++++++++++++ src/frame.ts | 7 +++++-- src/zindex.ts | 15 ++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/data/restore.ts b/src/data/restore.ts index b003cb3d..57a0265f 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -370,6 +370,24 @@ const repairBoundElement = ( } }; +/** + * Remove an element's frameId if its containing frame is non-existent + * + * NOTE mutates elements. + */ +const repairFrameMembership = ( + element: Mutable, + elementsMap: Map>, +) => { + if (element.frameId) { + const containingFrame = elementsMap.get(element.frameId); + + if (!containingFrame) { + element.frameId = null; + } + } +}; + export const restoreElements = ( elements: ImportedDataState["elements"], /** NOTE doesn't serve for reconciliation */ @@ -410,6 +428,10 @@ export const restoreElements = ( // repair binding. Mutates elements. const restoredElementsMap = arrayToMap(restoredElements); for (const element of restoredElements) { + if (element.frameId) { + repairFrameMembership(element, restoredElementsMap); + } + if (isTextElement(element) && element.containerId) { repairBoundElement(element, restoredElementsMap); } else if (element.boundElements) { diff --git a/src/frame.ts b/src/frame.ts index 18273cea..1028f4c0 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -304,7 +304,7 @@ export const groupsAreCompletelyOutOfFrame = ( /** * Returns a map of frameId to frame elements. Includes empty frames. */ -export const groupByFrames = (elements: ExcalidrawElementsIncludingDeleted) => { +export const groupByFrames = (elements: readonly ExcalidrawElement[]) => { const frameElementsMap = new Map< ExcalidrawElement["id"], ExcalidrawElement[] @@ -591,6 +591,7 @@ export const updateFrameMembershipOfSelectedElements = ( elementsToFilter.forEach((element) => { if ( + element.frameId && !isFrameElement(element) && !isElementInFrame(element, allElements, appState) ) { @@ -598,7 +599,9 @@ export const updateFrameMembershipOfSelectedElements = ( } }); - return removeElementsFromFrame(allElements, [...elementsToRemove], appState); + return elementsToRemove.size > 0 + ? removeElementsFromFrame(allElements, [...elementsToRemove], appState) + : allElements; }; /** diff --git a/src/zindex.ts b/src/zindex.ts index 7ccb38e5..a1f051c5 100644 --- a/src/zindex.ts +++ b/src/zindex.ts @@ -376,9 +376,22 @@ function shift( ) => ExcalidrawElement[] | readonly ExcalidrawElement[], elementsToBeMoved?: readonly ExcalidrawElement[], ) { - let rootElements = elements.filter((element) => isRootElement(element)); + const elementsMap = arrayToMap(elements); const frameElementsMap = groupByFrames(elements); + // in case root is non-existent, we promote children elements to root + let rootElements = elements.filter( + (element) => + isRootElement(element) || + (element.frameId && !elementsMap.has(element.frameId)), + ); + // and remove non-existet root + for (const frameId of frameElementsMap.keys()) { + if (!elementsMap.has(frameId)) { + frameElementsMap.delete(frameId); + } + } + // shift the root elements first rootElements = shiftFunction( rootElements,