@excalidraw/math
Version:
Excalidraw math functions
217 lines (216 loc) • 10.2 kB
TypeScript
import type { ExcalidrawElement, Ordered, OrderedExcalidrawElement, SceneElementsMap } from "@excalidraw/element/types";
import type { DTO, ValueOf } from "@excalidraw/common/utility-types";
import type { AppState, ObservedAppState } from "@excalidraw/excalidraw/types";
import { StoreSnapshot } from "./store";
/**
* 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.
*/
export declare class Delta<T> {
readonly deleted: Partial<T>;
readonly inserted: Partial<T>;
private constructor();
static create<T>(deleted: Partial<T>, inserted: Partial<T>, modifier?: (delta: Partial<T>, partialType: "deleted" | "inserted") => Partial<T>, modifierOptions?: "deleted" | "inserted" | "both"): Delta<T>;
/**
* 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<T extends {
[key: string]: any;
}>(prevObject: T, nextObject: T, modifier?: (partial: Partial<T>) => Partial<T>, postProcess?: (deleted: Partial<T>, inserted: Partial<T>) => [Partial<T>, Partial<T>]): Delta<T>;
static empty(): Delta<unknown>;
static isEmpty<T>(delta: Delta<T>): boolean;
/**
* Merges deleted and inserted object partials.
*/
static mergeObjects<T extends {
[key: string]: unknown;
}>(prev: T, added: T, removed: T): T;
/**
* Merges deleted and inserted array partials.
*/
static mergeArrays<T>(prev: readonly T[] | null, added: readonly T[] | null | undefined, removed: readonly T[] | null | undefined, predicate?: (value: T) => string): T[];
/**
* Diff object partials as part of the `postProcess`.
*/
static diffObjects<T, K extends keyof T, V extends ValueOf<T[K]>>(deleted: Partial<T>, inserted: Partial<T>, property: K, setValue: (prevValue: V | undefined) => V): void;
/**
* Diff array partials as part of the `postProcess`.
*/
static diffArrays<T, K extends keyof T, V extends T[K]>(deleted: Partial<T>, inserted: Partial<T>, property: K, groupBy: (value: V extends ArrayLike<infer T> ? T : never) => string): void;
/**
* Compares if object1 contains any different value compared to the object2.
*/
static isLeftDifferent<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): boolean;
/**
* Compares if object2 contains any different value compared to the object1.
*/
static isRightDifferent<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): boolean;
/**
* Compares if shared properties of object1 and object2 contain any different value (aka inner join).
*/
static isInnerDifferent<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): boolean;
/**
* Compares if any properties of object1 and object2 contain any different value (aka full join).
*/
static isDifferent<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): boolean;
/**
* Returns sorted object1 keys that have distinct values.
*/
static getLeftDifferences<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): string[];
/**
* Returns sorted object2 keys that have distinct values.
*/
static getRightDifferences<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): string[];
/**
* Returns sorted keys of shared object1 and object2 properties that have distinct values (aka inner join).
*/
static getInnerDifferences<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): string[];
/**
* Returns sorted keys that have distinct values between object1 and object2 (aka full join).
*/
static getDifferences<T extends {}>(object1: T, object2: T, skipShallowCompare?: boolean): string[];
/**
* 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.
*/
private static distinctKeysIterator;
}
/**
* Encapsulates a set of application-level `Delta`s.
*/
export interface DeltaContainer<T> {
/**
* Inverses the `Delta`s while creating a new `DeltaContainer` instance.
*/
inverse(): DeltaContainer<T>;
/**
* Applies the `Delta`s to the previous object.
*
* @returns a tuple of the next object `T` with applied `Delta`s, and `boolean`, indicating whether the applied deltas resulted in a visible change.
*/
applyTo(previous: T, ...options: unknown[]): [T, boolean];
/**
* Checks whether all `Delta`s are empty.
*/
isEmpty(): boolean;
}
export declare class AppStateDelta implements DeltaContainer<AppState> {
readonly delta: Delta<ObservedAppState>;
private constructor();
static calculate<T extends ObservedAppState>(prevAppState: T, nextAppState: T): AppStateDelta;
static restore(appStateDeltaDTO: DTO<AppStateDelta>): AppStateDelta;
static empty(): AppStateDelta;
inverse(): AppStateDelta;
applyTo(appState: AppState, nextElements: SceneElementsMap): [AppState, boolean];
isEmpty(): boolean;
/**
* Mutates `nextAppState` be filtering out state related to deleted elements.
*
* @returns `true` if a visible change is found, `false` otherwise.
*/
private filterInvisibleChanges;
private static convertToAppStateKey;
private static filterSelectedElements;
private static filterSelectedGroups;
private static stripElementsProps;
private static stripStandaloneProps;
/**
* It is necessary to post process the partials in case of reference values,
* for which we need to calculate the real diff between `deleted` and `inserted`.
*/
private static postProcess;
private static orderAppStateKeys;
}
type ElementPartial<TElement extends ExcalidrawElement = ExcalidrawElement> = Omit<Partial<Ordered<TElement>>, "id" | "updated" | "seed">;
export type ApplyToOptions = {
excludedProperties: Set<keyof ElementPartial>;
};
/**
* Elements change is a low level primitive to capture a change between two sets of elements.
* It does so by encapsulating forward and backward `Delta`s, allowing to time-travel in both directions.
*/
export declare class ElementsDelta implements DeltaContainer<SceneElementsMap> {
readonly added: Record<string, Delta<ElementPartial>>;
readonly removed: Record<string, Delta<ElementPartial>>;
readonly updated: Record<string, Delta<ElementPartial>>;
private constructor();
static create(added: Record<string, Delta<ElementPartial>>, removed: Record<string, Delta<ElementPartial>>, updated: Record<string, Delta<ElementPartial>>, options?: {
shouldRedistribute: boolean;
}): ElementsDelta;
static restore(elementsDeltaDTO: DTO<ElementsDelta>): ElementsDelta;
private static satisfiesAddition;
private static satisfiesRemoval;
private static satisfiesUpdate;
private static satisfiesCommmonInvariants;
private static validate;
/**
* 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 `ElementsDelta` instance representing the `Delta` changes between the two sets of elements.
*/
static calculate<T extends OrderedExcalidrawElement>(prevElements: Map<string, T>, nextElements: Map<string, T>): ElementsDelta;
static empty(): ElementsDelta;
inverse(): ElementsDelta;
isEmpty(): boolean;
/**
* Update delta/s based on the existing elements.
*
* @param nextElements current elements
* @param modifierOptions defines which of the delta (`deleted` or `inserted`) will be updated
* @returns new instance with modified delta/s
*/
applyLatestChanges(prevElements: SceneElementsMap, nextElements: SceneElementsMap, modifierOptions?: "deleted" | "inserted"): ElementsDelta;
applyTo(elements: SceneElementsMap, snapshot?: StoreSnapshot["elements"], options?: ApplyToOptions): [SceneElementsMap, boolean];
private static createApplier;
private static createGetter;
private static applyDelta;
/**
* Check for visible changes regardless of whether they were removed, added or updated.
*/
private static checkForVisibleDifference;
/**
* Resolves conflicts for all previously added, removed and updated elements.
* Updates the previous deltas with all the changes after conflict resolution.
*
* // TODO: revisit since some bound arrows seem to be often redrawn incorrectly
*
* @returns all elements affected by the conflict resolution
*/
private resolveConflicts;
/**
* Non deleted affected elements of removed elements (before and after applying delta),
* should be unbound ~ bindings should not point from non deleted into the deleted element/s.
*/
private static unbindAffected;
/**
* Non deleted affected elements of added or updated element/s (before and after applying delta),
* should be rebound (if possible) with the current element ~ bindings should be bidirectional.
*/
private static rebindAffected;
private static redrawTextBoundingBoxes;
private static redrawBoundArrows;
private static reorderElements;
/**
* It is necessary to post process the partials in case of reference values,
* for which we need to calculate the real diff between `deleted` and `inserted`.
*/
private static postProcess;
private static stripIrrelevantProps;
}
export {};