dc.graph
Version:
Graph visualizations integrated with crossfilter and dc.js
144 lines (137 loc) • 5.53 kB
JavaScript
dc_graph.move_nodes = function(options) {
options = options || {};
var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes');
var fix_nodes_group = dc_graph.fix_nodes_group(options.fix_nodes_group || 'fix-nodes-group');
var _selected = [], _startPos = null, _downNode, _moveStarted;
var _brush, _drawGraphs, _selectNodes, _restoreBackgroundClick, _keyboard;
var _maybeSelect = null;
function isUnion(event) {
return event.shiftKey;
}
function isToggle(event) {
return is_a_mac ? event.metaKey : event.ctrlKey;
}
function selection_changed(diagram) {
return function(selection, refresh) {
if(refresh === undefined)
refresh = true;
_selected = selection;
};
}
function for_each_selected(f, selected) {
selected = selected || _selected;
selected.forEach(function(key) {
var n = _mode.parent().getWholeNode(key);
f(n, key);
});
}
function draw(diagram, node, edge) {
node.on('mousedown.move-nodes', function(n) {
// Need a more general way for modes to say "I got this"
if(_drawGraphs && _drawGraphs.usePorts() && _drawGraphs.usePorts().eventPort())
return;
if(!_keyboard.modKeysMatch(_mode.modKeys()))
return;
_startPos = dc_graph.event_coords(diagram);
_downNode = d3.select(this);
// if the node under the mouse is not in the selection, need to
// make that node selected
var key = diagram.nodeKey.eval(n);
var selected = _selected;
if(_selected.indexOf(key)<0) {
selected = [key];
_maybeSelect = key;
}
else _maybeSelect = null;
for_each_selected(function(n) {
n.original_position = [n.cola.x, n.cola.y];
}, selected);
if(_brush)
_brush.deactivate();
});
function mouse_move() {
if(_startPos) {
if(!(d3.event.buttons & 1)) {
mouse_up();
return;
}
if(_maybeSelect)
select_nodes_group.set_changed([_maybeSelect]);
var pos = dc_graph.event_coords(diagram);
var dx = pos[0] - _startPos[0],
dy = pos[1] - _startPos[1];
if(!_moveStarted && Math.hypot(dx, dy) > _mode.dragSize()) {
_moveStarted = true;
// prevent click event for this node setting selection just to this
if(_downNode)
_downNode.style('pointer-events', 'none');
}
if(_moveStarted) {
for_each_selected(function(n) {
n.cola.x = n.original_position[0] + dx;
n.cola.y = n.original_position[1] + dy;
});
var node2 = node.filter(function(n) { return _selected.includes(n.orig.key); }),
edge2 = edge.filter(function(e) {
return _selected.includes(e.source.orig.key) ||
_selected.includes(e.target.orig.key);
});
diagram.reposition(node2, edge2);
}
}
}
function mouse_up() {
if(_startPos) {
if(_moveStarted) {
_moveStarted = false;
if(_downNode) {
_downNode.style('pointer-events', null);
_downNode = null;
}
var fixes = [];
for_each_selected(function(n, id) {
fixes.push({
id: id,
pos: {x: n.cola.x, y: n.cola.y}
});
});
fix_nodes_group.request_fixes(fixes);
}
if(_brush)
_brush.activate();
_startPos = null;
}
}
node
.on('mousemove.move-nodes', mouse_move)
.on('mouseup.move-nodes', mouse_up);
diagram.svg()
.on('mousemove.move-nodes', mouse_move)
.on('mouseup.move-nodes', mouse_up);
}
function remove(diagram, node, edge) {
node.on('mousedown.move-nodes', null);
node.on('mousemove.move-nodes', null);
node.on('mouseup.move-nodes', null);
}
var _mode = dc_graph.mode('move-nodes', {
draw: draw,
remove: remove,
parent: function(p) {
select_nodes_group.on('set_changed.move-nodes', p ? selection_changed(p) : null);
if(p) {
_brush = p.child('brush');
_drawGraphs = p.child('draw-graphs');
_selectNodes = p.child('select-nodes');
_keyboard = p.child('keyboard');
if(!_keyboard)
p.child('keyboard', _keyboard = dc_graph.keyboard());
}
else _brush = _drawGraphs = _selectNodes = null;
}
});
// minimum distance that is considered a drag, not a click
_mode.dragSize = property(5);
_mode.modKeys = property(null);
return _mode;
};