Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active December 19, 2015 18:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pbogden/6000283 to your computer and use it in GitHub Desktop.
Save pbogden/6000283 to your computer and use it in GitHub Desktop.
sculling ladder

This UI demonstrates the D3 collapsable tree applied to a rowing ladder. In the fully functional app, a user can "schedule" a race between two or more rowers, then "update" a race with the winner. Winners advance to the next round, and racing continues until someone wins the regatta.

Click on a blue bar to collapse or expand the tree. This is a convenient space saver, especially in mobile browsers. This UI demo will detect orientation changes and redraw the rowing ladder into the new browser width.

This is a UI demo only -- a fully functional application needs a back-end server with a persistent database.

function drawTree(b){function k(a){var e=l.nodes(m);e.forEach(function(c,a){c.x=a*g});var b=n.selectAll("g.node").data(e,function(a){return a.id||(a.id=++q)}),h=b.enter().append("svg:g").attr("class","node").attr("transform",function(c){return"translate("+a.y0+","+a.x0+")"}).style("opacity",1E-6);h.append("svg:rect").attr("y",-g/2).attr("height",g).attr("width",r).style("fill",p).on("click",s);h.append("svg:text").attr("dy",5.5).attr("dx",9.5).text(function(a){return a.name});h.transition().duration(d).attr("transform",
function(a){return"translate("+a.y+","+a.x+")"}).style("opacity",1);b.transition().duration(d).attr("transform",function(a){return"translate("+a.y+","+a.x+")"}).style("opacity",1).select("rect").style("fill-opacity",0.5).style("stroke","#3182bd").style("stroke-width",1.5).style("fill",p);b.exit().transition().duration(d).attr("transform",function(c){return"translate("+a.y+","+a.x+")"}).style("opacity",1E-6).remove();b=n.selectAll("path.link").data(l.links(e),function(a){return a.target.id});b.enter().insert("svg:path",
"g").attr("class","link").attr("d",function(c){c={x:a.x0,y:a.y0};return f({source:c,target:c})}).transition().duration(d).attr("d",f);b.transition().duration(d).attr("d",f);b.exit().transition().duration(d).attr("d",function(c){c={x:a.x,y:a.y};return f({source:c,target:c})}).remove();e.forEach(function(a){a.x0=a.x;a.y0=a.y})}function s(a){console.log("Click -- d: "+a.name+", y: "+a.y+", x: "+a.x);a.children?(a._children=a.children,a.children=null):(a.children=a._children,a._children=null);k(a)}function p(a){return a._children?
"#3182bd":a.children?"#c6dbef":"#fd8d3c"}b||(console.log("WARNING...windowWidth = "+b),b=960);var q=0;b=Math.min(960,b);var g=40,r=0.8*b,d=400,m,l=d3.layout.tree().size([800,100]),f=d3.svg.diagonal().projection(function(a){return[a.y,a.x]}),n=d3.select("#chart").append("svg:svg").attr("width",b).attr("height",800).append("svg:g").attr("transform","translate(20,30)");d3.json("sculling.json",function(a,b){console.log("D3 reading data..err is: "+a+", data: "+b);b.x0=0;b.y0=0;k(m=b)})};
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<link rel="apple-touch-icon" sizes="114x114" href="pbc-retina.png">
<meta name="apple-touch-fullscreen" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="drawTree.js"></script>
<link rel="stylesheet" href="sculling.css" type="text/css">
<link rel="stylesheet" href="mobile.css" type="text/css">
</head>
<body>
<div id="rowingLogo" role="banner">
<a href=""><img src="pbcoar.gif" width="320" height="47" alt="Rowing Tree"/></a>
</div>
<div class="nav" role="navigation">
<ul>
<li><a href="" class="update">Schedule</a></li>
<li><a href="" class="update">Update</a></li>
</ul>
</div>
<div id="show-rower" class="content scaffold-show" role="main">
<h1>Standings:</h1>
</div>
<div id="chart"></div>
<script>
drawTree(window.innerWidth);
window.addEventListener("orientationchange", function() {
var chartNode = document.getElementById("chart");
while (chartNode.hasChildNodes()) {
chartNode.removeChild(chartNode.lastChild);
}
drawTree(window.innerWidth);
}, false);
</script>
<div class="footer" role="contentinfo"></div>
</body>
</html>
/* Styles for mobile devices */
@media screen and (max-width: 480px) {
.nav {
padding: 0.5em;
}
.nav li {
margin: 0 0.5em 0 0;
padding: 0.25em;
}
/* Hide individual steps in pagination, just have next & previous */
.pagination .step, .pagination .currentStep {
display: none;
}
.pagination .prevLink {
float: left;
}
.pagination .nextLink {
float: right;
}
/* pagination needs to wrap around floated buttons */
.pagination {
overflow: hidden;
}
/* slightly smaller margin around content body */
fieldset,
.property-list {
padding: 0.3em 1em 1em;
}
input, textarea {
width: 100%;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
select, input[type=checkbox], input[type=radio], input[type=submit], input[type=button], input[type=reset] {
width: auto;
}
/* hide all but the first column of list tables */
.scaffold-list td:not(:first-child),
.scaffold-list th:not(:first-child) {
display: none;
}
.scaffold-list thead th {
text-align: center;
}
/* stack form elements */
.fieldcontain {
margin-top: 0.6em;
}
.fieldcontain label,
.fieldcontain .property-label,
.fieldcontain .property-value {
display: block;
float: none;
margin: 0 0 0.25em 0;
text-align: left;
width: auto;
}
.errors ul,
.message p {
margin: 0.5em;
}
.error ul {
margin-left: 0;
}
}
/* FONT STACK */
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
}
h1 {
line-height: 1.1;
}
/* BASE LAYOUT */
html {
background-color: #ddd;
background-image: -moz-linear-gradient(center top, #aaa, #ddd);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #aaa), color-stop(1, #ddd));
background-image: linear-gradient(top, #aaa, #ddd);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#aaaaaa', EndColorStr = '#dddddd');
background-repeat: no-repeat;
height: 100%;
/* change the box model to exclude the padding from the calculation of 100% height (IE8+) */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html.no-cssgradients {
background-color: #aaa;
}
.ie6 html {
height: 100%;
}
html * {
margin: 0;
}
body {
background: #ffffff;
color: #333333;
margin: 0 auto;
max-width: 960px;
overflow-x: hidden; /* prevents box-shadow causing a horizontal scrollbar in firefox when viewport < 960px wide */
-moz-box-shadow: 0 0 0.3em #255b17;
-webkit-box-shadow: 0 0 0.3em #255b17;
box-shadow: 0 0 0.3em #255b17;
}
#rowingLogo {
background-color: #ffffff;
}
/* replace with .no-boxshadow body if you have modernizer available */
.ie6 body,
.ie7 body,
.ie8 body {
border-color: #255b17;
border-style: solid;
border-width: 0 1px;
}
.ie6 body {
height: 100%;
}
a:link, a:visited, a:hover {
color: #48802c;
}
a:hover, a:active {
outline: none; /* prevents outline in webkit on active links but retains it for tab focus */
}
h1 {
color: #48802c;
font-weight: normal;
font-size: 1.25em;
margin: 0.8em 0 0.3em 0;
}
ul {
padding: 0;
}
img {
border: 0;
}
/* GENERAL */
.content {
}
.content h1 {
border-bottom: 1px solid #CCCCCC;
margin: 0.8em 1em 0.3em;
padding: 0 0.25em;
}
.scaffold-list h1 {
border: none;
}
.footer {
background: #f78181;
color: #000;
clear: both;
font-size: 0.8em;
margin-top: 1.5em;
padding: 1em;
min-height: 1em;
}
.footer a {
color: #255b17;
}
/* NAVIGATION MENU */
.nav {
background-color: #efefef;
padding: 0.5em 0.75em;
-moz-box-shadow: 0 0 3px 1px #aaaaaa;
-webkit-box-shadow: 0 0 3px 1px #aaaaaa;
box-shadow: 0 0 3px 1px #aaaaaa;
zoom: 1;
}
.nav ul {
overflow: hidden;
padding-left: 0;
zoom: 1;
}
.nav li {
display: block;
float: left;
list-style-type: none;
margin-right: 0.5em;
padding: 0;
}
.nav a {
color: #666666;
display: block;
padding: 0.25em 0.7em;
text-decoration: none;
-moz-border-radius: 0.3em;
-webkit-border-radius: 0.3em;
border-radius: 0.3em;
}
.nav a:active, .nav a:visited {
color: #666666;
}
.nav a:focus, .nav a:hover {
background-color: #999999;
color: #ffffff;
outline: none;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
}
.no-borderradius .nav a:focus, .no-borderradius .nav a:hover {
background-color: transparent;
color: #444444;
text-decoration: underline;
}
.nav a.home, .nav a.list, .nav a.create, .nav a.update {
background-position: 0.7em center;
background-repeat: no-repeat;
text-indent: 25px;
}
.nav a.home {
background-image: url(../images/skin/house.png);
}
.nav a.list {
background-image: url(../images/skin/database_table.png);
}
.nav a.create {
background-image: url(../images/skin/database_add.png);
}
/* CREATE/EDIT FORMS AND SHOW PAGES */
fieldset,
.property-list {
margin: 0.6em 1.25em 0 1.25em;
padding: 0.3em 1.8em 1.25em;
position: relative;
zoom: 1;
border: none;
}
.property-list .fieldcontain {
list-style: none;
overflow: hidden;
zoom: 1;
}
.fieldcontain {
margin-top: 1em;
}
.fieldcontain label,
.fieldcontain .property-label {
color: #666666;
text-align: right;
width: 25%;
}
.fieldcontain .property-label {
float: left;
}
.fieldcontain .property-value {
display: block;
margin-left: 27%;
}
label {
cursor: pointer;
display: inline-block;
margin: 0 0.25em 0 0;
}
input, select, textarea {
background-color: #fcfcfc;
border: 1px solid #cccccc;
font-size: 1em;
padding: 0.2em 0.4em;
}
select {
padding: 0.2em 0.2em 0.2em 0;
}
select[multiple] {
vertical-align: top;
}
textarea {
width: 250px;
height: 150px;
overflow: auto; /* IE always renders vertical scrollbar without this */
vertical-align: top;
}
input[type=checkbox], input[type=radio] {
background-color: transparent;
border: 0;
padding: 0;
}
input:focus, select:focus, textarea:focus {
background-color: #ffffff;
border: 1px solid #eeeeee;
outline: 0;
-moz-box-shadow: 0 0 0.5em #ffffff;
-webkit-box-shadow: 0 0 0.5em #ffffff;
box-shadow: 0 0 0.5em #ffffff;
}
.required-indicator {
color: #48802C;
display: inline-block;
font-weight: bold;
margin-left: 0.3em;
position: relative;
top: 0.1em;
}
ul.one-to-many {
display: inline-block;
list-style-position: inside;
vertical-align: top;
}
.ie6 ul.one-to-many, .ie7 ul.one-to-many {
display: inline;
zoom: 1;
}
ul.one-to-many li.add {
list-style-type: none;
}
/* EMBEDDED PROPERTIES */
fieldset.embedded {
background-color: transparent;
border: 1px solid #CCCCCC;
margin-left: 0;
margin-right: 0;
padding-left: 0;
padding-right: 0;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
fieldset.embedded legend {
margin: 0 1em;
}
/* TABLES */
table {
border-top: 1px solid #DFDFDF;
border-collapse: collapse;
width: 100%;
margin-bottom: 1em;
}
tr {
border: 0;
}
tr>td:first-child, tr>th:first-child {
padding-left: 1.25em;
}
tr>td:last-child, tr>th:last-child {
padding-right: 1.25em;
}
td, th {
line-height: 1.5em;
padding: 0.5em 0.6em;
text-align: left;
vertical-align: top;
}
th {
background-color: #efefef;
background-image: -moz-linear-gradient(top, #ffffff, #eaeaea);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffffff), color-stop(1, #eaeaea));
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#ffffff', EndColorStr = '#eaeaea');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#eaeaea')";
color: #666666;
font-weight: bold;
line-height: 1.7em;
padding: 0.2em 0.6em;
}
thead th {
white-space: nowrap;
}
th a {
display: block;
text-decoration: none;
}
th a:link, th a:visited {
color: #666666;
}
th a:hover, th a:focus {
color: #333333;
}
th.sortable a {
background-position: right;
background-repeat: no-repeat;
padding-right: 1.1em;
}
th.asc a {
background-image: url(../images/skin/sorted_asc.gif);
}
th.desc a {
background-image: url(../images/skin/sorted_desc.gif);
}
.odd {
background: #f7f7f7;
}
.even {
background: #ffffff;
}
th:hover, tr:hover {
background: #E1F2B6;
}
/* ACTION BUTTONS */
.buttons {
background-color: #efefef;
overflow: hidden;
padding: 0.3em;
-moz-box-shadow: 0 0 3px 1px #aaaaaa;
-webkit-box-shadow: 0 0 3px 1px #aaaaaa;
box-shadow: 0 0 3px 1px #aaaaaa;
margin: 0.1em 0 0 0;
border: none;
}
.buttons input,
.buttons a {
background-color: transparent;
border: 0;
color: #666666;
cursor: pointer;
display: inline-block;
margin: 0 0.25em 0;
overflow: visible;
padding: 0.25em 0.7em;
text-decoration: none;
-moz-border-radius: 0.3em;
-webkit-border-radius: 0.3em;
border-radius: 0.3em;
}
.buttons input:hover, .buttons input:focus,
.buttons a:hover, .buttons a:focus {
background-color: #999999;
color: #ffffff;
outline: none;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.no-borderradius .buttons input:hover, .no-borderradius .buttons input:focus,
.no-borderradius .buttons a:hover, .no-borderradius .buttons a:focus {
background-color: transparent;
color: #444444;
text-decoration: underline;
}
.buttons .delete, .buttons .edit, .buttons .save {
background-position: 0.7em center;
background-repeat: no-repeat;
text-indent: 25px;
}
.ie6 .buttons input.delete, .ie6 .buttons input.edit, .ie6 .buttons input.save,
.ie7 .buttons input.delete, .ie7 .buttons input.edit, .ie7 .buttons input.save {
padding-left: 36px;
}
a.skip {
position: absolute;
left: -9999px;
}
<!-- THIS STYLE ELEMENT IS FOR D3 -->
.node rect {
cursor: pointer;
fill: #fff;
fill-opacity: .5;
stroke: #3182bd;
stroke-width: 1.5px;
}
.node text {
font: 16px sans-serif;
pointer-events: none;
}
path.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
{"name":"Round #2 -- 2 race(s) to go","children":[{"name":"Race Winner TBD","children":[{"name":"Pete Red -- winner in Round #1","children":[{"name":"Pete Red","children":null},{"name":"Greg Blue","children":null}]},{"name":"Phil Black -- winner in Round #1","children":[{"name":"Phil Black","children":null},{"name":"Bill White","children":null}]}]},{"name":"Race Winner TBD","children":[{"name":"Chris Brown -- winner in Round #1","children":[{"name":"Nick Green","children":null},{"name":"Chris Brown","children":null}]},{"name":"Tim Beige -- winner in Round #1","children":[{"name":"Chad Gray","children":null},{"name":"Tim Beige","children":null}]},{"name":"Davis Orange -- winner in Round #1","children":[{"name":"Davis Orange","children":null},{"name":"Dick Tan","children":null}]}]}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment