UNPKG

@thi.ng/atom

Version:

Mutable wrappers for nested immutable values with optional undo/redo history and transaction support

161 lines 7.84 kB
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