mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-02-18 13:29:36 +01:00
fix: always re-generate index of defined moved elements (#8040)
This commit is contained in:
parent
2f9526da24
commit
eddbe55f50
@ -1477,19 +1477,28 @@ export class ElementsChange implements Change<SceneElementsMap> {
|
||||
return elements;
|
||||
}
|
||||
|
||||
const previous = Array.from(elements.values());
|
||||
const reordered = orderByFractionalIndex([...previous]);
|
||||
const unordered = Array.from(elements.values());
|
||||
const ordered = orderByFractionalIndex([...unordered]);
|
||||
const moved = Delta.getRightDifferences(unordered, ordered, true).reduce(
|
||||
(acc, arrayIndex) => {
|
||||
const candidate = unordered[Number(arrayIndex)];
|
||||
if (candidate && changed.has(candidate.id)) {
|
||||
acc.set(candidate.id, candidate);
|
||||
}
|
||||
|
||||
if (
|
||||
!flags.containsVisibleDifference &&
|
||||
Delta.isRightDifferent(previous, reordered, true)
|
||||
) {
|
||||
return acc;
|
||||
},
|
||||
new Map(),
|
||||
);
|
||||
|
||||
if (!flags.containsVisibleDifference && moved.size) {
|
||||
// we found a difference in order!
|
||||
flags.containsVisibleDifference = true;
|
||||
}
|
||||
|
||||
// let's synchronize all invalid indices of moved elements
|
||||
return arrayToMap(syncMovedIndices(reordered, changed)) as typeof elements;
|
||||
// synchronize all elements that were actually moved
|
||||
// could fallback to synchronizing all invalid indices
|
||||
return arrayToMap(syncMovedIndices(ordered, moved)) as typeof elements;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,27 +133,11 @@ const getMovedIndicesGroups = (
|
||||
let i = 0;
|
||||
|
||||
while (i < elements.length) {
|
||||
if (
|
||||
movedElements.has(elements[i].id) &&
|
||||
!isValidFractionalIndex(
|
||||
elements[i]?.index,
|
||||
elements[i - 1]?.index,
|
||||
elements[i + 1]?.index,
|
||||
)
|
||||
) {
|
||||
if (movedElements.has(elements[i].id)) {
|
||||
const indicesGroup = [i - 1, i]; // push the lower bound index as the first item
|
||||
|
||||
while (++i < elements.length) {
|
||||
if (
|
||||
!(
|
||||
movedElements.has(elements[i].id) &&
|
||||
!isValidFractionalIndex(
|
||||
elements[i]?.index,
|
||||
elements[i - 1]?.index,
|
||||
elements[i + 1]?.index,
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (!movedElements.has(elements[i].id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -17320,7 +17320,7 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 6,
|
||||
"version": 7,
|
||||
"width": 10,
|
||||
"x": 10,
|
||||
"y": 0,
|
||||
@ -17352,7 +17352,7 @@ exports[`history > singleplayer undo/redo > should support changes in elements'
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 9,
|
||||
"version": 10,
|
||||
"width": 10,
|
||||
"x": 40,
|
||||
"y": 40,
|
||||
@ -17604,7 +17604,7 @@ History {
|
||||
"index": "a2",
|
||||
},
|
||||
"inserted": {
|
||||
"index": "a0",
|
||||
"index": "Zz",
|
||||
},
|
||||
},
|
||||
"id41" => Delta {
|
||||
@ -17612,7 +17612,7 @@ History {
|
||||
"index": "a3",
|
||||
},
|
||||
"inserted": {
|
||||
"index": "a0V",
|
||||
"index": "a0",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -336,7 +336,7 @@ History {
|
||||
"groupIds": [
|
||||
"id5",
|
||||
],
|
||||
"index": "a1V",
|
||||
"index": "a2",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
@ -348,9 +348,11 @@ History {
|
||||
"groupIds": [
|
||||
"id5",
|
||||
],
|
||||
"index": "a3",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
"index": "a2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -719,7 +721,7 @@ History {
|
||||
"groupIds": [
|
||||
"id4",
|
||||
],
|
||||
"index": "a1V",
|
||||
"index": "a2",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
@ -731,9 +733,11 @@ History {
|
||||
"groupIds": [
|
||||
"id4",
|
||||
],
|
||||
"index": "a3",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
"index": "a2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1856,7 +1860,7 @@ History {
|
||||
"groupIds": [
|
||||
"id5",
|
||||
],
|
||||
"index": "a1V",
|
||||
"index": "a2",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
@ -1868,9 +1872,11 @@ History {
|
||||
"groupIds": [
|
||||
"id5",
|
||||
],
|
||||
"index": "a3",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [],
|
||||
"index": "a2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -12810,7 +12816,7 @@ History {
|
||||
"id5",
|
||||
"id3",
|
||||
],
|
||||
"index": "a1V",
|
||||
"index": "a2",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [
|
||||
@ -12825,11 +12831,13 @@ History {
|
||||
"id5",
|
||||
"id3",
|
||||
],
|
||||
"index": "a3",
|
||||
},
|
||||
"inserted": {
|
||||
"groupIds": [
|
||||
"id3",
|
||||
],
|
||||
"index": "a2",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -77,97 +77,6 @@ describe("sync invalid indices with array order", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("should NOT sync index when it is already valid", () => {
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a2" },
|
||||
{ id: "B", index: "a4" },
|
||||
],
|
||||
movedElements: ["A"],
|
||||
expect: {
|
||||
validInput: true,
|
||||
unchangedElements: ["A", "B"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a2" },
|
||||
{ id: "B", index: "a4" },
|
||||
],
|
||||
movedElements: ["B"],
|
||||
expect: {
|
||||
validInput: true,
|
||||
unchangedElements: ["A", "B"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("should NOT sync indices when they are already valid", () => {
|
||||
{
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a1" },
|
||||
{ id: "B", index: "a0" },
|
||||
{ id: "C", index: "a2" },
|
||||
],
|
||||
movedElements: ["B", "C"],
|
||||
expect: {
|
||||
// this should not sync 'C'
|
||||
unchangedElements: ["A", "C"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a1" },
|
||||
{ id: "B", index: "a0" },
|
||||
{ id: "C", index: "a2" },
|
||||
],
|
||||
movedElements: ["A", "B"],
|
||||
expect: {
|
||||
// but this should sync 'A' as it's invalid!
|
||||
unchangedElements: ["C"],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a0" },
|
||||
{ id: "B", index: "a2" },
|
||||
{ id: "C", index: "a1" },
|
||||
{ id: "D", index: "a1" },
|
||||
{ id: "E", index: "a2" },
|
||||
],
|
||||
movedElements: ["B", "D", "E"],
|
||||
expect: {
|
||||
// should not sync 'E'
|
||||
unchangedElements: ["A", "C", "E"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A" },
|
||||
{ id: "B" },
|
||||
{ id: "C", index: "a0" },
|
||||
{ id: "D", index: "a2" },
|
||||
{ id: "E" },
|
||||
{ id: "F", index: "a3" },
|
||||
{ id: "G" },
|
||||
{ id: "H", index: "a1" },
|
||||
{ id: "I", index: "a2" },
|
||||
{ id: "J" },
|
||||
],
|
||||
movedElements: ["A", "B", "D", "E", "F", "G", "J"],
|
||||
expect: {
|
||||
// should not sync 'D' and 'F'
|
||||
unchangedElements: ["C", "D", "F"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("should sync when fractional index is not defined", () => {
|
||||
testMovedIndicesSync({
|
||||
elements: [{ id: "A" }],
|
||||
@ -384,6 +293,122 @@ describe("sync invalid indices with array order", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("should sync all moved elements regardless of their validity", () => {
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a2" },
|
||||
{ id: "B", index: "a4" },
|
||||
],
|
||||
movedElements: ["A"],
|
||||
expect: {
|
||||
validInput: true,
|
||||
unchangedElements: ["B"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a2" },
|
||||
{ id: "B", index: "a4" },
|
||||
],
|
||||
movedElements: ["B"],
|
||||
expect: {
|
||||
validInput: true,
|
||||
unchangedElements: ["A"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "C", index: "a2" },
|
||||
{ id: "D", index: "a3" },
|
||||
{ id: "A", index: "a0" },
|
||||
{ id: "B", index: "a1" },
|
||||
],
|
||||
movedElements: ["C", "D"],
|
||||
expect: {
|
||||
unchangedElements: ["A", "B"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a1" },
|
||||
{ id: "B", index: "a2" },
|
||||
{ id: "D", index: "a4" },
|
||||
{ id: "C", index: "a3" },
|
||||
{ id: "F", index: "a6" },
|
||||
{ id: "E", index: "a5" },
|
||||
{ id: "H", index: "a8" },
|
||||
{ id: "G", index: "a7" },
|
||||
{ id: "I", index: "a9" },
|
||||
],
|
||||
movedElements: ["D", "F", "H"],
|
||||
expect: {
|
||||
unchangedElements: ["A", "B", "C", "E", "G", "I"],
|
||||
},
|
||||
});
|
||||
|
||||
{
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a1" },
|
||||
{ id: "B", index: "a0" },
|
||||
{ id: "C", index: "a2" },
|
||||
],
|
||||
movedElements: ["B", "C"],
|
||||
expect: {
|
||||
unchangedElements: ["A"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a1" },
|
||||
{ id: "B", index: "a0" },
|
||||
{ id: "C", index: "a2" },
|
||||
],
|
||||
movedElements: ["A", "B"],
|
||||
expect: {
|
||||
unchangedElements: ["C"],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A", index: "a0" },
|
||||
{ id: "B", index: "a2" },
|
||||
{ id: "C", index: "a1" },
|
||||
{ id: "D", index: "a1" },
|
||||
{ id: "E", index: "a2" },
|
||||
],
|
||||
movedElements: ["B", "D", "E"],
|
||||
expect: {
|
||||
unchangedElements: ["A", "C"],
|
||||
},
|
||||
});
|
||||
|
||||
testMovedIndicesSync({
|
||||
elements: [
|
||||
{ id: "A" },
|
||||
{ id: "B" },
|
||||
{ id: "C", index: "a0" },
|
||||
{ id: "D", index: "a2" },
|
||||
{ id: "E" },
|
||||
{ id: "F", index: "a3" },
|
||||
{ id: "G" },
|
||||
{ id: "H", index: "a1" },
|
||||
{ id: "I", index: "a2" },
|
||||
{ id: "J" },
|
||||
],
|
||||
movedElements: ["A", "B", "D", "E", "F", "G", "J"],
|
||||
expect: {
|
||||
unchangedElements: ["C", "H", "I"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("should generate fractions for explicitly moved elements", () => {
|
||||
describe("should generate a fraction between 'A' and 'C'", () => {
|
||||
testMovedIndicesSync({
|
||||
|
Loading…
Reference in New Issue
Block a user