JSFiddle - React, Tailwind, and code Playground

by James MacAulay

HTML

<script src="http://fb.me/react-with-addons-0.9.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.9.0.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore.js"></script>
<script src="http://s3.amazonaws.com/es6-promises/promise-0.1.1.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.8/bacon.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://s3.amazonaws.com/jamesmacaulay.code/react-bacon/0.0.3/react-bacon.js"></script>
<div id="content"></div>
<script src="http://fb.me/react-js-fiddle-integration.js"></script>

CSS

body {
    font-family: Helvetica;
    padding: 20px;
}

.avatar {
    float: right;
}

.avatar img {
    width: 50px;
}

JavaScript 1.7

/** @jsx React.DOM */

var BaconMixin = ReactBacon.BaconMixin;

var GitHub = {
  get: function(url) {
    var response = Promise.resolve($.ajax(url, {dataType: 'jsonp'}));
    return response.then(function(data) {
      if (data.meta && data.meta.status !== 200) {
        throw new Error(data.data.message);
      }
      return data.data;
    });
  },
  getUser: function(username) {
    return GitHub.get("https://api.github.com/users/"+username);
  },
  getGists: function(username) {
    return GitHub.get("https://api.github.com/users/"+username+"/gists");
  }
};

var GitHubWidget = React.createClass({
  getInitialState: function() {
    return {username: ""};
  },
  handleUsernameChange: function(username) {
    this.setState({username: username});
  },
  render: function() {
    return (
      <div>
        <Avatar username={this.state.username} />
        <UsernameInput handleChange={this.handleUsernameChange} />
        <GistList username={this.state.username} />
      </div>
    );
  }
});

var Avatar = React.createClass({
  mixins: [BaconMixin],
  propTypes: {
    username: React.PropTypes.string.isRequired
  },
  getInitialState: function() {
    return {user: null};
  },
  componentWillMount: function() {
    var users = this.propsProperty('username').flatMapLatest(
      _.compose(Bacon.fromPromise, GitHub.getUser)
    ).mapError(null);
    this.plug(users, 'user');
  },
  render: function() {
    if (this.state.user) {
      return (
        <div className="avatar">
          <a href={this.state.user.html_url}><img src={this.state.user.avatar_url} /></a>
        </div>
      );
    } else {
      return <div className="avatar" />;
    }
  }
})

var UsernameInput = React.createClass({
  mixins: [BaconMixin],
  propTypes: {
    handleChange: React.PropTypes.func.isRequired
  },
  sendUsername: function(username) {
    this.props.handleChange(username);
  },
  componentWillMount: function() {
    this.eventStream('inputChanged').
     ...