forked from AlessandraSozzi's block: D3 drag and drop: manually reorder rows and columns of a matrix
forked from wenzelmk's block: D3 drag and drop: manually reorder rows and columns of a matrix
license: gpl-3.0 |
forked from AlessandraSozzi's block: D3 drag and drop: manually reorder rows and columns of a matrix
forked from wenzelmk's block: D3 drag and drop: manually reorder rows and columns of a matrix
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<head> | |
<style> | |
.background { | |
fill: #fff; | |
} | |
rect { | |
stroke: #fff; | |
stroke-width: 1; | |
} | |
text { | |
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; | |
cursor: move; | |
} | |
</style> | |
<title> MATRIX DRAG AND DROP </title> | |
</head> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var n = 6; | |
var margin = {top: 50, right: 30, bottom: 30, left: 50}, | |
width = 800 - margin.left - margin.right, | |
height = 800 - margin.top - margin.bottom; | |
var y = d3.scale.ordinal().rangeBands([0, height],0,1), | |
x = d3.scale.ordinal().rangeBands([0, width],0,1), | |
c = d3.scale.category20().domain(d3.range(36)); | |
var dragging = {}; | |
var svg = d3.select("body") | |
.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.style("margin-left", margin.left + "px") | |
.append("g") | |
.attr("id", "matrix") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var matrixA = [], | |
matrixB = [], | |
nodes = [{"name": "A"}, | |
{"name": "B"}, | |
{"name": "C"}, | |
{"name": "D"}, | |
{"name": "E"}, | |
{"name":"F"} ], | |
n = nodes.length; | |
nodes.forEach(function(node, i) { | |
node.index = i; | |
node.count = 0; | |
matrixA[i] = d3.range(n).map(function(j) { | |
return {x: j, y: i, z: 1 + (n * i) + j}; }); | |
matrixB[i] = d3.range(n).map(function(j) { | |
return {x: i, y: j, z: (n * j) + 1 + i}; }); | |
}); | |
// Default order | |
var orders = { | |
name: d3.range(n).sort(function(a, b) { return d3.ascending(nodes[a].name, nodes[b].name); }) | |
}; | |
// The default sort order. | |
y.domain(orders.name); | |
x.domain(orders.name); | |
svg.append("rect") | |
.attr("class", "background") | |
.attr("width", width) | |
.attr("height", height); | |
var row = svg.selectAll(".row") | |
.data(matrixA) | |
.enter().append("g") | |
.attr("class", "row") | |
.attr("transform", function(d, i) { | |
return "translate(0," + y(i) + ")"; }) | |
.each(row); | |
var column = svg.selectAll(".column") | |
.data(matrixB) | |
.enter().append("g") | |
.attr("class", "column") | |
.attr("transform", function(d, i) { | |
return "translate(" + x(i) + ")rotate(-90)"; }) | |
.each(column); | |
var drag_behavior = d3.behavior.drag(); | |
var trigger; | |
d3.selectAll(".row").call(d3.behavior.drag() | |
.origin(function(d) { | |
return {y: y(d[0].y)}; | |
}) | |
.on("dragstart", function(d) { | |
trigger = d3.event.sourceEvent.target.className.baseVal; | |
if (trigger == "label") { | |
d3.selectAll(".cellrow").attr("opacity", 1); | |
dragging[d[0].y] = y(d[0].y); | |
// Move the row that is moving on the front | |
sel = d3.select(this); | |
sel.moveToFront(); | |
} | |
}) | |
.on("drag", function(d) { | |
// Hide what is in the back | |
if (trigger == "label") { | |
d3.selectAll(".cellcolumn").attr("opacity", 0); | |
dragging[d[0].y] = Math.min(height, Math.max(0, d3.event.y)); | |
orders.name.sort(function(a, b) { return position(a) - position(b); }); | |
y.domain(orders.name); | |
d3.selectAll(".row").attr("transform", function(d, i) { | |
return "translate(0," + position(d[0].y) + ")"; | |
}); | |
} | |
}) | |
.on("dragend", function(d) { | |
if (trigger == "label") { | |
delete dragging[d[0].y]; | |
transition(d3.select(this)).attr("transform", "translate(0," + y(d[0].y) + ")"); | |
d3.selectAll(".column").each(function(d) { | |
d3.select(this).selectAll(".cellcolumn").attr("x", function(d) { | |
return -y(d.y)-90; }); | |
}); | |
} | |
}) | |
); | |
row.append("text") | |
.attr("class", "label") | |
.attr("x", 60) | |
.attr("y", y.rangeBand() / 2) | |
.attr("dy", ".32em") | |
.attr("text-anchor", "end") | |
.text(function(d, i) { return nodes[i].name; }); | |
d3.selectAll(".column").call(drag_behavior | |
.origin(function(d) { | |
return {x: x(d[0].x)}; | |
}) | |
.on("dragstart", function(d) { | |
trigger = d3.event.sourceEvent.target.className.baseVal; | |
if (trigger == "label") { | |
d3.selectAll(".cellcolumn").attr("opacity", 1); | |
dragging[d[0].x] = x(d[0].x); | |
// Move the column that is moving on the front | |
sel = d3.select(this); | |
sel.moveToFront(); | |
} | |
}) | |
.on("drag", function(d) { | |
// Hide what is in the back | |
// console.log(d3.event.x); | |
// console.log(d3.event.y); | |
if (trigger == "label") { | |
d3.selectAll(".cellrow").attr("opacity", 0); | |
dragging[d[0].x] = Math.min(width, Math.max(0, d3.event.x)); | |
orders.name.sort(function(a, b) { return cPosition(a) - cPosition(b); }); | |
x.domain(orders.name); | |
d3.selectAll(".column").attr("transform", function(d, i) { | |
return "translate(" + cPosition(d[0].x) + ")rotate(-90)"; | |
}); | |
} | |
}) | |
.on("dragend", function(d) { | |
delete dragging[d[0].x]; | |
transition(d3.select(this)).attr("transform", "translate(" + x(d[0].x) + ")rotate(-90)"); | |
d3.selectAll(".row").each(function(d, i) { | |
d3.select(this).selectAll(".cellrow").attr("x", function(d) { | |
return x(d.x); }); | |
}); | |
}) | |
); | |
column.append("text") | |
.attr("class", "label") | |
.attr("x", -60) | |
.attr("y", x.rangeBand() / 2) | |
.attr("dy", ".32em") | |
.attr("text-anchor", "start") | |
.text(function(d, i) { return nodes[i].name; }); | |
function row(row) { | |
var cell = d3.select(this).selectAll(".cellrow") | |
.data(row.filter(function(d) { | |
return d.z; | |
})) | |
.enter().append("rect") | |
.attr("class", "cellrow") | |
.attr("x", function(d) { | |
return y(d.x); | |
}) | |
.attr("width", y.rangeBand()) | |
.attr("height", y.rangeBand()) | |
.style("fill", function(d) { | |
return c(d.z); | |
}); | |
} | |
function column(column) { | |
var cell = d3.select(this).selectAll(".cellcolumn") | |
.data(column.filter(function(d) { | |
return d.z; | |
})) | |
.enter().append("rect") | |
.attr("class", "cellcolumn") | |
.attr("x", function(d) { | |
return -x(d.y)-90; | |
}) | |
.attr("width", x.rangeBand()) | |
.attr("height", x.rangeBand()) | |
.style("fill", function(d) { | |
return c(d.z); | |
}); | |
} | |
d3.selection.prototype.moveToFront = function() { | |
return this.each(function(){ | |
this.parentNode.appendChild(this); | |
}); | |
}; | |
function position(d) { | |
var v = dragging[d]; | |
return v == null ? y(d) : v; | |
} | |
function cPosition(d) { | |
var v = dragging[d]; | |
return v == null ? x(d) : v; | |
} | |
function transition(g) { | |
return g.transition().duration(500); | |
} | |
</script> | |
</body> |