@infinite-canvas-tutorial/webcomponents
Version:
WebComponents UI implementation
149 lines • 5.84 kB
JavaScript
/**
* Borrow from https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/change.ts#L62
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isShallowEqual = exports.Delta = void 0;
/**
* Represents the difference between two objects of the same type.
*
* Both `deleted` and `inserted` partials represent the same set of added, removed or updated properties, where:
* - `deleted` is a set of all the deleted values
* - `inserted` is a set of all the inserted (added, updated) values
*
* Keeping it as pure object (without transient state, side-effects, etc.), so we won't have to instantiate it on load.
*/
class Delta {
constructor(deleted, inserted) {
this.deleted = deleted;
this.inserted = inserted;
}
static create(deleted, inserted, modifier, modifierOptions) {
const modifiedDeleted = modifier && modifierOptions !== 'inserted' ? modifier(deleted) : deleted;
const modifiedInserted = modifier && modifierOptions !== 'deleted' ? modifier(inserted) : inserted;
return new Delta(modifiedDeleted, modifiedInserted);
}
/**
* Calculates the delta between two objects.
*
* @param prevObject - The previous state of the object.
* @param nextObject - The next state of the object.
*
* @returns new delta instance.
*/
static calculate(prevObject, nextObject, modifier, postProcess) {
if (prevObject === nextObject) {
return Delta.empty();
}
const deleted = {};
const inserted = {};
// O(n^3) here for elements, but it's not as bad as it looks:
// - we do this only on store recordings, not on every frame (not for ephemerals)
// - we do this only on previously detected changed elements
// - we do shallow compare only on the first level of properties (not going any deeper)
// - # of properties is reasonably small
for (const key of this.distinctKeysIterator('full', prevObject, nextObject)) {
deleted[key] = prevObject[key];
inserted[key] = nextObject[key];
}
const [processedDeleted, processedInserted] = postProcess
? postProcess(deleted, inserted)
: [deleted, inserted];
return Delta.create(processedDeleted, processedInserted, modifier);
}
static empty() {
return new Delta({}, {});
}
static isEmpty(delta) {
return (!Object.keys(delta.deleted).length && !Object.keys(delta.inserted).length);
}
/**
* Iterator comparing values of object properties based on the passed joining strategy.
*
* @yields keys of properties with different values
*
* WARN: it's based on shallow compare performed only on the first level and doesn't go deeper than that.
*/
static *distinctKeysIterator(join, object1, object2, skipShallowCompare = false) {
if (object1 === object2) {
return;
}
let keys = [];
if (join === 'left') {
keys = Object.keys(object1);
}
else if (join === 'right') {
keys = Object.keys(object2);
}
else if (join === 'full') {
keys = Array.from(new Set([...Object.keys(object1), ...Object.keys(object2)]));
}
for (const key of keys) {
const object1Value = object1[key];
const object2Value = object2[key];
if (object1Value !== object2Value) {
if (!skipShallowCompare &&
typeof object1Value === 'object' &&
typeof object2Value === 'object' &&
object1Value !== null &&
object2Value !== null &&
(0, exports.isShallowEqual)(object1Value, object2Value)) {
continue;
}
yield key;
}
}
}
}
exports.Delta = Delta;
/**
* Returns whether object/array is shallow equal.
* Considers empty object/arrays as equal (whether top-level or second-level).
*/
const isShallowEqual = (objA, objB, comparators, debug = false) => {
const aKeys = Object.keys(objA);
const bKeys = Object.keys(objB);
if (aKeys.length !== bKeys.length) {
if (debug) {
console.warn(`%cisShallowEqual: objects don't have same properties ->`, 'color: #8B4000', objA, objB);
}
return false;
}
if (comparators && Array.isArray(comparators)) {
for (const key of comparators) {
const ret = objA[key] === objB[key] ||
_defaultIsShallowComparatorFallback(objA[key], objB[key]);
if (!ret) {
if (debug) {
console.warn(`%cisShallowEqual: ${key} not equal ->`, 'color: #8B4000', objA[key], objB[key]);
}
return false;
}
}
return true;
}
return aKeys.every((key) => {
const comparator = comparators === null || comparators === void 0 ? void 0 : comparators[key];
const ret = comparator
? comparator(objA[key], objB[key])
: objA[key] === objB[key] ||
_defaultIsShallowComparatorFallback(objA[key], objB[key]);
if (!ret && debug) {
console.warn(`%cisShallowEqual: ${key} not equal ->`, 'color: #8B4000', objA[key], objB[key]);
}
return ret;
});
};
exports.isShallowEqual = isShallowEqual;
/** use as a fallback after identity check (for perf reasons) */
const _defaultIsShallowComparatorFallback = (a, b) => {
// consider two empty arrays equal
if (Array.isArray(a) &&
Array.isArray(b) &&
a.length === 0 &&
b.length === 0) {
return true;
}
return a === b;
};
//# sourceMappingURL=Delta.js.map
;