fix fractional indices on adding new elements

This commit is contained in:
Ryan Di 2023-12-05 23:04:17 +08:00
parent 5bc23d6dee
commit 7dfba985f9
4 changed files with 62 additions and 37 deletions

View File

@ -268,6 +268,7 @@ import {
muteFSAbortError, muteFSAbortError,
isTestEnv, isTestEnv,
easeOut, easeOut,
arrayToMap,
} from "../utils"; } from "../utils";
import { import {
createSrcDoc, createSrcDoc,
@ -2921,7 +2922,7 @@ class App extends React.Component<AppProps, AppState> {
...newElements, ...newElements,
]; ];
this.scene.replaceAllElements(nextElements); this.scene.replaceAllElements(nextElements, arrayToMap(newElements));
newElements.forEach((newElement) => { newElements.forEach((newElement) => {
if (isTextElement(newElement) && isBoundToContainer(newElement)) { if (isTextElement(newElement) && isBoundToContainer(newElement)) {
@ -3147,10 +3148,10 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getElementIndex(frameId), this.scene.getElementIndex(frameId),
); );
} else { } else {
this.scene.replaceAllElements([ this.scene.replaceAllElements(
...this.scene.getElementsIncludingDeleted(), [...this.scene.getElementsIncludingDeleted(), ...textElements],
...textElements, arrayToMap(textElements),
]); );
} }
this.setState({ this.setState({
@ -6137,10 +6138,10 @@ class App extends React.Component<AppProps, AppState> {
height, height,
}); });
this.scene.replaceAllElements([ this.scene.replaceAllElements(
...this.scene.getElementsIncludingDeleted(), [...this.scene.getElementsIncludingDeleted(), element],
element, arrayToMap([element]),
]); );
return element; return element;
}; };
@ -6192,10 +6193,10 @@ class App extends React.Component<AppProps, AppState> {
validated: null, validated: null,
}); });
this.scene.replaceAllElements([ this.scene.replaceAllElements(
...this.scene.getElementsIncludingDeleted(), [...this.scene.getElementsIncludingDeleted(), element],
element, arrayToMap([element]),
]); );
return element; return element;
}; };
@ -6473,10 +6474,10 @@ class App extends React.Component<AppProps, AppState> {
? newMagicFrameElement(constructorOpts) ? newMagicFrameElement(constructorOpts)
: newFrameElement(constructorOpts); : newFrameElement(constructorOpts);
this.scene.replaceAllElements([ this.scene.replaceAllElements(
...this.scene.getElementsIncludingDeleted(), [...this.scene.getElementsIncludingDeleted(), frame],
frame, arrayToMap([frame]),
]); );
this.setState({ this.setState({
multiElement: null, multiElement: null,

View File

@ -30,14 +30,14 @@ const isValidFractionalIndex = (
const getContiguousMovedIndices = ( const getContiguousMovedIndices = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
movedElementsMap: Record<string, ExcalidrawElement>, movedElementsMap: Map<string, ExcalidrawElement>,
) => { ) => {
const result: number[][] = []; const result: number[][] = [];
const contiguous: number[] = []; const contiguous: number[] = [];
for (let i = 0; i < elements.length; i++) { for (let i = 0; i < elements.length; i++) {
const element = elements[i]; const element = elements[i];
if (movedElementsMap[element.id]) { if (movedElementsMap.has(element.id)) {
if (contiguous.length) { if (contiguous.length) {
if (contiguous[contiguous.length - 1] + 1 === i) { if (contiguous[contiguous.length - 1] + 1 === i) {
contiguous.push(i); contiguous.push(i);
@ -59,9 +59,22 @@ const getContiguousMovedIndices = (
return result; 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 = ( export const fixFractionalIndices = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
movedElementsMap: Record<string, ExcalidrawElement>, movedElementsMap: Map<string, ExcalidrawElement>,
) => { ) => {
const fixedElements = elements.slice(); const fixedElements = elements.slice();
const contiguousMovedIndices = getContiguousMovedIndices( const contiguousMovedIndices = getContiguousMovedIndices(
@ -95,7 +108,7 @@ export const fixFractionalIndices = (
); );
} }
} catch (e) { } 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 pre = -1;
let suc = 1; let suc = 1;
const normalized: ExcalidrawElement[] = [];
for (const element of allElements) { for (const element of allElements) {
const predecessor = allElements[pre]?.fractionalIndex || null; const predecessor = allElements[pre]?.fractionalIndex || null;
const successor = allElements[suc]?.fractionalIndex || null; const successor = allElements[suc]?.fractionalIndex || null;
@ -178,20 +193,20 @@ export const normalizeFractionalIndicies = (
successor, successor,
); );
mutateElement( normalized.push({
element, ...element,
{ fractionalIndex: nextFractionalIndex,
fractionalIndex: nextFractionalIndex, });
},
false,
);
} catch (e) { } catch (e) {
console.error("normalizing fractional index", e); console.error("normalizing fractional index", e);
normalized.push(element);
} }
} else {
normalized.push(element);
} }
pre++; pre++;
suc++; suc++;
} }
return allElements; return normalized;
}; };

View File

@ -11,7 +11,8 @@ import { getSelectedElements } from "./selection";
import { AppState } from "../types"; import { AppState } from "../types";
import { Assert, SameType } from "../utility-types"; import { Assert, SameType } from "../utility-types";
import { randomInteger } from "../random"; import { randomInteger } from "../random";
import { normalizeFractionalIndicies } from "../fractionalIndex"; import { fixFractionalIndices } from "../fractionalIndex";
import { arrayToMap } from "../utils";
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"]; type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
type ElementKey = ExcalidrawElement | ElementIdKey; type ElementKey = ExcalidrawElement | ElementIdKey;
@ -230,9 +231,14 @@ class Scene {
replaceAllElements( replaceAllElements(
nextElements: readonly ExcalidrawElement[], nextElements: readonly ExcalidrawElement[],
mapElementIds = true, mapOfIndicesToFix?: Map<string, ExcalidrawElement>,
) { ) {
const _nextElements = normalizeFractionalIndicies(nextElements); let _nextElements;
if (mapOfIndicesToFix) {
_nextElements = fixFractionalIndices(nextElements, mapOfIndicesToFix);
} else {
_nextElements = nextElements;
}
this.elements = _nextElements; this.elements = _nextElements;
const nextFrameLikes: ExcalidrawFrameLikeElement[] = []; const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
@ -306,7 +312,7 @@ class Scene {
element, element,
...this.elements.slice(index), ...this.elements.slice(index),
]; ];
this.replaceAllElements(nextElements); this.replaceAllElements(nextElements, arrayToMap([element]));
} }
insertElementsAtIndex(elements: ExcalidrawElement[], index: number) { insertElementsAtIndex(elements: ExcalidrawElement[], index: number) {
@ -321,14 +327,17 @@ class Scene {
...this.elements.slice(index), ...this.elements.slice(index),
]; ];
this.replaceAllElements(nextElements); this.replaceAllElements(nextElements, arrayToMap(elements));
} }
addNewElement = (element: ExcalidrawElement) => { addNewElement = (element: ExcalidrawElement) => {
if (element.frameId) { if (element.frameId) {
this.insertElementAtIndex(element, this.getElementIndex(element.frameId)); this.insertElementAtIndex(element, this.getElementIndex(element.frameId));
} else { } else {
this.replaceAllElements([...this.elements, element]); this.replaceAllElements(
[...this.elements, element],
arrayToMap([element]),
);
} }
}; };

View File

@ -235,9 +235,9 @@ const getTargetElementsMap = <T extends ExcalidrawElement>(
) => { ) => {
return indices.reduce((acc, index) => { return indices.reduce((acc, index) => {
const element = elements[index]; const element = elements[index];
acc[element.id] = element; acc.set(element.id, element);
return acc; return acc;
}, {} as Record<string, ExcalidrawElement>); }, new Map<string, ExcalidrawElement>());
}; };
const shiftElementsByOne = ( const shiftElementsByOne = (