wallace 0.0.2 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/component.js DELETED
@@ -1,372 +0,0 @@
1
- import {KeyedPool, InstancePool, SequentialPool} from './pool'
2
- import {buildComponent, createComponent} from './utils'
3
- import {und, makeEl} from './helpers'
4
- import mountie from './mountie'
5
- import {Wrapper} from './wrapper'
6
- import {Lookup} from './lookup'
7
-
8
- const noop = function() {}
9
-
10
- /**
11
- * Represents a component.
12
- */
13
- export function Component(parent) {
14
- const s = this
15
- s.parent = parent // The parent component
16
- s.props = undefined // The props passed to the component. May be changed.
17
-
18
- // These will be set during build
19
- s.e = null // the element
20
- s.el = null // the named wrappers
21
-
22
- // Internal state objects
23
- s.__nv = [] // Nested components
24
- s.__ov = {} // The old values for watches to compare against
25
- }
26
-
27
-
28
-
29
- Component.extend = function(opts) {
30
- var base = opts._base || Component
31
- // TODO: do we care about allowing this?
32
- // var NewComponent = opts.hasOwnProperty('constructor') ? opts.constructor : function(parent) {
33
-
34
- // TODO: This ends up being the name of the function, used in logging etc.
35
- // Maybe change with Object.defineProperty() or use object hack {name: func....}
36
- var CustomComponent = function(parent) {
37
- base.call(this, parent)
38
- }
39
- delete opts._base
40
- CustomComponent.prototype = Object.create(base && base.prototype, {
41
- constructor: { value: CustomComponent, writable: true, configurable: true }
42
- })
43
- Object.assign(CustomComponent.prototype, opts)
44
- return CustomComponent
45
- }
46
-
47
-
48
- var proto = Component.prototype
49
-
50
- proto.onUpdate = noop
51
- proto.afterUpdate = noop
52
- proto.onInit = noop
53
- proto.afterInit = noop
54
-
55
- /**
56
- * Gets called once immediately after building.
57
- * Sets initial props extracted from __html__.
58
- * Note there is an issue here, in that we rely on there being initial props to call init
59
- * on nested components.
60
- */
61
- proto.init = function() {
62
- this.onInit()
63
- for (let key in this.__ip) {
64
- let nestedComponent = this.el[key]
65
- let callback = this.__ip[key]
66
- if (callback) {
67
- nestedComponent.props = callback(this, this.props)
68
- }
69
- nestedComponent.init()
70
- }
71
- this.afterInit()
72
- }
73
- /**
74
- * Calls a function somewhere up the parent tree.
75
- */
76
- proto.bubble = function(name) {
77
- let target = this.parent
78
- while (target) {
79
- // console.log(target.name)
80
- let func = target[name]
81
- if (func) {
82
- // We don't really care about performance here, so accessing arguments is fine.
83
- // TODO: maybe we do care, so pass as array? Or use proxy?
84
- return func.apply(target, Array.prototype.slice.call(arguments, 1))
85
- }
86
- target = target.parent
87
- }
88
- throw new Error('Bubble popped.')
89
- }
90
- /**
91
- * Move the component to new parent. Necessary if sharing a pool.
92
- */
93
- proto.move = function(newParent) {
94
- if (this.parent && this.parent.__nv) {
95
- const nv = this.parent.__nv
96
- nv.splice(nv.indexOf(this), 1)
97
- }
98
- this.parent = newParent
99
- }
100
- /**
101
- * Builds a nested component of the specified class. Its up to you how you use it.
102
- */
103
- proto.nest = function(cls, props) {
104
- const child = createComponent(cls, this, props || this.props)
105
- this.__nv.push(child)
106
- return child
107
- }
108
- /**
109
- * Lookup a watched value during update. Returns an object with {o, n, c}
110
- * (oldValue, newValue, changed).
111
- * You must call this.resetLookups before calling this during an update.
112
- * The point is to pool the result so it doesn't have to be repeated.
113
- */
114
- proto.lookup = function(query) {
115
- return this.__qc.get(this, query)
116
- }
117
- /**
118
- * Resets the lookups, must be called before calling this.lookup() during an update.
119
- */
120
- proto.resetLookups = function() {
121
- this.__qc.reset()
122
- }
123
- /**
124
- * Sets the props and updates the component.
125
- */
126
- proto.setProps = function(props) {
127
- this.props = props
128
- this.update()
129
- return this
130
- }
131
- /**
132
- * Call this if you want to get mount() and unmount() callbacks.
133
- */
134
- proto.trackMounting = function() {
135
- this.__mt.track(this)
136
- }
137
- /**
138
- * Updates the component.
139
- */
140
- proto.update = function() {
141
- this.onUpdate()
142
- this.resetLookups()
143
- this.updateSelf()
144
- this.updateNested()
145
- this.afterUpdate()
146
- }
147
- /**
148
- * Loops over watches skipping shielded watches if elements are hidden.
149
- */
150
- proto.updateSelf = function() {
151
- let i = 0, watch, wrapper, shieldCount, shieldQuery, shieldQueryResult, shouldBeVisible
152
- const watches = this.__wc
153
- const il = watches.length
154
- while (i < il) {
155
- watch = watches[i]
156
- wrapper = this.el[watch.wk]
157
- shieldQuery = watch.sq
158
- i ++
159
- shouldBeVisible = true
160
- if (shieldQuery) {
161
- // Get the newValue for shieldQuery using lookup
162
- shieldQueryResult = !!this.lookup(shieldQuery).n
163
-
164
- // Determine if shouldBeVisible based on reverseShield
165
- // i.e. whether "shieldQuery===true" means show or hide.
166
- shouldBeVisible = watch.rv ? shieldQueryResult : !shieldQueryResult
167
-
168
- // The number of watches to skip if this element is not visible
169
- shieldCount = shouldBeVisible ? 0 : watch.sc
170
-
171
- // Set the element visibility
172
- wrapper.hidden(!shouldBeVisible)
173
- i += shieldCount
174
- }
175
- if (shouldBeVisible) {
176
- applyWatchCallbacks(this, wrapper, watch.cb)
177
- }
178
- }
179
- }
180
- /**
181
- * Update nested components (but not repeat elements).
182
- */
183
- proto.updateNested = function() {
184
- // These are user created by calling nest()
185
- const items = this.__nv
186
- for (let i=0, il=items.length; i<il; i++) {
187
- let child = items[i]
188
- if (child.__ia()) {
189
- child.update()
190
- }
191
- }
192
- // These are created with directives, and whose props arguments may need reprocessed.
193
- for (let key in this.__ip) {
194
- let callback = this.__ip[key]
195
- let nestedComponent = this.el[key]
196
- if (callback) {
197
- nestedComponent.setProps(callback(this, this.props))
198
- } else {
199
- nestedComponent.update()
200
- }
201
- }
202
- }
203
- /**
204
- * Calls the callback if the value has changed (
205
- */
206
- // changed(name, callback) {
207
- // const n = this.__ov[name]
208
- // const o = this.props[name]
209
- // if (n !== o) {
210
- // callback(n, o)
211
- // }
212
- // }
213
-
214
-
215
- /**
216
- * Creates a watch.
217
- */
218
- proto.__wa = function(wrapperKey, shieldQuery, reverseShield, shieldCount, callbacks) {
219
- return {
220
- wk: wrapperKey, // The key of the corresponding wrapper.
221
- sq: shieldQuery, // The shield query key
222
- rv: reverseShield, // whether shieldQuery should be flipped
223
- sc: shieldCount, // The number of items to shield
224
- cb: callbacks // The callbacks - object
225
- }
226
- }
227
-
228
-
229
- const applyWatchCallbacks = (component, wrapper, callbacks) => {
230
-
231
- for (let key in callbacks) {
232
- let callback = callbacks[key]
233
- // TODO: change this to use constant.
234
- if (key === '*') {
235
- callback.call(component, wrapper, component.props, component)
236
- } else {
237
- // TODO: will this transpile to something different?
238
- // means: {new, old, changed}
239
- const {n, o, c} = component.lookup(key)
240
- if (c) {
241
- callback.call(component, n, o, wrapper, component.props, component)
242
- }
243
- }
244
- }
245
- }
246
-
247
-
248
- /**
249
- * The global mount tracker.
250
- */
251
- proto.__mt = mountie
252
-
253
- /**
254
- * Nest Internal. For building a nested component declared in the html.
255
- */
256
- proto.__ni = function(path, cls) {
257
- const child = buildComponent(cls, this)
258
- this.__gw(path).replace(child.e)
259
- return child
260
- }
261
-
262
- /**
263
- *
264
- * @param {function} baseClass - the base class to extend from
265
- * @param {object} [prototypeExtras] - an object with extra things to be added to the prototype
266
- * @param {function} [prototypeExtras] - the function to be used as constructor
267
- */
268
- proto.__ex = function(baseClass, prototypeExtras, constructorFunction) {
269
- // const base = baseClass || Component
270
- var subClass = constructorFunction || function(parent) {
271
- baseClass.call(this, parent)
272
- }
273
- subClass.prototype = Object.create(baseClass && baseClass.prototype, {
274
- constructor: { value: subClass, writable: true, configurable: true }
275
- });
276
- if (prototypeExtras) {
277
- Object.assign(subClass.prototype, prototypeExtras);
278
- }
279
- return subClass
280
- }
281
-
282
-
283
- /**
284
- * Create a component pool.
285
- */
286
- proto.pool = function(cls, keyFn) {
287
- return keyFn ? new KeyedPool(cls, keyFn) : new SequentialPool(cls)
288
- }
289
-
290
- /**
291
- * Create an instance pool, for switches.
292
- */
293
- proto.__ic = function(mappings, fallback) {
294
- return new InstancePool(mappings, fallback)
295
- }
296
-
297
- /**
298
- * Build the DOM. We pass prototype as local var for compactness.
299
- */
300
- proto.__bd = function(prototype) {
301
- if (prototype.__cn === undefined) {
302
- prototype.__cn = makeEl(prototype.__ht)
303
- }
304
- this.e = prototype.__cn.cloneNode(true)
305
- }
306
-
307
- // proto.__bd = function(prototype, clone) {
308
- // if (clone && !prototype.__cn) {
309
- // prototype.__cn = makeEl(prototype.__ht)
310
- // }
311
- // this.e = clone ? prototype.__cn.cloneNode(true) : makeEl(prototype.__ht)
312
- // }
313
-
314
- // proto.__bd = function(prototype) {
315
- // this.e = makeEl(prototype.__ht)
316
- // }
317
-
318
- /**
319
- * Returns a regular wrapper around element at path, where path is an array of indices.
320
- * This is used by the babel plugin.
321
- */
322
- proto.__gw = function(path) {
323
- return new Wrapper(this.__fe(path))
324
- }
325
-
326
- /**
327
- * Finds an element at specified path, where path is an array of indices.
328
- * This is used by the babel plugin.
329
- */
330
- proto.__fe = function(path) {
331
- return path.reduce((acc, index) => acc.childNodes[index], this.e)
332
- }
333
-
334
- /**
335
- * Is Attached.
336
- * Determines whether this component is attached to the DOM.
337
- */
338
- proto.__ia = function() {
339
- let e = this.e
340
- while (e) {
341
- if (e === document) {
342
- return true
343
- }
344
- e = e.parentNode
345
- }
346
- return false
347
- }
348
-
349
- /**
350
- * Creates a lookup.
351
- */
352
- proto.__lu = function(callbacks) {
353
- return new Lookup(callbacks)
354
- }
355
-
356
- /**
357
- * Creates an anonymous stub component class
358
- */
359
- proto.__sv = function() {
360
- const cls = function(parent) {
361
- Component.call(this, parent)
362
- }
363
- cls.prototype = new Component()
364
- return cls
365
- }
366
-
367
- /**
368
- * Toggles visibility, like wrapper.
369
- */
370
- proto.hide = function(hidden) {
371
- this.e.hidden = hidden
372
- }
package/src/helpers.js DELETED
@@ -1,16 +0,0 @@
1
- export const doc = document
2
- const throwAway = doc.createElement('template')
3
-
4
- /**
5
- * Create an element from html string
6
- */
7
- export function makeEl(html) {
8
- throwAway.innerHTML = html
9
- return throwAway.content.firstChild
10
- }
11
-
12
- /**
13
- * Some utility functions
14
- */
15
- export const und = x => x === undefined
16
- export const isStr = x => typeof x === 'string'
package/src/index.js DELETED
@@ -1,18 +0,0 @@
1
- import {createComponent, h, mount, wrap} from './utils'
2
- import {isStr} from './helpers'
3
- import {Component} from './component'
4
- import {KeyedPool, InstancePool, SequentialPool} from './pool'
5
- import {Wrapper} from './wrapper'
6
-
7
- module.exports = {
8
- createComponent,
9
- h,
10
- mount,
11
- KeyedPool,
12
- InstancePool,
13
- isStr,
14
- SequentialPool,
15
- Component,
16
- Wrapper,
17
- wrap
18
- }
package/src/lookup.js DELETED
@@ -1,38 +0,0 @@
1
- import {und} from './helpers'
2
-
3
- /**
4
- * Used internally.
5
- * An object which pools the results of lookup queries so we don't have to
6
- * repeat them in the same component.
7
- * The Lookup instance will be shared between instances of a component.
8
- * Must call reset() on every update.
9
- */
10
- export function Lookup(callbacks) {
11
- this.callbacks = callbacks
12
- this.run = {}
13
- }
14
-
15
- Lookup.prototype = {
16
- get: function(component, key) {
17
- const run = this.run
18
- if (run[key] === undefined) {
19
- // Verbose but efficient way as it avoids lookups?
20
- // Or is this harmful to performance because we're just reading values more than calling functions?
21
- let o = component.__ov[key]
22
- // TODO: is this checking for watchOnce?
23
- o = und(o) ? '' : o
24
- // console.log(key)
25
- const n = this.callbacks[key](component, component.props)
26
- const c = n !== o
27
- component.__ov[key] = n
28
- const rtn = {n, o, c}
29
- run[key] = rtn
30
- return rtn
31
- }
32
- return run[key]
33
- },
34
- reset: function() {
35
- this.run = {}
36
- }
37
- }
38
-
package/src/mountie.js DELETED
@@ -1,23 +0,0 @@
1
- /**
2
- * Wallace's crude way of tracking mounting and unmounting.
3
- */
4
-
5
- const trackedComponents = []
6
-
7
- export default {
8
- track: function (component) {
9
- trackedComponents.push({component: component, isAttached: component.__ia()})
10
- },
11
- flush: function () {
12
- for (let i=0, il=trackedComponents.length; i<il; i++) {
13
- let trackedComponent = trackedComponents[i]
14
- let component = trackedComponent.component
15
- let attachedNow = component.__ia()
16
- if (attachedNow !== trackedComponent.isAttached) {
17
- let fn = attachedNow ? component.mount : component.unmount
18
- fn.apply(component)
19
- trackedComponent.isAttached = attachedNow
20
- }
21
- }
22
- }
23
- }
package/src/pool.js DELETED
@@ -1,173 +0,0 @@
1
- import {createComponent} from './utils'
2
-
3
- /**
4
- * Pools same type components, retrieving by sequence.
5
- * Must not be shared.
6
- *
7
- * @param {class} componentClass - The class of Component to create.
8
- * @param {function} keyFn - A function which obtains the key to pool by
9
- */
10
- export function KeyedPool(componentClass, keyFn) {
11
- this._v = componentClass
12
- this._f = keyFn
13
- this._k = [] // keys
14
- this._p = {} // pool of component instances
15
- }
16
- const proto = KeyedPool.prototype
17
-
18
- /**
19
- * Retrieves a single component. Though not used in Wallace itself, it may
20
- * be used elsewhere, such as in the router.
21
- *
22
- * @param {Object} item - The item which will be passed as props.
23
- * @param {Component} parent - The parent component.
24
- */
25
- proto.getOne = function(item, parent) {
26
- return this._get(this._p, this._v, this._f(item), item, parent)
27
- }
28
-
29
- /**
30
- * Updates the element's childNodes to match the items.
31
- * Performance is important.
32
- *
33
- * @param {DOMElement} e - The DOM element to patch.
34
- * @param {Array} items - Array of items which will be passed as props.
35
- * @param {Component} parent - The parent component.
36
- */
37
- proto.patch = function(e, items, parent) {
38
- const pool = this._p
39
- const componentClass = this._v
40
- const keyFn = this._f
41
- const childNodes = e.childNodes
42
- const itemsLength = items.length
43
- const oldKeySequence = this._k
44
- const newKeys = []
45
- let item, key, component, childElementCount = oldKeySequence.length + 1
46
- for (let i=0; i<itemsLength; i++) {
47
- item = items[i]
48
- key = keyFn(item)
49
- component = this._get(pool, componentClass, key, item, parent)
50
- newKeys.push(key)
51
- if (i > childElementCount) {
52
- e.appendChild(component.e)
53
- } else if (key !== oldKeySequence[i]) {
54
- e.insertBefore(component.e, childNodes[i])
55
- pull(oldKeySequence, key, i)
56
- }
57
- }
58
- this._k = newKeys
59
- trimChildren(e, childNodes, itemsLength)
60
- }
61
-
62
- // Internal
63
- proto._get = function(pool, componentClass, key, item, parent) {
64
- let component
65
- if (pool.hasOwnProperty(key)) {
66
- component = pool[key]
67
- component.setProps(item)
68
- } else {
69
- component = createComponent(componentClass, parent, item)
70
- pool[key] = component;
71
- }
72
- return component
73
- }
74
-
75
- /**
76
- * Pools same type components, retrieving by sequence.
77
- * Must not be shared.
78
- *
79
- * @param {class} componentClass - The class of Component to create.
80
- */
81
- export function SequentialPool(componentClass) {
82
- this._v = componentClass
83
- this._p = [] // pool of component instances
84
- this._c = 0 // Child element count
85
- }
86
-
87
- /**
88
- * Updates the element's childNodes to match the items.
89
- * Performance is important.
90
- *
91
- * @param {DOMElement} e - The DOM element to patch.
92
- * @param {Array} items - Array of items which will be passed as props.
93
- * @param {Component} parent - The parent component.
94
- */
95
- SequentialPool.prototype.patch = function(e, items, parent) {
96
- const pool = this._p
97
- const componentClass = this._v
98
- const childNodes = e.childNodes
99
- const itemsLength = items.length
100
- let item, component, poolCount = pool.length, childElementCount = this._c
101
-
102
- for (let i=0; i<itemsLength; i++) {
103
- item = items[i]
104
- if (i < poolCount) {
105
- component = pool[i]
106
- component.setProps(item)
107
- } else {
108
- component = createComponent(componentClass, parent, item)
109
- pool.push(component)
110
- poolCount ++
111
- }
112
- if (i >= childElementCount) {
113
- e.appendChild(component.e)
114
- }
115
- }
116
- this._c = itemsLength
117
- trimChildren(e, childNodes, itemsLength)
118
- }
119
-
120
- /**
121
- * An object which creates and pools components according to the mappings provided.
122
- * If there is no match in the mappings, the fallback function is called.
123
- *
124
- * Note that the fallback must return an instance (of Component or Wrapper) whereas
125
- * mappings must specify component classes.
126
- *
127
- * You can rely solely on the fallback if you like.
128
- *
129
- * @param {Object} mappings - a mapping of format key->componentClass
130
- * @param {function} fallback - a function to call when no key is provided.
131
- *
132
- */
133
- export function InstancePool(mappings, fallback) {
134
- this._m = mappings
135
- this._f = fallback
136
- this._i = {} // Instances
137
- }
138
-
139
- InstancePool.prototype.getOne = function(key, parentComponent) {
140
- if (!this._i.hasOwnProperty(key)) {
141
- this._i[key] = this._m.hasOwnProperty(key) ?
142
- parentComponent.nest(this._m[key]) : this._f(key, parentComponent)
143
- }
144
- return this._i[key]
145
- }
146
-
147
- /**
148
- * Trims the unwanted child elements from the end.
149
- *
150
- * @param {Node} e
151
- * @param {Array} childNodes
152
- * @param {Int} itemsLength
153
- */
154
- function trimChildren(e, childNodes, itemsLength) {
155
- let lastIndex = childNodes.length - 1
156
- let keepIndex = itemsLength - 1
157
- for (let i=lastIndex; i>keepIndex; i--) {
158
- e.removeChild(childNodes[i])
159
- }
160
- }
161
-
162
- /**
163
- * Pulls an item forward in an array, to replicate insertBefore.
164
- * @param {Array} arr
165
- * @param {any} item
166
- * @param {Int} to
167
- */
168
- function pull(arr, item, to) {
169
- const position = arr.indexOf(item)
170
- if (position != to) {
171
- arr.splice(to, 0, arr.splice(position, 1)[0])
172
- }
173
- }
package/src/utils.js DELETED
@@ -1,64 +0,0 @@
1
- import {doc, isStr, makeEl} from './helpers'
2
- import {Wrapper} from './wrapper'
3
-
4
- /**
5
- * Creates and mounts a component onto an element.
6
- *
7
- * @param {unsure} elementOrId Either a string representing an id, or an element.
8
- * @param {class} cls The class of Component to create
9
- * @param {object} props The props to pass to the component (optional)
10
- * @param {object} parent The parent component (optional)
11
- */
12
- export function mount(elementOrId, cls, props, parent) {
13
- const component = createComponent(cls, parent, props)
14
- const nodeToReplace = getElement(elementOrId)
15
- nodeToReplace.parentNode.replaceChild(component.e, nodeToReplace)
16
- return component
17
- }
18
-
19
- /**
20
- * returns a Wrapper around an element.
21
- *
22
- * @param {unsure} elementOrId Either a string representing an id, or an element.
23
- */
24
- export function wrap(elementOrId) {
25
- return new Wrapper(getElement(elementOrId))
26
- }
27
-
28
-
29
- function getElement(elementOrId) {
30
- return isStr(elementOrId) ? document.getElementById(elementOrId) : elementOrId
31
- }
32
-
33
-
34
- /**
35
- * Creates a component and initialises it.
36
- *
37
- * @param {class} cls The class of Component to create
38
- * @param {object} parent The parent component (optional)
39
- * @param {object} props The props to pass to the component (optional)
40
- */
41
- export function createComponent(cls, parent, props) {
42
- const component = buildComponent(cls, parent)
43
- component.props = props
44
- component.init()
45
- component.update()
46
- return component
47
- }
48
-
49
- /**
50
- * Builds a component.
51
- */
52
- export function buildComponent(cls, parent) {
53
- const component = new cls(parent)
54
- component.__bv(component, cls.prototype)
55
- return component
56
- }
57
-
58
-
59
- /**
60
- * Creates a wrapper of type tag e.g. h('div')
61
- */
62
- export function h(tag) {
63
- return new Wrapper(doc.createElement(tag))
64
- }