@thi.ng/atom
Version:
Mutable wrappers for nested immutable values with optional undo/redo history and transaction support
161 lines • 7.84 kB
TypeScript
import type { DeepPath, Event, INotify, Listener, OptPathVal, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, PathVal, Predicate2, Watch } from "@thi.ng/api";
import { type HistoryEventType, type IAtom, type IHistory, type SwapFn } from "./api.js";
export declare const defHistory: <T>(state: IAtom<T>, maxLen?: number, changed?: Predicate2<T>) => History<T>;
/**
* Undo/redo history stack wrapper for atoms and cursors. Implements
* {@link IAtom} interface and so can be used directly in place and delegates to
* wrapped atom/cursor.
*
* @remarks
* Value changes are only recorded in history if `changed` predicate returns
* truthy value, or else by calling {@link History.record} directly. This class
* too implements the
* [`INotify`](https://docs.thi.ng/umbrella/api/interfaces/INotify.html)
* interface to support event listeners for {@link History.undo},
* {@link History.redo} and {@link History.record}.
*/
export declare class History<T> implements IHistory<T>, INotify<HistoryEventType> {
state: IAtom<T>;
maxLen: number;
changed: Predicate2<T>;
history: T[];
future: T[];
/**
* @param state - parent state
* @param maxLen - max size of undo stack
* @param changed - predicate to determine changed values (default `!equiv(a,b)`)
*/
constructor(state: IAtom<T>, maxLen?: number, changed?: Predicate2<T>);
get value(): T;
set value(val: T);
canUndo(): boolean;
canRedo(): boolean;
/**
* Clears history & future stacks
*/
clear(): void;
/**
* Attempts to re-apply most recent historical value to atom and
* returns it if successful (i.e. there's a history).
*
* @remarks
* Before the switch, first records the atom's current value into
* the future stack (to enable {@link History.redo} feature).
* Returns `undefined` if there's no history.
*
* If undo was possible, the `History.EVENT_UNDO` event is emitted
* after the restoration with both the `prev` and `curr` (restored)
* states provided as event value (and object with these two keys).
* This allows for additional state handling to be executed, e.g.
* application of the "Command pattern". See
* {@link History.addListener} for registering event listeners.
*/
undo(): T | undefined;
/**
* Attempts to re-apply most recent value from future stack to atom
* and returns it if successful (i.e. there's a future).
*
* @remarks
* Before the switch, first records the atom's current value into
* the history stack (to enable {@link History.undo} feature).
* Returns `undefined` if there's no future (so sad!).
*
* If redo was possible, the `History.EVENT_REDO` event is emitted
* after the restoration with both the `prev` and `curr` (restored)
* states provided as event value (and object with these two keys).
* This allows for additional state handling to be executed, e.g.
* application of the "Command pattern". See
* {@link History.addListener} for registering event listeners.
*/
redo(): T | undefined;
/**
* `IReset.reset()` implementation. Delegates to wrapped
* atom/cursor, but too applies `changed` predicate to determine if
* there was a change and if the previous value should be recorded.
*
* @param val - replacement value
*/
reset(val: T): T;
resetIn(path: Path0, val: T): T;
resetIn<A>(path: Path1<T, A>, val: PathVal<T, [A]>): T;
resetIn<A, B>(path: Path2<T, A, B>, val: PathVal<T, [A, B]>): T;
resetIn<A, B, C>(path: Path3<T, A, B, C>, val: PathVal<T, [A, B, C]>): T;
resetIn<A, B, C, D>(path: Path4<T, A, B, C, D>, val: PathVal<T, [A, B, C, D]>): T;
resetIn<A, B, C, D, E>(path: Path5<T, A, B, C, D, E>, val: PathVal<T, [A, B, C, D, E]>): T;
resetIn<A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>, val: PathVal<T, [A, B, C, D, E, F]>): T;
resetIn<A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>, val: PathVal<T, [A, B, C, D, E, F, G]>): T;
resetIn<A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>, val: PathVal<T, [A, B, C, D, E, F, G, H]>): T;
resetIn<A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>, val: any): T;
resetInUnsafe(path: Path, val: any): T;
/**
* `ISwap.swap()` implementation. Delegates to wrapped atom/cursor,
* but too applies `changed` predicate to determine if there was a
* change and if the previous value should be recorded.
*
* @param fn - update function
* @param args - additional args passed to `fn`
*/
swap(fn: SwapFn<T, T>, ...args: any[]): T;
swapIn<A>(path: Path0, fn: SwapFn<T, T>, ...args: any[]): T;
swapIn<A>(path: Path1<T, A>, fn: SwapFn<OptPathVal<T, [A]>, PathVal<T, [A]>>, ...args: any[]): T;
swapIn<A, B>(path: Path2<T, A, B>, fn: SwapFn<OptPathVal<T, [A, B]>, PathVal<T, [A, B]>>, ...args: any[]): T;
swapIn<A, B, C>(path: Path3<T, A, B, C>, fn: SwapFn<OptPathVal<T, [A, B, C]>, PathVal<T, [A, B, C]>>, ...args: any[]): T;
swapIn<A, B, C, D>(path: Path4<T, A, B, C, D>, fn: SwapFn<OptPathVal<T, [A, B, C, D]>, PathVal<T, [A, B, C, D]>>, ...args: any[]): T;
swapIn<A, B, C, D, E>(path: Path5<T, A, B, C, D, E>, fn: SwapFn<OptPathVal<T, [A, B, C, D, E]>, PathVal<T, [A, B, C, D, E]>>, ...args: any[]): T;
swapIn<A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>, fn: SwapFn<OptPathVal<T, [A, B, C, D, E, F]>, PathVal<T, [A, B, C, D, E, F]>>, ...args: any[]): T;
swapIn<A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>, fn: SwapFn<OptPathVal<T, [A, B, C, D, E, F, G]>, PathVal<T, [A, B, C, D, E, F, G]>>, ...args: any[]): T;
swapIn<A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>, fn: SwapFn<OptPathVal<T, [A, B, C, D, E, F, G, H]>, PathVal<T, [A, B, C, D, E, F, G, H]>>, ...args: any[]): T;
swapIn<A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>, fn: SwapFn<any, any>, ...args: any[]): T;
swapInUnsafe(path: Path, fn: SwapFn<any, any>, ...args: any[]): T;
/**
* Records given state in history. This method is only needed when
* manually managing snapshots, i.e. when applying multiple swaps on
* the wrapped atom directly, but not wanting to create an history
* entry for each change.
*
* @remarks
* **DO NOT call this explicitly if using {@link History.reset} /
* {@link History.swap} etc.**
*
* If no `state` is given, uses the wrapped atom's current state
* value (user code SHOULD always call without arg).
*
* If recording succeeded, the `History.EVENT_RECORD` event is
* emitted with the recorded state provided as event value.
*
* @param state - state to record
*/
record(state?: T): void;
/**
* Returns wrapped atom's **current** value.
*/
deref(): T;
/**
* `IWatch.addWatch()` implementation. Delegates to wrapped
* atom/cursor.
*
* @param id - watch ID
* @param fn - watch function
*/
addWatch(id: string, fn: Watch<T>): boolean;
/**
* `IWatch.removeWatch()` implementation. Delegates to wrapped
* atom/cursor.
*
* @param id - watch iD
*/
removeWatch(id: string): boolean;
/**
* `IWatch.notifyWatches()` implementation. Delegates to wrapped
* atom/cursor.
*
* @param oldState -
* @param newState -
*/
notifyWatches(oldState: T, newState: T): void;
release(): boolean;
addListener(id: HistoryEventType, fn: Listener<HistoryEventType>, scope?: any): boolean;
removeListener(id: HistoryEventType, fn: Listener<HistoryEventType>, scope?: any): boolean;
notify(e: Event<HistoryEventType>): boolean;
}
//# sourceMappingURL=history.d.ts.map