Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create widget function for creating widgets #6

Closed
Gozala opened this issue Oct 21, 2012 · 5 comments
Closed

Create widget function for creating widgets #6

Gozala opened this issue Oct 21, 2012 · 5 comments

Comments

@Gozala
Copy link
Contributor

Gozala commented Oct 21, 2012

reflex should have a function for defining interactive, self contained widgets, that take input source and produce stream of changes. Probably we need some way of creating nested widgets too!

@Gozala
Copy link
Contributor Author

Gozala commented Oct 21, 2012

I'm thinking about something along this lines:

var open = require("dom-reduce/event")
var writer = require("reflex/writer")
var map = require("reducers/map")
var channel = require("reducers/channel")
var emit = require("reducers/emit")
var reduce = require("reducers/reduce")

function widget(read, write) {
  return function make(input, options) {
    // Create / hook an instance using options passed
    var instance = write(input, null, options)
    var output = read(instance)
    output.view = instance
    return output
  }
}

var rangeWriter = writer(function swap(output, data) {
  output.querySelector(".caption").textContent = data.value
}, function close(output) {
  var parent = output.parentElement
  if (parent) parent.removeChild(output)
}, function open(options) {
  options = options || {}
  var root = document.createElement("div")
  root.className = "widget"

  var range = document.createElement("input")
  range.className = "input range"
  range.type = "range"
  range.max = options.max || 100
  range.min = options.min || 0
  range.value = options.value === void(0) ? range.max / 2
                                          : options.value
  root.appendChild(range)

  var caption = document.createElement("label")
  caption.className = "caption"
  root.appendChild(caption)

  return root
})

var slider = widget(function(instance) {
  var changes = open(instance, "change")
  return map(changes, function(event) {
    return event.target.value
  })
}, rangeWriter)

var state = channel()
var s = slider(state, { max: 100, min: 0, value: 50 })
document.body.appendChild(s.view)

@Raynos
Copy link
Collaborator

Raynos commented Oct 24, 2012

How would it work with collectionWriter.

You would want to call read for each one.

@Gozala
Copy link
Contributor Author

Gozala commented Oct 28, 2012

Draft for the implementation of component that me and @Raynos have outlined:

function component(read, write) {
  /**
  Component takes read / write functions for each of the nested component
  and returns `react` function that can take `input` stream of application state
  changes and returns `input` of user events
  **/

  return function react(state, options) {
    var hash = {}                      // hash of items that are already known
    var inputs = channel()       // sequence of streams where each one is update per nested component. 
    reduce(state, function(current, state) {
      var delta = diff(current, state)
      Object.keys(delta).forEach(function(id) {
        if (delta[id] === null) hash[id] = null
        else if (!hash[id]) {
          hash[id] = true
          // filter each input to only updates for component with this id.
          var fork = filter(state, function(data) { return id in data })
          // Map it to actual update deltas.
          var updates = map(fork, function(state) { return state[id] })
          // Create an output where updates are written
          var output = write(updates, options)
          // and read inputs from that output
          var input = read(output, options)
          // create stream of deltas by mapping each update back to id
          var deltas = map(output, function(update) {
            var value = {}
            value[id] = update
            return value
          })
          // emit deltas stream to the stream of all inputs
          emit(inputs, deltas)
        }
      })
    })
    // return joined stream of all inputs from all nested components.
    flatten(inputs)
  }
}

@Gozala
Copy link
Contributor Author

Gozala commented Oct 28, 2012

Draft for the implementation of compound component that me and @Raynos have outlined:

function compoundComponent(mapping) {
  /**
  Takes mapping of JSON paths for a state snapshot and `react`-or function
  to which scoped updates are forwarded. In return it returns `input` stream
  of user events all all the nested components joined in the state model
  structure.
  **/
  return function react(state) {
    var inputs = Object.keys(mapping).map(function(id) {
      // Filter updates to the component with the given `id`.
      var fork = filter(state, function(data) { return id in data })
      // Selects a deltas for the given item.
      var updates = map(fork, function(state) { return state[id] })
      // Get an user input for the component by passing scope updates to
      // the reactor function associated with an `id`.
      var input = mapping[id](updates)
      // Join back user input into same state structure.
      return map(input, function(update) {
        var value = {}
        value[id] = update
        return value
      })
    })
    // Merge updates from all components together into stream of deltas
    // for the application state.
    return flatten(inputs)
  }
}

@Gozala
Copy link
Contributor Author

Gozala commented Oct 28, 2012

This now is now duplicate of #16

@Gozala Gozala closed this as completed Oct 22, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants