normalize before replacing
This commit is contained in:
parent
00ffa08e28
commit
1e132e33ae
@ -43,7 +43,6 @@ import {
|
||||
measureBaseline,
|
||||
} from "../element/textElement";
|
||||
import { normalizeLink } from "./url";
|
||||
import { normalizeFractionalIndexing } from "../zindex";
|
||||
|
||||
type RestoredAppState = Omit<
|
||||
AppState,
|
||||
@ -583,9 +582,7 @@ export const restore = (
|
||||
elementsConfig?: { refreshDimensions?: boolean; repairBindings?: boolean },
|
||||
): RestoredDataState => {
|
||||
return {
|
||||
elements: normalizeFractionalIndexing(
|
||||
restoreElements(data?.elements, localElements, elementsConfig),
|
||||
),
|
||||
elements: restoreElements(data?.elements, localElements, elementsConfig),
|
||||
appState: restoreAppState(data?.appState, localAppState || null),
|
||||
files: data?.files || {},
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ import { getSelectedElements } from "./selection";
|
||||
import { AppState } from "../types";
|
||||
import { Assert, SameType } from "../utility-types";
|
||||
import { randomInteger } from "../random";
|
||||
import { normalizeFractionalIndexing } from "../zindex";
|
||||
|
||||
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
|
||||
type ElementKey = ExcalidrawElement | ElementIdKey;
|
||||
@ -231,10 +232,12 @@ class Scene {
|
||||
nextElements: readonly ExcalidrawElement[],
|
||||
mapElementIds = true,
|
||||
) {
|
||||
this.elements = nextElements;
|
||||
const _nextElements = normalizeFractionalIndexing(nextElements);
|
||||
|
||||
this.elements = _nextElements;
|
||||
const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
|
||||
this.elementsMap.clear();
|
||||
nextElements.forEach((element) => {
|
||||
_nextElements.forEach((element) => {
|
||||
if (isFrameLikeElement(element)) {
|
||||
nextFrameLikes.push(element);
|
||||
}
|
||||
|
173
src/zindex.ts
173
src/zindex.ts
@ -1,4 +1,4 @@
|
||||
import { bumpVersion } from "./element/mutateElement";
|
||||
import { bumpVersion, mutateElement } from "./element/mutateElement";
|
||||
import { isFrameLikeElement } from "./element/typeChecks";
|
||||
import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types";
|
||||
import { getElementsInGroup } from "./groups";
|
||||
@ -490,74 +490,75 @@ function shiftElementsAccountingForFrames(
|
||||
// -----------------------------------------------------------------------------
|
||||
type FractionalIndex = ExcalidrawElement["fractionalIndex"];
|
||||
|
||||
const fractionalIndexCompare = {
|
||||
isSmallerThan(indexA: string, indexB: string) {
|
||||
return indexA < indexB;
|
||||
},
|
||||
|
||||
isGreaterThan(indexA: string, indexB: string) {
|
||||
return indexA > indexB;
|
||||
},
|
||||
};
|
||||
|
||||
const isValidFractionalIndex = (
|
||||
index: FractionalIndex,
|
||||
predecessorFractionalIndex: FractionalIndex,
|
||||
successorFractionalIndex: FractionalIndex,
|
||||
predecessor: FractionalIndex,
|
||||
successor: FractionalIndex,
|
||||
) => {
|
||||
if (index) {
|
||||
if (predecessorFractionalIndex) {
|
||||
if (successorFractionalIndex) {
|
||||
return (
|
||||
fractionalIndexCompare.isGreaterThan(
|
||||
index,
|
||||
predecessorFractionalIndex,
|
||||
) &&
|
||||
fractionalIndexCompare.isSmallerThan(index, successorFractionalIndex)
|
||||
);
|
||||
}
|
||||
return fractionalIndexCompare.isGreaterThan(
|
||||
index,
|
||||
predecessorFractionalIndex,
|
||||
);
|
||||
}
|
||||
|
||||
if (successorFractionalIndex) {
|
||||
return fractionalIndexCompare.isSmallerThan(
|
||||
index,
|
||||
successorFractionalIndex,
|
||||
);
|
||||
}
|
||||
|
||||
if (!predecessor && !successor) {
|
||||
return index.length > 0;
|
||||
}
|
||||
|
||||
if (!predecessor) {
|
||||
// first element
|
||||
return index < successor!;
|
||||
}
|
||||
|
||||
if (!successor) {
|
||||
// last element
|
||||
return predecessor! < index;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const generateFractionalIndex = (
|
||||
predecessorFractionalIndex: string | null,
|
||||
successorFractionalIndex: string | null,
|
||||
index: FractionalIndex,
|
||||
predecessor: FractionalIndex,
|
||||
successor: FractionalIndex,
|
||||
) => {
|
||||
if (predecessorFractionalIndex && successorFractionalIndex) {
|
||||
if (predecessorFractionalIndex < successorFractionalIndex) {
|
||||
return generateKeyBetween(
|
||||
predecessorFractionalIndex,
|
||||
successorFractionalIndex,
|
||||
);
|
||||
} else if (predecessorFractionalIndex > successorFractionalIndex) {
|
||||
return generateKeyBetween(
|
||||
successorFractionalIndex,
|
||||
predecessorFractionalIndex,
|
||||
);
|
||||
}
|
||||
return generateKeyBetween(predecessorFractionalIndex, null);
|
||||
if (index) {
|
||||
if (!predecessor && !successor) {
|
||||
return index;
|
||||
}
|
||||
|
||||
return generateKeyBetween(
|
||||
predecessorFractionalIndex,
|
||||
successorFractionalIndex,
|
||||
);
|
||||
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(null, null);
|
||||
};
|
||||
|
||||
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 = (
|
||||
allElements: readonly ExcalidrawElement[],
|
||||
) => {
|
||||
let predecessor = -1;
|
||||
let successor = 1;
|
||||
|
||||
const normalizedElementsMap = arrayToMap(allElements);
|
||||
let pre = -1;
|
||||
let suc = 1;
|
||||
|
||||
for (const element of allElements) {
|
||||
const predecessorFractionalIndex =
|
||||
normalizedElementsMap.get(allElements[predecessor]?.id)
|
||||
?.fractionalIndex || null;
|
||||
const predecessor = allElements[pre]?.fractionalIndex || null;
|
||||
const successor = allElements[suc]?.fractionalIndex || null;
|
||||
|
||||
const successorFractionalIndex =
|
||||
normalizedElementsMap.get(allElements[successor]?.id)?.fractionalIndex ||
|
||||
null;
|
||||
|
||||
try {
|
||||
if (
|
||||
!isValidFractionalIndex(
|
||||
element.fractionalIndex,
|
||||
predecessorFractionalIndex,
|
||||
successorFractionalIndex,
|
||||
)
|
||||
!isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
|
||||
) {
|
||||
try {
|
||||
const nextFractionalIndex = generateFractionalIndex(
|
||||
predecessorFractionalIndex,
|
||||
successorFractionalIndex,
|
||||
element.fractionalIndex,
|
||||
predecessor,
|
||||
successor,
|
||||
);
|
||||
|
||||
normalizedElementsMap.set(element.id, {
|
||||
...element,
|
||||
mutateElement(
|
||||
element,
|
||||
{
|
||||
fractionalIndex: nextFractionalIndex,
|
||||
});
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
predecessor++;
|
||||
successor++;
|
||||
}
|
||||
pre++;
|
||||
suc++;
|
||||
}
|
||||
|
||||
return [...normalizedElementsMap.values()];
|
||||
return allElements;
|
||||
};
|
||||
|
||||
// public API
|
||||
@ -618,31 +611,25 @@ export const moveOneLeft = (
|
||||
allElements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
return normalizeFractionalIndexing(
|
||||
shiftElementsByOne(allElements, appState, "left"),
|
||||
);
|
||||
return shiftElementsByOne(allElements, appState, "left");
|
||||
};
|
||||
|
||||
export const moveOneRight = (
|
||||
allElements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
return normalizeFractionalIndexing(
|
||||
shiftElementsByOne(allElements, appState, "right"),
|
||||
);
|
||||
return shiftElementsByOne(allElements, appState, "right");
|
||||
};
|
||||
|
||||
export const moveAllLeft = (
|
||||
allElements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
return normalizeFractionalIndexing(
|
||||
shiftElementsAccountingForFrames(
|
||||
return shiftElementsAccountingForFrames(
|
||||
allElements,
|
||||
appState,
|
||||
"left",
|
||||
shiftElementsToEnd,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@ -650,12 +637,10 @@ export const moveAllRight = (
|
||||
allElements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
return normalizeFractionalIndexing(
|
||||
shiftElementsAccountingForFrames(
|
||||
return shiftElementsAccountingForFrames(
|
||||
allElements,
|
||||
appState,
|
||||
"right",
|
||||
shiftElementsToEnd,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user