@difizen/mana-observable
Version:
87 lines (86 loc) • 2.74 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ObservableConfig } from "./config";
import { Notifiable } from "./notifiable";
import { Notifier } from "./notifier";
import { InstanceValue, ObservableProperties, Observability } from "./utils";
var propertyRelatedNotifier = Symbol('propertyRelatedNotifier');
// redefine observable properties
export function defineProperty(target, property, defaultValue) {
var notifier = Notifier.getOrCreate(target, property);
/**
* notify notifier when property changed
*/
var onChange = function onChange() {
if (ObservableConfig.paused) {
return;
}
Notifier.trigger(target, property);
};
/**
* set observable property value and register onChange listener
* @param value
* @param notifier
*/
var handleValue = function handleValue(value) {
InstanceValue.set(target, property, value);
if (notifier) {
var last = Observability.getDisposable(propertyRelatedNotifier, notifier);
if (last) {
last.dispose();
}
}
if (Notifiable.is(value)) {
var valueNotifier = Notifiable.getNotifier(value);
if (notifier && valueNotifier) {
var toDispose = valueNotifier.onChangeSync(onChange);
Observability.setDisposable(propertyRelatedNotifier, toDispose, notifier);
}
}
};
var initialValue = target[property] === undefined ? defaultValue : target[property];
handleValue(Notifiable.transform(initialValue));
// property getter
var getter = function getter() {
var value = Reflect.getMetadata(property, target);
return value;
};
// property setter
var setter = function setter(value) {
var notifiableValue = Notifiable.transform(value);
var oldValue = InstanceValue.get(target, property);
if (notifiableValue !== oldValue) {
handleValue(notifiableValue);
onChange();
}
};
// define property
if (Reflect.deleteProperty(target, property)) {
Reflect.defineProperty(target, property, {
configurable: true,
enumerable: true,
get: getter,
set: setter
});
}
// mark observable property
ObservableProperties.add(target, property);
Observability.mark(target, property);
}
export function observable(target) {
if (!Observability.canBeObservable(target)) {
return target;
}
var origin = Observability.getOrigin(target);
var properties = ObservableProperties.find(origin);
if (!properties) {
var notifiableValue = Notifiable.transform(origin);
if (Notifiable.is(notifiableValue)) {
Observability.mark(origin);
}
return notifiableValue;
}
properties.forEach(function (property) {
return defineProperty(origin, property);
});
return origin;
}