|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
font-family: "Helvetica Neue", Helvetica, sans-serif; |
|
} |
|
#first, #second { |
|
float: left; |
|
margin-left: 3em; |
|
padding: 1em; |
|
border: 1px solid #aaaaaa; |
|
} |
|
ul { |
|
list-style: none; |
|
} |
|
img { |
|
height: 3em; |
|
} |
|
.button { |
|
display: inline-block; |
|
border: 1px solid #aaaaaa; |
|
border-radius: .4em; |
|
width: 4em; |
|
height: 2em; |
|
line-height: 2em; |
|
text-align: center; |
|
cursor: pointer; |
|
|
|
/* Prevent selection when clicking repeatedly */ |
|
-webkit-touch-callout: none; |
|
-webkit-user-select: none; |
|
-khtml-user-select: none; |
|
-moz-user-select: none; |
|
-ms-user-select: none; |
|
user-select: none; |
|
} |
|
.page-number { |
|
display: inline-block; |
|
width: 3em; |
|
text-align: center; |
|
} |
|
</style> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://unpkg.com/d3-template-plugin@1.3.0/build/d3-template.min.js"></script> |
|
|
|
<div id="first"> |
|
<h3>Contributors to the D3 repo (top 97)</h3> |
|
|
|
<!-- Pagination controls --> |
|
<div class="pagination-controls" data-list-id="contributors-list" data-template-id="first"> |
|
<span class="button first"><<</span> |
|
<span class="button previous"><</span> |
|
<span class="page-number">{{.|pageNumber}}</span> |
|
/ |
|
<span class="page-number">{{.|pageCount}}</span> |
|
<span class="button next">></span> |
|
<span class="button last">>></span> |
|
</div> |
|
|
|
<!-- Paginated list --> |
|
<ul id="contributors-list" data-start-index="0" data-page-size="5" data-repeat="{{.|paginate}}"> |
|
<li> |
|
<img data-attr-src="{{avatar_url}}" alt="{{login}}"> |
|
<span>{{login}}</span>: <span>{{contributions}}</span> contributions |
|
</li> |
|
</ul> |
|
</div> |
|
<div id="second"> |
|
<h3>42 odd numbers</h3> |
|
|
|
<!-- Pagination controls --> |
|
<div class="pagination-controls" data-list-id="numbers-list" data-template-id="second"> |
|
<span class="button first"><<</span> |
|
<span class="button previous"><</span> |
|
<span class="page-number">{{.|pageNumber}}</span> |
|
/ |
|
<span class="page-number">{{.|pageCount}}</span> |
|
<span class="button next">></span> |
|
<span class="button last">>></span> |
|
</div> |
|
|
|
<!-- Paginated list --> |
|
<ul id="numbers-list" data-start-index="0" data-page-size="10" data-repeat="{{.|paginate}}"> |
|
<li>{{.}}</li> |
|
</ul> |
|
</div> |
|
<script> |
|
// Prepare pagination (has to be performed before templates are created) |
|
// These preperations are generic for all paginated lists |
|
|
|
// Create custom filters |
|
d3.renderFilter("paginate", function(d) { |
|
var listElement = d3.select(this); |
|
var startIndex = +listElement.attr("data-start-index"); |
|
var pageSize = +listElement.attr("data-page-size"); |
|
return d.slice(startIndex, startIndex + pageSize); |
|
}); |
|
d3.renderFilter("pageNumber", function(d) { |
|
var listId = d3.select(this).selectAll(function() { return allParents(this); }).attr("data-list-id"); |
|
var listElement = d3.select("#" + listId); |
|
var startIndex = +listElement.attr("data-start-index"); |
|
var pageSize = +listElement.attr("data-page-size"); |
|
return Math.floor(startIndex / pageSize) + 1; |
|
}); |
|
d3.renderFilter("pageCount", function(d) { |
|
var listId = d3.select(this).selectAll(function() { return allParents(this); }).attr("data-list-id"); |
|
var listElement = d3.select("#" + listId); |
|
var pageSize = +listElement.attr("data-page-size"); |
|
return Math.floor((d.length + pageSize - 1) / pageSize); |
|
}); |
|
|
|
// Add event handlers for page navigation |
|
d3.selectAll(".pagination-controls .button").on("click", function(d) { |
|
var button = d3.select(this); |
|
var listId = button.selectAll(function() { return allParents(this); }).attr("data-list-id"); |
|
var listElement = d3.select("#" + listId); |
|
var startIndex = +listElement.attr("data-start-index"); |
|
var pageSize = +listElement.attr("data-page-size"); |
|
var listLength = d.length; |
|
|
|
// Calculate new start index |
|
var newStartIndex = startIndex; |
|
if(button.classed("first")) { |
|
newStartIndex = 0; |
|
} else if(button.classed("previous")) { |
|
newStartIndex = Math.max(startIndex - pageSize, 0); |
|
} else if(button.classed("next")) { |
|
newStartIndex = Math.min(startIndex + pageSize, listLength); |
|
} else if(button.classed("last")) { |
|
newStartIndex = listLength; |
|
} |
|
newStartIndex = Math.floor(newStartIndex / pageSize) * pageSize; // Start on page boundary |
|
|
|
// Render new page content |
|
if(newStartIndex !== startIndex) { |
|
listElement.attr("data-start-index", newStartIndex); |
|
var templateId = button.selectAll(function() { return allParents(this); }).attr("data-template-id"); |
|
d3.select("#" + templateId).render(d); |
|
} |
|
}); |
|
|
|
// Create list of contributors |
|
// See https://developer.github.com/v3/repos/#list-contributors |
|
d3.json("https://api.github.com/repos/d3/d3/contributors?per_page=97", function(error, data) { |
|
if(error) { |
|
throw error; |
|
} |
|
|
|
// Create template and render data |
|
d3.select("#first") |
|
.template() |
|
.render(data) |
|
; |
|
}); |
|
|
|
// Create list of odd numbers |
|
var oddNumbers = new Array(42); |
|
for(var i = 0; i < oddNumbers.length; i++) { |
|
oddNumbers[i] = 1 + 2 * i; |
|
} |
|
d3.select("#second") |
|
.template() |
|
.render(oddNumbers) |
|
; |
|
|
|
// Helper function: Select all parent nodes |
|
function allParents(node) { |
|
var parents = []; |
|
while(node) { |
|
node = node.parentNode; |
|
if(node) { |
|
parents.push(node); |
|
} |
|
} |
|
return parents; |
|
} |
|
</script> |