mobx
Version:
Simple, scalable state management.
232 lines (207 loc) • 7.64 kB
text/typescript
import {
IEnhancer,
IEqualsComparer,
IObservableArray,
IObservableMapInitialValues,
IMapEntries,
IReadonlyMapEntries,
IKeyValueMap,
IObservableSetInitialValues,
IObservableValue,
ObservableMap,
ObservableSet,
ObservableValue,
asDynamicObservableObject,
createObservableArray,
deepEnhancer,
extendObservable,
isES6Map,
isES6Set,
isObservable,
isPlainObject,
referenceEnhancer,
Annotation,
shallowEnhancer,
refStructEnhancer,
AnnotationsMap,
assign,
createObservableAnnotation,
createAutoAnnotation,
initObservable,
decorateObservable20223_
} from "../internal"
import { createDecoratorAnnotation, type DecoratorAnnotation } from "./decoratorannotation"
import type { ClassAccessorAndFieldDecorator } from "../types/decorator_fills"
export const OBSERVABLE = "observable"
export const OBSERVABLE_REF = "observable.ref"
export const OBSERVABLE_SHALLOW = "observable.shallow"
export const OBSERVABLE_STRUCT = "observable.struct"
export type CreateObservableOptions = {
name?: string
equals?: IEqualsComparer<any>
deep?: boolean
defaultDecorator?: Annotation
autoBind?: boolean
}
// Predefined bags of create observable options, to avoid allocating temporarily option objects
// in the majority of cases
export const defaultCreateObservableOptions: CreateObservableOptions = {
deep: true,
name: undefined,
defaultDecorator: undefined
}
Object.freeze(defaultCreateObservableOptions)
export function asCreateObservableOptions(thing: any): CreateObservableOptions {
return thing || defaultCreateObservableOptions
}
const observableAnnotation = createObservableAnnotation(OBSERVABLE)
const observableRefAnnotation = createObservableAnnotation(OBSERVABLE_REF, {
enhancer_: referenceEnhancer
})
const observableShallowAnnotation = createObservableAnnotation(OBSERVABLE_SHALLOW, {
enhancer_: shallowEnhancer
})
const observableStructAnnotation = createObservableAnnotation(OBSERVABLE_STRUCT, {
enhancer_: refStructEnhancer
})
function createObservableDecoratorAnnotation(
annotation: Annotation
): DecoratorAnnotation<ClassAccessorAndFieldDecorator> {
return createDecoratorAnnotation(annotation, decorateObservable20223_)
}
export function getEnhancerFromOptions(options: CreateObservableOptions): IEnhancer<any> {
return options.deep === true
? deepEnhancer
: options.deep === false
? referenceEnhancer
: getEnhancerFromAnnotation(options.defaultDecorator)
}
export function getAnnotationFromOptions(
options?: CreateObservableOptions
): Annotation | undefined {
return options ? options.defaultDecorator ?? createAutoAnnotation(options) : undefined
}
export function getEnhancerFromAnnotation(annotation?: Annotation): IEnhancer<any> {
return !annotation ? deepEnhancer : annotation.options_?.enhancer_ ?? deepEnhancer
}
/**
* Turns an object, array or function into a reactive structure.
* @param v the value which should become observable.
*/
function createObservable(v: any, arg2?: any, arg3?: any) {
if (arg2 && typeof arg2.kind === "string") {
return decorateObservable20223_(observableAnnotation, v, arg2)
}
// already observable - ignore
if (isObservable(v)) {
return v
}
// plain object
if (isPlainObject(v)) {
return observable.object(v, arg2, arg3)
}
// Array
if (Array.isArray(v)) {
return observable.array(v, arg2)
}
// Map
if (isES6Map(v)) {
return observable.map(v, arg2)
}
// Set
if (isES6Set(v)) {
return observable.set(v, arg2)
}
// other object - ignore
if (typeof v === "object" && v !== null) {
return v
}
// anything else
return observable.box(v, arg2)
}
export interface IObservableValueFactory {
<T>(value: T, options?: CreateObservableOptions): IObservableValue<T>
<T>(value?: T, options?: CreateObservableOptions): IObservableValue<T | undefined>
}
export interface IObservableMapFactory {
<K = any, V = any>(): ObservableMap<K, V>
<K, V>(initialValues?: IMapEntries<K, V>, options?: CreateObservableOptions): ObservableMap<
K,
V
>
<K, V>(
initialValues?: IReadonlyMapEntries<K, V>,
options?: CreateObservableOptions
): ObservableMap<K, V>
<K, V>(initialValues?: IKeyValueMap<V>, options?: CreateObservableOptions): ObservableMap<K, V>
<K, V>(initialValues?: Map<K, V>, options?: CreateObservableOptions): ObservableMap<K, V>
<K = any, V = any>(initialValues: undefined, options?: CreateObservableOptions): ObservableMap<
K,
V
>
}
export interface IObservableFactory extends Annotation, ClassAccessorAndFieldDecorator {
<T = any>(value: T[], options?: CreateObservableOptions): IObservableArray<T>
<T = any>(value: Set<T>, options?: CreateObservableOptions): ObservableSet<T>
<K = any, V = any>(value: Map<K, V>, options?: CreateObservableOptions): ObservableMap<K, V>
<T extends object>(
value: T,
annotations?: AnnotationsMap<T, never>,
options?: CreateObservableOptions
): T
box: IObservableValueFactory
array: <T = any>(initialValues?: T[], options?: CreateObservableOptions) => IObservableArray<T>
set: <T = any>(
initialValues?: IObservableSetInitialValues<T>,
options?: CreateObservableOptions
) => ObservableSet<T>
map: IObservableMapFactory
object: <T = any>(
props: T,
annotations?: AnnotationsMap<T, never>,
options?: CreateObservableOptions
) => T
}
const observableFactories: IObservableFactory = {
box<T = any>(value: T, options?: CreateObservableOptions): IObservableValue<T> {
const o = asCreateObservableOptions(options)
return new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
},
array<T = any>(initialValues?: T[], options?: CreateObservableOptions): IObservableArray<T> {
const o = asCreateObservableOptions(options)
return createObservableArray(initialValues, getEnhancerFromOptions(o), o.name)
},
map<K = any, V = any>(
initialValues?: IObservableMapInitialValues<K, V>,
options?: CreateObservableOptions
): ObservableMap<K, V> {
const o = asCreateObservableOptions(options)
return new ObservableMap<K, V>(initialValues, getEnhancerFromOptions(o), o.name)
},
set<T = any>(
initialValues?: IObservableSetInitialValues<T>,
options?: CreateObservableOptions
): ObservableSet<T> {
const o = asCreateObservableOptions(options)
return new ObservableSet<T>(initialValues, getEnhancerFromOptions(o), o.name)
},
object<T extends object = any>(
props: T,
annotations?: AnnotationsMap<T, never>,
options?: CreateObservableOptions
): T {
return initObservable(() =>
extendObservable(asDynamicObservableObject({}, options), props, annotations)
)
}
} as any
export const observableRef = createObservableDecoratorAnnotation(observableRefAnnotation)
export const observableShallow = createObservableDecoratorAnnotation(observableShallowAnnotation)
export const observableDeep = createObservableDecoratorAnnotation(observableAnnotation)
export const observableStruct = createObservableDecoratorAnnotation(observableStructAnnotation)
// eslint-disable-next-line
export var observable: IObservableFactory = assign(
createObservable,
observableAnnotation,
observableFactories
)