/* eslint-disable @typescript-eslint/no-explicit-any */ import { observable as mobxObservable, computed as mobxComputed, IObservableValue as mobxIObservableValue, isComputed as mobxIsComputed, IComputedValueOptions as mobxIComputedValueOptions, isBoxedObservable as mobxIsObservableValue, isObservableArray as mobxIsObservableArray, isObservableMap as mobxIsObservableMap, isObservableSet as mobxIsObservableSet, runInAction as mobxRunInAction, reaction as mobxReaction, CreateObservableOptions as mobxCreateObservableOptions, IObservableSetInitialValues as mobxIObservableSetInitialValues, IReactionPublic as mobxIReactionPublic, IMapEntries as mobxIMapEntries } from "mobx"; import { observable as jpfObservable, element } from "jpf"; function setDefaultOptions(target: TOptions, source: TOptions): TOptions { if (source) { target = target || {} as TOptions; Object.keys(source).forEach((key) => { if (!target.hasOwnProperty(key)) { target[key] = source[key]; } }); } return target; } export function runInAction(fn: () => T): T { return mobxRunInAction(fn); } export type IComputed = jpfObservable.IComputed; export type ISubcription = jpfObservable.ISubscription; export type IObservableArray = jpfObservable.IObservableArray; export type IObservableMap = jpfObservable.IObservableMap; export type IObservableSet = jpfObservable.IObservableSet; export type IObservableValue = jpfObservable.IObservableValue; export type ISubscribable = jpfObservable.ISubscribable; export function unwrap(value: TValue | jpfObservable.ISubscribable): TValue { if (mobxIsComputed(value) || mobxIsObservableValue(value)) { return (value as mobxIObservableValue).get(); } if (mobxIsObservableArray(value)) { return value.slice() as unknown as TValue; } if (mobxIsObservableMap(value)) { return new Map(value) as unknown as TValue; } if (mobxIsObservableSet(value)) { return new Set(value) as unknown as TValue; } return value as TValue; } export function isComputed(value: unknown): value is jpfObservable.IComputed { return mobxIsComputed(value); } export function isObservableValue(value): value is jpfObservable.IObservableValue { return mobxIsObservableValue(value); } export function isObservableArray(value): value is jpfObservable.IObservableArray { return mobxIsObservableArray(value); } export function isObservableMap(value): value is jpfObservable.IObservableMap { return mobxIsObservableMap(value); } export function isObservableSet(value): value is jpfObservable.IObservableSet { return mobxIsObservableSet(value); } export function isSubscribable(value: unknown): value is ISubscribable { return isComputed(value) || isObservableValue(value) || isObservableArray(value) || isObservableMap(value) || isObservableSet(value); } export function computed(func: () => TComputed, options?: mobxIComputedValueOptions): jpfObservable.IComputed { const computed = mobxComputed(func, options); const iComputed = computed as unknown as jpfObservable.IComputed; iComputed.subscribe = (effect: (arg: TComputed, prev: TComputed, r: mobxIReactionPublic) => void) => { element.subscriptionCounterIncrement(); const disposer = mobxReaction( () => { return unwrap(iComputed); }, (value: TComputed, prev: TComputed, r) => { effect(value, prev, r); } ); return { dispose: () => { disposer(); } } } return iComputed; } export function observableValue(value?: TValue | null, setter?: (value: TSetter, observableValue: jpfObservable.IObservableValue) => void, options?: mobxCreateObservableOptions): jpfObservable.IObservableValue { //Make sure deep is set to false if not provided setDefaultOptions(options, { deep: false }); const observableValue = mobxObservable.box(value || null, options) as unknown as jpfObservable.IObservableValue; observableValue.setValue = (newValue: TSetter) => { if (newValue === undefined) { newValue = null; } if (setter) { setter(newValue, observableValue); } else { observableValue["set"](newValue); } }; observableValue.setValueInAction = (newValue: TSetter | null) => { runInAction(() => { observableValue.setValue(newValue); }); }; observableValue.setInternal = (value: TValue | null) => { if (value === undefined) { value = null; } observableValue["set"](value); }; observableValue.setInternalInAction = (value: TValue | null) => { runInAction(() => { observableValue.setInternal(value); }); }; observableValue.subscribe = (effect: (arg: TValue, prev: TValue, r: mobxIReactionPublic) => void) => { element.subscriptionCounterIncrement(); const disposer = mobxReaction( () => { return unwrap(observableValue); }, (value: TValue, prev: TValue, r) => { effect(value, prev, r); } ); return { dispose: () => { disposer(); } } }; return observableValue; } export function observableArray>(initialValues?: Array | null, setter?: (value: TSetter, observableArray: jpfObservable.IObservableArray) => void, options?: mobxCreateObservableOptions): jpfObservable.IObservableArray { //Make sure deep is set to false if not provided options = setDefaultOptions(options, { deep: false }); const observableArray = mobxObservable.array(initialValues || null, options) as unknown as jpfObservable.IObservableArray; observableArray.setItems = (newValue: TSetter) => { if (newValue === undefined) { newValue = [] as unknown as TSetter; } if (setter) { setter(newValue, observableArray); } else { observableArray["replace"](newValue); } }; observableArray.setItemsInAction = (newValue: TSetter) => { runInAction(() => { observableArray.setItems(newValue); }); }; observableArray.setInternal = observableArray["replace"]; observableArray.setInternalInAction = (items: Array) => { runInAction(() => { observableArray.setInternal(items); }); }; observableArray.get = observableArray["toJSON"]; observableArray.subscribe = (effect: (arg: Array, prev: Array, r: mobxIReactionPublic) => void) => { element.subscriptionCounterIncrement(); const disposer = mobxReaction( () => { return unwrap(observableArray); }, (value: Array, prev: Array, r) => { effect(value, prev, r); } ); return{ dispose: () => { disposer(); } } }; return observableArray; } export function observableMap(initialValues?: mobxIMapEntries, options?: mobxCreateObservableOptions): jpfObservable.IObservableMap { //Add a deep property if the property does not already exist const defaultOptions = { deep: false }; options = { ...defaultOptions, ...options }; return mobxObservable.map(initialValues, options); } export function observableSet(initialValues?: mobxIObservableSetInitialValues, options?: mobxCreateObservableOptions): jpfObservable.IObservableSet { //Add a deep property if the property does not already exist const defaultOptions = { deep: false }; options = { ...defaultOptions, ...options }; return mobxObservable.set(initialValues, options); } const observable = jpfObservable.observable; //Overwrite jpf observable implementation with mobx implementation observable.unwrap = unwrap; observable.isComputed = isComputed; observable.isObservableValue = isObservableValue; observable.isObservableArray = isObservableArray; observable.isObservableMap = isObservableMap; observable.isObservableSet = isObservableSet; observable.computed = computed observable.observableValue = observableValue; observable.observableArray = observableArray; observable.observableMap = observableMap; observable.observableSet = observableSet;