indices with jitter
This commit is contained in:
parent
b734f7cba8
commit
bf53d90c68
@ -37,7 +37,7 @@
|
|||||||
"eslint-plugin-react": "7.32.2",
|
"eslint-plugin-react": "7.32.2",
|
||||||
"fake-indexeddb": "3.1.7",
|
"fake-indexeddb": "3.1.7",
|
||||||
"firebase": "8.3.3",
|
"firebase": "8.3.3",
|
||||||
"fractional-indexing": "3.2.0",
|
"fractional-indexing-jittered": "0.9.0",
|
||||||
"i18next-browser-languagedetector": "6.1.4",
|
"i18next-browser-languagedetector": "6.1.4",
|
||||||
"idb-keyval": "6.0.3",
|
"idb-keyval": "6.0.3",
|
||||||
"image-blob-reduce": "3.0.1",
|
"image-blob-reduce": "3.0.1",
|
||||||
|
@ -43,7 +43,7 @@ import {
|
|||||||
measureBaseline,
|
measureBaseline,
|
||||||
} from "../element/textElement";
|
} from "../element/textElement";
|
||||||
import { normalizeLink } from "./url";
|
import { normalizeLink } from "./url";
|
||||||
import { normalizeFractionalIndicies } from "../fractionalIndex";
|
import { restoreFractionalIndicies } from "../fractionalIndex";
|
||||||
|
|
||||||
type RestoredAppState = Omit<
|
type RestoredAppState = Omit<
|
||||||
AppState,
|
AppState,
|
||||||
@ -461,7 +461,7 @@ export const restoreElements = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalizeFractionalIndicies(restoredElements) as ExcalidrawElement[];
|
return restoreFractionalIndicies(restoredElements) as ExcalidrawElement[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const coalesceAppStateValue = <
|
const coalesceAppStateValue = <
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { mutateElement } from "./element/mutateElement";
|
import { mutateElement } from "./element/mutateElement";
|
||||||
import { ExcalidrawElement } from "./element/types";
|
import { ExcalidrawElement } from "./element/types";
|
||||||
import { generateKeyBetween, generateNKeysBetween } from "fractional-indexing";
|
import {
|
||||||
|
generateKeyBetween,
|
||||||
|
generateNJitteredKeysBetween,
|
||||||
|
} from "fractional-indexing-jittered";
|
||||||
|
|
||||||
type FractionalIndex = ExcalidrawElement["fractionalIndex"];
|
type FractionalIndex = ExcalidrawElement["fractionalIndex"];
|
||||||
|
|
||||||
@ -10,19 +13,23 @@ const isValidFractionalIndex = (
|
|||||||
successor: FractionalIndex,
|
successor: FractionalIndex,
|
||||||
) => {
|
) => {
|
||||||
if (index) {
|
if (index) {
|
||||||
|
if (predecessor && successor) {
|
||||||
|
return predecessor < index && index < successor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successor && !predecessor) {
|
||||||
|
// first element
|
||||||
|
return index < successor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predecessor && !successor) {
|
||||||
|
// last element
|
||||||
|
return predecessor < index;
|
||||||
|
}
|
||||||
|
|
||||||
if (!predecessor && !successor) {
|
if (!predecessor && !successor) {
|
||||||
return index.length > 0;
|
return index.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!predecessor) {
|
|
||||||
// first element
|
|
||||||
return index < successor!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!successor) {
|
|
||||||
// last element
|
|
||||||
return predecessor! < index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -89,7 +96,7 @@ export const fixFractionalIndices = (
|
|||||||
elements[movedIndices[movedIndices.length - 1] + 1]?.fractionalIndex ||
|
elements[movedIndices[movedIndices.length - 1] + 1]?.fractionalIndex ||
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const newKeys = generateNKeysBetween(
|
const newKeys = generateNJitteredKeysBetween(
|
||||||
predecessor,
|
predecessor,
|
||||||
successor,
|
successor,
|
||||||
movedIndices.length,
|
movedIndices.length,
|
||||||
@ -114,36 +121,6 @@ export const fixFractionalIndices = (
|
|||||||
return elements as ExcalidrawElement[];
|
return elements as ExcalidrawElement[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateFractionalIndex = (
|
|
||||||
index: FractionalIndex,
|
|
||||||
predecessor: FractionalIndex,
|
|
||||||
successor: FractionalIndex,
|
|
||||||
) => {
|
|
||||||
if (index) {
|
|
||||||
if (!predecessor && !successor) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!predecessor) {
|
|
||||||
// first element in the array
|
|
||||||
// insert before successor
|
|
||||||
return generateKeyBetween(null, successor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!successor) {
|
|
||||||
// last element in the array
|
|
||||||
// insert after predecessor
|
|
||||||
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) => {
|
const compareStrings = (a: string, b: string) => {
|
||||||
return a < b ? -1 : 1;
|
return a < b ? -1 : 1;
|
||||||
};
|
};
|
||||||
@ -163,6 +140,36 @@ export const orderByFractionalIndex = (allElements: ExcalidrawElement[]) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const restoreFractionalIndex = (
|
||||||
|
index: FractionalIndex,
|
||||||
|
predecessor: FractionalIndex,
|
||||||
|
successor: FractionalIndex,
|
||||||
|
) => {
|
||||||
|
if (index) {
|
||||||
|
if (!predecessor && !successor) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successor && !predecessor) {
|
||||||
|
// first element in the array
|
||||||
|
// insert before successor
|
||||||
|
return generateKeyBetween(null, successor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predecessor && !successor) {
|
||||||
|
// last element in the array
|
||||||
|
// insert after predecessor
|
||||||
|
return generateKeyBetween(predecessor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// both predecessor and successor exist
|
||||||
|
// insert after predecessor
|
||||||
|
return generateKeyBetween(predecessor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateKeyBetween(null, null);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* normalize the fractional indicies of the elements in the given array such that
|
* normalize the fractional indicies of the elements in the given array such that
|
||||||
* every element in the array has a fractional index smaller than its successor's
|
* every element in the array has a fractional index smaller than its successor's
|
||||||
@ -170,7 +177,7 @@ export const orderByFractionalIndex = (allElements: ExcalidrawElement[]) => {
|
|||||||
* note that this function is not pure, it mutates elements whose fractional indicies
|
* note that this function is not pure, it mutates elements whose fractional indicies
|
||||||
* need updating
|
* need updating
|
||||||
*/
|
*/
|
||||||
export const normalizeFractionalIndicies = (
|
export const restoreFractionalIndicies = (
|
||||||
allElements: readonly ExcalidrawElement[],
|
allElements: readonly ExcalidrawElement[],
|
||||||
) => {
|
) => {
|
||||||
let pre = -1;
|
let pre = -1;
|
||||||
@ -186,7 +193,7 @@ export const normalizeFractionalIndicies = (
|
|||||||
!isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
|
!isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const nextFractionalIndex = generateFractionalIndex(
|
const nextFractionalIndex = restoreFractionalIndex(
|
||||||
element.fractionalIndex,
|
element.fractionalIndex,
|
||||||
predecessor,
|
predecessor,
|
||||||
successor,
|
successor,
|
||||||
@ -197,7 +204,6 @@ export const normalizeFractionalIndicies = (
|
|||||||
fractionalIndex: nextFractionalIndex,
|
fractionalIndex: nextFractionalIndex,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("normalizing fractional index", e);
|
|
||||||
normalized.push(element);
|
normalized.push(element);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -209,3 +215,34 @@ export const normalizeFractionalIndicies = (
|
|||||||
|
|
||||||
return normalized;
|
return normalized;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const validateFractionalIndicies = (
|
||||||
|
elements: readonly ExcalidrawElement[],
|
||||||
|
) => {
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
const element = elements[i];
|
||||||
|
const successor = elements[i + 1];
|
||||||
|
|
||||||
|
if (successor) {
|
||||||
|
if (element.fractionalIndex && successor.fractionalIndex) {
|
||||||
|
if (element.fractionalIndex >= successor.fractionalIndex) {
|
||||||
|
console.log(
|
||||||
|
"this is the case",
|
||||||
|
element.fractionalIndex,
|
||||||
|
successor.fractionalIndex,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"this is the other case",
|
||||||
|
element.fractionalIndex,
|
||||||
|
successor.fractionalIndex,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
@ -11,7 +11,10 @@ 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 { fixFractionalIndices } from "../fractionalIndex";
|
import {
|
||||||
|
fixFractionalIndices,
|
||||||
|
validateFractionalIndicies,
|
||||||
|
} from "../fractionalIndex";
|
||||||
import { arrayToMap } from "../utils";
|
import { arrayToMap } from "../utils";
|
||||||
|
|
||||||
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
|
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
|
||||||
@ -240,6 +243,12 @@ class Scene {
|
|||||||
_nextElements = nextElements;
|
_nextElements = nextElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
if (!validateFractionalIndicies(_nextElements)) {
|
||||||
|
console.error("fractional indices consistency has been compromised");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.elements = _nextElements;
|
this.elements = _nextElements;
|
||||||
const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
|
const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];
|
||||||
this.elementsMap.clear();
|
this.elementsMap.clear();
|
||||||
|
@ -4891,10 +4891,10 @@ form-data@^4.0.0:
|
|||||||
combined-stream "^1.0.8"
|
combined-stream "^1.0.8"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
fractional-indexing@3.2.0:
|
fractional-indexing-jittered@0.9.0:
|
||||||
version "3.2.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/fractional-indexing/-/fractional-indexing-3.2.0.tgz#1193e63d54ff4e0cbe0c79a9ed6cfbab25d91628"
|
resolved "https://registry.yarnpkg.com/fractional-indexing-jittered/-/fractional-indexing-jittered-0.9.0.tgz#53a5f05acc4a8f8ceb5cb5a326122deb2cf8105c"
|
||||||
integrity sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==
|
integrity sha512-3XIGQbmuEIg1j/qdHLDVyCH1vNMUyeBhM+5d6Su03fTiHzmQLE5nOqvDXLDgg240lLBIsLyJa3xZIUqO57yrAQ==
|
||||||
|
|
||||||
fs-extra@^11.1.0:
|
fs-extra@^11.1.0:
|
||||||
version "11.1.1"
|
version "11.1.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user