feat: fractional indexing (#7359)

* Introducing fractional indices as part of `element.index`

* Ensuring invalid fractional indices are always synchronized with the array order

* Simplifying reconciliation based on the fractional indices

* Moving reconciliation inside the `@excalidraw/excalidraw` package

---------

Co-authored-by: Marcel Mraz <marcel@excalidraw.com>
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Ryan Di 2024-04-04 20:51:11 +08:00 committed by GitHub
parent bbdcd30a73
commit 32df5502ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 3640 additions and 2047 deletions

View file

@ -1,6 +1,6 @@
import { bumpVersion } from "./element/mutateElement";
import { isFrameLikeElement } from "./element/typeChecks";
import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types";
import { syncMovedIndices } from "./fractionalIndex";
import { getElementsInGroup } from "./groups";
import { getSelectedElements } from "./scene";
import Scene from "./scene/Scene";
@ -234,9 +234,9 @@ const getTargetElementsMap = <T extends ExcalidrawElement>(
) => {
return indices.reduce((acc, index) => {
const element = elements[index];
acc[element.id] = element;
acc.set(element.id, element);
return acc;
}, {} as Record<string, ExcalidrawElement>);
}, new Map<string, ExcalidrawElement>());
};
const shiftElementsByOne = (
@ -246,6 +246,7 @@ const shiftElementsByOne = (
) => {
const indicesToMove = getIndicesToMove(elements, appState);
const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
let groupedIndices = toContiguousGroups(indicesToMove);
if (direction === "right") {
@ -312,12 +313,9 @@ const shiftElementsByOne = (
];
});
return elements.map((element) => {
if (targetElementsMap[element.id]) {
return bumpVersion(element);
}
return element;
});
syncMovedIndices(elements, targetElementsMap);
return elements;
};
const shiftElementsToEnd = (
@ -383,26 +381,27 @@ const shiftElementsToEnd = (
}
}
const targetElements = Object.values(targetElementsMap).map((element) => {
return bumpVersion(element);
});
const targetElements = Array.from(targetElementsMap.values());
const leadingElements = elements.slice(0, leadingIndex);
const trailingElements = elements.slice(trailingIndex + 1);
const nextElements =
direction === "left"
? [
...leadingElements,
...targetElements,
...displacedElements,
...trailingElements,
]
: [
...leadingElements,
...displacedElements,
...targetElements,
...trailingElements,
];
return direction === "left"
? [
...leadingElements,
...targetElements,
...displacedElements,
...trailingElements,
]
: [
...leadingElements,
...displacedElements,
...targetElements,
...trailingElements,
];
syncMovedIndices(nextElements, targetElementsMap);
return nextElements;
};
function shiftElementsAccountingForFrames(