Skip to content

Commit

Permalink
groups feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Ni55aN committed Jun 26, 2017
1 parent 08a6454 commit d04a74e
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 14 deletions.
166 changes: 157 additions & 9 deletions src/editor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Connection} from './connection';
import {ContextMenu} from './contextmenu';
import {Group} from './group';
import {Node} from './node';

export class NodeEditor {
Expand All @@ -11,6 +11,7 @@ export class NodeEditor {
this.event = event;
this.active = null;
this.nodes = [];
this.groups = [];
this.builders = builders;

this.pickedOutput = null;
Expand Down Expand Up @@ -115,13 +116,19 @@ export class NodeEditor {
.attr('cx', d.position[0] += self.x.invert(d3.event.dx))
.attr('cy', d.position[1] += self.y.invert(d3.event.dy));
self.update();
}).on('end', function (d) {
self.groups.forEach(group => {
var contain = group.containNode(d);
var cover = group.isCoverNode(d);

if (contain && !cover)
group.removeNode(d);
else if (!contain && cover)
group.addNode(d);
});
}))
.attr('rx', function () {
return 8;
})
.attr('ry', function () {
return 8;
});
.attr('rx', 8)
.attr('ry', 8);

rects.exit().remove();

Expand Down Expand Up @@ -168,6 +175,124 @@ export class NodeEditor {

}

updateGroups() {
var self = this;

var rects = this.view
.selectAll('rect.group')
.data(this.groups, function (d) { return d.name; });

rects.enter()
.append('rect')
.attr('class', 'group')
.on('click', function (d) {
self.selectGroup(d);
})
.each(function () {
d3.select(this).moveToBack();
})
.call(d3.drag().on('drag', function (d) {
d3.select(this)
.attr('cx', d.position[0] += self.x.invert(d3.event.dx))
.attr('cy', d.position[1] += self.y.invert(d3.event.dy));
for (var i in d.nodes) {
var node = d.nodes[i];

node.position[0] += self.x.invert(d3.event.dx);
node.position[1] += self.y.invert(d3.event.dy);
}
self.update();
}));

rects.exit().remove();

this.view.selectAll('rect.group')
.attr('x', function (d) {
return self.x(d.position[0]);
})
.attr('y', function (d) {
return self.y(d.position[1]);
})
.attr('width', function (d) {
return self.x(d.width);
})
.attr('height', function (d) {
return self.y(d.height);
})
.attr('class', function (d) {
return self.active === d ? 'group active' : 'group';
});

var handlers = this.view
.selectAll('rect.group-handler')
.data(this.groups, function (d) { return d.name; });

handlers.enter()
.append('rect')
.attr('class', 'group-handler')
.call(d3.drag().on('drag', function (d) {
d3.select(this)
.attr('cx', d.setWidth(d.width + self.x.invert(d3.event.dx)))
.attr('cy', d.setHeight(d.height + self.y.invert(d3.event.dy)));
self.update();
}).on('end', function (d) {
self.nodes.forEach(node => {
if (d.isCoverNode(node))
d.addNode(node);
else
d.removeNode(node);

});
}));

handlers.exit().remove();

this.view.selectAll('rect.group-handler')
.attr('x', function (d) {
return self.x(d.position[0] + d.width - 2/3*d.handler.size);
})
.attr('y', function (d) {
return self.y(d.position[1] + d.height - 2/3*d.handler.size);
})
.attr('width', function (d) {
return self.x(d.handler.size);
})
.attr('height', function (d) {
return self.y(d.handler.size);
})
.attr('class', 'group-handler');

var titles = this.view
.selectAll('text.group-title')
.data(this.groups, function (d) { return d.id; });

titles.enter()
.append('text')
.classed('group-title', true)
.on('click', function (d) {
var title = prompt('Please enter title of the group', d.title.text);

d.title.text = title;
self.update();
});

titles.exit().remove();

this.view.selectAll('text.group-title')
.attr('x', function (d) {
return self.x(d.position[0] + d.margin);
})
.attr('y', function (d) {
return self.y(d.position[1] + d.margin + d.title.size);
})
.text(function (d) {
return d.title.text;
})
.attr('font-size', function (d) {
return self.x(d.title.size) + 'px';
});
}

