UNPKG

talio

Version:

a smaller, less confuse, mercury

133 lines (118 loc) 4.07 kB
Delegator = require 'dom-delegator' extend = require 'xtend' immupdate = require 'immupdate' # ~ export virtual-dom and h, just because it is cheap module.exports['virtual-dom'] = require 'virtual-dom' module.exports.h = require 'virtual-dom/h' # / # ~ event handlers module.exports.sendEvent = require 'value-event/event' module.exports.sendValue = require 'value-event/value' module.exports.sendClick = require 'value-event/click' module.exports.sendSubmit = require 'value-event/submit' module.exports.sendChange = require 'value-event/change' module.exports.sendKey = require 'value-event/key' module.exports.sendDetail = (require 'value-event/base-event') (ev, broadcast) -> detail = ev._rawEvent.detail data = extend detail, this.data broadcast data # / # ~ state factory class TalioState type: 'TalioState' constructor: (@state) -> silentlyUpdate: -> u = immupdate.bind @, @state @state = u.apply @, arguments change: -> @silentlyUpdate.apply @, arguments @cb(@state) if @cb subscribe: (cb) -> @cb = cb itself: -> @state get: (prop) -> ret = @state try for degree in prop.split '.' ret = ret[degree] catch e return ret module.exports.StateFactory = (dict) -> new TalioState dict # / state factory # ~ mainloop raf = require('raf') TypedError = require('error/typed') InvalidUpdateInRender = TypedError( type: 'talio.invalid.update.in-render' message: 'talio: Unexpected update occurred in loop.\n' + 'We are currently rendering a view, ' + 'you can\'t change state right now.\n' + 'The diff is: {stringDiff}.\n' + 'SUGGESTED FIX: find the state mutation in your view ' + 'or rendering function and remove it.\n' + 'The view should not have any side effects.\n' diff: null stringDiff: null) mainloop = (initialState, view, channels, opts) -> opts = opts or {} currentState = initialState create = opts.create diff = opts.diff patch = opts.patch redrawScheduled = false tree = opts.initialTree or view(currentState, channels) target = opts.target or create(tree, opts) inRenderingTransaction = false currentState = null update = (state) -> if inRenderingTransaction throw InvalidUpdateInRender( diff: state._diff stringDiff: JSON.stringify(state._diff)) if currentState == null and !redrawScheduled redrawScheduled = true raf redraw currentState = state return redraw = -> redrawScheduled = false return if currentState == null inRenderingTransaction = true try newTree = view(currentState, channels) catch e console.error "We had a problem while rendering the tree with the following state:", currentState console.debug "Aborting the render." inRenderingTransaction = false newTree = tree console.debug e.stack if opts.createOnly inRenderingTransaction = false create newTree, opts else patches = diff(tree, newTree, opts) inRenderingTransaction = false target = patch(target, patches, opts) tree = newTree currentState = null return return { target: target update: update } # / mainloop # ~ the function that starts everything module.exports.Delegator = Delegator module.exports.delegator = Delegator() module.exports.run = (domnode, vrender, handlers, BaseState) -> # create a blank state if none provided if not BaseState or BaseState.type != 'TalioState' BaseState = new TalioState {} # allocate handlers in the dom-delegator for the supplied channels createChannel = (acc, name) -> acc[name] = Delegator.allocateHandle( handlers[name].bind(handlers, BaseState) ) return acc channels = Object.keys(handlers).reduce createChannel, {} theloop = mainloop(BaseState.itself(), vrender, channels, diff: require 'virtual-dom/vtree/diff' patch: require 'virtual-dom/vdom/patch' create: require 'virtual-dom/vdom/create-element' ) domnode.appendChild theloop.target BaseState.subscribe (state) -> theloop.update state # /