|
@@ -0,0 +1,75 @@
|
|
|
|
+interface UnsubscribeFunction {
|
|
|
|
+ ():void
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+interface SubscribeFunction {
|
|
|
|
+ (subscriber:Function):UnsubscribeFunction
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export interface Pub {
|
|
|
|
+ subscribe: SubscribeFunction
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const createPub = (state:object={}): Pub => {
|
|
|
|
+ if (typeof state?.subscribe === 'function' || state instanceof Promise) return state
|
|
|
|
+
|
|
|
|
+ let subscribers:Function[] = []
|
|
|
|
+ const childUnsubcribers = {}
|
|
|
|
+ const runSubscribers = (prop:string|symbol , oldValue, newValue) => subscribers.forEach(subscriber => subscriber(state, prop, oldValue, newValue))
|
|
|
|
+ const subscribe = (subscriber:Function):UnsubscribeFunction => (subscribers.push(subscriber),
|
|
|
|
+ () => {
|
|
|
|
+ subscribers = subscribers.filter(sb => sb !== subscriber)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ state.subscribe = subscribe
|
|
|
|
+
|
|
|
|
+ const proxy = new Proxy(state, {
|
|
|
|
+ get(obj:object, prop:string|symbol){
|
|
|
|
+ return obj[prop]
|
|
|
|
+ },
|
|
|
|
+ deleteProperty(obj:object, prop:string|symbol){
|
|
|
|
+ runSubscribers(prop, obj[prop], undefined)
|
|
|
|
+ if (childUnsubcribers[prop]){
|
|
|
|
+ childUnsubcribers[prop]()
|
|
|
|
+ delete childUnsubcribers[prop]
|
|
|
|
+ }
|
|
|
|
+ delete obj[prop]
|
|
|
|
+ },
|
|
|
|
+ set(obj, prop, value){
|
|
|
|
+ if (prop === 'subscribe') throw new SyntaxError('You cannot substitute subscribe to other value')
|
|
|
|
+ const oldValue = obj[prop]
|
|
|
|
+ if (oldValue === value && ((typeof value !== 'object') || (oldValue && oldValue.subscribe === 'function'))) return true
|
|
|
|
+
|
|
|
|
+ if (oldValue && childUnsubcribers[prop]){
|
|
|
|
+ childUnsubcribers[prop]()
|
|
|
|
+ delete childUnsubcribers[prop]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (value && typeof value === 'object' && !childUnsubcribers[prop]){
|
|
|
|
+ value = createPub(value)
|
|
|
|
+ if (!(value instanceof Promise)){
|
|
|
|
+ // console.log('save child unsubscriber for', prop, childUnsubcribers)
|
|
|
|
+ childUnsubcribers[prop] = value.subscribe(value => runSubscribers(prop, null, value))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ obj[prop] = value
|
|
|
|
+ runSubscribers(prop, oldValue, value)
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ for (const key in state){
|
|
|
|
+
|
|
|
|
+ if (state[key] && typeof state[key] === 'object'){
|
|
|
|
+ proxy[key] = state[key]
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return proxy
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export default createPub;
|
|
|
|
+
|