@infinite-canvas-tutorial/webcomponents
Version:
WebComponents UI implementation
190 lines • 8.57 kB
JavaScript
/**
* Borrow from https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/change.ts#L399
*/
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementsChange = void 0;
const Delta_1 = require("./Delta");
class ElementsChange {
static empty() {
return ElementsChange.create(new Map(), new Map(), new Map());
}
static stripIrrelevantProps(partial) {
const { id, version, versionNonce } = partial, strippedPartial = __rest(partial, ["id", "version", "versionNonce"]);
return strippedPartial;
}
/**
* Calculates the `Delta`s between the previous and next set of elements.
*
* @param prevElements - Map representing the previous state of elements.
* @param nextElements - Map representing the next state of elements.
*
* @returns `ElementsChange` instance representing the `Delta` changes between the two sets of elements.
*/
static calculate(prevElements, nextElements) {
if (prevElements === nextElements) {
return ElementsChange.empty();
}
const added = new Map();
const removed = new Map();
const updated = new Map();
// this might be needed only in same edge cases, like during collab, when `isDeleted` elements get removed or when we (un)intentionally remove the elements
for (const prevElement of prevElements.values()) {
const nextElement = nextElements.get(prevElement.id);
if (!nextElement) {
const deleted = Object.assign(Object.assign({}, prevElement), { isDeleted: false });
const inserted = { isDeleted: true };
const delta = Delta_1.Delta.create(deleted, inserted, ElementsChange.stripIrrelevantProps);
removed.set(prevElement.id, delta);
}
}
for (const nextElement of nextElements.values()) {
const prevElement = prevElements.get(nextElement.id);
if (!prevElement) {
const deleted = { isDeleted: true };
const inserted = Object.assign(Object.assign({}, nextElement), { isDeleted: false });
const delta = Delta_1.Delta.create(deleted, inserted, ElementsChange.stripIrrelevantProps);
added.set(nextElement.id, delta);
continue;
}
if (prevElement.versionNonce !== nextElement.versionNonce) {
const delta = Delta_1.Delta.calculate(prevElement, nextElement, ElementsChange.stripIrrelevantProps);
if (
// making sure we don't get here some non-boolean values (i.e. undefined, null, etc.)
typeof prevElement.isDeleted === 'boolean' &&
typeof nextElement.isDeleted === 'boolean' &&
prevElement.isDeleted !== nextElement.isDeleted) {
// notice that other props could have been updated as well
if (prevElement.isDeleted && !nextElement.isDeleted) {
added.set(nextElement.id, delta);
}
else {
removed.set(nextElement.id, delta);
}
continue;
}
// making sure there are at least some changes
if (!Delta_1.Delta.isEmpty(delta)) {
updated.set(nextElement.id, delta);
}
}
}
return ElementsChange.create(added, removed, updated);
}
static create(added, removed, updated, options = { shouldRedistribute: false }) {
let change;
if (options.shouldRedistribute) {
const nextAdded = new Map();
const nextRemoved = new Map();
const nextUpdated = new Map();
const deltas = [...added, ...removed, ...updated];
for (const [id, delta] of deltas) {
if (this.satisfiesAddition(delta)) {
nextAdded.set(id, delta);
}
else if (this.satisfiesRemoval(delta)) {
nextRemoved.set(id, delta);
}
else {
nextUpdated.set(id, delta);
}
}
change = new ElementsChange(nextAdded, nextRemoved, nextUpdated);
}
else {
change = new ElementsChange(added, removed, updated);
}
return change;
}
constructor(added, removed, updated) {
this.added = added;
this.removed = removed;
this.updated = updated;
}
inverse() {
const inverseInternal = (deltas) => {
const inversedDeltas = new Map();
for (const [id, delta] of deltas.entries()) {
inversedDeltas.set(id, Delta_1.Delta.create(delta.inserted, delta.deleted));
}
return inversedDeltas;
};
const added = inverseInternal(this.added);
const removed = inverseInternal(this.removed);
const updated = inverseInternal(this.updated);
// notice we inverse removed with added not to break the invariants
return ElementsChange.create(removed, added, updated);
}
/**
* Update delta/s based on the existing elements.
*
* @param elements current elements
* @param modifierOptions defines which of the delta (`deleted` or `inserted`) will be updated
* @returns new instance with modified delta/s
*/
applyLatestChanges(elements) {
const modifier = (element) => (partial) => {
// (element: OrderedExcalidrawElement) => (partial: ElementPartial) => {
const latestPartial = {};
for (const key of Object.keys(partial)) {
// do not update following props:
// - `boundElements`, as it is a reference value which is postprocessed to contain only deleted/inserted keys
switch (key) {
// case 'boundElements':
// latestPartial[key] = partial[key];
// break;
default:
latestPartial[key] = element[key];
}
}
return latestPartial;
};
const applyLatestChangesInternal = (deltas) => {
const modifiedDeltas = new Map();
for (const [id, delta] of deltas.entries()) {
const existingElement = elements.get(id);
if (existingElement) {
const modifiedDelta = Delta_1.Delta.create(delta.deleted, delta.inserted, modifier(existingElement), 'inserted');
modifiedDeltas.set(id, modifiedDelta);
}
else {
modifiedDeltas.set(id, delta);
}
}
return modifiedDeltas;
};
const added = applyLatestChangesInternal(this.added);
const removed = applyLatestChangesInternal(this.removed);
const updated = applyLatestChangesInternal(this.updated);
return ElementsChange.create(added, removed, updated, {
shouldRedistribute: true, // redistribute the deltas as `isDeleted` could have been updated
});
}
applyTo(elements, snapshot) {
const nextElements = new Map(elements);
return [nextElements, false];
}
isEmpty() {
return (this.added.size === 0 &&
this.removed.size === 0 &&
this.updated.size === 0);
}
}
exports.ElementsChange = ElementsChange;
ElementsChange.satisfiesAddition = ({ deleted, inserted, }) =>
// dissallowing added as "deleted", which could cause issues when resolving conflicts
deleted.isDeleted === true && !inserted.isDeleted;
ElementsChange.satisfiesRemoval = ({ deleted, inserted, }) => !deleted.isDeleted && inserted.isDeleted === true;
ElementsChange.satisfiesUpdate = ({ deleted, inserted, }) => !!deleted.isDeleted === !!inserted.isDeleted;
//# sourceMappingURL=ElementsChange.js.map
;