normalize before replacing

mrazator/test-fractional-index-and-granular-history
Ryan Di 1 year ago
parent 00ffa08e28
commit 1e132e33ae

@ -43,7 +43,6 @@ import {
measureBaseline, measureBaseline,
} from "../element/textElement"; } from "../element/textElement";
import { normalizeLink } from "./url"; import { normalizeLink } from "./url";
import { normalizeFractionalIndexing } from "../zindex";
type RestoredAppState = Omit< type RestoredAppState = Omit<
AppState, AppState,
@ -583,9 +582,7 @@ export const restore = (
elementsConfig?: { refreshDimensions?: boolean; repairBindings?: boolean }, elementsConfig?: { refreshDimensions?: boolean; repairBindings?: boolean },
): RestoredDataState => { ): RestoredDataState => {
return { return {
elements: normalizeFractionalIndexing( elements: restoreElements(data?.elements, localElements, elementsConfig),
restoreElements(data?.elements, localElements, elementsConfig),
),
appState: restoreAppState(data?.appState, localAppState || null), appState: restoreAppState(data?.appState, localAppState || null),
files: data?.files || {}, files: data?.files || {},
}; };

@ -11,6 +11,7 @@ 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 { normalizeFractionalIndexing } from "../zindex";
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"]; type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
type ElementKey = ExcalidrawElement | ElementIdKey; type ElementKey = ExcalidrawElement | ElementIdKey;
@ -231,10 +232,12 @@ class Scene {
nextElements: readonly ExcalidrawElement[], nextElements: readonly ExcalidrawElement[],
mapElementIds = true, mapElementIds = true,
) { ) {
this.elements = nextElements; const _nextElements = normalizeFractionalIndexing(nextElements);
this.elements = _nextElements;
const nextFrameLikes: ExcalidrawFrameLikeElement[] = []; const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
this.elementsMap.clear(); this.elementsMap.clear();
nextElements.forEach((element) => { _nextElements.forEach((element) => {
if (isFrameLikeElement(element)) { if (isFrameLikeElement(element)) {
nextFrameLikes.push(element); nextFrameLikes.push(element);
} }

@ -1,4 +1,4 @@
import { bumpVersion } from "./element/mutateElement"; import { bumpVersion, mutateElement } from "./element/mutateElement";
import { isFrameLikeElement } from "./element/typeChecks"; import { isFrameLikeElement } from "./element/typeChecks";
import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types"; import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types";
import { getElementsInGroup } from "./groups"; import { getElementsInGroup } from "./groups";
@ -490,74 +490,75 @@ function shiftElementsAccountingForFrames(
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type FractionalIndex = ExcalidrawElement["fractionalIndex"]; type FractionalIndex = ExcalidrawElement["fractionalIndex"];
const fractionalIndexCompare = {
isSmallerThan(indexA: string, indexB: string) {
return indexA < indexB;
},
isGreaterThan(indexA: string, indexB: string) {
return indexA > indexB;
},
};
const isValidFractionalIndex = ( const isValidFractionalIndex = (
index: FractionalIndex, index: FractionalIndex,
predecessorFractionalIndex: FractionalIndex, predecessor: FractionalIndex,
successorFractionalIndex: FractionalIndex, successor: FractionalIndex,
) => { ) => {
if (index) { if (index) {
if (predecessorFractionalIndex) { if (!predecessor && !successor) {
if (successorFractionalIndex) { return index.length > 0;
return (
fractionalIndexCompare.isGreaterThan(
index,
predecessorFractionalIndex,
) &&
fractionalIndexCompare.isSmallerThan(index, successorFractionalIndex)
);
}
return fractionalIndexCompare.isGreaterThan(
index,
predecessorFractionalIndex,
);
} }
if (successorFractionalIndex) { if (!predecessor) {
return fractionalIndexCompare.isSmallerThan( // first element
index, return index < successor!;
successorFractionalIndex,
);
} }
return index.length > 0; if (!successor) {
// last element
return predecessor! < index;
}
} }
return false; return false;
}; };
const generateFractionalIndex = ( const generateFractionalIndex = (
predecessorFractionalIndex: string | null, index: FractionalIndex,
successorFractionalIndex: string | null, predecessor: FractionalIndex,
successor: FractionalIndex,
) => { ) => {
if (predecessorFractionalIndex && successorFractionalIndex) { if (index) {
if (predecessorFractionalIndex < successorFractionalIndex) { if (!predecessor && !successor) {
return generateKeyBetween( return index;
predecessorFractionalIndex,
successorFractionalIndex,
);
} else if (predecessorFractionalIndex > successorFractionalIndex) {
return generateKeyBetween(
successorFractionalIndex,
predecessorFractionalIndex,
);
} }
return generateKeyBetween(predecessorFractionalIndex, null);
if (!predecessor) {
// first element in the array
return generateKeyBetween(null, successor);
}
if (!successor) {
// last element in the array
return generateKeyBetween(predecessor, null);
}
// both predecessor and successor exist
// insert after predecessor
return generateKeyBetween(predecessor, null);
} }
return generateKeyBetween( return generateKeyBetween(null, null);
predecessorFractionalIndex, };
successorFractionalIndex,
); const compareStrings = (a: string, b: string) => {
return a < b ? -1 : 1;
};
export const orderByFractionalIndex = (allElements: ExcalidrawElement[]) => {
return allElements.sort((a, b) => {
if (a.fractionalIndex && b.fractionalIndex) {
if (a.fractionalIndex < b.fractionalIndex) {
return -1;
} else if (a.fractionalIndex > b.fractionalIndex) {
return 1;
}
return compareStrings(a.id, b.id);
}
return 0;
});
}; };
/** /**
@ -568,47 +569,39 @@ const generateFractionalIndex = (
export const normalizeFractionalIndexing = ( export const normalizeFractionalIndexing = (
allElements: readonly ExcalidrawElement[], allElements: readonly ExcalidrawElement[],
) => { ) => {
let predecessor = -1; let pre = -1;
let successor = 1; let suc = 1;
const normalizedElementsMap = arrayToMap(allElements);
for (const element of allElements) { for (const element of allElements) {
const predecessorFractionalIndex = const predecessor = allElements[pre]?.fractionalIndex || null;
normalizedElementsMap.get(allElements[predecessor]?.id) const successor = allElements[suc]?.fractionalIndex || null;
?.fractionalIndex || null;
const successorFractionalIndex =
normalizedElementsMap.get(allElements[successor]?.id)?.fractionalIndex ||
null;
try { if (
if ( !isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
!isValidFractionalIndex( ) {
element.fractionalIndex, try {
predecessorFractionalIndex,
successorFractionalIndex,
)
) {
const nextFractionalIndex = generateFractionalIndex( const nextFractionalIndex = generateFractionalIndex(
predecessorFractionalIndex, element.fractionalIndex,
successorFractionalIndex, predecessor,
successor,
); );
normalizedElementsMap.set(element.id, { mutateElement(
...element, element,
fractionalIndex: nextFractionalIndex, {
}); fractionalIndex: nextFractionalIndex,
},
false,
);
} catch (e) {
console.error(e);
} }
} catch (e) {
console.error(e);
} }
pre++;
predecessor++; suc++;
successor++;
} }
return [...normalizedElementsMap.values()]; return allElements;
}; };
// public API // public API
@ -618,31 +611,25 @@ export const moveOneLeft = (
allElements: readonly ExcalidrawElement[], allElements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
) => { ) => {
return normalizeFractionalIndexing( return shiftElementsByOne(allElements, appState, "left");
shiftElementsByOne(allElements, appState, "left"),
);
}; };
export const moveOneRight = ( export const moveOneRight = (
allElements: readonly ExcalidrawElement[], allElements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
) => { ) => {
return normalizeFractionalIndexing( return shiftElementsByOne(allElements, appState, "right");
shiftElementsByOne(allElements, appState, "right"),
);
}; };
export const moveAllLeft = ( export const moveAllLeft = (
allElements: readonly ExcalidrawElement[], allElements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
) => { ) => {
return normalizeFractionalIndexing( return shiftElementsAccountingForFrames(
shiftElementsAccountingForFrames( allElements,
allElements, appState,
appState, "left",
"left", shiftElementsToEnd,
shiftElementsToEnd,
),
); );
}; };
@ -650,12 +637,10 @@ export const moveAllRight = (
allElements: readonly ExcalidrawElement[], allElements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
) => { ) => {
return normalizeFractionalIndexing( return shiftElementsAccountingForFrames(
shiftElementsAccountingForFrames( allElements,
allElements, appState,
appState, "right",
"right", shiftElementsToEnd,
shiftElementsToEnd,
),
); );
}; };

Loading…
Cancel
Save