refactor code

mrazator/test-fractional-index-and-granular-history
Ryan Di 1 year ago
parent a7154227cf
commit d1a9c593cc

@ -18,7 +18,6 @@ import throttle from "lodash.throttle";
import { newElementWith } from "../../src/element/mutateElement";
import { BroadcastedExcalidrawElement } from "./reconciliation";
import { encryptData } from "../../src/data/encryption";
import { normalizeFractionalIndexing } from "../../src/zindex";
class Portal {
collab: TCollabClass;
@ -160,7 +159,7 @@ class Portal {
const data: SocketUpdateDataSource[typeof updateType] = {
type: updateType,
payload: {
elements: normalizeFractionalIndexing(syncableElements),
elements: syncableElements,
},
};

@ -1,7 +1,7 @@
import { ExcalidrawElement } from "../../src/element/types";
import { AppState } from "../../src/types";
import { arrayToMap, arrayToMapWithIndex } from "../../src/utils";
import { orderByFractionalIndex } from "../../src/zindex";
import { orderByFractionalIndex } from "../../src/fractionalIndex";
export type ReconciledElements = readonly ExcalidrawElement[] & {
_brand: "reconciledElements";

@ -0,0 +1,123 @@
import { mutateElement } from "./element/mutateElement";
import { ExcalidrawElement } from "./element/types";
import { generateKeyBetween } from "fractional-indexing";
type FractionalIndex = ExcalidrawElement["fractionalIndex"];
const isValidFractionalIndex = (
index: FractionalIndex,
predecessor: FractionalIndex,
successor: FractionalIndex,
) => {
if (index) {
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 = (
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) => {
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;
});
};
/**
* 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
*
* note that this function is not pure, it mutates elements whose fractional indicies
* need updating
*/
export const normalizeFractionalIndicies = (
allElements: readonly ExcalidrawElement[],
) => {
let pre = -1;
let suc = 1;
for (const element of allElements) {
const predecessor = allElements[pre]?.fractionalIndex || null;
const successor = allElements[suc]?.fractionalIndex || null;
if (
!isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
) {
try {
const nextFractionalIndex = generateFractionalIndex(
element.fractionalIndex,
predecessor,
successor,
);
mutateElement(
element,
{
fractionalIndex: nextFractionalIndex,
},
false,
);
} catch (e) {
console.error("normalizing fractional index", e);
}
}
pre++;
suc++;
}
return allElements;
};

@ -11,7 +11,7 @@ import { getSelectedElements } from "./selection";
import { AppState } from "../types";
import { Assert, SameType } from "../utility-types";
import { randomInteger } from "../random";
import { normalizeFractionalIndexing } from "../zindex";
import { normalizeFractionalIndicies } from "../fractionalIndex";
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
type ElementKey = ExcalidrawElement | ElementIdKey;
@ -232,7 +232,7 @@ class Scene {
nextElements: readonly ExcalidrawElement[],
mapElementIds = true,
) {
const _nextElements = normalizeFractionalIndexing(nextElements);
const _nextElements = normalizeFractionalIndicies(nextElements);
this.elements = _nextElements;
const nextFrameLikes: ExcalidrawFrameLikeElement[] = [];

@ -1,4 +1,4 @@
import { bumpVersion, mutateElement } from "./element/mutateElement";
import { bumpVersion } from "./element/mutateElement";
import { isFrameLikeElement } from "./element/typeChecks";
import { ExcalidrawElement, ExcalidrawFrameLikeElement } from "./element/types";
import { getElementsInGroup } from "./groups";
@ -6,7 +6,6 @@ import { getSelectedElements } from "./scene";
import Scene from "./scene/Scene";
import { AppState } from "./types";
import { arrayToMap, findIndex, findLastIndex } from "./utils";
import { generateKeyBetween } from "fractional-indexing";
const isOfTargetFrame = (element: ExcalidrawElement, frameId: string) => {
return element.frameId === frameId || element.id === frameId;
@ -486,124 +485,6 @@ function shiftElementsAccountingForFrames(
);
}
// fractional indexing
// -----------------------------------------------------------------------------
type FractionalIndex = ExcalidrawElement["fractionalIndex"];
const isValidFractionalIndex = (
index: FractionalIndex,
predecessor: FractionalIndex,
successor: FractionalIndex,
) => {
if (index) {
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 = (
index: FractionalIndex,
predecessor: FractionalIndex,
successor: FractionalIndex,
) => {
if (index) {
if (!predecessor && !successor) {
return index;
}
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;
});
};
/**
* normalize the fractional indicies of the elements in the given array such that
* a. all elements have a fraction index between floor and ceiling as defined above
* b. for every element, its fractional index is greater than its predecessor's and smaller than its successor's
*/
export const normalizeFractionalIndexing = (
allElements: readonly ExcalidrawElement[],
) => {
let pre = -1;
let suc = 1;
for (const element of allElements) {
const predecessor = allElements[pre]?.fractionalIndex || null;
const successor = allElements[suc]?.fractionalIndex || null;
if (
!isValidFractionalIndex(element.fractionalIndex, predecessor, successor)
) {
try {
const nextFractionalIndex = generateFractionalIndex(
element.fractionalIndex,
predecessor,
successor,
);
mutateElement(
element,
{
fractionalIndex: nextFractionalIndex,
},
false,
);
} catch (e) {
console.error(e);
}
}
pre++;
suc++;
}
return allElements;
};
// public API
// -----------------------------------------------------------------------------

Loading…
Cancel
Save