diff --git a/src/components/App.tsx b/src/components/App.tsx index c579d8b1cd..b71c8a8c9d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -268,6 +268,7 @@ import { muteFSAbortError, isTestEnv, easeOut, + arrayToMap, } from "../utils"; import { createSrcDoc, @@ -2921,7 +2922,7 @@ class App extends React.Component { ...newElements, ]; - this.scene.replaceAllElements(nextElements); + this.scene.replaceAllElements(nextElements, arrayToMap(newElements)); newElements.forEach((newElement) => { if (isTextElement(newElement) && isBoundToContainer(newElement)) { @@ -3147,10 +3148,10 @@ class App extends React.Component { this.scene.getElementIndex(frameId), ); } else { - this.scene.replaceAllElements([ - ...this.scene.getElementsIncludingDeleted(), - ...textElements, - ]); + this.scene.replaceAllElements( + [...this.scene.getElementsIncludingDeleted(), ...textElements], + arrayToMap(textElements), + ); } this.setState({ @@ -6137,10 +6138,10 @@ class App extends React.Component { height, }); - this.scene.replaceAllElements([ - ...this.scene.getElementsIncludingDeleted(), - element, - ]); + this.scene.replaceAllElements( + [...this.scene.getElementsIncludingDeleted(), element], + arrayToMap([element]), + ); return element; }; @@ -6192,10 +6193,10 @@ class App extends React.Component { validated: null, }); - this.scene.replaceAllElements([ - ...this.scene.getElementsIncludingDeleted(), - element, - ]); + this.scene.replaceAllElements( + [...this.scene.getElementsIncludingDeleted(), element], + arrayToMap([element]), + ); return element; }; @@ -6473,10 +6474,10 @@ class App extends React.Component { ? newMagicFrameElement(constructorOpts) : newFrameElement(constructorOpts); - this.scene.replaceAllElements([ - ...this.scene.getElementsIncludingDeleted(), - frame, - ]); + this.scene.replaceAllElements( + [...this.scene.getElementsIncludingDeleted(), frame], + arrayToMap([frame]), + ); this.setState({ multiElement: null, diff --git a/src/fractionalIndex.ts b/src/fractionalIndex.ts index e31806264d..6f0ad98a4d 100644 --- a/src/fractionalIndex.ts +++ b/src/fractionalIndex.ts @@ -30,14 +30,14 @@ const isValidFractionalIndex = ( const getContiguousMovedIndices = ( elements: readonly ExcalidrawElement[], - movedElementsMap: Record, + movedElementsMap: Map, ) => { const result: number[][] = []; const contiguous: number[] = []; for (let i = 0; i < elements.length; i++) { const element = elements[i]; - if (movedElementsMap[element.id]) { + if (movedElementsMap.has(element.id)) { if (contiguous.length) { if (contiguous[contiguous.length - 1] + 1 === i) { contiguous.push(i); @@ -59,9 +59,22 @@ const getContiguousMovedIndices = ( return result; }; +export const generateFractionalIndexBetween = ( + predecessor: FractionalIndex, + successor: FractionalIndex, +) => { + if (predecessor && successor) { + if (predecessor < successor) { + return generateKeyBetween(predecessor, successor); + } + return null; + } + return generateKeyBetween(predecessor, successor); +}; + export const fixFractionalIndices = ( elements: readonly ExcalidrawElement[], - movedElementsMap: Record, + movedElementsMap: Map, ) => { const fixedElements = elements.slice(); const contiguousMovedIndices = getContiguousMovedIndices( @@ -95,7 +108,7 @@ export const fixFractionalIndices = ( ); } } catch (e) { - console.error("error generating fractional indices", e); + console.error("error fixing fractional indices", e); } } @@ -164,6 +177,8 @@ export const normalizeFractionalIndicies = ( let pre = -1; let suc = 1; + const normalized: ExcalidrawElement[] = []; + for (const element of allElements) { const predecessor = allElements[pre]?.fractionalIndex || null; const successor = allElements[suc]?.fractionalIndex || null; @@ -178,20 +193,20 @@ export const normalizeFractionalIndicies = ( successor, ); - mutateElement( - element, - { - fractionalIndex: nextFractionalIndex, - }, - false, - ); + normalized.push({ + ...element, + fractionalIndex: nextFractionalIndex, + }); } catch (e) { console.error("normalizing fractional index", e); + normalized.push(element); } + } else { + normalized.push(element); } pre++; suc++; } - return allElements; + return normalized; }; diff --git a/src/scene/Scene.ts b/src/scene/Scene.ts index 9399a15c3d..dca1e02c49 100644 --- a/src/scene/Scene.ts +++ b/src/scene/Scene.ts @@ -11,7 +11,8 @@ import { getSelectedElements } from "./selection"; import { AppState } from "../types"; import { Assert, SameType } from "../utility-types"; import { randomInteger } from "../random"; -import { normalizeFractionalIndicies } from "../fractionalIndex"; +import { fixFractionalIndices } from "../fractionalIndex"; +import { arrayToMap } from "../utils"; type ElementIdKey = InstanceType["elementId"]; type ElementKey = ExcalidrawElement | ElementIdKey; @@ -230,9 +231,14 @@ class Scene { replaceAllElements( nextElements: readonly ExcalidrawElement[], - mapElementIds = true, + mapOfIndicesToFix?: Map, ) { - const _nextElements = normalizeFractionalIndicies(nextElements); + let _nextElements; + if (mapOfIndicesToFix) { + _nextElements = fixFractionalIndices(nextElements, mapOfIndicesToFix); + } else { + _nextElements = nextElements; + } this.elements = _nextElements; const nextFrameLikes: ExcalidrawFrameLikeElement[] = []; @@ -306,7 +312,7 @@ class Scene { element, ...this.elements.slice(index), ]; - this.replaceAllElements(nextElements); + this.replaceAllElements(nextElements, arrayToMap([element])); } insertElementsAtIndex(elements: ExcalidrawElement[], index: number) { @@ -321,14 +327,17 @@ class Scene { ...this.elements.slice(index), ]; - this.replaceAllElements(nextElements); + this.replaceAllElements(nextElements, arrayToMap(elements)); } addNewElement = (element: ExcalidrawElement) => { if (element.frameId) { this.insertElementAtIndex(element, this.getElementIndex(element.frameId)); } else { - this.replaceAllElements([...this.elements, element]); + this.replaceAllElements( + [...this.elements, element], + arrayToMap([element]), + ); } }; diff --git a/src/zindex.ts b/src/zindex.ts index aec2e30f53..b8250ef584 100644 --- a/src/zindex.ts +++ b/src/zindex.ts @@ -235,9 +235,9 @@ const getTargetElementsMap = ( ) => { return indices.reduce((acc, index) => { const element = elements[index]; - acc[element.id] = element; + acc.set(element.id, element); return acc; - }, {} as Record); + }, new Map()); }; const shiftElementsByOne = (