updateConnections() {

var self = this;
Expand Down Expand Up @@ -451,6 +576,7 @@ export class NodeEditor {
}

update() {
this.updateGroups();
this.updateConnections();
this.updateNodes();
this.updateSockets();
Expand Down Expand Up @@ -489,17 +615,28 @@ export class NodeEditor {
this.selectNode(node);
}

addGroup(group) {
this.groups.push(group);
this.update();
}

keyDown() {
if (this.dom !== document.activeElement)
return;

switch (d3.event.keyCode) {
case 46:
this.removeNode(this.active);
if (this.active instanceof Node)
this.removeNode(this.active);
else if (this.active instanceof Group)
this.removeGroup(this.active);
this.update();
break;
case 27:
case 71:
if (!(this.active instanceof Node)) { alert('Select the node for adding to group'); return; }
var group = new Group('Group', {nodes:[this.active]});

this.addGroup(group);
break;
}
}
Expand All @@ -516,6 +653,12 @@ export class NodeEditor {
this.update();
}

removeGroup(group) {
group.remove();
this.groups.splice(this.groups.indexOf(group), 1);
this.update();
}

removeConnection(connection) {
connection.remove();
this.event.connectionRemoved(connection);
Expand All @@ -529,6 +672,11 @@ export class NodeEditor {
this.update();
}

selectGroup(group) {
this.active = group;
this.update();
}

remove() {
this.dom.remove();
}
Expand Down
93 changes: 93 additions & 0 deletions src/group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
export class Group {

constructor(title, params) {
this.id = Group.incrementId();
this.title = {
text: title,
margin: 0.012,
size: 0.016
};

this.margin = 0.01;
this.nodes = [];
this.minWidth = 0.3;
this.minHeight = 0.14;
this.handler = {
size: 0.02
};

if (params.nodes)
this.coverNodes(params.nodes);
else {
this.position = params.position;
this.width = params.width;
this.height = params.height;
}
}

static incrementId() {
if (!this.latestId)
this.latestId = 1
else
this.latestId++
return this.latestId
}

setWidth(w) {
return this.width = Math.max(this.minWidth, w);
}

setHeight(h) {
return this.height = Math.max(this.minHeight, h);
}

isCoverNode(node) {
var gp = this.position;
var np = node.position;

return np[0] > gp[0] && np[1] > gp[1]
&& np[0] + node.width < gp[0] + this.width
&& np[1] + node.height < gp[1] + this.height;
}

coverNodes(nodes) {
var self = this;
var margin = 0.02;
var minX = Math.min(...nodes.map(node => node.position[0]));
var minY = Math.min(...nodes.map(node => node.position[1]));
var maxX = Math.max(...nodes.map(node => node.position[0] + node.width));
var maxY = Math.max(...nodes.map(node => node.position[1] + node.height));

nodes.forEach(node => {
if (node.group !== null) node.group.removeNode(node.group);
self.addNode(node);
});
this.position = [minX - margin, minY - 2 * margin];
this.setWidth(maxX - minX + 2 * margin);
this.setHeight(maxY - minY + 3 * margin);
}

containNode(node) {
return this.nodes.indexOf(node) !== -1;
}

addNode(node) {
if (this.containNode(node)) return false;
if (node.group !== null) node.group.removeNode(node);
node.group = this;
this.nodes.push(node);
return true;
}

removeNode(node) {
if (this.containNode(node))
this.nodes.splice(this.nodes.indexOf(node), 1);
node.group = null;
}

remove() {
this.nodes.forEach(node => {
node.group = null;
})
}
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Connection} from './connection';
import {ContextMenu} from './contextmenu';
import {Control} from './control';
import {Events} from './events';
import {Group} from './group';
import {Input} from './input';
import {Node} from './node';
import {NodeBuilder} from './nodebuilder';
Expand All @@ -15,6 +16,7 @@ export {
Control,
NodeEditor,
Events,
Group,
Input,
Node,
NodeBuilder,
Expand Down
1 change: 1 addition & 0 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class Node {

constructor(title, width) {
this.id = Node.incrementId();
this.group = null;
this.inputs = [];
this.outputs = [];
this.controls = [];
Expand Down
23 changes: 18 additions & 5 deletions src/style/style.sass
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
$node-color: #6e88ff

$node-color-active: #ffd92c
$group-color: rgba(15,80,255,0.2)
html,body
margin: 0
height: 100%
Expand All @@ -13,15 +14,27 @@ html,body
&:hover
fill: lighten($node-color,4%)
&.active
fill: #ffd92c
fill: $node-color-active
stroke: #e3c000

.group
rx: 16
ry: 16
fill: $group-color
cursor: pointer
&.active
stroke: #ffd92c
stroke-width: 6
.group-title
fill: white
font-family: sans-serif
.group-handler
fill: transparent
cursor: se-resize

.connection
fill: none
stroke: steelblue
stroke-width: 5px
&.active
stroke: #e3c000

.input,.output
cursor: pointer
Expand Down

0 comments on commit d04a74e

Please sign in to comment.