JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://mbostock.github.com/d3/d3.v2.js"></script>
<body>
    <div id="chart1" class="chart"></div>

CSS

body { font: 13px sans-serif; }
      rect { fill: #fff; }
      ul {
        list-style-type: none;
        margin: 0.5em 0em 0.5em 0em;
        width: 100%; }
        ul li {
          display: table-cell;
          vertical-align: middle;
          margin: 0em;
          padding: 0em 1em; }
      .axis { font-size: 1.5em; }
      .chart {
        background-color: #F7F2C5;
        width: 960px;
        height: 500px; }
      circle, .line {
        fill: none;
        stroke: steelblue;
        stroke-width: 2px; }
      circle {
        fill: white;
        fill-opacity: 0.2;
        cursor: move; }
        circle.selected {
          fill: #ff7f0e;
          stroke: #ff7f0e; }
        circle:hover {
          fill: #ff7f0e;
          stroke: #707f0e; }
        circle.selected:hover {
          fill: #ff7f0e;
          stroke: #ff7f0e; }

JavaScript

registerKeyboardHandler = function(callback) {
  var callback = callback;
  d3.select(window).on("keydown", callback);  
};

SimpleGraph = function(elemid, options) {
  var self = this;
  this.chart = document.getElementById(elemid);
  this.cx = this.chart.clientWidth;
  this.cy = this.chart.clientHeight;
  this.options = options || {};
  this.options.xmax = options.xmax || 30;
  this.options.xmin = options.xmin || 0;
  this.options.ymax = options.ymax || 10;
  this.options.ymin = options.ymin || 0;

  this.padding = {
     "top":    this.options.title  ? 40 : 20,
     "right":                 30,
     "bottom": this.options.xlabel ? 60 : 10,
     "left":   this.options.ylabel ? 70 : 45
  };

  this.size = {
    "width":  this.cx - this.padding.left - this.padding.right,
    "height": this.cy - this.padding.top  - this.padding.bottom
  };

  // x-scale
  this.x = d3.scale.linear()
      .domain([this.options.xmin, this.options.xmax])
      .range([0, this.size.width]);

  // drag x-axis logic
  this.downx = Math.NaN;

  // y-scale (inverted domain)
  this.y = d3.scale.linear()
      .domain([this.options.ymax, this.options.ymin])
      .nice()
      .range([0, this.size.height])
      .nice();

  // drag y-axis logic
  this.downy = Math.NaN;

  this.dragged = this.selected = null;

  this.line = d3.svg.line()
      .x(function(d, i) { return this.x(this.points[i].x); })
      .y(function(d, i) { return this.y(this.points[i].y); });

  var xrange =  (this.options.xmax - this.options.xmin),
      yrange2 = (this.options.ymax - this.options.ymin) / 2,
      yrange4 = yrange2 / 2,
      datacount = this.size.width/30;

  this.points = d3.range(datacount).map(function(i) { 
    return { x: i * xrange / datacount, y: this.options.ymin + yrange4 + Math.random() * yrange2 }; 
  }, self);

  this.vis = d3.select(this.chart).append("svg")
      .attr("width",  this.cx)
      .attr("height", this.cy)
      .append("g")
        .attr("transform", "translate(" +...