Created
March 31, 2014 19:42
-
-
Save billdwhite/9900551 to your computer and use it in GitHub Desktop.
d3 Multi Flow Container Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo = {}; | |
d3.demo.currentScale = 1; | |
d3.demo.app = function() { | |
"use strict"; | |
var width = 600, | |
height = 900, | |
margin = {top: 0, right: 0, bottom: 0, left: 0}, | |
base = null, | |
shapes = [], | |
wrapperBorder = 2, | |
canvas = null; | |
function app(selection) { | |
base = selection; | |
var svg = selection.append("svg") | |
.attr("class", "svg demo") | |
.style("shape-rendering", "auto") // shapeRendering options; [crispEdges|geometricPrecision|optimizeSpeed|auto] | |
.style("text-rendering", "auto") // textRendering options; [optimizeLegibility|geometricPrecision|optimizeSpeed|auto] | |
.style("color-rendering", "auto") // colorRendering options; [optimizeQuality|optimizeSpeed|auto] | |
.attr("width", width + (wrapperBorder*2)) | |
.attr("height", height + (wrapperBorder*2)); | |
var svgDefs = svg.append("defs"); | |
// clip path | |
svgDefs.append("clipPath") | |
.attr("id", "wrapperClipPath") | |
.attr("class", "clipPath") | |
.append("rect") | |
.attr("class", "background") | |
.attr("width", width) | |
.attr("height", height); | |
// minimap drop shadow | |
var shapeDropShadow = svgDefs.append("svg:filter") | |
.attr("id", "shapeDropShadow") | |
.attr("x", "-20%") | |
.attr("y", "-20%") | |
.attr("width", "140%") | |
.attr("height", "140%"); | |
shapeDropShadow.append("svg:feOffset") | |
.attr("result", "offOut") | |
.attr("in", "SourceGraphic") | |
.attr("dx", "1") | |
.attr("dy", "1"); | |
shapeDropShadow.append("svg:feColorMatrix") | |
.attr("result", "matrixOut") | |
.attr("in", "offOut") | |
.attr("type", "matrix") | |
.attr("values", "0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.5 0"); | |
shapeDropShadow.append("svg:feGaussianBlur") | |
.attr("result", "blurOut") | |
.attr("in", "matrixOut") | |
.attr("stdDeviation", "3"); | |
shapeDropShadow.append("svg:feBlend") | |
.attr("in", "SourceGraphic") | |
.attr("in2", "blurOut") | |
.attr("mode", "normal"); | |
var insetShadowFilter = svgDefs.append("svg:filter") | |
.attr("id", "insetShadowFilter"); | |
insetShadowFilter.append("svg:feOffset") | |
.attr("dx", "0") | |
.attr("dy", "0"); | |
insetShadowFilter.append("svg:feGaussianBlur") | |
.attr("stdDeviation", "5") | |
.attr("result", "offsetBlur"); | |
insetShadowFilter.append("svg:feComposite") | |
.attr("operator", "out") | |
.attr("in", "SourceGraphic") | |
.attr("in2", "offsetBlur") | |
.attr("result", "inverse"); | |
insetShadowFilter.append("svg:feFlood") | |
.attr("flood-color", "black") | |
.attr("flood-opacity", ".5") | |
.attr("result", "color"); | |
insetShadowFilter.append("svg:feComposite") | |
.attr("operator", "in") | |
.attr("in", "color") | |
.attr("in2", "inverse") | |
.attr("result", "shadow"); | |
insetShadowFilter.append("svg:feComposite") | |
.attr("operator", "over") | |
.attr("in", "shadow") | |
.attr("in2", "SourceGraphic"); | |
var outerWrapper = svg.append("g") | |
.attr("class", "wrapper outer"); | |
outerWrapper.append("rect") | |
.attr("class", "background") | |
.attr("rx", "6") | |
.attr("ry", "6") | |
.attr("width", width + wrapperBorder*2) | |
.attr("height", height + wrapperBorder*2); | |
var innerWrapper = outerWrapper.append("g") | |
.attr("class", "wrapper inner") | |
.attr("clip-path", "url(#wrapperClipPath)") | |
.attr("transform", "translate(" + (wrapperBorder) + "," + (wrapperBorder) + ")"); | |
innerWrapper.append("rect") | |
.attr("class", "background") | |
.attr("rx", "6") | |
.attr("ry", "6") | |
.attr("width", width) | |
.attr("height", height); | |
canvas = innerWrapper.append("g") | |
.attr("class", "canvas") | |
.attr("width", width) | |
.attr("height", height) | |
.attr("transform", "translate(0,0)"); | |
canvas.append("rect") | |
.attr("class", "background") | |
.attr("rx", "6") | |
.attr("ry", "6") | |
.attr("width", width) | |
.attr("height", height); | |
var shapesGroup = canvas.append("g") | |
.attr("class", "shapes") | |
.attr("transform", "translate(" + (-wrapperBorder) + "," + (-wrapperBorder) + ")"); | |
function getMousePoint(context) { | |
var p = d3.mouse(context), | |
t = d3.demo.util.getXYFromTranslate(canvas.attr("transform")); | |
p[0] = (p[0] - t[0]); | |
p[1] = (p[1] - t[1]); | |
return p; | |
} | |
// DRAG SELECTION HANDLERS | |
svg.on({ | |
mousedown: function() { | |
if (d3.event.target === canvas.select(".background").node()) { | |
shapes.forEach(function(shape) { // clear existing selections | |
shape.selected(false).render(); | |
}); | |
var p = getMousePoint(this); | |
this.selectionAnchorPoint = p; | |
canvas.append("rect") | |
.attr({ | |
rx: 4, | |
ry: 4, | |
class: "selection", | |
x: p[0], | |
y: p[1], | |
width: 0, | |
height: 0 | |
}); | |
} | |
}, | |
mousemove: function() { | |
var selectionRectangle = canvas.select("rect.selection"); | |
if (!selectionRectangle.empty()) { | |
var mp = getMousePoint(this), | |
ap = this.selectionAnchorPoint, | |
d = {x:0, y:0, width:0, height:0}; | |
// horizontal selection sizing | |
if (mp[0] < ap[0]) { | |
d.x = mp[0]; | |
d.width = ap[0] - mp[0]; | |
} else { | |
d.x = ap[0]; | |
d.width = mp[0] - ap[0]; | |
} | |
// vertical selection sizing | |
if (mp[1] < ap[1]) { | |
d.y = mp[1]; | |
d.height = ap[1] - mp[1]; | |
} else { | |
d.y = ap[1]; | |
d.height = mp[1] - ap[1]; | |
} | |
selectionRectangle.attr(d); | |
shapes.forEach(function(shape) { | |
var p1 = {x:shape.x(), y:shape.y()}, | |
p2 = {x:shape.x() + shape.width() ,y:shape.y() + shape.height()}, | |
p3 = {x:d.x, y:d.y}, | |
p4 = {x:d.x + d.width,y:d.y + d.height}; | |
shape.selected(!(p2.y < p3.y || p1.y > p4.y || p2.x < p3.x || p1.x > p4.x)).render(); | |
}); | |
} | |
}, | |
mouseup: function() { | |
canvas.selectAll("rect.selection").remove(); // remove selection frame | |
}, | |
mouseout: function() { | |
if (!d3.event.relatedTarget || d3.event.relatedTarget.tagName == 'HTML') { | |
canvas.selectAll("rect.selection").remove(); // remove selection frame | |
} | |
} | |
}); | |
// SELECTION DRAGGING HANDLERS | |
function handleShapeDragStart(event) { | |
shapes.forEach(function(shape) { | |
if (shape.selected() && shape.uuid() != event.shape.uuid()) { | |
shape.slaveDragStart(); | |
} | |
}); | |
} | |
function handleShapeDrag(event) { | |
shapes.forEach(function(shape) { | |
if (shape.selected() && shape.uuid() != event.shape.uuid()) { | |
shape.slaveDrag(); | |
} | |
}); | |
} | |
function handleShapeDragEnd(event) { | |
shapes.forEach(function(shape) { | |
if (shape.selected() && shape.uuid() != event.shape.uuid()) { | |
shape.slaveDragEnd(); | |
} | |
}); | |
} | |
/** ADD SHAPE **/ | |
demo.addShape = function(shape) { | |
shapes.push(shape); | |
shapesGroup.call(shape); | |
shape.on("shapeDragStart", handleShapeDragStart); // selection drag handler | |
shape.on("shapeDrag", handleShapeDrag); // selection drag handler | |
shape.on("shapeDragEnd", handleShapeDragEnd); // selection drag handler | |
}; | |
/** RENDER **/ | |
demo.render = function() { | |
svgDefs | |
.select(".clipPath .background") | |
.attr("width", width) | |
.attr("height", height); | |
svg | |
.attr("width", width + (wrapperBorder*2)) | |
.attr("height", height + (wrapperBorder*2)); | |
outerWrapper | |
.select(".background") | |
.attr("width", width + wrapperBorder*2) | |
.attr("height", height + wrapperBorder*2); | |
innerWrapper | |
.attr("transform", "translate(" + (wrapperBorder) + "," + (wrapperBorder) + ")") | |
.select(".background") | |
.attr("width", width) | |
.attr("height", height); | |
canvas | |
.attr("width", width) | |
.attr("height", height) | |
.select(".background") | |
.attr("width", width) | |
.attr("height", height); | |
}; | |
return demo; | |
} | |
//============================================================ | |
// Accessors | |
//============================================================ | |
app.width = function(value) { | |
if (!arguments.length) return width; | |
width = parseInt(value, 10); | |
width = width - margin.left - margin.right; | |
return this; | |
}; | |
app.height = function(value) { | |
if (!arguments.length) return height; | |
height = parseInt(value, 10); | |
height - margin.top - margin.bottom; | |
return this; | |
}; | |
return app; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.container = function() { | |
"use strict"; | |
var shape = d3.demo.shape(), | |
children = [], | |
flowbox = null; | |
function container(selection) { | |
shape.call(shape, selection); | |
d3.select(shape.node).classed("container", true); | |
flowbox = d3.demo.flowbox().shape(shape).x(5).y(30); | |
d3.select(shape.node).call(flowbox); | |
container.node = shape.node; | |
container.render = function() { | |
flowbox.children(children).render(); | |
setTimeout(shape.render, 250); // give flow time to run before rendering | |
}; | |
container.render(); | |
} | |
container.children = function(value) { | |
if (!arguments.length) { return children; } | |
children = value; | |
return this; | |
}; | |
return d3_shape_rebind(container, shape); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.flowbox = function() { | |
"use strict"; | |
var x = 0, | |
y = 0, | |
layout = d3.demo.layout.flow(), | |
shape = null, | |
base = null, | |
uuid = d3.demo.util.uuid(), | |
children = []; | |
var dispatch = d3.dispatch( | |
"flowboxMouseOver", | |
"flowboxMouseOut", | |
"flowboxMouseUp"); | |
function flowbox(selection) { | |
base = selection; | |
layout.nodeWidth(75).nodeHeight(24); | |
selection.each(function(d, i) { | |
var parent = d3.select(this); | |
var container = parent.append("g") | |
.classed("flowbox", "true") | |
.attr("transform", "translate(" + x + "," + y + ")") | |
.attr("clip-path", "url(#clippath" + uuid + ")"); | |
container.append("rect") | |
.classed("background", true); | |
flowbox.node = container.node(); | |
var clipPath = d3.select("svg defs") | |
.append("clipPath") | |
.attr("class", "clippath") | |
.attr("id", "clippath" + uuid); | |
clipPath.append("rect") | |
.attr("class", "background") | |
.attr("width", shape.width() - (flowbox.x() * 2)) | |
.attr("height", shape.height() - (flowbox.y() + flowbox.x())); | |
flowbox.render = function() { | |
container | |
.select(".background") | |
.attr("width", shape.width() - (flowbox.x() * 2)) | |
.attr("height", shape.height() - (flowbox.y() + flowbox.x())); | |
clipPath | |
.attr("width", shape.width() - (flowbox.x() * 2)) | |
.attr("height", shape.height() - (flowbox.y() + flowbox.x())) | |
.select(".background") | |
.attr("width", shape.width() - (flowbox.x() * 2)) | |
.attr("height", shape.height() - (flowbox.y() + flowbox.x())); | |
layout.width(shape.width()); | |
var nodes = layout(children); | |
var node = container.selectAll("g.node") | |
.data(nodes[0], function(d) { return d.id || (d.id = ++i); }); | |
var nodeEnter = node.enter().append("svg:g") | |
.attr("class", "node") | |
.attr("transform", function(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
}) | |
.style("opacity", 1e-6); | |
nodeEnter.append("svg:rect") | |
.attr("class", "background") | |
.attr("rx", "4") | |
.attr("ry", "4") | |
.attr("height", function(d) { return d.height; }) | |
.attr("width", function(d) { return d.width; }); | |
nodeEnter.append("svg:text") | |
.attr("class", "label nodelabel") | |
.attr("transform", "translate(5, 15)") | |
.text(function(d) { return d.name; }); | |
// Transition nodes to their new position. | |
nodeEnter.transition() | |
.duration(300) | |
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
.style("opacity", 1); | |
var nodeUpdate = node.transition() | |
.duration(300); | |
nodeUpdate.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
.style("opacity", 1) | |
.select("rect"); | |
nodeUpdate.select(".background") | |
.attr("height", function(d) { return d.height; }) | |
.attr("width", function(d) { return d.width; }); | |
// Transition exiting nodes to the parent's new position. | |
node.exit().transition() | |
.duration(300) | |
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
.style("opacity", 1e-6) | |
.remove(); | |
}; | |
flowbox.render(); | |
shape.on("shapeUpdated.container", flowbox.render); // render when shape changes size/shape, etc | |
}); | |
} | |
flowbox.children = function(value) { | |
if (!arguments.length) { return children; } | |
children = value; | |
flowbox.render(); | |
return this; | |
}; | |
flowbox.shape = function(value) { | |
if (!arguments.length) { return r; } | |
shape = value; | |
return this; | |
}; | |
flowbox.x = function(value) { | |
if (!arguments.length) { return x; } | |
x = parseInt(value, 10); | |
return this; | |
}; | |
flowbox.y = function(value) { | |
if (!arguments.length) { return y; } | |
y = parseInt(value, 10); | |
return this; | |
}; | |
d3.rebind(flowbox, dispatch, "on"); | |
return flowbox; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.handle = function() { | |
"use strict"; | |
var width = 8, | |
height = 20, | |
x = 0, | |
y = 0, | |
type = null, | |
base = null; | |
var dispatch = d3.dispatch( | |
"handleMouseover", | |
"handleMouseout", | |
"handleDragStart", | |
"handleDrag", | |
"handleDragEnd"); | |
function handle(selection, evented) { | |
base = selection; | |
evented.on("shapeResize", function() { | |
//log("handle: shapeResize()"); | |
}); | |
selection.each(function(d, i) { | |
var parent = d3.select(this); | |
var container = parent.append("g") | |
.attr("class", "handle") | |
.attr("transform", "translate(" + x + "," + y + ")"); | |
handle.node = container.node(); | |
var background = container.append("rect") | |
.attr("class", "background") | |
.attr("width", width) | |
.attr("height", height) | |
.on("mouseover", function(event) { | |
d3.select(this).classed("hover", true); | |
dispatch.handleMouseover({ | |
handle: this, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}) | |
.on("mouseout", function(event) { | |
d3.select(this).classed("hover", false); | |
dispatch.handleMouseout({ | |
handle: this, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}); | |
switch(type) { | |
case handle.TYPE_TOP: | |
case handle.TYPE_BOTTOM: | |
background.classed("vertical", true); | |
break; | |
case handle.TYPE_LEFT: | |
case handle.TYPE_RIGHT: | |
background.classed("horizontal", true); | |
break; | |
default: | |
break; | |
} | |
var drag = d3.behavior.drag() | |
.origin(Object) | |
.on("dragstart", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
container.classed("dragging", true); | |
dispatch.handleDragStart({ | |
handle: this, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}) | |
.on("drag", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
var selection = d3.select(this); | |
switch(type) { | |
case handle.TYPE_TOP: | |
y += d3.event.dy; | |
break; | |
case handle.TYPE_BOTTOM: | |
y += d3.event.dy; | |
break; | |
case handle.TYPE_LEFT: | |
x += d3.event.dx; | |
break; | |
case handle.TYPE_RIGHT: | |
x += d3.event.dx; | |
break; | |
default: | |
break; | |
} | |
container.attr("transform", "translate(" + x + "," + y + ")"); | |
dispatch.handleDrag({ | |
handle: this, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}) | |
.on("dragend", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
dispatch.handleDragEnd({ | |
handle: this, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
container.classed("dragging", false); | |
}); | |
container.call(drag); | |
handle.render = function() { | |
container.attr("transform", "translate(" + x + "," + y + ")"); | |
}; | |
container.call(drag); | |
}); | |
} | |
handle.x = function(value) { | |
if (!arguments.length) return x; | |
x = parseInt(value, 10); | |
return this; | |
}; | |
handle.y = function(value) { | |
if (!arguments.length) return y; | |
y = parseInt(value, 10); | |
return this; | |
}; | |
handle.width = function(value) { | |
if (!arguments.length) return width; | |
width = parseInt(value, 10); | |
return this; | |
}; | |
handle.height = function(value) { | |
if (!arguments.length) return height; | |
height = parseInt(value, 10); | |
return this; | |
}; | |
handle.type = function(value) { | |
if (!arguments.length) return type; | |
type = value; | |
return this; | |
}; | |
handle.TYPE_RIGHT = "right"; | |
handle.TYPE_LEFT = "left"; | |
handle.TYPE_TOP = "top"; | |
handle.TYPE_BOTTOM = "bottom"; | |
d3.rebind(handle, dispatch, "on"); | |
return handle; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.layout = {}; | |
d3.demo.layout.flow = function() { | |
var hierarchy = d3.layout.hierarchy().sort(null).value(null), | |
nodeWidth = 75, | |
nodeHeight = 50, | |
containerHeight = 20, | |
width = 0, | |
height = 0, | |
padding = {top:10, left:10, bottom:10, right:10}, | |
margin = {top:10, left:10, bottom:10, right:10}; | |
function flow(d, i) { | |
var nodes = hierarchy.call(this, d, i), | |
root = nodes[0]; | |
function firstWalk(node) { | |
var children = node.children ? node.children : node.length>0 ? node : null; | |
if (children && children.length > 0) { | |
var n = children.length, | |
i = -1, | |
child; | |
while (++i < n) { | |
child = children[i]; | |
firstWalk(child); | |
} | |
gridLayout(node, children, node.depth); | |
} else { | |
node.width = node._children ? width - (node.depth * (padding.left + padding.right)) - (padding.left + padding.right) : nodeWidth; | |
node.height = node._children ? containerHeight : nodeHeight; | |
} | |
} | |
function secondWalk(node) { | |
var children = node.children; | |
if (children && children.length > 0) { | |
var i = -1, | |
n = children.length, | |
child; | |
while (++i < n) { | |
child = children[i]; | |
child.x += node.x; | |
child.y += node.y; | |
secondWalk(child); | |
} | |
} | |
} | |
function gridLayout(node, children, depth) { | |
var paddingValue = node.parent ? padding.left + padding.right : margin.left + margin.right; | |
var availableWidth = width - (depth * (paddingValue)) - (paddingValue), | |
currentX = padding.left, | |
currentY = padding.top, | |
tallestChildHeight = 0; | |
children.forEach(function(child) { | |
if ((currentX + child.width + padding.right) >= availableWidth) { | |
currentX = padding.right; | |
currentY += tallestChildHeight; | |
tallestChildHeight = 0; | |
} | |
child.x = currentX; | |
child.y = currentY; | |
currentX += child.width + padding.right; | |
tallestChildHeight = Math.max(tallestChildHeight, child.height + padding.bottom); | |
}); | |
node.width = availableWidth; | |
node.height = currentY + tallestChildHeight; | |
node.x = node.parent ? padding.left : margin.left; | |
node.y = node.parent ? padding.top : margin.top; | |
} | |
firstWalk(root); | |
secondWalk(root); | |
height = root.height; | |
return nodes; | |
} | |
flow.margin = function(_) { | |
if (!arguments.length) return margin; | |
margin.top = typeof _.top != 'undefined' ? _.top : margin.top; | |
margin.right = typeof _.right != 'undefined' ? _.right : margin.right; | |
margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; | |
margin.left = typeof _.left != 'undefined' ? _.left : margin.left; | |
return this; | |
}; | |
flow.padding = function(_) { | |
if (!arguments.length) return padding; | |
padding.top = typeof _.top != 'undefined' ? _.top : padding.top; | |
padding.right = typeof _.right != 'undefined' ? _.right : padding.right; | |
padding.bottom = typeof _.bottom != 'undefined' ? _.bottom : padding.bottom; | |
padding.left = typeof _.left != 'undefined' ? _.left : padding.left; | |
return this; | |
}; | |
flow.width = function(value) { | |
if (!arguments.length) return width; | |
width = parseInt(value); | |
return this; | |
}; | |
flow.height = function(value) { | |
if (!arguments.length) return height; | |
height = parseInt(value); | |
return this; | |
}; | |
flow.nodeWidth = function(value) { | |
if (!arguments.length) return nodeWidth; | |
nodeWidth = parseInt(value); | |
return this; | |
}; | |
flow.nodeHeight = function(value) { | |
if (!arguments.length) return nodeHeight; | |
nodeHeight = parseInt(value); | |
return this; | |
}; | |
flow.containerHeight = function(value) { | |
if (!arguments.length) return containerHeight; | |
containerHeight = parseInt(value); | |
return this; | |
}; | |
return flow; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.shape = function() { | |
"use strict"; | |
var width = 150, | |
height = 100, | |
x = 0, | |
y = 0, | |
label = "", | |
selected = false, | |
base = null, | |
uuid = d3.demo.util.uuid(); | |
var dispatch = d3.dispatch( | |
"shapeDragStart", | |
"shapeDrag", | |
"shapeDragEnd", | |
"shapeResize", | |
"shapeUpdated"); | |
function shape(selection) { | |
base = selection; | |
selection.each(function(d, i) { | |
var parent = d3.select(this); | |
var container = parent.append("g") | |
.attr("class", "shape") | |
.attr("transform", "translate(" + x + "," + y + ")"); | |
shape.node = container.node(); | |
var background = container.append("rect") | |
.attr("class", "background") | |
.attr("filter", "url('#shapeDropShadow')") | |
.attr("width", width) | |
.attr("height", height) | |
.attr("rx", "4") | |
.attr("ry", "4"); | |
var labelText = container.append("text") | |
.attr("class", "label") | |
.attr("transform", "translate(5, 20)") | |
.text(label); | |
// HANDLES | |
var rightHandle = d3.demo.handle(); | |
rightHandle | |
.x(width - rightHandle.width()/2) | |
.y(height/2 - rightHandle.height()/2) | |
.type(rightHandle.TYPE_RIGHT) | |
.on("handleDrag", function(event) { | |
width += d3.event.dx; | |
shape.render(); | |
dispatch.shapeUpdated({ | |
this: container, | |
shape: shape | |
}); | |
}) | |
.on("handleDragEnd", function(event) { | |
dispatch.shapeUpdated({ | |
this: container, | |
shape: shape | |
}); | |
}); | |
container.call(rightHandle, shape); | |
var drag = d3.behavior.drag() | |
.on("dragstart", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
d3.demo.util.bringToFront(container.node()); | |
dispatch.shapeDragStart({ | |
shape: shape, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}) | |
.on("drag", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
background.classed("dragging", true); | |
dispatch.shapeDrag({ | |
shape: shape, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
x += d3.event.dx; | |
y += d3.event.dy; | |
shape.render(); | |
}) | |
.on("dragend", function(d) { | |
d3.event.sourceEvent.stopImmediatePropagation(); | |
background.classed("dragging", false); | |
dispatch.shapeDragEnd({ | |
shape: shape, | |
pos: [d3.event.x, d3.event.y] | |
}); | |
}); | |
container.call(drag); | |
shape.render = function() { | |
container.attr("transform", "translate(" + x + "," + y + ")") | |
.attr("width", width) | |
.attr("height", height) | |
.classed("selected", selected) | |
.select(".background") | |
.attr("width", width) | |
.attr("height", height); | |
dispatch.shapeUpdated({ | |
this: container, | |
shape: this | |
}); | |
labelText.text(label); | |
rightHandle.x(width - rightHandle.width()/2).y(height/2 - rightHandle.height()/2).render(); | |
}; | |
}); | |
} | |
shape.x = function(value) { | |
if (!arguments.length) { return x; } | |
x = parseInt(value, 10); | |
return this; | |
}; | |
shape.y = function(value) { | |
if (!arguments.length) { return y; } | |
y = parseInt(value, 10); | |
return this; | |
}; | |
shape.width = function(value) { | |
if (!arguments.length) { return width; } | |
width = parseInt(value, 10); | |
if (shape.render) { shape.render(); } | |
return this; | |
}; | |
shape.height = function(value) { | |
if (!arguments.length) { return height; } | |
height = parseInt(value, 10); | |
if (shape.render) { shape.render(); } | |
return this; | |
}; | |
shape.label = function(value) { | |
if (!arguments.length) { return label; } | |
label = value; | |
return this; | |
}; | |
shape.selected = function(value) { | |
if (!arguments.length) { return selected; } | |
selected = value; | |
return this; | |
}; | |
shape.slaveDragStart = function() { | |
d3.select(this.node).select(".background").classed("dragging", true); | |
}; | |
shape.slaveDrag = function() { | |
x += d3.event.dx; | |
y += d3.event.dy; | |
shape.render(); | |
}; | |
shape.slaveDragEnd = function() { | |
d3.select(this.node).select(".background").classed("dragging", false); | |
}; | |
shape.uuid = function(value) { | |
if (!arguments.length) { return uuid; } | |
uuid = value; | |
return this; | |
}; | |
d3.rebind(shape, dispatch, "on"); | |
return shape; | |
}; | |
// A method assignment helper for hierarchy subclasses. | |
function d3_shape_rebind(object, hierarchy) { | |
d3.rebind(object, hierarchy, "render", "x", "y", "width", "height", "label", "selected", "slaveDragStart", "slaveDrag", "slaveDragEnd", "uuid", "on"); | |
return object; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.demo.util = {}; | |
d3.demo.util.bringToFront = function(element) { | |
element.parentNode.appendChild(element); | |
}; | |
d3.demo.util.getXYFromTranslate = function(translateString) { | |
var split = translateString.split(","); | |
var x = split[0] ? ~~split[0].split("(")[1] : 0; | |
var y = split[1] ? ~~split[1].split(")")[0] : 0; | |
return [x, y]; | |
}; | |
d3.demo.util.getXYFromTranslateOnNode = function(element) { | |
return d3.demo.util.getXYFromTranslate(element.getAttribute("transform")); | |
}; | |
d3.demo.util.makePoint = function(element, sourceElement, xy) { | |
var p = element.nearestViewportElement.createSVGPoint(); | |
var matrix = element.getTransformToElement(sourceElement.nearestViewportElement); | |
//matrix.scale(d3.demo.scale); | |
matrix.scale(1); | |
p.x = xy[0]; | |
p.y = xy[1]; | |
var sp = p.matrixTransform(matrix); | |
return sp; | |
}; | |
d3.demo.util.getScreenCoords = function(x, y, translate, scale) { | |
var xn = (x - translate[0]) / scale; | |
var yn = (y - translate[1]) / scale; | |
return { | |
x: xn, | |
y: yn | |
}; | |
}; | |
d3.demo.util.getCTMCoords = function(x, y, ctm) { | |
var xn = ctm.e + x * ctm.a; | |
var yn = ctm.f + y * ctm.d; | |
return { | |
x: xn, | |
y: yn | |
}; | |
}; | |
d3.demo.util.uuid = function() { | |
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); | |
}; | |
function s4() { | |
return Math.floor((1 + Math.random()) * 0x10000) | |
.toString(16) | |
.substring(1); | |
} | |
function log(message, args) { | |
console.log(message, args); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<style> | |
body { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
} | |
.demo .canvas .background { | |
fill: #F3F3F3; | |
stroke: #333333; | |
} | |
.demo .wrapper.outer > .background { | |
fill: #000000; | |
} | |
.demo .wrapper.inner > .background { | |
fill: #DDDDDD; | |
} | |
rect.selection { | |
stroke : #111111; | |
stroke-dasharray: 2px; | |
stroke-opacity : 0.75; | |
fill : transparent; | |
} | |
/* SHAPES */ | |
.demo .shape .background { | |
fill: #0078E7; | |
stroke: #333333; | |
stroke-width: 0.5; | |
} | |
.demo .shape.selected > .background { | |
stroke-dasharray: 2px; | |
stroke-width : 3; | |
stroke: #000000; | |
filter: ""; | |
} | |
.demo .shape .label { | |
font : 12px sans-serif; | |
pointer-events : none; | |
fill: #FFFFFF; | |
} | |
.demo .container .containerGroup .background, | |
.demo .flowbox > .background { | |
fill: #FFFFFF; | |
stroke: #999999; | |
} | |
.demo .flowbox .node > .background { | |
fill: #2ECC71; | |
stroke: #333333; | |
stroke-width: 0.5; | |
} | |
.demo .flowbox .node > .label { | |
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 11px; | |
fill: #000000; | |
stroke-width: 0; | |
} | |
.demo .clippath .background { | |
fill: #FFFFFF; | |
} | |
/* HANDLES */ | |
.demo .handle .background { | |
fill: #F1C40F; | |
stroke: #333333; | |
stroke-width: 1; | |
} | |
.demo .handle .vertical { | |
cursor: ns-resize; | |
} | |
.demo .handle .horizontal { | |
cursor: ew-resize; | |
} | |
/* SVG */ | |
svg *::selection { | |
background: transparent; | |
} | |
svg *::-moz-selection { | |
background: transparent; | |
} | |
svg *::-webkit-selection { | |
background: transparent; | |
} | |
/* HTML */ | |
body { | |
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 12px; | |
} | |
.pure-button { | |
font-family: inherit; | |
font-size: 100%; | |
*font-size: 90%; /*IE 6/7 - To reduce IE's oversized button text*/ | |
*overflow: visible; /*IE 6/7 - Because of IE's overly large left/right padding on buttons */ | |
padding: 0.5em 1em; | |
color: #444; /* rgba not supported (IE 8) */ | |
color: rgba(0, 0, 0, 0.80); /* rgba supported */ | |
*color: #444; /* IE 6 & 7 */ | |
border: 1px solid #999; /*IE 6/7/8*/ | |
border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ | |
background-color: #E6E6E6; | |
text-decoration: none; | |
border-radius: 2px; | |
margin: 0 5px; | |
} | |
.pure-button-hover, | |
.pure-button:hover, | |
.pure-button:focus { | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0); | |
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10))); | |
background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); | |
background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10)); | |
background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); | |
background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); | |
} | |
.pure-button:focus { | |
outline: 0; | |
} | |
.pure-button-active, | |
.pure-button:active { | |
box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; | |
} | |
/* Firefox: Get rid of the inner focus border */ | |
.pure-button::-moz-focus-inner{ | |
padding: 0; | |
border: 0; | |
} | |
.pure-button-primary, | |
.pure-button-selected, | |
a.pure-button-primary, | |
a.pure-button-selected { | |
background-color: rgb(0, 120, 231); | |
color: #fff; | |
} | |
ul { | |
margin-top: 4px; | |
} | |
</style> | |
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript"></script> | |
<script src="demo.base.js"></script> | |
<script src="demo.util.js"></script> | |
<script src="demo.shape.js"></script> | |
<script src="demo.container.js"></script> | |
<script src="demo.handle.js"></script> | |
<script src="demo.layout.flow.js"></script> | |
<script src="demo.flowbox.js"></script> | |
</head> | |
<body> | |
<ul> | |
<li>Use resize handles to change width of shapes and reflow children</li> | |
<li>Drag containers around and drag on the background to use selection rectangle</li> | |
<li>Add additional container using the link provided</li> | |
</ul></h5> | |
<button id="addContainer" class="pure-button pure-button-primary">Add Container</button> | |
<p/> | |
<div id="demo"></div> | |
<script type="text/javascript"> | |
var lastXY = 10; | |
var canvasWidth = 800; | |
var demo = d3.demo.app().width(600).height(600); | |
d3.select("#demo").call(demo); | |
d3.select("#addContainer").on("click", function() { | |
addContainer("Container").x(lastXY).y(lastXY).width(200).height(350); | |
lastXY += 10; | |
}); | |
function addContainer(titleArg) { | |
var container = d3.demo.container(); | |
demo.addShape(container | |
.label(titleArg) | |
.children([{id:1,name:"child1"}, {id:2,name:"child2"}, {id:3,name:"child3"}, | |
{id:4,name:"child4"}, {id:5,name:"child5"}, {id:6,name:"child6"}, | |
{id:7,name:"child7"}, {id:8,name:"child8"}, {id:9,name:"child9"}, | |
{id:10,name:"child10"}, {id:11,name:"child11"}, {id:12,name:"child12"}, | |
{id:13,name:"child13"}, {id:14,name:"child14"}, {id:15,name:"child15"}])); | |
return container; | |
} | |
addContainer("RESIZE ME").x(20).y(10).width(200).height(475); | |
addContainer("RESIZE ME").x(300).y(10).width(210).height(400); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment