jacomyal / domino.js

[deprecated] A JavaScript cascading controller for fast interactive Web interfaces prototyping.
https://jacomyal.github.io/domino.js
MIT License
54 stars 7 forks source link

[reboot-proposal] Investigate cursors #66

Closed Yomguithereal closed 9 years ago

Yomguithereal commented 9 years ago

Reflexions about cursors as a discussion starter.

Yomguithereal commented 9 years ago

Cursors

A cursor is a way of expressing a data traversal originating from the functional zippers.

Let's consider the following JavaScript object:

var atom = {
  primitive: 3,
  one: {
    subone: {
      hello: 'world'
    },
    subtow: {
      colors: ['blue', 'yellow']
    }
  },
  two: {
    name: 'John',
    surname: 'Dillinger'
  }
};

Using a cursor on it would produce the following:

// Considering that the cursor function is left-curried to
// hit the atom object shown above
cursor('primitive')
>>> 3

cursor('two')
>>> {name: 'John', surname: 'Dillinger'}

cursor('one', 'subone')
>>> {hello: 'world'}

cursor('one', 'subone', 'colors', 1)
>>> 'yellow'

// Exposing tree-like traversal
var c = cursor('two')

c.select('name');
>>> 'John'

c.up();
>>> // Same as atom

c.up().select('one', 'subtow', 'colors', 0).next();
>>> 'yellow'

Nothing too fancy here and can be implemented in an easy reduce.

But, would be possible to (see Immutable helpers from React concerning the update syntax) do the following:

var c = cursor('one', 'subtow');

// Update the cursor in an immutable-clever way
c.update({colors: {$push: 'purple'}});
cursor('one').update('subtow', {colors: {$push: 'purple'}});

// Or in a "bourrin" fashion
c.update({new: 'object', totally: 'different'});

// Listen to updates concerning this cursor
c.on('update', function(details) {
  // Something was updated under the cursor
  // What's more, precise details are known
});

// Listen to removal of the cursor
// (meaning it is no longer relevant because its root has disappeared)
c.on('removal', fn);
Yomguithereal commented 9 years ago

Comparisons

A lot of people are currently thinking about cursors.

The library Om makes it one of its topmost features.

However cursors are currently conflicting with what domino.js offers, which is a store of top-level very important properties.

In domino, only top-level properties are considered as first-class citizen while nested properties have no "official" value or recognition.

In Om, as a comparison, the user defines the state of its application as an atom which is basically the same thing as the atom object presented above and any component is free to hit on any level of the app state tree.

Yomguithereal commented 9 years ago

Attempt of cursors in domino.js and consequences

A controller

var controller = new domino({

  // Defining the state of the application like you want
  state: {
    primitive: 3,
    one: {
      subone: {
        hello: 'world'
      },
      subtow: {
        colors: ['blue', 'yellow']
      }
    },
    two: {
      name: 'John',
      surname: 'Dillinger'
    },
    notSetYetButNullForLegibility: null
  },

  // Storing important cursors used elsewhere?
  cursors: {
    colors: ['one', 'subtow', 'colors']
  },

  // Facet are still good
  facets: {
    colorsString: function() {
        return this.cursors.colors.get().split(', ');
    }
  }
});

A component

// Creating a cursor can be done anywhere
// I like the select name, but my naming...
var cursor = controller.select('one', 'subtow', 'colors');

// Idem as
var cursor = controller.cursors.colors;

// Component is rendered when the cursor has updated data
var Colors = React.createClass({
  mixins: [cursor.mixin],
  renderItem: function(color) {
    return (
      <li>
        <span>{color}</span>
      </li>
    );
  },
  render: function() {
    return <ul>{this.cursor.get().map(this.renderItem)}</ul>;
  }
});

// Component can emit an update upstream
var ColorsAdder = React.createClass({
  handleClick: function() {
    var color = this.refs.color.getDOMNode().value;
    cursor.push(color);
    // Same thing as
    cursor.update({$push: color});
    // Same thing as
    cursor.update(cursor.get().concat(color));
  },
  render: function() {
    return (
      <div>
        <input ref="color" type="text" />
        <button onClick={this.handleClick} type="button">Add</button>
      </div>
    );
  }
});

Notes

Yomguithereal commented 9 years ago

Resource

Immutable-js from Facebook. Cursor implementation.