JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html>
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<p>Drag nodes around, shift-drag to create new nodes, double click a node to edit its content (enter to confirm)</p>
</body>
</html>
CSS
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
.node {
position: absolute;
-moz-border-radius: 100px;
-webkit-border-radius: 100px;
border-radius: 100px;
border: 3px solid black;
width: 100px;
height: 100px;
text-align: center;
background-color: white;
}
.node .content {
position: relative;
top: 40px;
}
.node input.content {
top: 40px;
}
.line {
position: absolute;
border: 2px solid black;
width: 400px;
z-index: -1;
-webkit-transform-origin: 0 0;
}
JavaScript
var $node = function($parent, c, p) {
var $content = $('<p>').addClass('content');
var $ret = $('<div>').addClass('node').append($content).appendTo($parent);
$ret.data('connections', []);
$ret.content = function(c) {
if(c === undefined) {
return $(this).data('content');
}
else {
$(this).data('content', c);
$('.content', this).text(c);
}
};
content($ret, c);
xy($ret, p);
return $ret;
};
var content = function($e, c) {
if(c === undefined) {
return $e.data('content');
}
else {
$e.data('content', c);
$('.content', $e).text(c);
}
};
var xy = function($e, p) {
if(p === undefined) {
var pos = $e.position();
return {
x: pos.left + parseInt($e.css('width'), 10) / 2,
y: pos.top + parseInt($e.css('height'), 10) / 2
};
}
else {
$e.css('left', p.x - parseInt($e.css('width'), 10) / 2 + 'px');
$e.css('top', p.y - parseInt($e.css('height'), 10) / 2 + 'px');
}
};
var angle = function(x, y) {
var ret = Math.atan2(y, x);
ret += ret < 0? 2 * Math.PI: 0;
ret = Math.min(Math.PI * 2, ret);
return ret + Math.PI;
};
var dist = function(xOffset, yOffset) {
return Math.sqrt(xOffset * xOffset + yOffset * yOffset);
};
var $connection = function($parent, $p1, $p2) {
var $ret = $('<div>').addClass('line').appendTo($parent);
$ret.data('p1', $p1);
$ret.data('p2', $p2);
$ret.render = function() {
var $elem = $(this);
var $p1 = xy($elem.data('p1'));
var $p2 = xy($elem.data('p2'));
$elem.css('left', $p1.x + 'px');
$elem.css('top', $p1.y + 'px');
var xOffset = $p1.x - $p2.x;
var yOffset = $p1.y - $p2.y;
var rot = angle(xOffset, yOffset);
var d = dist(xOffset, yOffset);
$elem.css('-webkit-transform', 'rotate3d(0, 0, 1, ' + rot + 'rad)');
$elem.css('width',...