-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
109 lines (96 loc) · 2.86 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const assert = require('assert')
const nanobus = require('nanobus')
const clone = require('clone')
const { diff } = require('just-diff')
const Component = require('./component')
module.exports = class HyperC {
constructor (selector) {
assert.ok(typeof selector === 'string' || typeof selector === 'object', 'hyperc selector should be type String or HTMLElement')
if (typeof selector === 'string') {
selector = document.querySelector(selector)
}
this.stage = new window.createjs.Stage(selector)
this.stage.name = 'HyperC Root Stage'
this.emitter = nanobus('hyperc.emit')
this.state = {}
this._views = []
var self = this
document.addEventListener('DOMContentLoaded', () => {
self.doDiff()
})
this.emitter.on('render', () => {
self.doDiff()
})
window.hyperc = self
}
use (cb) {
assert.strictEqual(typeof cb, 'function', 'hyperc.use: cb should be type function')
cb(this.state, this.emitter, this)
}
render (Cls) {
assert(Cls.prototype instanceof Component, 'render only accepts classes inherited Component')
var container = new window.createjs.Container()
this.stage.addChild(container)
this._views.push({
Component: Cls,
container,
components: {}
})
}
doDiff () {
var results
// do diffing seperately for each view
for (var view of this._views) {
// slice items from state with static getItems call
var items = view.Component.getItems(this.state)
var initialItems = []
if (typeof items === 'object') initialItems = {}
if (!view.prevItems) {
results = diff(initialItems, items)
} else {
results = diff(view.prevItems, items)
}
view.prevItems = clone(items)
for (var patch of results) {
this.applyPatch(patch, view)
}
}
// render updates
this.update()
}
applyPatch (patch, view) {
var self = this
var idx = patch.path[0]
var item = view.Component.getItems(this.state)[idx]
var component
if (patch.op === 'add') {
if (patch.path.length > 1) {
patch.op = 'replace'
} else {
component = new view.Component(this.state, (eventName, data) => {
self.emitter.emit(eventName, data)
}, item)
component.container.name = `${view.Component.name}-${idx}`
view.container.addChild(component.container)
view.components[idx] = component
component.create(item)
}
}
if (patch.op === 'remove') {
if (patch.path.length > 2) {
patch.op = 'replace'
} else {
component = view.components[idx]
view.container.removeChild(component.container)
delete view.components[idx]
}
}
if (patch.op === 'replace') {
component = view.components[idx]
component.update(item, patch)
}
}
update () {
this.stage.update()
}
}