Built with blockbuilder.org
forked from vjwilson's block: pie chart browser usage
| license: mit |
Built with blockbuilder.org
forked from vjwilson's block: pie chart browser usage
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <style> | |
| body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
| </style> | |
| <link rel="stylesheet" href="styles.css"> | |
| </head> | |
| <body> | |
| <div class="search-block"> | |
| <label for="username">GitHub Username</label> | |
| <input name="username" id="username" value="vjwilson"> | |
| <button type="button" id="search">Graph Repos</button> | |
| </div> | |
| <div class="github-link" id="github-link"> | |
| </div> | |
| <div class="container"> | |
| </div> | |
| <script> | |
| const container = d3.select('.container') | |
| const svg = container.append("svg") | |
| .attr("width", 960) | |
| .attr("height", 500); | |
| const width = svg.attr("width"); | |
| const height = svg.attr("height"); | |
| const radius = Math.min(width, height) / 2; | |
| const searchBox = document.getElementById('username'); | |
| const triggerBtn = document.getElementById('search'); | |
| let username = searchBox.value; | |
| const githubLink = document.getElementById('github-link'); | |
| triggerBtn.addEventListener('click', function(e) { | |
| username = searchBox.value.trim(); | |
| graphRepos(username); | |
| }); | |
| searchBox.addEventListener('keypress', function(e) { | |
| const code = e.which; | |
| if (code === 13) { | |
| username = searchBox.value.trim(); | |
| graphRepos(username); | |
| } | |
| }); | |
| graphRepos(username); | |
| function graphRepos(user) { | |
| svg.selectAll("*").remove(); | |
| while (githubLink.firstChild) { | |
| githubLink.removeChild(githubLink.firstChild); | |
| } | |
| fetch(`https://api.github.com/users/${user}/repos?per_page=100`) | |
| .then(function(response) { | |
| if (!response.ok) { | |
| throw new Error("HTTP error, status = " + response.status); | |
| } | |
| return response.json(); | |
| }) | |
| .then(function(repos) { | |
| if (!repos.length) { | |
| throw new Error(`No public repos for user ${user}`); | |
| } | |
| const g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); | |
| const totals = repos.reduce((sums, repo) => { | |
| sums[repo.language] = (sums[repo.language] || 0) + 1; | |
| return sums; | |
| }, {}); | |
| const int = Object.entries(totals); | |
| const newData = int.map((sum) => { | |
| return { | |
| language: sum[0], | |
| count: sum[1] | |
| }; | |
| }); | |
| const color = d3.scaleOrdinal(['#4daf4a','#377eb8','#ff7f00','#984ea3','#e41a1c']); | |
| // Generate the pie | |
| const pie = d3.pie().value(function(d) { | |
| return d.count; | |
| }); | |
| // Generate the arcs | |
| const arc = d3.arc() | |
| .innerRadius(0) | |
| .outerRadius(radius - 50); | |
| const label = d3.arc() | |
| .outerRadius(radius - 5) | |
| .innerRadius(radius - 35); | |
| //Generate groups | |
| const arcs = g.selectAll(".arc") | |
| .data(pie(newData)) | |
| .enter() | |
| .append("g") | |
| .attr("class", "arc") | |
| //Draw arc paths | |
| arcs.append("path") | |
| .attr("fill", function(d) { | |
| return color(d.data.language); | |
| }) | |
| .attr('stroke', 'white') | |
| .attr("d", arc); | |
| arcs.append("text") | |
| .attr("transform", function(d) { | |
| return "translate(" + label.centroid(d) + ")"; | |
| }) | |
| .attr('text-anchor', 'middle') | |
| .attr('font-size', '14px') | |
| .text(function(d) { | |
| return d.data.language + ' (' + d.data.count + ')'; | |
| }); | |
| const newLink = document.createElement('a'); | |
| newLink.setAttribute('href', `https://github.com/${user}?tab=repositories`); | |
| newLink.setAttribute('target', '_blank'); | |
| newLink.setAttribute('rel', 'noopener noreferrer'); | |
| newLink.setAttribute('target', '_top'); | |
| const newContent = document.createTextNode(`View repos for ${user}`); | |
| newLink.appendChild(newContent); | |
| githubLink.append(newLink); | |
| svg.append("g") | |
| .attr("transform", "translate(" + 40 + "," + (height / 2) + ")rotate(270)") | |
| .append("text") | |
| .text("Public Repos by Language") | |
| .attr("class", "title") | |
| .attr('font-size', '24px') | |
| .attr('text-anchor', 'middle'); | |
| }) | |
| .catch(function(error) { | |
| var p = document.createElement('p'); | |
| p.classList.add('error') | |
| p.appendChild( | |
| document.createTextNode('Error: ' + error.message) | |
| ); | |
| document.body.insertBefore(p, container.node()); | |
| }); | |
| } | |
| </script> | |
| </body> |
| *:focus { | |
| outline: thin rebeccapurple dotted; | |
| } | |
| .search-block { | |
| border: 1px solid rebeccapurple; | |
| border-radius: 4px; | |
| padding: .5rem; | |
| position: absolute; | |
| width: 200px; | |
| } | |
| .search-block label { | |
| color: grey; | |
| display: block; | |
| margin-bottom: .125rem; | |
| } | |
| .search-block input { | |
| color: rebeccapurple; | |
| font-size: 1rem; | |
| margin-bottom: .75rem; | |
| width: 100% | |
| } | |
| .search-block button { | |
| background-color: rebeccapurple; | |
| color: white; | |
| display: block; | |
| font-size: 1rem; | |
| font-weight: bold; | |
| width: 100%; | |
| } | |
| .github-link { | |
| padding: 1rem; | |
| position: absolute; | |
| right: 0; | |
| } | |
| .error { | |
| color: red; | |
| position: absolute; | |
| left: 0; | |
| text-align: center; | |
| top: 50%; | |
| width: 100%; | |
| z-index: 1; | |
| } |