diff options
| author | bozo.kopic <bozo.kopic@gmail.com> | 2017-07-02 02:45:51 +0200 |
|---|---|---|
| committer | bozo.kopic <bozo.kopic@gmail.com> | 2017-07-02 02:45:51 +0200 |
| commit | c75e05b2fcbd08ec77a2ac85837f9f58829a44f6 (patch) | |
| tree | 446e9f371f0847b78832a8ff5594c56e6c891d7b /src_js | |
| parent | 268bb2951c9026e8935c0a99d188416422351a0e (diff) | |
build environment and basic folder structure
Diffstat (limited to 'src_js')
| -rw-r--r-- | src_js/hatter/common.js | 2 | ||||
| -rw-r--r-- | src_js/hatter/lenses.js | 21 | ||||
| -rw-r--r-- | src_js/hatter/main.js | 19 | ||||
| -rw-r--r-- | src_js/hatter/renderer.js | 126 | ||||
| -rw-r--r-- | src_js/hatter/vt.js | 4 |
5 files changed, 172 insertions, 0 deletions
diff --git a/src_js/hatter/common.js b/src_js/hatter/common.js new file mode 100644 index 0000000..829685b --- /dev/null +++ b/src_js/hatter/common.js @@ -0,0 +1,2 @@ + +export const defaultState = {}; diff --git a/src_js/hatter/lenses.js b/src_js/hatter/lenses.js new file mode 100644 index 0000000..39da314 --- /dev/null +++ b/src_js/hatter/lenses.js @@ -0,0 +1,21 @@ +import R from 'ramda'; + + +export const index = R.lensIndex; + +export const prop = R.lensProp; + +export function path(...xs) { + return R.reduce((acc, i) => R.compose(acc, pathParamToLens(i)), + R.identity, xs); +} + +function pathParamToLens(x) { + switch (typeof(x)) { + case 'function': return x; + case 'number': return index(x); + case 'string': return prop(x); + case 'object': if (Array.isArray(x)) return R.apply(path, x); + } + throw 'Invalid path parameter'; +} diff --git a/src_js/hatter/main.js b/src_js/hatter/main.js new file mode 100644 index 0000000..f5bee07 --- /dev/null +++ b/src_js/hatter/main.js @@ -0,0 +1,19 @@ +import bean from 'bean'; +import R from 'ramda'; + +import r from 'hatter/renderer'; +import * as l from 'hatter/lenses'; +import * as common from 'hatter/common'; +import * as vt from 'hatter/vt'; + +import 'static!static/index.html'; +import 'style/main.scss'; + + +function main() { + let root = document.body.appendChild(document.createElement('div')); + r.init(root, common.defaultState, vt.main); +} + + +bean.on(window, 'load', main); diff --git a/src_js/hatter/renderer.js b/src_js/hatter/renderer.js new file mode 100644 index 0000000..f09d40f --- /dev/null +++ b/src_js/hatter/renderer.js @@ -0,0 +1,126 @@ +import Delegator from 'dom-delegator'; +import R from 'ramda'; +import bean from 'bean'; +import vh from 'virtual-dom/h'; +import diff from 'virtual-dom/diff'; +import patch from 'virtual-dom/patch'; +import createElement from 'virtual-dom/create-element'; + +import * as l from 'hatter/lenses'; + + +const delegator = Delegator(); +const vhTypes = ['VirtualNode', 'Widget']; + + +function vhFromArray(node) { + if (!node) + return []; + if (typeof node == 'string' || vhTypes.includes(node.type)) + return node; + if (!Array.isArray(node)) + throw 'Invalid node structure'; + if (node.length < 1) + return []; + if (typeof node[0] != 'string') + return node.map(vhFromArray); + let hasProps = (node.length > 1 && + typeof node[1] == 'object' && + !Array.isArray(node[1]) && + !vhTypes.includes(node[1].type)); + let children = R.flatten(node.slice(hasProps ? 2 : 1).map(vhFromArray)); + let result = hasProps ? vh(node[0], node[1], children) : + vh(node[0], children); + + // disable SoftSetHook for input + if (result.tagName == 'INPUT' && + result.properties && + result.properties.value && + typeof(result.properties.value) === 'object') { + result.properties.value = result.properties.value.value; + } + + return result; +} + + +class VTreeRenderer { + + constructor(el) { + this._el = el; + this._vtree = null; + } + + render(vtree) { + let vt = vhFromArray(vtree); + if (vt.type == 'VirtualNode') { + if (this._vtree) { + let d = diff(this._vtree, vt); + patch(this._el.firstChild, d); + } else { + while (this._el.firstChild) + this._el.removeChild(this._el.firstChild); + this._el.appendChild(createElement(vt)); + } + this._vtree = vt; + } else { + this._vtree = null; + while (this._el.firstChild) + this._el.removeChild(this._el.firstChild); + } + } + +} + + +export class Renderer { + + constructor(el, initState, vtCb) { + this.init(el, initState, vtCb); + } + + init(el, initState, vtCb) { + this._state = null; + this._changeCbs = []; + this._vtCb = vtCb; + this._r = new VTreeRenderer(el || document.querySelector('body')); + if (initState) + this.change(R.identity, _ => initState); + } + + view(...lenses) { + return R.view(R.apply(l.path, lenses), this._state); + } + + set(lens, value) { + if (arguments.length < 2) { + value = lens; + lens = R.identity; + } + this.change(lens, _ => value); + } + + change(lens, cb) { + if (arguments.length < 2) { + cb = lens; + lens = R.identity; + } + if (this._changeCbs.push(cb) > 1) + return; + let startingSubState = this.view(lens); + while (this._changeCbs.length > 0) { + this._state = R.over(l.path(lens), this._changeCbs[0], this._state); + this._changeCbs.shift(); + } + if (!this._vtCb || + (this._state && R.equals(startingSubState, this.view(lens)))) + return; + this._r.render(this._vtCb(this._state)); + bean.fire(this, 'render', this._state); + } + +} + + +const defaultRenderer = new Renderer(); +export default defaultRenderer; diff --git a/src_js/hatter/vt.js b/src_js/hatter/vt.js new file mode 100644 index 0000000..c1fcb9f --- /dev/null +++ b/src_js/hatter/vt.js @@ -0,0 +1,4 @@ + +export function main() { + return ['div', 'Hatter']; +} |
