Skip to content

Instantly share code, notes, and snippets.

@kumavis
Last active August 29, 2015 14:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kumavis/c654143c4bcdea21e618 to your computer and use it in GitHub Desktop.
Save kumavis/c654143c4bcdea21e618 to your computer and use it in GitHub Desktop.
How to Mercury (kumavis style)

First define your component:

  • value types and defaults for each key
  • 'channels', actions that potentially modify state or perform i/o. triggered by button presses, mouse movements, etc
var Value = require('observ')
var Component = require('../lib/component.js')
var Wallet = require('../wallet/index.js')
var Identity = require('../identity/index.js')
var render = require('./render.js')

module.exports = Component({
  // simple values, arrays
  currentView:     { type: Value,    default: 'wallet' },
  // other components
  wallet:          { type: Wallet,   default: null     },
  currentIdentity: { type: Identity, default: null     },

  channels: {
    confirmSendEthereum: confirmSendEthereum,
    otherControllerAction: otherControllerAction,
  },
}, render)

function confirmSendEthereum(state) {
  // manipulate state or do i/o here
}

function otherControllerAction(state) {
  // manipulate state or do i/o here
}

Then build your template:

  • template takes a state pojo and returns vdom
  • setup state pojo for sub-components
var h = require('virtual-dom/virtual-hyperscript')
var stateExtend = require('../lib/stateExtend')
var Wallet = require('../wallet/index.js')

module.exports = function render(state) {

  // optional: mixin and overwrite wallet internal state with our higher state
  var walletState = stateExtend(state.wallet, {
    identity: state.currentIdentity,
    channels: {
      sendEthereum: state.channels.confirmSendEthereum,
    }
  })

  // render state into vdom
  return h('div.my-app', [
    // render some elements and text
    h('span', 'current-view: '+state.currentView),
    // render sub-component
    Wallet.render(walletState),
  ])
}
@kumavis
Copy link
Author

kumavis commented Feb 5, 2015

Any and all secret sauce is here:

function Component(definition, render) {
  var componentClass = function(state) {
    return setupComponent(definition, state)
  }
  componentClass.render = render
  return componentClass
}

// Takes a component definition, and a pojo state
function setupComponent(definition, state) {
  state = state || {}
  // copy definition
  definition = extend(definition)
  // extract channels
  var channels = definition.channels || {}
  delete definition.channels
  // build component
  var componentBase = { channels: Value(null) }
  for (key in definition) {
    var type = definition[key].type
    // use provided value or default
    var value = (state[key] !== undefined) ? state[key] : definition[key].default
    componentBase[key] = type(value)
  }
  var component = struct(componentBase)
  // activate channels
  component.channels.set(setupChannels(channels, component))
  // component is ready
  return component
}

function setupChannels(funcs, context) {
  return Object.keys(funcs).reduce(createHandle, {});

  function createHandle(acc, name) {
    var handle = Delegator.allocateHandle(funcs[name].bind(null, context))
    acc[name] = handle;
    return acc;
  }
}

// intelligently merges pojo states and channels
function stateExtend() {
  var newState = extend.apply(null, arguments)
  var channelArguments = [].slice.apply(arguments)
    .map(function(item){ return item.channels })
    .filter(function(item){ return !!item })
  newState.channels = extend.apply(null, channelArguments)
  return newState
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment