Skip to content

Instantly share code, notes, and snippets.

@mattiamanzati
Created December 25, 2016 11:05
Show Gist options
  • Save mattiamanzati/34f82da45b2caa28784cabb407c76e6c to your computer and use it in GitHub Desktop.
Save mattiamanzati/34f82da45b2caa28784cabb407c76e6c to your computer and use it in GitHub Desktop.
import {computed, observable, IObservableArray, extras, BaseAtom, _, intercept, IArrayWillChange, IArrayWillSplice, observe, asFlat} from 'mobx'
import {NodeType, NodeFactory, NodePatch} from '../core/interfaces'
import {Node, getNode} from '../core/node'
import {addHiddenFinalProp, createPatcher} from '../core/utils'
interface ObservableArrayAdministration<T>{
values: T[]
atom: BaseAtom
updateArrayLength(oldLength: number, delta: number)
}
const patchArray = <T>(array: T[], patches: NodePatch[]) =>
patches
.reduce(
(value: T[], patch: NodePatch) => {
// get a copy and if array is observable, get rid of it
const source = value.slice()
// handle the relative patching method
switch(patch.type){
case 'update':
source[patch.index] = patch.newValue
return source
case 'splice':
source.splice(patch.index, patch.removedCount, ...patch.added)
return source
}
},
array
)
export class ArrayNode<T> extends Node<T[]>{
public readonly subType: NodeFactory<T>
private adm: ObservableArrayAdministration<T>
@observable private _patches: NodePatch[] = []
constructor(subType: NodeFactory<T>){
super(observable(asFlat([])))
// store the default values
this.value = []
this.subType = subType
// make the array reactive
this.adm = _.getAdministration(this.instance)
intercept(this.instance as IObservableArray<T>, change => {
this._patches.push(change)
return change
})
// update the instance whenever necessary
observe(this.instance as IObservableArray<T>, change => {
this.updateInstanceArray()
})
}
// update the instance
protected updateInstanceArray() {
const patchedArray = patchArray(this.value, this._patches)
// update the observable array and report it has changed.
this.adm.updateArrayLength(this.instance.length, patchedArray.length - this.instance.length)
this.adm.values = patchedArray
this.adm.atom.reportChanged()
}
acceptChanges(){
// TODO: instead of applying all patches at once, perform then one by one awaiting async listeners
let patchedArray = patchArray(this.value, this._patches)
this.value = patchedArray
this.adm.atom.reportChanged()
}
}
// array
export function arrayOf<T>(type: NodeFactory<T>): NodeFactory<T[]>{
return (state?: T[]) =>
(new ArrayNode<T>(type)).instance
}
let rows = []
let rowsBinding = arrayOf(() => ({a: 1}))
const doc = rowsBinding()
// make a change
doc.push({a:2})
console.log(getNode(doc).instance[0], getNode(doc).value[0]) // ==> {a: 2} undefined
// accept changes
console.log('accepting changes...')
getNode(doc).acceptChanges()
console.log(getNode(doc).instance[0], getNode(doc).value[0]) // ==> {a: 2} {a: 2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment