UNPKG

@activejs/core

Version:

Pragmatic, Reactive State Management for JavaScript Apps

468 lines (467 loc) 16.9 kB
import { Observable, Subject } from 'rxjs'; import { ClearCacheOptions, DispatchOptions, DispatchValueProducer, UnitConfig, UnitEvents, UnitStreamObservableProducer } from '../models'; import { Base } from './abstract-base'; import { Stream } from './stream'; /** * UnitBase serves as the base for all the ActiveJS Units: GenericUnit, BoolUnit, ListUnit, etc. * It extends {@link Base}. * * This is an internal construct, normally you'd never have to use this class directly. * However, if you're just reading the documentation, or want to learn more about how ActiveJS works, * or want to extend this class to build something on your own, please continue. * * UnitBase creates the foundation of all the ActiveJS Units. * It implements the features like: * - dispatching, clearing and resetting the value * - caching the dispatched values * - navigating through the cached values, by methods like goBack, goForward, jump etc. * - observable events to listen to including but not limited to the above mentioned actions * - freezing/unfreezing the Unit * - muting/unmuting the Unit * - persisting and retrieving the value to/from persistent-storage * - debouncing the dispatch * - resetting the Unit * - etc. * * @category 2. Abstract */ export declare abstract class UnitBase<T> extends Base<T> { /** * Configured options. * Combination of global-options {@link GlobalUnitConfig} and the options passed on instantiation. */ readonly config: Readonly<UnitConfig<T>>; /** * @internal please do not use. */ protected readonly eventsSubject: Subject<UnitEvents<T>>; /** * On-demand observable events. */ readonly events$: Observable<UnitEvents<T>>; /** * @internal please do not use. */ private _isFrozen; /** * Indicates whether the Unit is frozen or not. * See {@link freeze} for more details. * * Note: It's not the same as [Object.isFrozen](https://cutt.ly/WyFdzPD). */ get isFrozen(): boolean; /** * @internal please do not use. */ private emitOnUnmute; /** * @internal please do not use. */ private _isMuted; /** * Indicates whether the Unit is muted or not. * See {@link mute} for more details. */ get isMuted(): boolean; /** * Indicates whether the value is undefined or not. * * It should be preferred if the Unit is configured to be immutable, as it doesn't create a copy. */ get isEmpty(): boolean; /** * Size of the cache, dictating how many values can be cached at a given time. * * @default `2` * @minimum `1` * @maximum `Infinity` */ readonly cacheSize: number; /** * @internal please do not use. */ protected readonly _cachedValues: T[]; /** * Count of all the cached values. */ get cachedValuesCount(): number; /** * @internal please do not use. */ private _cacheIndex; /** * Index of the current {@link value} in the {@link UnitBase.cachedValues} */ get cacheIndex(): number; /** * @internal please do not use. */ private _initialValue; /** * @internal please do not use. */ protected _value: T; /** * The initialValue provided on instantiation. * Creates a copy if the Unit is configured to be immutable. * * @category Access Value */ initialValue(): T; /** * Current value of the Unit. * Creates a copy if the Unit is configured to be immutable. * * @default * BoolUnit: `false` \ * NumUnit: `0` \ * StringUnit: `''` \ * ListUnit: `[]` \ * DictUnit: `{}` \ * GenericUnit: `undefined` * * @category Access Value */ value(): T; /** * If the Unit has a non-primitive value, * use it to get access to the current {@link value}, without creating a deep-copy. * * This can come in handy if the Unit is configured to be immutable, and you want to perform a non-mutating action * without creating a deep-copy of the value. * * @category Access Value */ rawValue(): T; /** * All the cached values. * Creates a copy if the Unit is configured to be immutable. * * @category Access Value */ cachedValues(): T[]; /** * @internal please do not use. */ private initialValueRaw; /** * @internal please do not use. */ protected defaultValue(): T; /** * @internal please do not use. */ protected constructor(config?: UnitConfig<T>); /** * A helper method that creates a stream by subscribing to the Observable returned by the param `observableProducer` callback. * * Ideally the callback function creates an Observable by applying `Observable.pipe`. * * Just know that you should catch the error in a sub-pipe (ie: do not let it propagate to the main-pipe), otherwise * as usual the stream will stop working, and will not react on any further emissions. * * @param observableProducer A callback function that should return an Observable. * * @category Common */ createStream<R>(observableProducer: UnitStreamObservableProducer<this, R>): Stream; /** * Given a value, this function determines whether it should be dispatched or not. \ * The dispatch is denied in following circumstances: * - If the Unit is frozen. {@link isFrozen} * - If {@link UnitConfig.distinctDispatchCheck} is set to `true`, and the new-value === current-value, * - If {@link UnitConfig.customDispatchCheck} returns a `falsy` value. * * If the Unit is not frozen, you can bypass other dispatch-checks by passing param `force = true`. * * This function is used internally, when a value is dispatched {@link dispatch}. \ * Even initialValue {@link UnitConfig.initialValue} dispatch has to pass this check. * * You can also use it to check if the value will be dispatched or not before dispatching it. * * @param value The value to be dispatched. * @param force Whether dispatch-checks should be bypassed or not. * @returns A boolean indicating whether the param `value` would pass the dispatch-checks if dispatched. * * @category Common Units */ wouldDispatch(value: T, force?: boolean): boolean; dispatch(value: T, options?: DispatchOptions): boolean | undefined; dispatch(valueProducer: DispatchValueProducer<T>, options?: DispatchOptions): boolean | undefined; /** * To manually re-emit the last emitted value again. \ * It doesn't work if the Unit is frozen {@link isFrozen} or muted {@link isMuted}. * * Note: Even if the Unit is immutable, it does not create a copy of the Unit's value, * it merely re-emits the last emitted value. * * @returns `true` if replayed successfully, otherwise `false`. * * @triggers {@link EventReplay} * @category Common */ replay(): boolean; /** * Go back in the cache and re-emit the previous value from the cache, \ * without creating a new entry in the cache. * * It can be used as Undo. * * It doesn't work if the Unit is frozen {@link isFrozen}. * It only works if there's a previously dispatched value in the cache. \ * ie: the {@link cacheIndex} is not 0 * * @returns `true` if the cache-navigation was successful, otherwise `false`. * * @triggers {@link EventUnitJump} * @category Cache Navigation */ goBack(): boolean; /** * After going back in the cache (ie: re-emitting an old value from the cache), \ * use this method to go to the next value, without creating a new entry in the cache. * * It can be used as Redo. * * It doesn't work if the Unit is frozen {@link isFrozen}. * It only works if the current {@link value} is not the last value in the cache. \ * ie: the {@link cacheIndex} is not equal to `cachedValuesCount - 1` * * @returns `true` if the cache-navigation was successful, otherwise `false` * * @triggers {@link EventUnitJump} * @category Cache Navigation */ goForward(): boolean; /** * Use this method to re-emit the first value in the cache, \ * without creating a new entry in the cache. * * It doesn't work if the Unit is frozen {@link isFrozen}. * It only works if the {@link cacheIndex} is not already at the last value in the cache. \ * ie: the {@link cacheIndex} is not 0. * * @returns `true` if the cache-navigation was successful, otherwise `false` * * @triggers {@link EventUnitJump} * @category Cache Navigation */ jumpToStart(): boolean; /** * After going back in the cache (ie: re-emitting an old value from the cache), \ * use this method to re-dispatch the last (latest) value in the cache, \ * without creating a new entry in the cache. * * It doesn't work if the Unit is frozen {@link isFrozen}. * It only works if the {@link cacheIndex} is not already at the last value in the cache. * * @returns `true` if the cache-navigation was successful, otherwise `false` * * @triggers {@link EventUnitJump} * @category Cache Navigation */ jumpToEnd(): boolean; /** * Use this method to re-emit a value from the cache, by jumping specific steps backwards or forwards, \ * without creating a new entry in the cache. * * It doesn't work if the Unit is frozen {@link isFrozen} or `steps` is not a `number`. * It only works if the new calculated index is in the bounds of {@link cachedValues}, \ * ie: the new-index is >= 0, and less than {@link cachedValuesCount}, but \ * not equal to current {@link cacheIndex}. * * @param steps Number of steps to jump in the cache, negative to jump backwards, positive to jump forwards * @returns `true` if the cache-navigation was successful, otherwise `false`. * * @triggers {@link EventUnitJump} * @category Cache Navigation */ jump(steps: number): boolean; /** * Get cached value at a given index. * * @param index The index of cached value * @returns The cached value if it exists, otherwise undefined * * @category Common Units */ getCachedValue(index: number): T | undefined; /** * Clears the cached values, current {@link value} stays intact, but it gets removed from the cache. \ * Meaning, if you dispatch a new value you can't {@link goBack}. \ * To keep the last value in the cache, pass `{leaveLast: true}` in the param `options`. * * It only works if the Unit is not frozen and there's something left to clear after evaluating the param `options`. * * Similar to preserving the last value, you can preserve the first value by passing `{leaveFirst: true}`. * Or preserve both first and last value by passing both options together. * * @param options Clear cache options * @returns `true` if the cache was cleared, otherwise `false` * * @triggers {@link EventUnitClearCache} * @category Common Units */ clearCache(options?: ClearCacheOptions): boolean; /** * Clears the value by dispatching the default value. \ * It only works if the Unit is not frozen, and {@link emitCount} is not 0, and value is not empty {@link isEmpty}. * * @returns `true` if the value was cleared, otherwise `false` * * @triggers {@link EventUnitClearValue} * @category Common Units */ clearValue(): boolean; /** * Holistically clears the Unit, \ * unfreezes using {@link unfreeze}, \ * clears the value using {@link clearValue}, \ * completely clears cache using {@link clearCache}, \ * in that specific order. * * @param options Clear cache options for {@link clearCache}. * * @triggers {@link EventUnitClear} * @category Common Units */ clear(options?: ClearCacheOptions): void; /** * Resets the value by dispatching the initial-value, {@link UnitConfig.initialValue} if provided, \ * otherwise dispatches the default value. * * It only works if the Unit is not frozen, \ * and the {@link value} is not equal to the {@link initialValue}. * * @returns `true` if the reset was successful, otherwise `false` * * @triggers {@link EventUnitResetValue} * @category Common Units */ resetValue(): boolean; /** * Holistically resets the Unit, \ * unfreezes using {@link unfreeze}, \ * resets the value using {@link resetValue}, \ * clears cache using {@link clearCache} and by default leaves last value; \ * in that specific order. * * @param options Clear cache options for {@link clearCache}. default is `{leaveLast: true}` * * @triggers {@link EventUnitReset} * @category Common Units */ reset(options?: ClearCacheOptions): void; /** * Temporarily disables most of the functions of the Unit, except {@link unfreeze}, \ * {@link mute}/{@link unmute}, {@link clear} and {@link reset}. * * It's not the same as `Object.freeze`. * * Freezing prevents any new values from getting dispatched, \ * it disables all the mutating functions. \ * Which eventually ensures that no event is emitted while the Unit is frozen, \ * however all the read operations and operations that do not emit a value are allowed. * * @triggers {@link EventUnitFreeze} * @category Common Units */ freeze(): void; /** * Unfreezes the Unit, and re-enables all the functions disabled by {@link freeze}. \ * It only works if the Unit is frozen. * * @triggers {@link EventUnitUnfreeze} * @category Common Units */ unfreeze(): void; /** * Mute the Unit, to stop emitting values as well as events, so that the subscribers are not triggered. \ * All other functionalities stay unaffected. ie: cache it still updated, value is still updated. * * Note: If you subscribe to the default Observable while the Unit is muted, \ * it will replay the last value emitted before muting the Unit, \ * because new values are not being emitted. * * @category Common Units */ mute(): void; /** * Unmute the Unit, to resume emitting values, and events. \ * If a value was dispatched while the Unit was muted, the most recent value immediately gets emitted, \ * so that subscribers can be in sync again. \ * However, other {@link events$} are lost, and they will only emit on the next event. * * It only works if the Unit is muted. \ * Moreover, it works even if the Unit is frozen, \ * but no value will be emitted because no values would have been dispatched while the Unit was frozen. * * @triggers {@link EventUnitUnmute} * @category Common Units */ unmute(): void; /** * Clears persisted value from persistent storage. \ * It doesn't turn off persistence, future values will get persisted again. * * It only works if the Unit is configured to be persistent. ie: `options.persistent` is true. * * @returns `true` if the Unit is configured to be persistent, otherwise `false`. * * @triggers {@link EventUnitClearPersistedValue} * @category Common Units */ clearPersistedValue(): boolean; /** * @internal please do not use. */ protected abstract isValidValue(value: any): boolean; /** * @internal please do not use. */ protected deepCopyMaybe<U>(o: U): U; /** * @internal please do not use. */ protected checkSerializabilityMaybe(o: any): void; /** * @internal please do not use. */ protected updateValueAndCache(value: T, options?: DispatchOptions, skipCache?: boolean): void; /** * @internal please do not use. */ protected applyFallbackValue(value: T): T; /** * @internal please do not use. */ private distinctCheck; /** * @internal please do not use. */ private dispatchMiddleware; /** * @internal please do not use. */ private dispatchActual; /** * @internal please do not use. */ private shouldDispatchInitialValue; /** * @internal please do not use. */ private updateCache; /** * @internal please do not use. */ private dispatchInitialValue; /** * @internal please do not use. */ private updateValueInPersistentStorage; /** * @internal please do not use. */ private restoreValueFromPersistentStorage; }