master
parent f29c758ac8
commit 2e23d57d8d
  1. 54
      css/style.css
  2. 508
      index.html
  3. 1564
      js/tsne_vis.js
  4. 12
      modules/bootstrap.min.js
  5. 5
      modules/d3-legend/Gruntfile.js
  6. 204
      modules/d3-legend/LICENSE
  7. 35
      modules/d3-legend/README.md
  8. 2
      modules/d3-legend/bower.json
  9. 6141
      modules/d3-legend/d3-legend.js
  10. 58
      modules/d3-legend/d3-legend.min.js
  11. 68
      modules/d3-legend/docs/color.md
  12. 3
      modules/d3-legend/docs/contents.md
  13. 4
      modules/d3-legend/docs/d3-legend.min.js
  14. 389
      modules/d3-legend/docs/docs.js
  15. 3
      modules/d3-legend/docs/docs.min.js
  16. 8
      modules/d3-legend/docs/helpers.md
  17. 743
      modules/d3-legend/docs/index.html
  18. 318
      modules/d3-legend/docs/legends.js
  19. 4
      modules/d3-legend/docs/markdown.js
  20. 64
      modules/d3-legend/docs/size.md
  21. 56
      modules/d3-legend/docs/symbol.md
  22. 14
      modules/d3-legend/index.js
  23. 1012
      modules/d3-legend/indexRollup.js
  24. 1
      modules/d3-legend/indexRollup.js.map
  25. 1004
      modules/d3-legend/indexRollupNext.js
  26. 1
      modules/d3-legend/indexRollupNext.js.map
  27. 6402
      modules/d3-legend/package-lock.json
  28. 56
      modules/d3-legend/package.json
  29. 26
      modules/d3-legend/rollup.config.js
  30. 378
      modules/d3-legend/src/color.js
  31. 12
      modules/d3-legend/src/helpers.js
  32. 260
      modules/d3-legend/src/legend.js
  33. 367
      modules/d3-legend/src/size.js
  34. 305
      modules/d3-legend/src/symbol.js
  35. 14
      modules/d3-legend/src/web.js
  36. 1
      modules/d3-legend/test/mocha.opts
  37. 20
      modules/d3-legend/test/test.js
  38. 153
      modules/d3-legend/test/test.legendcolor.js
  39. 75
      modules/d3-legend/test/test.legendsize.js
  40. 75
      modules/d3-legend/test/test.legendsymbol.js
  41. 7
      modules/d3-legend/tsconfig.json
  42. 84
      modules/d3-legend/types/d3-svg-legend.d.ts
  43. 37
      modules/d3-star/d3-starPlot.js
  44. 3
      modules/three.js-r101/.gitattributes
  45. 28
      modules/three.js-r101/.github/CONTRIBUTING.md
  46. 41
      modules/three.js-r101/.github/ISSUE_TEMPLATE.md
  47. 10
      modules/three.js-r101/.gitignore
  48. 8
      modules/three.js-r101/.npmignore
  49. 5
      modules/three.js-r101/.travis.yml
  50. 965
      modules/three.js-r101/three.js
  51. BIN
      textures/circle-sprite.png

@ -1,10 +1,15 @@
/* Custom styling */
/* Main html/body configurations */
html, body {
max-width: 100%;
margin-top: 15px;
font: serif;
}
.container-fluid {
margin-top: 5px;
}
#param-correlation {
flex: 1;
display: flex;
@ -103,6 +108,7 @@ cursor: default;
border: 1px solid black;
position: absolute;
display: block;
z-index: 2;
}
/* Styling of the overview canvas */
@ -117,7 +123,7 @@ cursor: default;
#knnBarChart {
width: 50vw;
height: 4.2vw;
margin-top: 4.4vw;
margin-top: 7.2vw;
border: 1px solid black;
position: absolute;
display: block;
@ -153,6 +159,7 @@ cursor: default;
width: 11vw;
height: 11vw;
position: absolute;
border: 1px solid black;
}
/* Number of Points font-size */
@ -173,14 +180,6 @@ svg#legend4 {
overflow: auto;
}
/* This is the styling for the thumbnails list*/
#ThumbNailsList{
border: 1px solid black;
width: 15.4vw;
height: 13vw;
padding: 10px 10px 10px 10px;
}
/* ShepardHeatmap Styling for the different rectangles used there */
rect {
stroke: #E6E6E6;
@ -269,6 +268,12 @@ rect {
.show {display:block;}
/* This is for the Correlation bar chart */
#correlation{
margin-left: 10px;
margin-top: -20px;
border: 1px solid black;
}
.y.axis line {
fill: none;
}
@ -300,7 +305,7 @@ rect {
/*shape-rendering: crispEdges;*/
}
/* Styling for the annotation circle */
.annotation circle {
fill: none;
stroke: black;
@ -320,4 +325,29 @@ rect {
#toggleAnnotator {
color: darkgray;
}
}
#selectionLabel{
border: 1px solid grey;
}
#starPlot{
border: 1px solid grey;
margin-left:10px;
}
.parent-row{
display: flex;
}
.children-columns{
display: flex;
}
#extra-information {
margin-top: 25px;
margin-left: 10px;
}
#kNNInfo {
margin-top: 40px;
}

@ -1,249 +1,335 @@
<html>
<head>
<!-- Ignore Favicon -> Solved the warning... -->
<!-- Ignore Favicon -> Solved the warning in console of the browser. -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- Importing D3 module -->
<script src='./modules/d3v3/d3.min.js'></script>
<script src='./modules/d3/d3.min.js'></script>
<script src='./modules/d3-star/d3-starPlot.js'></script>
<script src='./modules/d3-annotations/d3-annotator.js'></script>
<script src="./modules/d3-tip/tip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.13.0/d3-legend.js"></script>
<script src="./modules/savage.toggle-switch.js" type="text/javascript"></script>
<script src="./modules/papa/papaparse.min.js"></script>
<script src="./modules/jquery/jquery.min.js"></script>
<!-- Basic scripts -->
<script src="./js/tsne.js"></script>
<script src="./js/checkbox.js"></script>
<script src="./js/data_form_handler.js"></script>
<script src="./lasso.js"></script>
<!-- CSS -->
<link rel="stylesheet" href="./css/style.css"/>
<link rel="stylesheet" href="./css/bootstrap.min.css"/>
<link rel="stylesheet" href="./css/dc.css"/>
<!-- Bootstrap -->
<script src="./modules/bootstrap.min.js"></script>
<!-- To be removed!!!-->
<script src='./modules/three.js-r101/three.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js'></script>
</head>
<!-- Importing D3 minified versions v3 and v5. -->
<script src='./modules/d3v3/d3.min.js'></script>
<script src='./modules/d3/d3.min.js'></script>
<!-- Importing additional modules such as starPlot, annotator, tip, legend, switch, papaParse, and Jquery. -->
<script src='./modules/d3-star/d3-starPlot.js'></script>
<script src='./modules/d3-annotations/d3-annotator.js'></script>
<script src="./modules/d3-tip/tip.js"></script>
<script src="./modules/d3-legend/d3-legend.min.js"></script>
<script src="./modules/savage.toggle-switch.js" type="text/javascript"></script>
<script src="./modules/papa/papaparse.min.js"></script>
<script src="./modules/jquery/jquery.min.js"></script>
<body>
<!-- Basic scripts that we execute. -->
<script src="./js/tsne.js"></script>
<script src="./js/checkbox.js"></script>
<script src="./js/data_form_handler.js"></script>
<script src="./lasso.js"></script>
<!-- CSS - Styling -->
<link rel="stylesheet" href="./css/style.css"/>
<link rel="stylesheet" href="./css/bootstrap.min.css"/>
<link rel="stylesheet" href="./css/dc.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<!-- Bootstrap -->
<script src="./modules/bootstrap.min.js"></script>
</head>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">t-SNE Overview</h3>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">t-SNE Overview</h3>
</div>
<div class="row">
<div class="panel-body">
<div class="col-md-6">
<canvas id = "tSNEcanvas"></canvas>
</div>
<div class="col-md-2">
<div class="legend" id = "legend3"></div>
</div>
</div>
</div>
<div id="control-panel">
<div class="param">
<label for="param-lim">Points radius scaling factor</label>
<input id="param-lim" type="range" min="0.25" max="4" value="2", step="0.25">
<output for="param-lim" id="param-lim-value">2</output>
</div>
</div>
</div>
</div>
<div class="row">
<div class="panel-body">
<div class="col-md-6">
<canvas id = "tSNEcanvas"></canvas>
</div>
<div class="col-md-2">
<div class="legend" id = "legend3"></div>
<div class="col-md-6">
<svg id="SvgAnnotator"></svg>
<svg id="modtSNEcanvas_svg"></svg>
<div id="modtSNEDiv">
<canvas id = "modtSNEcanvas" ></canvas>
</div>
</div>
<div class="col-md-1" style="width: 3.499999995%">
<svg id="legend1"></svg>
</div>
<div class="col-md-2" style="width: 21.499999995%">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Select different modes</h3>
</div>
<div class="panel-body">
<div id="resetAllFilters" style="margin-bottom: 40px">
<button class="btn btn-info btn-block active" onclick="setLayerProj();">Layer: t-SNE projection</button>
<button class="btn btn-info btn-block" onclick="setLayerComp();">Layer: kNN limiter and data features comparison</button>
<button class="btn btn-info btn-block" onclick="setLayerSche();">Layer: Schema investigation</button>
</div>
</div>
<button class="btn btn-info btn-block" onclick="setReset();">Reset all filters</button>
<label style="margin-top: 15px">
<input id="controls" type="checkbox">
Hide annotations' controllers
</label>
</div>
</div>
</div>
<div id="control-panel">
<div class="param">
<label for="param-lim">Points radius scaling factor</label>
<input id="param-lim" type="range" min="0.25" max="4" value="2", step="0.25">
<output for="param-lim" id="param-lim-value">2</output>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Shepard Heatmap</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<svg id="sheparheat"></svg>
</div>
<div class="col-md-2">
<svg id="legend4"></svg>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<svg id="correlation"></svg>
</div>
</div>
</div>
<div class="col-md-6">
<svg id="SvgAnnotator"></svg>
<svg id="modtSNEcanvas_svg"></svg>
<canvas id = "modtSNEcanvas" ></canvas>
</div>
<div class="col-md-1">
<svg id="legend1"></svg>
</div>
<div class="col-md-2">
<div id="ThumbNailsList">
<label for="male">Neighborhood Preservation</label>
<select id="param-neighborHood" name ="param-neighborHood">
<option selected="selected" value="color">Color</option>
<option value="size">Size</option>
</select>
<output for="param-neighborHood" id="param-neighborHood-value"></output>
<label for="male">Final Cost (Kullback-Leibler)</label>
<label>Vice versa</label>
<div id="param-correlation">
<div class="param">
<label for="param-corr">Correlation threshold</label>
<input id="param-corr" type="range" min="0" max="750" value="150", step="50">
<output for="param-corr" id="param-corr-value">150</output>
</div>
Type the text of the form: <textarea type="text" id="comment" name="textforAnnotator">Please, provide your comment.</textarea>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">t-SNE Parameters</h3>
</div>
<div class="panel-body">
<div id="control-panel" data-sr="enter left over 8s">
<div class="param">
<label id="data" for="param-dataset">Data sets</label>
<select id="param-dataset" name="param-dataset" onChange="changeDataset(this.value);">
<option value="winequality-red.csv">Red Wine - Quality</option>
<option value="iris.csv" selected>Iris</option>
<option value="mnist.csv" >Mnist</option>
<option value="Frogs_MFCCs_s.csv" >Frogs</option>
<option value="empty">Add New File</option>
</select>
</div>
<div class="param">
<label for="param-perplexity">Perplexity</label>
<input id="param-perplexity" type="range" min="2" max="100" value="30", step="1">
<output for="param-perplexity" id="param-perplexity-value">30</output>
</div>
<div class="param">
<label for="param-learningrate">Learning rate</label>
<input id="param-learningrate" type="range" min="1" max="300" value="10", step="1">
<output for="param-learningrate" id="param-learningrate-value">10</output>
</div>
<div class="param">
<label for="param-maxiter">Max iterations</label>
<input id="param-maxiter" type="range" min="10" max="1000" value="10", step="10">
<output for="param-maxiter" id="param-maxiter-value">10</output>
</div>
<div class="param">
<label for="param-distance">Distance metric</label>
<select id="param-distance" name="param-distance">
<option value="euclideanDist" selected>Euclidean distance</option>
<option value="jaccardDist">Jaccard dissimilarity</option>
</select>
<output for="param-distance" id="param-distance-value"></output>
</div>
<div class="param">
<label for="param-transform">Data transform</label>
<select id="param-transform" name="param-transform">
<option value="noTrans" selected>No transform</option>
<option value="logTrans">Log10</option>
<option value="asinhTrans">asinh</option>
<option value="binTrans">Binarize</option>
</select>
<output for="param-transform" id="param-transform-value"></output>
</div>
<p><div id="run-button"><button class="btn btn-primary btn-block" onclick="getData();">Run New t-SNE</button></div></p>
</div>
<p><div id="toggleAnnotator"><button class="btn btn-primary btn-block" onclick="setAnnotator();">Add a new comment</button></div></p>
<p><div id="continue"><button class="btn btn-primary btn-block" onclick="setContinue();">Continue with the analysis</button></div></p>
</div>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<div id="starPlot"></div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Shepard Heatmap</h3>
</div>
<div class="panel-body">
</div>
<div class="row">
<div class="col-md-6">
<svg id="sheparheat"></svg>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Visual Mapping Parameters</h3>
</div>
<div class="row">
<div class="panel-body">
<div class="col-md-12">
<div id="ThumbNailsList">
<label for="male">Neighborhood Preservation</label>
<select id="param-neighborHood" name ="param-neighborHood">
<option selected="selected" value="color">Color</option>
<option value="size">Size</option>
</select>
<output for="param-neighborHood" id="param-neighborHood-value"></output>
<label for="male">Final Cost (Kullback-Leibler)</label>
<label id="selectionLabel" style="margin-top:4px">Vice versa</label>
<div id="param-correlation">
<div class="param">
<label for="param-corr">Correlation threshold</label>
<input id="param-corr" type="range" min="0" max="750" value="150", step="50">
<output for="param-corr" id="param-corr-value">150</output>
</div>
Visualization annotating form: <textarea type="text" id="comment" name="textforAnnotator" style="margin-top:4px" placeholder="Please, provide your comment."></textarea>
</div>
<div id="commBtn">
<div class="param" style="margin-top:4px">
<div id="toggleAnnotator"><button class="btn btn-default btn-block" onclick="setAnnotator();">Add a new comment</button></div>
</div>
<div class="param" style="margin-top:4px">
<div id="continue"><button class="btn btn-default btn-block active" onclick="setContinue();">Continue with the analysis</button></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-2">
<svg id="legend4"></svg>
<div class = col-md-6>
<svg id="knnBarChart"></svg>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<svg id="correlation"></svg>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">t-SNE Parameters</h3>
</div>
<div class="panel-body">
<div id="control-panel" data-sr="enter left over 8s">
<div class="param">
<label id="data" for="param-dataset">Data sets</label>
<select id="param-dataset" name="param-dataset" onChange="changeDataset(this.value);">
<option value="winequality-red.csv">Red Wine - Quality</option>
<option value="iris.csv" selected>Iris</option>
<option value="mnist.csv" >Mnist</option>
<option value="Frogs_MFCCs_s.csv" >Frogs</option>
<option value="empty">Add New File</option>
</select>
<div class="col-md-3" id="extra-information" style="width: 24.5999995%">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">t-SNE and data set's extra information</h3>
</div>
<div class="panel-body">
<div id="cost"></div>
<br>
<div id="datasetDetails"></div>
</div>
</div>
<div class="panel panel-default" id="kNNInfo">
<div class="panel-heading">
<h3 class="panel-title">kNN barchart information</h3>
</div>
<div class="panel-body">
<div id="cost"></div>
<br>
<div id="datasetDetails"></div>
</div>
</div>
</div>
<div class="param">
<label for="param-perplexity">Perplexity</label>
<input id="param-perplexity" type="range" min="2" max="100" value="30", step="1">
<output for="param-perplexity" id="param-perplexity-value">30</output>
</div>
<div class="param">
<label for="param-learningrate">Learning rate</label>
<input id="param-learningrate" type="range" min="1" max="300" value="10", step="1">
<output for="param-learningrate" id="param-learningrate-value">10</output>
</div>
<div class="param">
<label for="param-maxiter">Max iterations</label>
<input id="param-maxiter" type="range" min="10" max="1000" value="10", step="10">
<output for="param-maxiter" id="param-maxiter-value">10</output>
</div>
<div class="param">
<label for="param-distance">Distance metric</label>
<select id="param-distance" name="param-distance">
<option value="euclideanDist" selected>Euclidean distance</option>
<option value="jaccardDist">Jaccard dissimilarity</option>
</select>
<output for="param-distance" id="param-distance-value"></output>
</div>
<div class="param">
<label for="param-transform">Data transform</label>
<select id="param-transform" name="param-transform">
<option value="noTrans" selected>No transform</option>
<option value="logTrans">Log10</option>
<option value="asinhTrans">asinh</option>
<option value="binTrans">Binarize</option>
</select>
<output for="param-transform" id="param-transform-value"></output>
</div>
<p><div id="run-button"><button class="btn btn-primary btn-block" onclick="getData();">Run New tSNE</button></div></p>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<div id="starPlot"></div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Switch Mode</h3>
</div>
<div class="panel-body">
<div id="cost"></div>
<div id="datasetDetails"></div>
<div id="toggleLassoAndLine"></div>
</div>
</div>
</div>
<div class = col-md-6>
<svg id="knnBarChart"></svg>
</div>
</div>
</div>
<script>
$('#param-lim').bind('input', function () { $('#param-lim-value').text($('#param-lim').val()); });
/* This script is used in order to give functionalities to the different buttons provide through the front-end. */
$('#param-lim').bind('input', function () { $('#param-lim-value').text($('#param-lim').val()); });
$('#param-corr').bind('input', function () { $('#param-corr-value').text($('#param-corr').val()); });
$('#param-perplexity').bind('input', function () { $('#param-perplexity-value').text($('#param-perplexity').val()); });
$('#param-earlyexag').bind('input', function () { $('#param-earlyexag-value').text($('#param-earlyexag').val()); });
$('#param-learningrate').bind('input', function () { $('#param-learningrate-value').text($('#param-learningrate').val()); });
$('#param-maxiter').bind('input', function () { $('#param-maxiter-value').text($('#param-maxiter').val()); });
SaVaGe.ToggleSwitch({container: "#toggleLassoAndLine", onChange: function(toggler){ setToggle(toggler.getValue()); }} );
// Get the container element
var btnContainer = document.getElementById("commBtn");
// Get all buttons with class="btn" inside the container
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
let current = document.getElementById("commBtn").getElementsByClassName("active");
console.log(current);
current[0].className = current[0].className.replace("btn btn-default btn-block active", "btn btn-default btn-block");
this.className += " active";
});
}
// Get the container element
var btnContainer2 = document.getElementById("resetAllFilters");
// Get all buttons with class="btn" inside the container
var btns2 = btnContainer2.getElementsByClassName("btn");
for (var i = 0; i < btns2.length; i++) {
btns2[i].addEventListener("click", function() {
let current = document.getElementsByClassName("active");
console.log(current);
current[0].className = current[0].className.replace("btn btn-info btn-block active", "btn btn-info btn-block");
this.className += " active";
});
}
</script>
<script>
/* On resize refresh the shepardHeatmap */
window.onresize = function(event) {
if ( document.getElementById('cost').hasChildNodes() ) {
// Clear legend for the shepardHeatmap
var svgLegend = d3.select("#legend4");
svgLegend.selectAll("*").remove();
// Clear the SheapHeardmap before refreshing
var svg = d3.select("#sheparheat");
svg.selectAll("*").remove();
// Call the ShepardHeatmap again to be redrawn
ShepardHeatMap();
<script>
/* On resize refresh the shepardHeatmap */
window.onresize = function(event) {
if ( document.getElementById('cost').hasChildNodes() ) {
// Clear legend for the shepardHeatmap
var svgLegend = d3.select("#legend4");
svgLegend.selectAll("*").remove();
// Clear the SheapHeardmap before refreshing
var svg = d3.select("#sheparheat");
svg.selectAll("*").remove();
// Call the ShepardHeatmap again to be redrawn
ShepardHeatMap();
}
}
}
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
}
</script>
</script>
<!--
Load the visualization script
-->
<script src="./js/tsne_vis.js"></script>
<!-- Load the core visualization script. -->
<script src="./js/tsne_vis.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -5,11 +5,14 @@ module.exports = function(grunt){
// configure plugins
grunt.initConfig({
browserify: {
dist: {
files: {
'd3-legend.js': ['src/web.js'],
},
options: {
browserifyOptions: { debug: true },
transform: [ ['babelify', {'presets': ['es2015']}] ]
}
},
docs: {

@ -1,5 +1,203 @@
Copyright (c) 2015, Susie Lu
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2015, Susie Lu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -2,40 +2,45 @@
Full documentation: [http://d3-legend.susielu.com](http://d3-legend.susielu.com)
## Looking for compatibility with d3 v3?
- You can see the code for the d3 legend that works with d3 v3 in the [v3 branch](https://github.com/susielu/d3-legend/tree/v3)
- [Documentation](http://d3-legend-v3.susielu.com) for the v3 version of the legend
## d3-legend v4 updates (npm version 2.0.0 and higher)
- Flattened naming for accessing functions
- d3.legend.color => d3.legendColor
- d3.legend.size => d3.legendSize
- d3.legend.symbol => d3.legendSymbol
- NPM package no longer binds to global d3, is now just an object with the three legend functions
## Usage
### Using just the minified file
You must inclue the [d3 library](http://d3js.org/) before including the legend file. Then you can simply add the compiled js file to your website:
You must include the [d3 library](http://d3js.org/) before including the legend file. Then you can simply add the compiled js file to your website:
- d3-legend.min.js
- d3-legend.js (Human readable version)
### Using CDN
You can also add latest version of [d3-legend hosted on cdnjs](https://cdnjs.com/libraries/d3-legend/1.13.0).
You can also add the latest version of [d3-legend hosted on cdnjs](https://cdnjs.com/libraries/d3-legend).
### Using npm
Already using d3? Great! You can add the d3 legend as a node module by running:
You can add the d3 legend as a node module by running:
`npm i d3-svg-legend -S`
If not, install both this way:
To use the version compatible with d3v3 run:
`npm i d3-svg-legend@1.x -S`
`npm i d3@^3.0.0 d3-svg-legend -S`
Using the import syntax `import legend from 'd3-svg-legend'` gives access to the three legend types as an object. You can also import them independently for example `import { legendColor } from 'd3-svg-legend'`
Please note, d3 is now a peer dependency, you will have to have the npm d3 module separately. This component works with any 3.x version of d3.
If you `require('d3-svg-legend')` we attach to d3 as `d3.legend`. If you'd like to use d3-legend without extending d3, `require('d3-svg-legend/no-extend')`. For example:
```
var d3 = require('d3')
var legend = require('d3-svg-legend/no-extend')
var svg = d3.select("#svg-color-quant");
var quantize = d3.scale.quantize()
var quantize = d3.scaleQuantize()
.domain([ 0, 0.15 ])
.range(d3.range(9).map(function(i) { return "q" + i + "-9"; }));
@ -43,7 +48,7 @@ svg.append("g")
.attr("class", "legendQuant")
.attr("transform", "translate(20,20)");
var colorLegend = legend.color()
var colorLegend = d3.legendColor()
.labelFormat(d3.format(".2f"))
.useClass(true)
.scale(quantize);
@ -54,4 +59,4 @@ svg.select(".legendQuant")
```
## Feedback
I would love to hear from you about any additional features that would be useful, please say hi on twitter [@DataToViz](https://www.twitter.com/DataToViz).
I would love to hear from you about any additional features that would be useful, please say hi on twitter [@DataToViz](https://www.twitter.com/DataToViz).

@ -21,6 +21,6 @@
"tests"
],
"dependencies": {
"d3": "~3.5.6"
"d3": "^4.3.0"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,75 +1,103 @@
d3.legend.**color()**
d3.**legendColor()**
Constructs a new color legend. The legend component expects a d3 scale as the basic input, but also has a number of optional parameters for changing the default display such as vertical or horizontal orientation, shape of the symbol next to the label, symbol sizing, and label formatting.
color.**scale(d3.scale)**
legendColor.**scale(d3.scale)**
Creates a new d3 legend based on the scale. The code determines the type of scale and generates the appropriate symbol and label pairs.
color.**cells(number or [numbers])**
legendColor.**cells(number or [numbers])**
This parameter is only valid for continuous scales (like linear and log). When there is no indication from the domain or range for the number of steps in the legend you may want to display, it defaults to five steps in equal increments. You can pass the cells function a single number which will create equal increments for that number of steps, or an array of the [specific steps](#color-linear-custom) you want the legend to display.
color.**orient(string)**
legendColor.**cellFilter(function)**
This function is run as a filter function against the array of cells. If you have a function(d){ return true or false }, d has a .data and a .label property as it iterates over each cell it will display. Create a false condition for any cells you want to exclude from being displayed. An example: [Color - Ordinal Scale Legend, custom shape](#color-ordinal).
legendColor.**orient(string)**
Accepts "vertical" or "horizontal" for legend orientation. Default set to "vertical."
color.**ascending(boolean)**
legendColor.**ascending(boolean)**
If you pass this a true, it will reverse the order of the scale.
color.**shape(string[, path-string])**
legendColor.**shape(string[, path-string])**
Accepts "rect", "circle", "line", or "path". If you choose "path," you must also pass a second parameter as a path string. Defaults to "rect." An example: [Color - Ordinal Scale Legend, custom shape](#color-ordinal).
color.**shapeWidth(number)**
legendColor.**shapeWidth(number)**
Only applies to shape of "rect" or "line." Default set to 15px.
color.**shapeHeight(number)**
legendColor.**shapeHeight(number)**
Only applies to shape of "rect." Default set to 15px.
color.**shapeRadius(number)**
legendColor.**shapeRadius(number)**
Only applies to shape of "circle." Default set to 10px.
color.**shapePadding(number)**
legendColor.**shapePadding(number)**
Applies to all shapes. Determines vertical or horizontal spacing between shapes depending on the respective orient setting. Default set to 2px.
color.**useClass(boolean)**
legendColor.**useClass(boolean)**
The default behavior is for the legend to set the fill of the legend's symbols (except for the "line" shape which uses stroke). If you set useClass to `true` then it will apply the scale's output as classes to the shapes instead of the fill or stroke. An example: [Color - Quantile Scale Legend](#color-quant).
color.**classPrefix(string)**
legendColor.**classPrefix(string)**
Adds this string to the beginning of all of the components of the legend that have a class. This allows for namespacing of the classes.
color.**title(string)**
legendColor.**title(string)**
Sets the legend's title to the string. Automatically moves the legend cells down based on the size of the title. An example: [Symbol - Ordinal Scale](#symbol-ordinal).
color.**labels([string])**
legendColor.**titleWidth(number)**
Will break the legend title into multiple lines based on the width in pixels. An example: [Color - Quantile Scale Legend](#color-quant).
legendColor.**labels([string] or function(options))**
Passing a string:
Sets the legend labels to the array of strings passed to the legend. If the array is not the same length as the array the legend calculates, it merges the values and gives the calculated labels for the remaining items. An example: [Size - Linear Scale Legend, Lines](#size-line).
color.**labelAlign(string)**
Passing a function:
This function is called for each generated label and gives you the options:
- i: current index
- genLength: total length of generated labels
- generatedLabels: array of generated labels
- domain: array from input scale
- range: array from input scale
This allows you to make any custom functions to handle labels. An example: [Color - Threshold Scale, Custom Labels](#color-threshold)
List of [helper functions](#helpers).
legendColor.**labelAlign(string)**
Only used if the legend's orient is set to "horizontal." Accepts "start", "middle", or "end" as inputs to determine if the labels are aligned on the left, middle or right under the symbol in a horizontal legend. An example: [Size - Linear Scale Legend, Lines](#size-line).
color.**labelFormat(d3.format)**
legendColor.**labelFormat(d3.format or d3.format string)**
Takes a [d3.format](https://github.com/mbostock/d3/wiki/Formatting) and applies that styling to the legend labels. Default is set to `d3.format(".01f")`.
color.**labelOffset(number)**
legendColor.**locale(d3.format locale)**
Takes a [d3.format locale](https://github.com/d3/d3-format/tree/master/locale) and applies it to the legend labels. Default is set to [US english](https://github.com/d3/d3-format/blob/master/locale/en-US.json).
legendColor.**labelOffset(number)**
A value that determines how far the label is from the symbol in each legend item. Default set to 10px.
color.**labelDelimiter(string)**
legendColor.**labelDelimiter(string)**
Change the default "to" text when working with a quant scale.
color.**on(string, function)**
legendColor.**labelWrap(number)**
Add text wrapping to the cell labels. In orient horizontal you can use this in combination with shapePadding to get the desired spacing. An exampe: [Size - Linear Scale](#size-line). In orient vertical this will automatically scale the cells to fit the label.An example: [Symbol - Ordinal Scale](#symbol-ordinal)
legendColor.**on(string, function)**
There are three custom event types you can bind to the legend: "cellover", "cellout", and "cellclick" An exampe: [Symbol - Ordinal Scale](#symbol-ordinal)
There are three custom event types you can bind to the legend: "cellover", "cellout", and "cellclick" An example: [Symbol - Ordinal Scale](#symbol-ordinal)

@ -2,6 +2,7 @@
- [Documentation](#color-doc)
- [Examples](#color-examples)
- [Quantile Scale Legend](#color-quant)
- [Threshold Scale Legend](#color-threshold)
- [Linear Scale Legend - Horizontal](#color-linear)
- [Linear Scale Legend - 10 cells](#color-linear-10)
- [Linear Scale Legend - Custom cells](#color-linear-custom)
@ -17,4 +18,4 @@
- [Examples](#symbol-examples)
- [Ordinal Scale Legend - Custom Symbols](#symbol-ordinal)
- [Summary of Functions](#summary) - table of which functions are shared across legend types
- [Summary of Functions](#summary) - table of which functions are shared across legend types

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,8 @@
d3.**legendHelpers()**
Convenience functions for using this module
legendHelpers.**thresholdLabels**
Changes the labels so the first one label says "Less than _first-threshold_" and the last one says "More than _last-threshold_".
Example: [Color - Threshold Scale, Custom Labels](#color-threshold)

@ -1,5 +1,6 @@
<!DOCTYPE html>
<meta charset="utf-8">
<head>
@ -14,119 +15,162 @@
<link rel="stylesheet" href="prism.css">
<style>
svg {
padding: 10px 15px;
margin-bottom: 5px;
display: block;
background: white;
border: 1px solid $grey;
}
svg {
padding: 10px 15px;
margin-bottom: 5px;
display: block;
background: white;
border: 1px solid $grey;
}
pre[class*="language-"]{
margin-top: 0px;
margin-bottom: 30px;
border: 1px solid #E4E4E4;
}
pre[class*="language-"] {
margin-top: 0px;
margin-bottom: 30px;
border: 1px solid #E4E4E4;
}
:not(pre) > code[class*="language-"], pre[class*="language-"] {
background: #FFFFFF;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #FFFFFF;
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
color: #323232;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #323232;
}
.token.function {
color: #989898;
}
.token.function {
color: #989898;
}
.token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted {
color: #00993C;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #00993C;
}
h4 {
display: inline-block;
}
h4 {
display: inline-block;
}
strong {
color: #272727;
}
strong {
color: #272727;
}
text {
font: 12px sans-serif;
}
text {
font: 12px sans-serif;
}
hr {
border-top: 1px solid #999;
}
hr {
border-top: 1px solid #999;
}
table tbody tr:not(:last-child) td{
border-bottom: 2px solid #E7E7E7;
}
table tbody tr:not(:last-child) td {
border-bottom: 2px solid #E7E7E7;
}
table tbody td:not(:last-child), table thead th:not(:last-child) {
border-right: 2px solid #e7e7e7
}
table tbody td:not(:last-child),
table thead th:not(:last-child) {
border-right: 2px solid #e7e7e7
}
table td:not(:first-child){
width: 60px;
}
table td:not(:first-child) {
width: 60px;
}
table td.included{
background-color: rgb(102, 178, 155);
}
table td.included {
background-color: rgb(102, 178, 155);
}
@media only screen and (max-width: 480px){
table {
font-size: 11px;
@media only screen and (max-width: 480px) {
table {
font-size: 11px;
}
}
}
#contents ul li {
font-size: 16px;
}
#contents ul li {
font-size: 16px;
}
.legendSize rect, .legendSize circle{
fill: rgb(46, 73, 123);
}
.legendSize rect,
.legendSize circle {
fill: rgb(46, 73, 123);
}
.legendSizeHorz circle{
stroke: rgb(71, 187, 94);
fill: none;
}
.legendSizeHorz circle {
stroke: rgb(71, 187, 94);
fill: none;
}
.legendSizeLine line {
stroke: rgb(46, 73, 123);
}
.legendSymbol path {
fill: rgb(64, 108, 189);
}
.q0-9 {
fill: rgb(247, 251, 255);
}
.q1-9 {
fill: rgb(222, 235, 247);
}
.q2-9 {
fill: rgb(198, 219, 239);
}
.q3-9 {
fill: rgb(158, 202, 225);
}
.legendSizeLine line {
stroke: rgb(46, 73, 123);
}
.q4-9 {
fill: rgb(107, 174, 214);
}
.legendSymbol path {
fill: rgb(64, 108, 189);
}
.q5-9 {
fill: rgb(66, 146, 198);
}
.q6-9 {
fill: rgb(33, 113, 181);
}
.q0-9 { fill:rgb(247,251,255); }
.q1-9 { fill:rgb(222,235,247); }
.q2-9 { fill:rgb(198,219,239); }
.q3-9 { fill:rgb(158,202,225); }
.q4-9 { fill:rgb(107,174,214); }
.q5-9 { fill:rgb(66,146,198); }
.q6-9 { fill:rgb(33,113,181); }
.q7-9 { fill:rgb(8,81,156); }
.q8-9 { fill:rgb(8,48,107); }
.q7-9 {
fill: rgb(8, 81, 156);
}
.q8-9 {
fill: rgb(8, 48, 107);
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="txt-center">
<h1 class="column12">d3 SVG Legend</h1>
<h1 class="column12">d3 SVG Legend (v4)</h1>
<div class="column6 prefix3">
<p>Tired of making legends for your data visualizations? Me too, enjoy.</p>
<p>A library to make legends in svg-land easy as pie.</p>
<img src="d3-legend.jpg">
<p>By <a href="http://www.susielu.com">Susie Lu</a></p>
<p>By
<a href="http://www.susielu.com">Susie Lu</a>
</p>
</div>
<nav class="column6 prefix3 nav nav-small">
<a href="#usage">Usage</a>
@ -134,6 +178,7 @@
<a href="#color">Color</a>
<a href="#size">Size</a>
<a href="#symbol">Symbol</a>
<a href="#helpers">Helpers</a>
<a href="#summary">Summary of Functions</a>
</nav>
</div>
@ -149,28 +194,51 @@
<h3>Client-side</h3>
<h4>CDN</h4>
<p>You can add latest version of d3-legend hosted on cdnjs.<p>
<a href="https://cdnjs.com/libraries/d3-legend">https://cdnjs.com/libraries/d3-legend</a>
<h4>Version</h4>
<p>This is the version compatible with
<b>d3v4</b>,
<a href="http://d3-legend-v3.susielu.com/">please go here for the version compatible with d3v3</a>
</p>
<h4>Include the file directly</h4>
<p>You must include the <a href="http://d3js.org/">d3 library</a> before including the legend file. Then you can simply add the compiled js file to your website:</p>
<p>Changes when moving to d3v4 of this component</p>
<ul>
<li><a href="https://raw.githubusercontent.com/susielu/d3-legend/master/d3-legend.min.js">All legends</a></li>
<li><a href="https://github.com/susielu/d3-legend/blob/master/d3-legend.js">All legends - human readable</a></li>
<li>Flattened naming for accessing functions</li>
<ul>
<li>d3.legend.color => d3.legendColor</li>
<li>d3.legend.size => d3.legendSize</li>
<li>d3.legend.symbol => d3.legendSymbol</li>
</ul>
<li>NPM package no longer binds to global d3, is now just an object with the three legend functions</li>
</ul>
<h3>npm</h3>
<p>Already using d3? Great! You can add the d3 legend as a node module by running:<p>
<code>npm i d3-svg-legend -S</code>
<p>If not, install both this way:</p>
<code>npm i d3@^3.0.0 d3-svg-legend -S</code>
<h3>Github + feedback</h3>
<p>The full source code is available on <a href="https://github.com/susielu/d3-legend">github</a>. I would love to hear from you about any additional features that would be useful, please say hi on twitter <a href="https://www.twitter.com/DataToViz">@DataToViz</a>.</p>
<h4>CDN</h4>
<p>You can add the latest version of d3-legend hosted on cdnjs.
<p>
<a href="https://cdnjs.com/libraries/d3-legend">https://cdnjs.com/libraries/d3-legend</a>
<h4>Include the file directly</h4>
<p>You must include the
<a href="http://d3js.org/">d3 library</a> before including the legend file. Then you can simply add the compiled js file to your website:</p>
<ul>
<li>
<a href="https://raw.githubusercontent.com/susielu/d3-legend/master/d3-legend.min.js">All legends (d3v4)</a>
</li>
<li>
<a href="https://github.com/susielu/d3-legend/blob/master/d3-legend.js">All legends - human readable (d3v4)</a>
</li>
</ul>
<h3>npm</h3>
<p>Already using d3? Great! You can add the d3 legend as a node module by running:
<p>
<code>npm i d3-svg-legend -S</code>
<h3>Github + feedback</h3>
<p>The full source code is available on
<a href="https://github.com/susielu/d3-legend">github</a>. I would love to hear from you about any additional features that would be useful, please say
hi on twitter
<a href="https://www.twitter.com/DataToViz">@DataToViz</a>.</p>
</div>
</div>
</section>
@ -178,7 +246,7 @@
<div class="container">
<div class="column6 prefix3">
<h2>Contents</h2>
<div id="contents-md"></div>
<div id="contents-md"></div>
</div>
</div>
</section>
@ -190,20 +258,20 @@
<h3 id="color-doc">Documentation</h3>
<div id="color-md"> </div>
</div>
</div>
</div>
</section>
<section class="bg-light" id="color-examples">
<div class="container">
<div class="column6 prefix3">
<h3 >Examples</h3>
</section>
<section class="bg-light" id="color-examples">
<div class="container">
<div class="column6 prefix3">
<h3>Examples</h3>
<h4 id="color-quant">Quantile Scale Legend</h4>
<svg height=200 width=160 id="svg-color-quant"></svg>
<svg height=240 width=160 id="svg-color-quant"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var quantize = d3.scale.quantize()
var quantize = d3.scaleQuantize()
.domain([ 0, 0.15 ])
.range(d3.range(9).map(function(i) { return "q" + i + "-9"; }));
@ -213,9 +281,12 @@ svg.append("g")
.attr("class", "legendQuant")
.attr("transform", "translate(20,20)");
var legend = d3.legend.color()
var legend = d3.legendColor()
.labelFormat(d3.format(".2f"))
.useClass(true)
.title("A really really really really
really long title")
.titleWidth(100)
.scale(quantize);
svg.select(".legendQuant")
@ -223,12 +294,54 @@ svg.select(".legendQuant")
</code>
</pre>
<h4 id="color-quant">Log Scale Legend</h4>
<svg height=160 width=100 id="svg-color-log"></svg>
<h4 id="color-threshold">Treshold Scale Legend</h4>
<svg height=150 width=210 id="svg-color-threshold"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var thresholdScale = d3.scaleThreshold()
.domain([ 0, 1000, 2500, 5000, 10000 ])
.range(d3.range(6)
.map(function(i) { return "q" + i + "-9"}));
var svg = d3.select("svg");
svg.append("g")
.attr("class", "legendQuant")
.attr("transform", "translate(20,20)");
var legend = d3.legendColor()
.labelFormat(d3.format(".2f"))
.labels(d3.legendHelpers.thresholdLabels)
.useClass(true)
.scale(thresholdScale)
/*
----legendHelpers.thresholdLabels----
function({ i, genLength, generatedLabels }){
if (i === 0 ){
return generatedLabels[i]
.replace('NaN to', 'Less than')
} else if (i === genLength - 1) {
return `More than
${generatedLabels[genLength - 1]
.replace(' to NaN', '')}`
}
return generatedLabels[i]
}
*/
svg.select(".legendQuant")
.call(legend);
</code>
</pre>
<h4 id="color-log">Log Scale Legend</h4>
<svg height=160 width=100 id="svg-color-log"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var log = d3.scale.log()
var log = d3.scaleLog()
.domain([ 0.1, 100, 1000 ])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
@ -238,7 +351,7 @@ svg.append("g")
.attr("class", "legendLog")
.attr("transform", "translate(20,20)");
var logLegend = d3.legend.color()
var logLegend = d3.legendColor()
.cells([0.1, 5, 10, 50, 100, 500, 1000])
.scale(log);
@ -249,11 +362,11 @@ svg.select(".legendLog")
</pre>
<h4 id="color-linear">Linear Scale Legend - Horizontal</h4>
<svg height=60 width=200 id="svg-color-linear"></svg>
<svg height=60 width=200 id="svg-color-linear"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var linear = d3.scale.linear()
var linear = d3.scaleLinear()
.domain([0,10])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
@ -263,7 +376,7 @@ svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
var legendLinear = d3.legend.color()
var legendLinear = d3.legendColor()
.shapeWidth(30)
.orient('horizontal')
.scale(linear);
@ -274,11 +387,11 @@ svg.select(".legendLinear")
</pre>
<h4 id="color-linear-10">Linear Scale Legend - 10 cells</h4>
<svg height=60 width=360 id="svg-color-linear-10"></svg>
<svg height=60 width=360 id="svg-color-linear-10"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var linear = d3.scale.linear()
var linear = d3.scaleLinear()
.domain([0,10])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
@ -288,7 +401,7 @@ svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
var legendLinear = d3.legend.color()
var legendLinear = d3.legendColor()
.shapeWidth(30)
.cells(10)
.orient('horizontal')
@ -300,11 +413,11 @@ svg.select(".legendLinear")
</pre>
<h4 id="color-linear-custom">Linear Scale Legend - Custom cells</h4>
<svg height=60 width=200 id="svg-color-linear-custom"></svg>
<svg height=60 width=200 id="svg-color-linear-custom"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var linear = d3.scale.linear()
var linear = d3.scaleLinear()
.domain([0,10])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
@ -314,7 +427,7 @@ svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
var legendLinear = d3.legend.color()
var legendLinear = d3.legendColor()
.shapeWidth(30)
.cells([1, 2, 3, 6, 8])
.orient('horizontal')
@ -325,12 +438,37 @@ svg.select(".legendLinear")
</code>
</pre>
<h4 id="color-sequential">Sequential Scale Legend</h4>
<svg height=60 width=350 id="svg-color-sequential"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var sequentialScale = d3.scaleSequential(d3.interpolateRainbow)
.domain([0,10]);
var svg = d3.select("svg");
svg.append("g")
.attr("class", "legendSequential")
.attr("transform", "translate(20,20)");
var legendSequential = d3.legendColor()
.shapeWidth(30)
.cells(10)
.orient("horizontal")
.scale(sequentialScale)
svg.select(".legendSequential")
.call(legendSequential);
</code>
</pre>
<h4 id="color-ordinal">Ordinal Scale Legend - Custom shape</h4>
<svg height=150 width=70 id="svg-color-ordinal"></svg>
<svg height=120 width=70 id="svg-color-ordinal"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var ordinal = d3.scale.ordinal()
var ordinal = d3.scaleOrdinal()
.domain(["a", "b", "c", "d", "e"])
.range([ "rgb(153, 107, 195)", "rgb(56, 106, 197)", "rgb(93, 199, 76)", "rgb(223, 199, 31)", "rgb(234, 118, 47)"]);
@ -340,12 +478,14 @@ svg.append("g")
.attr("class", "legendOrdinal")
.attr("transform", "translate(20,20)");
var legendOrdinal = d3.legend.color()
var legendOrdinal = d3.legendColor()
//d3 symbol creates a path-string, for example
//"M0,-8.059274488676564L9.306048591020996,
//8.059274488676564 -9.306048591020996,8.059274488676564Z"
.shape("path", d3.svg.symbol().type("triangle-up").size(150)())
.shape("path", d3.symbol().type(d3.symbolTriangle).size(150)())
.shapePadding(10)
//use cellFilter to hide the "e" cell
.cellFilter(function(d){ return d.label !== "e" })
.scale(ordinal);
svg.select(".legendOrdinal")
@ -365,20 +505,20 @@ svg.select(".legendOrdinal")
<h3 id="size-doc">Documentation</h3>
<div id="size-md"></div>
</div>
</div>
</div>
</section>
<section class="bg-light" id="size-examples">
<div class="container">
<div class="column6 prefix3">
<h3>Examples</h3>
<h4 id="size-linear">Linear Scale Legend - Circles</h4>
<svg height=100 width=350 id="svg-size-linear"></svg>
<pre class="column6 left-margin right-margin">
<h3>Examples</h3>
<h4 id="size-linear">Linear Scale Legend - Circles</h4>
<svg height=130 width=350 id="svg-size-linear"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var linearSize = d3.scale.linear().domain([0,10]).range([10, 30]);
var linearSize = d3.scaleLinear().domain([0,10]).range([10, 30]);
var svg = d3.select("svg");
@ -386,7 +526,7 @@ svg.append("g")
.attr("class", "legendSize")
.attr("transform", "translate(20, 40)");
var legendSize = d3.legend.size()
var legendSize = d3.legendSize()
.scale(linearSize)
.shape('circle')
.shapePadding(15)
@ -398,12 +538,12 @@ svg.select(".legendSize")
</code>
</pre>
<h4 id="size-line">Linear Scale Legend - Lines</h4>
<svg height=60 width=380 id="svg-size-line"></svg>
<pre class="column6 left-margin right-margin">
<h4 id="size-line">Linear Scale Legend - Lines</h4>
<svg height=140 width=380 id="svg-size-line"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var lineSize = d3.scale.linear().domain([0,10]).range([2, 10]);
var lineSize = d3.scaleLinear().domain([0,10]).range([2, 10]);
var svg = d3.select("svg");
@ -411,13 +551,16 @@ svg.append("g")
.attr("class", "legendSizeLine")
.attr("transform", "translate(0, 20)");
var legendSizeLine = d3.legend.size()
var legendSizeLine = d3.legendSize()
.scale(lineSize)
.shape("line")
.orient("horizontal")
//otherwise labels would have displayed:
// 0, 2.5, 5, 10
.labels(["tiny", "small", "medium", "large", "grand"])
.labels(["tiny testing at the beginning",
"small", "medium", "large", "grand,
all the way long label"])
.labelWrap(30)
.shapeWidth(40)
.labelAlign("start")
.shapePadding(10);
@ -438,31 +581,31 @@ svg.select(".legendSizeLine")
<h3 id="symbol-doc">Documentation</h3>
<div id="symbol-md"></div>
</div>
</div>
</div>
</section>
<section class="bg-light" id="symbol-examples">
<div class="container">
<div class="column6 prefix3">
<h3>Examples</h3>
<h4 id="symbol-ordinal">Ordinal Scale Legend - Custom symbols</h4>
<svg height=90 width=170 id="svg-symbol-ordinal"></svg>
<pre class="column6 left-margin right-margin">
<h4 id="symbol-ordinal">Ordinal Scale Legend - Custom symbols</h4>
<svg height=170 width=170 id="svg-symbol-ordinal"></svg>
<pre class="column6 left-margin right-margin">
<code class="language-javascript">
var triangleU = d3.svg.symbol().type('triangle-up')(),
circle = d3.svg.symbol().type('circle')(),
cross = d3.svg.symbol().type('cross')(),
diamond = d3.svg.symbol().type('diamond')(),
triangleD = d3.svg.symbol().type('triangle-down')();
var triangleU = d3.symbol().type(d3.symbolTriangle)(),
circle = d3.symbol().type(d3.symbolCircle)(),
cross = d3.symbol().type(d3.symbolCross)(),
diamond = d3.symbol().type(d3.symbolDiamond)(),
star = d3.symbol().type(d3.symbolStar)();
//example output of d3.svg.symbol().type('circle')();
//"M0,4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,
//-4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,4.51351666838205Z"
var symbolScale = d3.scale.ordinal()
.domain(['a','b','c', 'd', 'e'])
.range([ triangleU, circle, cross, diamond, triangleD] );
var symbolScale = d3.scaleOrdinal()
.domain(['a longer label','b','c', 'd', 'e'])
.range([ triangleU, circle, cross, diamond, start] );
var svg = d3.select("svg");
@ -470,9 +613,10 @@ svg.append("g")
.attr("class", "legendSymbol")
.attr("transform", "translate(20, 20)");
var legendPath = d3.legend.symbol()
var legendPath = d3.legendSymbol()
.scale(symbolScale)
.orient("horizontal")
.labelWrap(30)
.title("Symbol Legend Title")
.on("cellclick", function(d){alert("clicked " + d);});
@ -484,126 +628,162 @@ svg.select(".legendSymbol")
</div>
</section>
<section id="helpers">
<div class="container">
<div class="column6 prefix3">
<h2>Helpers</h2>
<h3 id="helpers-doc">Documentation</h3>
<div id="helpers-md"></div>
</div>
</div>
</section>
<section id="summary">
<div class="container">
<div class="column6 prefix3">
<h2>Summary of Functions</h2>
<table>
<tr>
<th>Function</th>
<th>Color</th>
<th>Size</th>
<th>Symbol</th>
</tr>
<tr>
<td>scale</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>cells</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>orient</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>ascending</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>shape</td>
<td class="included"></td>
<td class="included"></td>
<td></td>
</tr>
<tr>
<td>shapeWidth</td>
<td class="included"></td>
<td class="included"></td>
<td></td>
</tr>
<tr>
<td>shapeHeight</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>shapeRadius</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>shapePadding</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>useClass</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>classPrefix</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>title</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labels</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelAlign</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelFormat</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelOffset</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelDelimiter</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>on</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<th>Function</th>
<th>Color</th>
<th>Size</th>
<th>Symbol</th>
</tr>
<tr>
<td>scale</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>cells</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>cellFilter</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>orient</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>ascending</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>shape</td>
<td class="included"></td>
<td class="included"></td>
<td></td>
</tr>
<tr>
<td>shapeWidth</td>
<td class="included"></td>
<td class="included"></td>
<td></td>
</tr>
<tr>
<td>shapeHeight</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>shapeRadius</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>shapePadding</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>useClass</td>
<td class="included"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>classPrefix</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>title</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>titleWidth</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labels</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelAlign</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelFormat</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>locale</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelOffset</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelDelimiter</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>labelWrap</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
<tr>
<td>on</td>
<td class="included"></td>
<td class="included"></td>
<td class="included"></td>
</tr>
</table>
</div>
</div>
@ -613,23 +793,32 @@ svg.select(".legendSymbol")
<div class="container">
<div class="column6 prefix3">
<br />
<br />
<p>NOTES</p>
<p>Huge thanks to <a href="http://elijahmeeks.com/">Elijah Meeks</a> for discussing ideas and encouraging me to complete this project.</p>
<p>The styling and layout of this page is made with another project of mine, <a href="http://minimal-ui.susielu.com">minimal-ui.</a></p>
<p>The fonts on this page are provided by Google Fonts, and created by <a href="https://www.google.com/fonts/specimen/Montserrat">Julieta Ulanovsky</a> and <a href="https://www.google.com/fonts/specimen/Cardo">David Perry</a>.</p>
<p>Using <a href="http://prismjs.com/">Prism</a> for syntax highlighting.</p>
<p>And of course, thanks <a href="http://d3js.org/">d3.js</a> for creating such a lovely project and <a href="http://bost.ocks.org/mike/">Mike Bostock</a> for providing <a href="http://bost.ocks.org/mike/chart/">examples</a> on how to make your own components.</p>
<p>Huge thanks to
<a href="http://elijahmeeks.com/">Elijah Meeks</a> for discussing ideas and encouraging me to complete this project.</p>
<p>The styling and layout of this page is made with another project of mine,
<a href="http://minimal-ui.susielu.com">minimal-ui.</a>
</p>
<p>The fonts on this page are provided by Google Fonts, and created by
<a href="https://www.google.com/fonts/specimen/Montserrat">Julieta Ulanovsky</a> and
<a href="https://www.google.com/fonts/specimen/Cardo">David Perry</a>.</p>
<p>Using
<a href="http://prismjs.com/">Prism</a> for syntax highlighting.</p>
<p>And of course, thanks
<a href="http://d3js.org/">d3.js</a> for creating such a lovely project and
<a href="http://bost.ocks.org/mike/">Mike Bostock</a> for providing
<a href="http://bost.ocks.org/mike/chart/">examples</a> on how to make your own components.</p>
</div>
</div>
</section>
<script src="prism.js"></script>
<script src="prism.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<script src="d3-legend.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="d3-legend.min.js"></script>
<script src="docs.min.js"></script>
<script src="ga.js"></script>
</body>
<script src="docs.min.js"></script>
<script src="ga.js"></script>
</body>

@ -1,175 +1,267 @@
//Color: Quantile #svg-color-quant
var svg = d3.select("#svg-color-quant");
var quantize = d3.scale.quantize()
.domain([ 0, 0.15 ])
.range(d3.range(9).map(function(i) { return "q" + i + "-9"; }));
svg.append("g")
var svg = d3.select("#svg-color-quant")
var quantize = d3
.scaleQuantize()
.domain([0, 0.15])
.range(
d3.range(9).map(function(i) {
return "q" + i + "-9"
})
)
svg
.append("g")
.attr("class", "legendQuant")
.attr("transform", "translate(20,20)");
var legend = d3.legend.color()
.labelFormat(d3.format(".2f"))
.useClass(true)
.scale(quantize);
svg.select(".legendQuant")
.call(legend);
.attr("transform", "translate(20,20)")
var legend = d3
.legendColor()
.labelFormat(d3.format(".2f"))
.useClass(true)
.title("A really really really really really long title")
.titleWidth(100)
.scale(quantize)
svg.select(".legendQuant").call(legend)
//Color: Treshold #svg-color-threshold
var svg = d3.select("#svg-color-threshold")
var thresholdScale = d3
.scaleThreshold()
.domain([0, 1000, 2500, 5000, 10000])
.range(
d3.range(6).map(function(i) {
return "q" + i + "-9"
})
)
svg
.append("g")
.attr("class", "legendThreshold")
.attr("transform", "translate(20,20)")
var legend = d3
.legendColor()
.labelFormat(d3.format(".2f"))
.labels(d3.legendHelpers.thresholdLabels)
.useClass(true)
.shapeWidth(30)
.scale(thresholdScale)
svg.select(".legendThreshold").call(legend)
//Color: Log #svg-color-log
var svg = d3.select("#svg-color-log");
var svg = d3.select("#svg-color-log")
var log = d3.scale.log()
.domain([ 0.1, 100, 1000 ])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
var log = d3
.scaleLog()
.domain([0.1, 100, 1000])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"])
svg.append("g")
svg
.append("g")
.attr("class", "legendLog")
.attr("transform", "translate(20,20)");
.attr("transform", "translate(20,20)")
var logLegend = d3.legend.color()
.cells([0.1, 5, 10, 50, 100, 500, 1000])
.scale(log);
var logLegend = d3
.legendColor()
.cells([0.1, 5, 10, 50, 100, 500, 1000])
.scale(log)
svg.select(".legendLog")
.call(logLegend);
svg.select(".legendLog").call(logLegend)
//Color Linear #svg-color-linear
var linear = d3.scale.linear().domain([0,10]).range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
var linear = d3
.scaleLinear()
.domain([0, 10])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"])
svg = d3.select("#svg-color-linear");
svg = d3.select("#svg-color-linear")
svg.append("g")
svg
.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
.attr("transform", "translate(20,20)")
var legendLinear = d3.legend.color()
.shapeWidth(30)
.orient('horizontal')
.scale(linear);
var legendLinear = d3
.legendColor()
.shapeWidth(30)
.orient("horizontal")
.scale(linear)
//Color Linear #svg-color-linear-10
svg.select(".legendLinear")
.call(legendLinear);
svg.select(".legendLinear").call(legendLinear)
svg = d3.select("#svg-color-linear-10");
svg = d3.select("#svg-color-linear-10")
svg.append("g")
svg
.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
.attr("transform", "translate(20,20)")
legendLinear.cells(10);
legendLinear.cells(10)
svg.select(".legendLinear")
.call(legendLinear);
svg.select(".legendLinear").call(legendLinear)
//Linear #svg-color-linear-custom
svg.select(".legendLinear")
.call(legendLinear);
svg.select(".legendLinear").call(legendLinear)
svg = d3.select("#svg-color-linear-custom");
svg = d3.select("#svg-color-linear-custom")
svg.append("g")
svg
.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
legendLinear.cells([1, 2, 3, 6, 8]);
.attr("transform", "translate(20,20)")
svg.select(".legendLinear")
.call(legendLinear);
legendLinear.cells([1, 2, 3, 6, 8])
svg.select(".legendLinear").call(legendLinear)
//Ordinal #svg-color-ordinal
var ordinal = d3.scale.ordinal()
var ordinal = d3
.scaleOrdinal()
.domain(["a", "b", "c", "d", "e"])
.range([ "rgb(153, 107, 195)", "rgb(56, 106, 197)", "rgb(93, 199, 76)", "rgb(223, 199, 31)", "rgb(234, 118, 47)"]);
.range([
"rgb(153, 107, 195)",
"rgb(56, 106, 197)",
"rgb(93, 199, 76)",
"rgb(223, 199, 31)",
"rgb(234, 118, 47)"
])
svg = d3.select("#svg-color-ordinal")
svg
.append("g")
.attr("class", "legendOrdinal")
.attr("transform", "translate(20,20)")
var legendOrdinal = d3
.legendColor()
.shape(
"path",
d3
.symbol()
.type(d3.symbolTriangle)
.size(150)()
)
.shapePadding(10)
.cellFilter(function(d) {
return d.label !== "e"
})
.scale(ordinal)
svg = d3.select("#svg-color-ordinal");
svg.select(".legendOrdinal").call(legendOrdinal)
svg.append("g")
.attr("class", "legendOrdinal")
.attr("transform", "translate(20,20)");
var sequentialScale = d3.scaleSequential(d3.interpolateRainbow).domain([0, 10])
svg = d3.select("#svg-color-sequential")
var legendOrdinal = d3.legend.color()
.shape("path", d3.svg.symbol().type("triangle-up").size(150)())
.shapePadding(10)
.scale(ordinal);
svg
.append("g")
.attr("class", "legendSequential")
.attr("transform", "translate(20,20)")
svg.select(".legendOrdinal")
.call(legendOrdinal);
var legendSequential = d3
.legendColor()
.shapeWidth(30)
.cells(10)
.orient("horizontal")
.scale(sequentialScale)
svg.select(".legendSequential").call(legendSequential)
//Size: Linear Circle #svg-size-linear
var linearSize = d3.scale.linear().domain([0,10]).range([10, 30]);
var linearSize = d3
.scaleLinear()
.domain([0, 10])
.range([10, 30])
svg = d3.select("#svg-size-linear");
svg = d3.select("#svg-size-linear")
svg.append("g")
svg
.append("g")
.attr("class", "legendSize")
.attr("transform", "translate(20, 40)");
var legendSize = d3.legend.size()
.scale(linearSize)
.shape('circle')
.shapePadding(15)
.labelOffset(20)
.orient('horizontal');
.attr("transform", "translate(20, 40)")
var legendSize = d3
.legendSize()
.scale(linearSize)
.shape("circle")
.shapePadding(15)
.labelOffset(20)
.orient("horizontal")
.on("cellover", function() {
console.log("cellover", d3.event, d3.event.type)
})
svg.select(".legendSize")
.call(legendSize);
svg.select(".legendSize").call(legendSize)
//Size: Linear Line #svg-size-line
var lineSize = d3.scale.linear().domain([0,10]).range([2, 10]);
var lineSize = d3
.scaleLinear()
.domain([0, 10])
.range([2, 10])
svg = d3.select("#svg-size-line");
svg = d3.select("#svg-size-line")
svg.append("g")
svg
.append("g")
.attr("class", "legendSizeLine")
.attr("transform", "translate(0, 20)");
.attr("transform", "translate(0, 20)")
var legendSizeLine = d3.legend.size()
.scale(lineSize)
.shape("line")
.orient("horizontal")
.labels(["tiny", "small", "medium", "large", "grand"])
.shapeWidth(50)
.labelAlign("start")
.shapePadding(10);
var legendSizeLine = d3
.legendSize()
.scale(lineSize)
.shape("line")
.orient("horizontal")
.labels([
"tiny testing at the beginning",
"small",
"medium",
"large",
"grand, all the way long label"
])
.labelWrap(30)
.shapeWidth(50)
.labelAlign("start")
.shapePadding(10)
svg.select(".legendSizeLine")
.call(legendSizeLine);
svg.select(".legendSizeLine").call(legendSizeLine)
//Symbol: Ordinal #svg-symbol-ordinal
svg = d3.select("#svg-symbol-ordinal");
svg = d3.select("#svg-symbol-ordinal")
svg.append("g")
svg
.append("g")
.attr("class", "legendSymbol")
.attr("transform", "translate(20, 20)");
.attr("transform", "translate(20, 20)")
var triangleU = d3.svg.symbol().type('triangle-up')(),
circle = d3.svg.symbol().type('circle')(),
cross = d3.svg.symbol().type('cross')(),
diamond = d3.svg.symbol().type('diamond')(),
triangleD = d3.svg.symbol().type('triangle-down')();
var triangleU = d3.symbol().type(d3.symbolTriangle)(),
circle = d3.symbol().type(d3.symbolCircle)(),
cross = d3.symbol().type(d3.symbolCross)(),
diamond = d3.symbol().type(d3.symbolDiamond)(),
star = d3.symbol().type(d3.symbolStar)()
//example output of d3.svg.symbol().type('circle')();
//"M0,4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,
//-4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,4.51351666838205Z"
var symbolScale = d3.scale.ordinal()
.domain(['a','b','c', 'd', 'e'])
.range([ triangleU, circle, cross, diamond, triangleD] );
var symbolScale = d3
.scaleOrdinal()
.domain(["a longer label", "b", "c", "d", "e"])
.range([triangleU, circle, cross, diamond, star])
var legendPath = d3.legend.symbol()
var legendPath = d3
.legendSymbol()
.scale(symbolScale)
.orient("horizontal")
.title('Symbol Legend Title')
.on("cellclick", function(d){alert("clicked " + d);});
svg.select(".legendSymbol")
.call(legendPath);
//.orient("horizontal")
.labelWrap(30)
.title("Symbol Legend Title")
.on("cellclick", function(d) {
alert("clicked " + d)
})
svg.select(".legendSymbol").call(legendPath)

@ -4,8 +4,10 @@ var contents = require('./contents.md')
var color = require('./color.md');
var size = require('./size.md');
var symbol = require('./symbol.md');
var helpers = require('./helpers.md');
document.getElementById('contents-md').innerHTML = md(contents);
document.getElementById('color-md').innerHTML = md(color);
document.getElementById('size-md').innerHTML = md(size);
document.getElementById('symbol-md').innerHTML = md(symbol);
document.getElementById('symbol-md').innerHTML = md(symbol);
document.getElementById('helpers-md').innerHTML = md(helpers);

@ -1,63 +1,93 @@
d3.legend.**size()**
d3.**legendSize()**
Constructs a new size legend. The legend component expects a d3 scale as the basic input, but also has a number of optional parameters for changing the default display such as vertical or horizontal orientation, shape of the symbol next to the label, symbol sizing, and label formatting.
size.**scale(d3.scale)**
legendSize.**scale(d3.scale)**
Creates a new d3 legend based on the scale. The code determines the type of scale and generates the different symbol and label pairs. Expects a scale that has a numerical range.
size.**cells(number or [numbers])**
legendSize.**cells(number or [numbers])**
This parameter is only valid for continuous scales (like linear and log). When there is no indication from the domain or range for the number of steps in the legend you may want to display, it defaults to five steps in equal increments. You can pass the cells function a single number which will create equal increments for that number of steps, or an array of the [specific steps](#color-linear-custom) you want the legend to display.
size.**orient(string)**
legendSize.**cellFilter(function)**
This function is run as a filter function against the array of cells. If you have a function(d){ return true or false }, d has a .data and a .label property as it iterates over each cell it will display. Create a false condition for any cells you want to exclude from being displayed. An example: [Color - Ordinal Scale Legend, custom shape](#color-ordinal).
legendSize.**orient(string)**
Accepts "vertical" or "horizontal" for legend orientation. Default set to "vertical."
size.**ascending(boolean)**
legendSize.**ascending(boolean)**
If you pass this a true, it will reverse the order of the scale.
size.**shape(string)**
legendSize.**shape(string)**
Accepts "rect", "circle", or "line". Defaults to "rect." The assumption is that the scale's output will be used for the width and height if you select "rect," the radius if you select "circle," and the stroke-width if you select "line." If you want to have a custom shape of different sizes in your legend, use the symbol legend and make each path string for the sizes you want as the range array.
size.**shapeWidth(number)**
legendSize.**shapeWidth(number)**
Only applies to shape "line." Default set to 15px.
size.**shapePadding(number)**
legendSize.**shapePadding(number)**
Applies to all shapes. Determines vertical or horizontal spacing between shapes depending on the respective orient setting. Default set to 2px.
size.**classPrefix(string)**
legendSize.**classPrefix(string)**
Adds this string to the beginning of all of the components of the legend that have a class. This allows for namespacing of the classes.
size.**title(string)**
legendSize.**title(string)**
Sets the legend's title to the string. Automatically moves the legend cells down based on the size of the title. An example: [Symbol - Ordinal Scale](#symbol-ordinal).
size.**labels([string])**
legendSize.**titleWidth(number)**
Will break the legend title into multiple lines based on the width in pixels. An example: [Color - Quantile Scale Legend](#color-quant).
legendSize.**labels([string] or function(options))**
Passing a string:
Sets the legend labels to the array of strings passed to the legend. If the array is not the same length as the array the legend calculates, it merges the values and gives the calculated labels for the remaining items. An example: [Size - Linear Scale Legend, Lines](#size-line).
Passing a function:
This function is called for each generated label and gives you the options:
- i: current index
- genLength: total length of generated labels
- generatedLabels: array of generated labels
- domain: array from input scale
- range: array from input scale
This allows you to make any custom functions to handle labels. An example: [Color - Threshold Scale, Custom Labels](#color-threshold)
Sets the legend labels to the array of strings passed to the legend. If the array is not the same length as the array the legend calculates, it merges the values and gives the calculated labels for the remaining items. An example: [Size - Linear Scale Legend, Lines](#size-line)
List of [helper functions](#helpers).
size.**labelAlign(string)**
legendSize.**labelAlign(string)**
Only used if the legend's orient is set to "horizontal." Accepts "start", "middle", or "end" as inputs to determine if the labels are aligned on the left, middle or right under the symbol in a horizontal legend. An example: [Size - Linear Scale Legend, Lines](#size-line).
size.**labelFormat(d3.format)**
legendSize.**labelFormat(d3.format or d3.format string)**
Takes a [d3.format](https://github.com/mbostock/d3/wiki/Formatting) and applies that styling to the legend labels. Default is set to `d3.format(".01f")`.
size.**labelOffset(number)**
legendSize.**locale(d3.format locale)**
Takes a [d3.format locale](https://github.com/d3/d3-format/tree/master/locale) and applies it to the legend labels. Default is set to [US english](https://github.com/d3/d3-format/blob/master/locale/en-US.json).
legendSize.**labelOffset(number)**
A value that determines how far the label is from the symbol in each legend item. Default set to 10px.
size.**labelDelimiter(string)**
legendSize.**labelDelimiter(string)**
Change the default "to" text when working with a quant scale.
size.**on(string, function)**
legendColor.**labelWrap(number)**
Add text wrapping to the cell labels. In orient horizontal you can use this in combination with shapePadding to get the desired spacing. An exampe: [Size - Linear Scale](#size-line). In orient vertical this will automatically scale the cells to fit the label.An example: [Symbol - Ordinal Scale](#symbol-ordinal)
legendSize.**on(string, function)**
There are three custom event types you can bind to the legend: "cellover", "cellout", and "cellclick" An exampe: [Symbol - Ordinal Scale](#symbol-ordinal)

@ -1,55 +1,83 @@
d3.legend.**symbol()**
d3.**legendSymbol()**
Constructs a new symbol legend. The legend component expects a d3 scale as the basic input, but also has a number of optional parameters for changing the default display such as vertical or horizontal orientation, shape of the symbol next to the label, symbol sizing, and label formatting.
symbol.**scale()**
legendSymbol.**scale()**
Creates a new d3 legend based on the scale. The code determines the type of scale and generates the different symbol and label pairs. The scale's range will be used as the d-attribute in an svg path for each symbol in the legend.
symbol.**cells()**
legendSymbol.**cells()**
This parameter is only valid for continuous scales (like linear and log). When there is no indication from the domain or range for the number of steps in the legend you may want to display, it defaults to five steps in equal increments. You can pass the cells function a single number which will create equal increments for that number of steps, or an array of the [specific steps](#color-linear-custom) you want the legend to display.
symbol.**orient(string)**
legendSymbol.**cellFilter(function)**
This function is run as a filter function against the array of cells. If you have a function(d){ return true or false }, d has a .data and a .label property as it iterates over each cell it will display. Create a false condition for any cells you want to exclude from being displayed. An example: [Color - Ordinal Scale Legend, custom shape](#color-ordinal).
legendSymbol.**orient(string)**
Accepts "vertical" or "horizontal" for legend orientation. Default set to "vertical."
symbol.**ascending(boolean)**
legendSymbol.**ascending(boolean)**
If you pass this a true, it will reverse the order of the scale.
symbol.**shapePadding()**
legendSymbol.**shapePadding()**
Applies to all shapes. Determines vertical or horizontal spacing between shapes depending on the respective orient setting. Default set to 2px.
symbol.**classPrefix(string)**
legendSymbol.**classPrefix(string)**
Adds this string to the beginning of all of the components of the legend that have a class. This allows for namespacing of the classes.
symbol.**title(string)**
legendSymbol.**title(string)**
Sets the legend's title to the string. Automatically moves the legend cells down based on the size of the title. An example: [Symbol - Ordinal Scale](#symbol-ordinal).
symbol.**labels([string])**
legendSymbol.**titleWidth(number)**
Will break the legend title into multiple lines based on the width in pixels. An example: [Color - Quantile Scale Legend](#color-quant).
legendSymbol.**labels([string] or function(options))**
Passing a string:
Sets the legend labels to the array of strings passed to the legend. If the array is not the same length as the array the legend calculates, it merges the values and gives the calculated labels for the remaining items. An example: [Size - Linear Scale Legend, Lines](#size-line).
symbol.**labelAlign(string)**
Passing a function:
This function is called for each generated label and gives you the options:
- i: current index
- genLength: total length of generated labels
- generatedLabels: array of generated labels
- domain: array from input scale
- range: array from input scale
This allows you to make any custom functions to handle labels. An example: [Color - Threshold Scale, Custom Labels](#color-threshold)
List of [helper functions](#helpers).
legendSymbol.**labelAlign(string)**
Only used if the legend's orient is set to "horizontal." Accepts "start", "middle", or "end" as inputs to determine if the labels are aligned on the left, middle or right under the symbol in a horizontal legend. An example: [Size - Linear Scale Legend, Lines](#size-line).
symbol.**labelFormat(d3.format)**
legendSymbol.**labelFormat(d3.format or d3.format string)**
Takes a [d3.format](https://github.com/mbostock/d3/wiki/Formatting) and applies that styling to the legend labels. Default is set to `d3.format(".01f")`.
symbol.**labelOffset(number)**
legendSymbol.**locale(d3.format locale)**
Takes a [d3.format locale](https://github.com/d3/d3-format/tree/master/locale) and applies it to the legend labels. Default is set to [US english](https://github.com/d3/d3-format/blob/master/locale/en-US.json).
legendSymbol.**labelOffset(number)**
A value that determines how far the label is from the symbol in each legend item. Default set to 10px.
symbol.**labelDelimiter(string)**
legendSymbol.**labelDelimiter(string)**
Change the default "to" text when working with a quant scale.
symbol.**on(string, function)**
legendColor.**labelWrap(number)**
Add text wrapping to the cell labels. In orient horizontal you can use this in combination with shapePadding to get the desired spacing. An exampe: [Size - Linear Scale](#size-line). In orient vertical this will automatically scale the cells to fit the label.An example: [Symbol - Ordinal Scale](#symbol-ordinal)
legendSymbol.**on(string, function)**
There are three custom event types you can bind to the legend: "cellover", "cellout", and "cellclick" An exampe: [Symbol - Ordinal Scale](#symbol-ordinal)

@ -1,5 +1,13 @@
var d3 = require('d3');
import legendColor from './src/color'
import legendSize from './src/size'
import legendSymbol from './src/symbol'
import legendHelpers from './src/helpers'
d3.legend = require('./no-extend');
export { legendColor, legendSize, legendSymbol, legendHelpers }
module.exports = d3;
export default {
legendColor,
legendSize,
legendSymbol,
legendHelpers
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -1,37 +1,61 @@
{
"name": "d3-svg-legend",
"version": "1.13.0",
"version": "2.25.5",
"description": "A legend component for d3. Given a d3.scale it can create either a color legend, size legend, or symbol legend.",
"main": "index.js",
"main": "indexRollup.js",
"jsnext:main": "indexRollupNext.js",
"module": "indexRollupNext.js",
"files": [
"src",
"index.js",
"indexRollup.js",
"indexRollup.js.map",
"indexRollup.mjs",
"indexRollupNext.js",
"indexRollupNext.js.map",
"d3-legend.*",
"no-extend.js"
"types/*"
],
"keywords": [
"d3",
"d3-module",
"legend"
],
"directories": {
"example": "examples",
"test": "test"
},
"peerDependencies": {
"d3": "^3.0.0"
},
"devDependencies": {
"babel-plugin-external-helpers": "6.8.0",
"babel-preset-es2015": "6.13.2",
"babelify": "7.3.0",
"babelrc-rollup": "3.0.0",
"chai": "3.4.1",
"grunt": "0.4.5",
"grunt-browserify": "3.8.0",
"grunt-contrib-uglify": "0.9.1",
"marked": "0.3.5",
"marked": "0.3.9",
"mocha": "2.2.5",
"npm-watch": "0.1.7",
"reify": "0.3.8",
"rollup": "0.41.4",
"rollup-plugin-babel": "2.6.1",
"stringify": "3.1.0"
},
"scripts": {
"prepublish": "npm t",
"test": "mocha test/test.js"
"build": "rollup -c && grunt",
"prepublish": "npm t && npm run build",
"server": "http-server",
"start": "npm run server & npm run watch",
"watch": "npm-watch",
"grunt": "grunt",
"test": "mocha"
},
"watch": {
"grunt": [
"src/*.js ",
"docs/legends.js"
]
},
"repository": {
"type": "git",
@ -41,9 +65,19 @@
"name": "Susie Lu",
"url": "http://www.susielu.com"
},
"license": "ISC",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/susielu/d3-legend/issues"
},
"homepage": "http://d3-legend.susielu.com"
"homepage": "http://d3-legend.susielu.com",
"types": "./types/d3-svg-legend.d.ts",
"dependencies": {
"@types/d3-selection": "1.0.10",
"d3-array": "1.0.1",
"d3-dispatch": "1.0.1",
"d3-format": "1.0.2",
"d3-scale": "1.0.3",
"d3-selection": "1.0.2",
"d3-transition": "1.0.3"
}
}

@ -0,0 +1,26 @@
import babel from 'rollup-plugin-babel';
import babelrc from 'babelrc-rollup';
let pkg = require('./package.json');
let external = Object.keys(pkg.dependencies);
export default {
entry: 'index.js',
plugins: [
babel(babelrc())
],
external: external,
targets: [
{
dest: pkg['main'],
format: 'umd',
moduleName: 'indexRollup',
sourceMap: true
},
{
dest: pkg['jsnext:main'],
format: 'es',
sourceMap: true
}
]
};

@ -1,206 +1,308 @@
var helper = require('./legend');
import helper from "./legend"
import { dispatch } from "d3-dispatch"
import { scaleLinear } from "d3-scale"
import { formatLocale, formatSpecifier } from "d3-format"
module.exports = function(){
import { sum } from "d3-array"
var scale = d3.scale.linear(),
export default function color() {
let scale = scaleLinear(),
shape = "rect",
shapeWidth = 15,
shapeHeight = 15,
shapeRadius = 10,
shapePadding = 2,
cells = [5],
cellFilter,
labels = [],
classPrefix = "",
useClass = false,
title = "",
labelFormat = d3.format(".01f"),
locale = helper.d3_defaultLocale,
specifier = helper.d3_defaultFormatSpecifier,
labelOffset = 10,
labelAlign = "middle",
labelDelimiter = "to",
labelDelimiter = helper.d3_defaultDelimiter,
labelWrap,
orient = "vertical",
ascending = false,
path,
legendDispatcher = d3.dispatch("cellover", "cellout", "cellclick");
function legend(svg){
var type = helper.d3_calcType(scale, ascending, cells, labels, labelFormat, labelDelimiter),
legendG = svg.selectAll('g').data([scale]);
legendG.enter().append('g').attr('class', classPrefix + 'legendCells');
var cell = legendG.selectAll("." + classPrefix + "cell").data(type.data),
cellEnter = cell.enter().append("g", ".cell").attr("class", classPrefix + "cell").style("opacity", 1e-6),
shapeEnter = cellEnter.append(shape).attr("class", classPrefix + "swatch"),
shapes = cell.select("g." + classPrefix + "cell " + shape);
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher);
cell.exit().transition().style("opacity", 0).remove();
helper.d3_drawShapes(shape, shapes, shapeHeight, shapeWidth, shapeRadius, path);
helper.d3_addText(legendG, cellEnter, type.labels, classPrefix)
// sets placement
var text = cell.select("text"),
shapeSize = shapes[0].map( function(d){ return d.getBBox(); });
titleWidth,
legendDispatcher = dispatch("cellover", "cellout", "cellclick")
function legend(svg) {
const type = helper.d3_calcType(
scale,
ascending,
cells,
labels,
locale.format(specifier),
labelDelimiter
),
legendG = svg.selectAll("g").data([scale])
legendG
.enter()
.append("g")
.attr("class", classPrefix + "legendCells")
if (cellFilter) {
helper.d3_filterCells(type, cellFilter)
}
//sets scale
//everything is fill except for line which is stroke,
if (!useClass){
if (shape == "line"){
shapes.style("stroke", type.feature);
} else {
shapes.style("fill", type.feature);
}
let cell = svg
.select("." + classPrefix + "legendCells")
.selectAll("." + classPrefix + "cell")
.data(type.data)
const cellEnter = cell
.enter()
.append("g")
.attr("class", classPrefix + "cell")
cellEnter.append(shape).attr("class", classPrefix + "swatch")
let shapes = svg
.selectAll(
"g." + classPrefix + "cell " + shape + "." + classPrefix + "swatch"
)
.data(type.data)
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher)
cell
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes = shapes.merge(shapes)
helper.d3_drawShapes(
shape,
shapes,
shapeHeight,
shapeWidth,
shapeRadius,
path
)
const text = helper.d3_addText(
svg,
cellEnter,
type.labels,
classPrefix,
labelWrap
)
// we need to merge the selection, otherwise changes in the legend (e.g. change of orientation) are applied only to the new cells and not the existing ones.
cell = cellEnter.merge(cell)
// sets placement
const textSize = text.nodes().map(d => d.getBBox()),
shapeSize = shapes.nodes().map(d => d.getBBox())
//sets scale
//everything is fill except for line which is stroke,
if (!useClass) {
if (shape == "line") {
shapes.style("stroke", type.feature)
} else {
shapes.attr("class", function(d){ return classPrefix + "swatch " + type.feature(d); });
shapes.style("fill", type.feature)
}
} else {
shapes.attr("class", d => `${classPrefix}swatch ${type.feature(d)}`)
}
var cellTrans,
let cellTrans,
textTrans,
textAlign = (labelAlign == "start") ? 0 : (labelAlign == "middle") ? 0.5 : 1;
//positions cells and text
if (orient === "vertical"){
cellTrans = function(d,i) { return "translate(0, " + (i * (shapeSize[i].height + shapePadding)) + ")"; };
textTrans = function(d,i) { return "translate(" + (shapeSize[i].width + shapeSize[i].x +
labelOffset) + "," + (shapeSize[i].y + shapeSize[i].height/2 + 5) + ")"; };
} else if (orient === "horizontal"){
cellTrans = function(d,i) { return "translate(" + (i * (shapeSize[i].width + shapePadding)) + ",0)"; }
textTrans = function(d,i) { return "translate(" + (shapeSize[i].width*textAlign + shapeSize[i].x) +
"," + (shapeSize[i].height + shapeSize[i].y + labelOffset + 8) + ")"; };
}
textAlign = labelAlign == "start" ? 0 : labelAlign == "middle" ? 0.5 : 1
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign);
helper.d3_title(svg, legendG, title, classPrefix);
//positions cells and text
if (orient === "vertical") {
const cellSize = textSize.map((d, i) =>
Math.max(d.height, shapeSize[i].height)
)
cell.transition().style("opacity", 1);
cellTrans = (d, i) => {
const height = sum(cellSize.slice(0, i))
return `translate(0, ${height + i * shapePadding})`
}
textTrans = (d, i) =>
`translate( ${shapeSize[i].width +
shapeSize[i].x +
labelOffset}, ${shapeSize[i].y + shapeSize[i].height / 2 + 5})`
} else if (orient === "horizontal") {
cellTrans = (d, i) =>
`translate(${i * (shapeSize[i].width + shapePadding)},0)`
textTrans = (d, i) => `translate(${shapeSize[i].width * textAlign +
shapeSize[i].x},
${shapeSize[i].height + shapeSize[i].y + labelOffset + 8})`
}
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign)
helper.d3_title(svg, title, classPrefix, titleWidth)
cell.transition().style("opacity", 1)
}
legend.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return legend;
};
if (!arguments.length) return scale
scale = _
return legend
}
legend.cells = function(_) {
if (!arguments.length) return cells;
if (_.length > 1 || _ >= 2 ){
cells = _;
if (!arguments.length) return cells
if (_.length > 1 || _ >= 2) {
cells = _
}
return legend;
};
return legend
}
legend.cellFilter = function(_) {
if (!arguments.length) return cellFilter
cellFilter = _
return legend
}
legend.shape = function(_, d) {
if (!arguments.length) return shape;
if (_ == "rect" || _ == "circle" || _ == "line" || (_ == "path" && (typeof d === 'string')) ){
shape = _;
path = d;
if (!arguments.length) return shape
if (
_ == "rect" ||
_ == "circle" ||
_ == "line" ||
(_ == "path" && typeof d === "string")
) {
shape = _
path = d
}
return legend;
};
return legend
}
legend.shapeWidth = function(_) {
if (!arguments.length) return shapeWidth;
shapeWidth = +_;
return legend;
};
if (!arguments.length) return shapeWidth
shapeWidth = +_
return legend
}
legend.shapeHeight = function(_) {
if (!arguments.length) return shapeHeight;
shapeHeight = +_;
return legend;
};
if (!arguments.length) return shapeHeight
shapeHeight = +_
return legend
}
legend.shapeRadius = function(_) {
if (!arguments.length) return shapeRadius;
shapeRadius = +_;
return legend;
};
if (!arguments.length) return shapeRadius
shapeRadius = +_
return legend
}
legend.shapePadding = function(_) {
if (!arguments.length) return shapePadding;
shapePadding = +_;
return legend;
};
if (!arguments.length) return shapePadding
shapePadding = +_
return legend
}
legend.labels = function(_) {
if (!arguments.length) return labels;
labels = _;
return legend;
};
if (!arguments.length) return labels
labels = _
return legend
}
legend.labelAlign = function(_) {
if (!arguments.length) return labelAlign;
if (!arguments.length) return labelAlign
if (_ == "start" || _ == "end" || _ == "middle") {
labelAlign = _;
labelAlign = _
}
return legend;
};
return legend
}
legend.locale = function(_) {
if (!arguments.length) return locale
locale = formatLocale(_)
return legend
}
legend.labelFormat = function(_) {
if (!arguments.length) return labelFormat;
labelFormat = _;
return legend;
};
if (!arguments.length) return legend.locale().format(specifier)
specifier = formatSpecifier(_)
return legend
}
legend.labelOffset = function(_) {
if (!arguments.length) return labelOffset;
labelOffset = +_;
return legend;
};
if (!arguments.length) return labelOffset
labelOffset = +_
return legend
}
legend.labelDelimiter = function(_) {
if (!arguments.length) return labelDelimiter;
labelDelimiter = _;
return legend;
};
if (!arguments.length) return labelDelimiter
labelDelimiter = _
return legend
}
legend.labelWrap = function(_) {
if (!arguments.length) return labelWrap
labelWrap = _
return legend
}
legend.useClass = function(_) {
if (!arguments.length) return useClass;
if (_ === true || _ === false){
useClass = _;
if (!arguments.length) return useClass
if (_ === true || _ === false) {
useClass = _
}
return legend;
};
return legend
}
legend.orient = function(_){
if (!arguments.length) return orient;
_ = _.toLowerCase();
legend.orient = function(_) {
if (!arguments.length) return orient
_ = _.toLowerCase()
if (_ == "horizontal" || _ == "vertical") {
orient = _;
orient = _
}
return legend;
};
return legend
}
legend.ascending = function(_) {
if (!arguments.length) return ascending;
ascending = !!_;
return legend;
};
if (!arguments.length) return ascending
ascending = !!_
return legend
}
legend.classPrefix = function(_) {
if (!arguments.length) return classPrefix;
classPrefix = _;
return legend;
};
if (!arguments.length) return classPrefix
classPrefix = _
return legend
}
legend.title = function(_) {
if (!arguments.length) return title;
title = _;
return legend;
};
d3.rebind(legend, legendDispatcher, "on");
return legend;
};
if (!arguments.length) return title
title = _
return legend
}
legend.titleWidth = function(_) {
if (!arguments.length) return titleWidth
titleWidth = _
return legend
}
legend.textWrap = function(_) {
if (!arguments.length) return textWrap
textWrap = _
return legend
}
legend.on = function() {
const value = legendDispatcher.on.apply(legendDispatcher, arguments)
return value === legendDispatcher ? legend : value
}
return legend
}

@ -0,0 +1,12 @@
export const thresholdLabels = function({ i, genLength, generatedLabels }) {
if (i === 0) {
return generatedLabels[i].replace("NaN to", "Less than")
} else if (i === genLength - 1) {
return `${generatedLabels[genLength - 1].replace(" to NaN", "")} or more`
}
return generatedLabels[i]
}
export default {
thresholdLabels
}

@ -1,79 +1,136 @@
module.exports = {
import { select } from 'd3-selection'
import { format, formatPrefix } from 'd3-format'
d3_identity: function (d) {
return d;
},
const d3_identity = (d) => d
d3_mergeLabels: function (gen, labels) {
const d3_reverse = (arr) => {
const mirror = [];
for (let i = 0, l = arr.length; i < l; i++) {
mirror[i] = arr[l-i-1];
}
return mirror;
}
if(labels.length === 0) return gen;
//Text wrapping code adapted from Mike Bostock
const d3_textWrapping = (text, width) => {
text.each(function() {
var text = select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.2, //ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")) || 0,
tspan = text.text(null)
.append("tspan")
.attr("x", 0)
.attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width && line.length > 1) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan")
.attr("x", 0)
.attr("dy", lineHeight + dy + "em").text(word);
}
}
});
}
const d3_mergeLabels = (gen=[], labels, domain, range) => {
gen = (gen) ? gen : [];
if (typeof labels === "object"){
if(labels.length === 0) return gen;
var i = labels.length;
let i = labels.length;
for (; i < gen.length; i++) {
labels.push(gen[i]);
}
return labels;
},
} else if (typeof labels === "function") {
const customLabels = []
const genLength = gen.length
for (let i=0; i < genLength; i++){
customLabels.push(labels({
i,
genLength,
generatedLabels : gen,
domain,
range }))
}
return customLabels
}
d3_linearLegend: function (scale, cells, labelFormat) {
var data = [];
return gen;
}
if (cells.length > 1){
data = cells;
const d3_linearLegend = (scale, cells, labelFormat) => {
let data = [];
} else {
var domain = scale.domain(),
increment = (domain[domain.length - 1] - domain[0])/(cells - 1),
i = 0;
if (cells.length > 1){
data = cells;
for (; i < cells; i++){
data.push(domain[0] + i*increment);
}
} else {
const domain = scale.domain(),
increment = (domain[domain.length - 1] - domain[0])/(cells - 1)
let i = 0;
for (; i < cells; i++){
data.push(domain[0] + i*increment);
}
}
var labels = data.map(labelFormat);
const labels = data.map(labelFormat);
return {data: data,
labels: labels,
feature: d => scale(d)};
}
return {data: data,
labels: labels,
feature: function(d){ return scale(d); }};
},
const d3_quantLegend = (scale, labelFormat, labelDelimiter) => {
const labels = scale.range().map( d => {
const invert = scale.invertExtent(d);
return labelFormat(invert[0]) + " " + labelDelimiter + " " + labelFormat(invert[1]);
});
d3_quantLegend: function (scale, labelFormat, labelDelimiter) {
var labels = scale.range().map(function(d){
var invert = scale.invertExtent(d),
a = labelFormat(invert[0]),
b = labelFormat(invert[1]);
// if (( (a) && (a.isNan()) && b){
// console.log("in initial statement")
return labelFormat(invert[0]) + " " + labelDelimiter + " " + labelFormat(invert[1]);
// } else if (a || b) {
// console.log('in else statement')
// return (a) ? a : b;
// }
});
return {data: scale.range(),
labels: labels,
feature: this.d3_identity
};
},
return {data: scale.range(),
labels: labels,
feature: d3_identity
};
}
d3_ordinalLegend: function (scale) {
return {data: scale.domain(),
labels: scale.domain(),
feature: function(d){ return scale(d); }};
},
const d3_ordinalLegend= scale => ({data: scale.domain(),
labels: scale.domain(),
feature: d => scale(d) }
)
const d3_cellOver = (cellDispatcher, d, obj) => {
cellDispatcher.call("cellover", obj, d);
}
const d3_cellOut = (cellDispatcher, d, obj) => {
cellDispatcher.call("cellout", obj, d);
}
const d3_cellClick = (cellDispatcher, d, obj) => {
cellDispatcher.call("cellclick", obj, d);
}
d3_drawShapes: function (shape, shapes, shapeHeight, shapeWidth, shapeRadius, path) {
export default {
d3_drawShapes: (shape, shapes, shapeHeight, shapeWidth, shapeRadius, path) => {
if (shape === "rect"){
shapes.attr("height", shapeHeight).attr("width", shapeWidth);
shapes.attr("height", shapeHeight)
.attr("width", shapeWidth);
} else if (shape === "circle") {
shapes.attr("r", shapeRadius)//.attr("cx", shapeRadius).attr("cy", shapeRadius);
shapes.attr("r", shapeRadius)
} else if (shape === "line") {
shapes.attr("x1", 0).attr("x2", shapeWidth).attr("y1", 0).attr("y2", 0);
@ -83,36 +140,48 @@ module.exports = {
}
},
d3_addText: function (svg, enter, labels, classPrefix){
d3_addText: function (svg, enter, labels, classPrefix, labelWidth){
enter.append("text").attr("class", classPrefix + "label");
svg.selectAll("g." + classPrefix + "cell text." + classPrefix + "label")
.data(labels).text(this.d3_identity);
const text = svg.selectAll(`g.${classPrefix}cell text.${classPrefix}label`)
.data(labels)
.text(d3_identity);
if (labelWidth){
svg.selectAll(`g.${classPrefix}cell text.${classPrefix}label`)
.call(d3_textWrapping, labelWidth)
}
return text
},
d3_calcType: function (scale, ascending, cells, labels, labelFormat, labelDelimiter){
var type = scale.ticks ?
this.d3_linearLegend(scale, cells, labelFormat) : scale.invertExtent ?
this.d3_quantLegend(scale, labelFormat, labelDelimiter) : this.d3_ordinalLegend(scale);
const type = scale.invertExtent ?
d3_quantLegend(scale, labelFormat, labelDelimiter) : scale.ticks ?
d3_linearLegend(scale, cells, labelFormat) : d3_ordinalLegend(scale);
type.labels = this.d3_mergeLabels(type.labels, labels);
//for d3.scaleSequential that doesn't have a range function
const range = scale.range && scale.range() || scale.domain()
type.labels = d3_mergeLabels(type.labels, labels, scale.domain(), range);
if (ascending) {
type.labels = this.d3_reverse(type.labels);
type.data = this.d3_reverse(type.data);
type.labels = d3_reverse(type.labels);
type.data = d3_reverse(type.data);
}
return type;
},
d3_reverse: function(arr) {
var mirror = [];
for (var i = 0, l = arr.length; i < l; i++) {
mirror[i] = arr[l-i-1];
}
return mirror;
d3_filterCells: (type, cellFilter) => {
let filterCells = type.data.map((d, i) => ({ data: d, label: type.labels[i] }))
.filter(cellFilter)
const dataValues = filterCells.map(d => d.data)
const labelValues = filterCells.map(d => d.label)
type.data = type.data.filter(d => dataValues.indexOf(d) !== -1)
type.labels = type.labels.filter(d => labelValues.indexOf(d) !== -1)
return type
},
d3_placement: function (orient, cell, cellTrans, text, textTrans, labelAlign) {
d3_placement: (orient, cell, cellTrans, text, textTrans, labelAlign) => {
cell.attr("transform", cellTrans);
text.attr("transform", textTrans);
if (orient === "horizontal"){
@ -121,44 +190,45 @@ module.exports = {
},
d3_addEvents: function(cells, dispatcher){
var _ = this;
cells.on("mouseover.legend", function (d) { _.d3_cellOver(dispatcher, d, this); })
.on("mouseout.legend", function (d) { _.d3_cellOut(dispatcher, d, this); })
.on("click.legend", function (d) { _.d3_cellClick(dispatcher, d, this); });
},
d3_cellOver: function(cellDispatcher, d, obj){
cellDispatcher.cellover.call(obj, d);
cells.on("mouseover.legend", function (d) { d3_cellOver(dispatcher, d, this); })
.on("mouseout.legend", function (d) { d3_cellOut(dispatcher, d, this); })
.on("click.legend", function (d) { d3_cellClick(dispatcher, d, this); });
},
d3_cellOut: function(cellDispatcher, d, obj){
cellDispatcher.cellout.call(obj, d);
},
d3_cellClick: function(cellDispatcher, d, obj){
cellDispatcher.cellclick.call(obj, d);
},
d3_title: function(svg, cellsSvg, title, classPrefix){
d3_title: (svg, title, classPrefix, titleWidth) => {
if (title !== ""){
var titleText = svg.selectAll('text.' + classPrefix + 'legendTitle');
const titleText = svg.selectAll('text.' + classPrefix + 'legendTitle');
titleText.data([title])
.enter()
.append('text')
.attr('class', classPrefix + 'legendTitle');
svg.selectAll('text.' + classPrefix + 'legendTitle')
.text(title)
if (titleWidth){
svg.selectAll('text.' + classPrefix + 'legendTitle')
.text(title)
.call(d3_textWrapping, titleWidth)
}
var yOffset = svg.select('.' + classPrefix + 'legendTitle')
.map(function(d) { return d[0].getBBox().height})[0],
xOffset = -cellsSvg.map(function(d) { return d[0].getBBox().x})[0];
const cellsSvg = svg.select('.' + classPrefix + 'legendCells')
const yOffset = svg.select('.' + classPrefix + 'legendTitle').nodes()
.map(d => d.getBBox().height)[0],
cellsSvg.attr('transform', 'translate(' + xOffset + ',' + (yOffset + 10) + ')');
xOffset = -cellsSvg.nodes().map(function(d) { return d.getBBox().x})[0];
cellsSvg.attr('transform', 'translate(' + xOffset + ',' + (yOffset) + ')');
}
}
},
d3_defaultLocale: {
format,
formatPrefix
},
d3_defaultFormatSpecifier: '.01f',
d3_defaultDelimiter: 'to'
}

@ -1,199 +1,286 @@
var helper = require('./legend');
module.exports = function(){
var scale = d3.scale.linear(),
import helper from "./legend"
import { dispatch } from "d3-dispatch"
import { scaleLinear } from "d3-scale"
import { formatLocale, formatSpecifier } from "d3-format"
import { sum, max } from "d3-array"
export default function size() {
let scale = scaleLinear(),
shape = "rect",
shapeWidth = 15,
shapePadding = 2,
cells = [5],
cellFilter,
labels = [],
useStroke = false,
classPrefix = "",
title = "",
labelFormat = d3.format(".01f"),
locale = helper.d3_defaultLocale,
specifier = helper.d3_defaultFormatSpecifier,
labelOffset = 10,
labelAlign = "middle",
labelDelimiter = "to",
labelDelimiter = helper.d3_defaultDelimiter,
labelWrap,
orient = "vertical",
ascending = false,
path,
legendDispatcher = d3.dispatch("cellover", "cellout", "cellclick");
function legend(svg){
var type = helper.d3_calcType(scale, ascending, cells, labels, labelFormat, labelDelimiter),
legendG = svg.selectAll('g').data([scale]);
legendG.enter().append('g').attr('class', classPrefix + 'legendCells');
var cell = legendG.selectAll("." + classPrefix + "cell").data(type.data),
cellEnter = cell.enter().append("g", ".cell").attr("class", classPrefix + "cell").style("opacity", 1e-6),
shapeEnter = cellEnter.append(shape).attr("class", classPrefix + "swatch"),
shapes = cell.select("g." + classPrefix + "cell " + shape);
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher);
titleWidth,
legendDispatcher = dispatch("cellover", "cellout", "cellclick")
function legend(svg) {
const type = helper.d3_calcType(
scale,
ascending,
cells,
labels,
locale.format(specifier),
labelDelimiter
),
legendG = svg.selectAll("g").data([scale])
if (cellFilter) {
helper.d3_filterCells(type, cellFilter)
}
cell.exit().transition().style("opacity", 0).remove();
legendG
.enter()
.append("g")
.attr("class", classPrefix + "legendCells")
let cell = svg
.select("." + classPrefix + "legendCells")
.selectAll("." + classPrefix + "cell")
.data(type.data)
const cellEnter = cell
.enter()
.append("g")
.attr("class", classPrefix + "cell")
cellEnter.append(shape).attr("class", classPrefix + "swatch")
let shapes = svg.selectAll(
"g." + classPrefix + "cell " + shape + "." + classPrefix + "swatch"
)
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher)
cell
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes = shapes.merge(shapes)
//creates shape
if (shape === "line") {
helper.d3_drawShapes(shape, shapes, 0, shapeWidth)
shapes.attr("stroke-width", type.feature)
} else {
helper.d3_drawShapes(
shape,
shapes,
type.feature,
type.feature,
type.feature,
path
)
}
//creates shape
if (shape === "line"){
helper.d3_drawShapes(shape, shapes, 0, shapeWidth);
shapes.attr("stroke-width", type.feature);
} else {
helper.d3_drawShapes(shape, shapes, type.feature, type.feature, type.feature, path);
}
const text = helper.d3_addText(
svg,
cellEnter,
type.labels,
classPrefix,
labelWrap
)
helper.d3_addText(legendG, cellEnter, type.labels, classPrefix)
// we need to merge the selection, otherwise changes in the legend (e.g. change of orientation) are applied only to the new cells and not the existing ones.
cell = cellEnter.merge(cell)
//sets placement
var text = cell.select("text"),
shapeSize = shapes[0].map(
function(d, i){
var bbox = d.getBBox()
var stroke = scale(type.data[i]);
//sets placement
if (shape === "line" && orient === "horizontal") {
bbox.height = bbox.height + stroke;
} else if (shape === "line" && orient === "vertical"){
bbox.width = bbox.width;
}
const textSize = text.nodes().map(d => d.getBBox()),
shapeSize = shapes.nodes().map((d, i) => {
const bbox = d.getBBox()
const stroke = scale(type.data[i])
return bbox;
});
if (shape === "line" && orient === "horizontal") {
bbox.height = bbox.height + stroke
} else if (shape === "line" && orient === "vertical") {
bbox.width = bbox.width
}
return bbox
})
var maxH = d3.max(shapeSize, function(d){ return d.height + d.y; }),
maxW = d3.max(shapeSize, function(d){ return d.width + d.x; });
const maxH = max(shapeSize, d => d.height + d.y),
maxW = max(shapeSize, d => d.width + d.x)
var cellTrans,
let cellTrans,
textTrans,
textAlign = (labelAlign == "start") ? 0 : (labelAlign == "middle") ? 0.5 : 1;
//positions cells and text
if (orient === "vertical"){
cellTrans = function(d,i) {
var height = d3.sum(shapeSize.slice(0, i + 1 ), function(d){ return d.height; });
return "translate(0, " + (height + i*shapePadding) + ")"; };
textTrans = function(d,i) { return "translate(" + (maxW + labelOffset) + "," +
(shapeSize[i].y + shapeSize[i].height/2 + 5) + ")"; };
textAlign = labelAlign == "start" ? 0 : labelAlign == "middle" ? 0.5 : 1
//positions cells and text
if (orient === "vertical") {
const cellSize = textSize.map((d, i) =>
Math.max(d.height, shapeSize[i].height)
)
const y =
shape == "circle" || shape == "line" ? shapeSize[0].height / 2 : 0
cellTrans = (d, i) => {
const height = sum(cellSize.slice(0, i))
return `translate(0, ${y + height + i * shapePadding})`
}
} else if (orient === "horizontal"){
cellTrans = function(d,i) {
var width = d3.sum(shapeSize.slice(0, i + 1 ), function(d){ return d.width; });
return "translate(" + (width + i*shapePadding) + ",0)"; };
textTrans = (d, i) => `translate( ${maxW + labelOffset},
${shapeSize[i].y + shapeSize[i].height / 2 + 5})`
} else if (orient === "horizontal") {
cellTrans = (d, i) => {
const width = sum(shapeSize.slice(0, i), d => d.width)
const y = shape == "circle" || shape == "line" ? maxH / 2 : 0
return `translate(${width + i * shapePadding}, ${y})`
}
textTrans = function(d,i) { return "translate(" + (shapeSize[i].width*textAlign + shapeSize[i].x) + "," +
(maxH + labelOffset ) + ")"; };
const offset = shape == "line" ? maxH / 2 : maxH
textTrans = (d, i) => {
return `translate( ${shapeSize[i].width * textAlign + shapeSize[i].x},
${offset + labelOffset})`
}
}
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign);
helper.d3_title(svg, legendG, title, classPrefix);
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign)
helper.d3_title(svg, title, classPrefix, titleWidth)
cell.transition().style("opacity", 1);
}
cell.transition().style("opacity", 1)
}
legend.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return legend;
};
if (!arguments.length) return scale
scale = _
return legend
}
legend.cells = function(_) {
if (!arguments.length) return cells;
if (_.length > 1 || _ >= 2 ){
cells = _;
if (!arguments.length) return cells
if (_.length > 1 || _ >= 2) {
cells = _
}
return legend;
};
return legend
}
legend.cellFilter = function(_) {
if (!arguments.length) return cellFilter
cellFilter = _
return legend
}
legend.shape = function(_, d) {
if (!arguments.length) return shape;
if (_ == "rect" || _ == "circle" || _ == "line" ){
shape = _;
path = d;
if (!arguments.length) return shape
if (_ == "rect" || _ == "circle" || _ == "line") {
shape = _
path = d
}
return legend;
};
return legend
}
legend.shapeWidth = function(_) {
if (!arguments.length) return shapeWidth;
shapeWidth = +_;
return legend;
};
if (!arguments.length) return shapeWidth
shapeWidth = +_
return legend
}
legend.shapePadding = function(_) {
if (!arguments.length) return shapePadding;
shapePadding = +_;
return legend;
};
if (!arguments.length) return shapePadding
shapePadding = +_
return legend
}
legend.labels = function(_) {
if (!arguments.length) return labels;
labels = _;
return legend;
};
if (!arguments.length) return labels
labels = _
return legend
}
legend.labelAlign = function(_) {
if (!arguments.length) return labelAlign;
if (!arguments.length) return labelAlign
if (_ == "start" || _ == "end" || _ == "middle") {
labelAlign = _;
labelAlign = _
}
return legend;
};
return legend
}
legend.locale = function(_) {
if (!arguments.length) return locale
locale = formatLocale(_)
return legend
}
legend.labelFormat = function(_) {
if (!arguments.length) return labelFormat;
labelFormat = _;
return legend;
};
if (!arguments.length) return legend.locale().format(specifier)
specifier = formatSpecifier(_)
return legend
}
legend.labelOffset = function(_) {
if (!arguments.length) return labelOffset;
labelOffset = +_;
return legend;
};
if (!arguments.length) return labelOffset
labelOffset = +_
return legend
}
legend.labelDelimiter = function(_) {
if (!arguments.length) return labelDelimiter;
labelDelimiter = _;
return legend;
};
legend.orient = function(_){
if (!arguments.length) return orient;
_ = _.toLowerCase();
if (!arguments.length) return labelDelimiter
labelDelimiter = _
return legend
}
legend.labelWrap = function(_) {
if (!arguments.length) return labelWrap
labelWrap = _
return legend
}
legend.orient = function(_) {
if (!arguments.length) return orient
_ = _.toLowerCase()
if (_ == "horizontal" || _ == "vertical") {
orient = _;
orient = _
}
return legend;
};
return legend
}
legend.ascending = function(_) {
if (!arguments.length) return ascending;
ascending = !!_;
return legend;
};
if (!arguments.length) return ascending
ascending = !!_
return legend
}
legend.classPrefix = function(_) {
if (!arguments.length) return classPrefix;
classPrefix = _;
return legend;
};
if (!arguments.length) return classPrefix
classPrefix = _
return legend
}
legend.title = function(_) {
if (!arguments.length) return title;
title = _;
return legend;
};
d3.rebind(legend, legendDispatcher, "on");
return legend;
};
if (!arguments.length) return title
title = _
return legend
}
legend.titleWidth = function(_) {
if (!arguments.length) return titleWidth
titleWidth = _
return legend
}
legend.on = function() {
const value = legendDispatcher.on.apply(legendDispatcher, arguments)
return value === legendDispatcher ? legend : value
}
return legend
}

@ -1,158 +1,239 @@
var helper = require('./legend');
module.exports = function(){
var scale = d3.scale.linear(),
import helper from "./legend"
import { dispatch } from "d3-dispatch"
import { scaleLinear } from "d3-scale"
import { formatLocale, formatSpecifier } from "d3-format"
import { sum, max } from "d3-array"
export default function symbol() {
let scale = scaleLinear(),
shape = "path",
shapeWidth = 15,
shapeHeight = 15,
shapeRadius = 10,
shapePadding = 5,
cells = [5],
cellFilter,
labels = [],
classPrefix = "",
useClass = false,
title = "",
labelFormat = d3.format(".01f"),
locale = helper.d3_defaultLocale,
specifier = helper.d3_defaultFormatSpecifier,
labelAlign = "middle",
labelOffset = 10,
labelDelimiter = "to",
labelDelimiter = helper.d3_defaultDelimiter,
labelWrap,
orient = "vertical",
ascending = false,
legendDispatcher = d3.dispatch("cellover", "cellout", "cellclick");
function legend(svg){
var type = helper.d3_calcType(scale, ascending, cells, labels, labelFormat, labelDelimiter),
legendG = svg.selectAll('g').data([scale]);
legendG.enter().append('g').attr('class', classPrefix + 'legendCells');
var cell = legendG.selectAll("." + classPrefix + "cell").data(type.data),
cellEnter = cell.enter().append("g", ".cell").attr("class", classPrefix + "cell").style("opacity", 1e-6),
shapeEnter = cellEnter.append(shape).attr("class", classPrefix + "swatch"),
shapes = cell.select("g." + classPrefix + "cell " + shape);
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher);
//remove old shapes
cell.exit().transition().style("opacity", 0).remove();
helper.d3_drawShapes(shape, shapes, shapeHeight, shapeWidth, shapeRadius, type.feature);
helper.d3_addText(legendG, cellEnter, type.labels, classPrefix)
// sets placement
var text = cell.select("text"),
shapeSize = shapes[0].map( function(d){ return d.getBBox(); });
var maxH = d3.max(shapeSize, function(d){ return d.height; }),
maxW = d3.max(shapeSize, function(d){ return d.width; });
titleWidth,
legendDispatcher = dispatch("cellover", "cellout", "cellclick")
function legend(svg) {
const type = helper.d3_calcType(
scale,
ascending,
cells,
labels,
locale.format(specifier),
labelDelimiter
),
legendG = svg.selectAll("g").data([scale])
if (cellFilter) {
helper.d3_filterCells(type, cellFilter)
}
var cellTrans,
legendG
.enter()
.append("g")
.attr("class", classPrefix + "legendCells")
let cell = svg
.select("." + classPrefix + "legendCells")
.selectAll("." + classPrefix + "cell")
.data(type.data)
const cellEnter = cell
.enter()
.append("g")
.attr("class", classPrefix + "cell")
cellEnter.append(shape).attr("class", classPrefix + "swatch")
let shapes = svg.selectAll("g." + classPrefix + "cell " + shape + "." + classPrefix + "swatch")
//add event handlers
helper.d3_addEvents(cellEnter, legendDispatcher)
//remove old shapes
cell
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes
.exit()
.transition()
.style("opacity", 0)
.remove()
shapes = shapes.merge(shapes)
helper.d3_drawShapes(
shape,
shapes,
shapeHeight,
shapeWidth,
shapeRadius,
type.feature
)
const text = helper.d3_addText(
svg,
cellEnter,
type.labels,
classPrefix,
labelWrap
)
// we need to merge the selection, otherwise changes in the legend (e.g. change of orientation) are applied only to the new cells and not the existing ones.
cell = cellEnter.merge(cell)
// sets placement
const textSize = text.nodes().map(d => d.getBBox()),
shapeSize = shapes.nodes().map(d => d.getBBox())
const maxH = max(shapeSize, d => d.height),
maxW = max(shapeSize, d => d.width)
let cellTrans,
textTrans,
textAlign = (labelAlign == "start") ? 0 : (labelAlign == "middle") ? 0.5 : 1;
//positions cells and text
if (orient === "vertical"){
cellTrans = function(d,i) { return "translate(0, " + (i * (maxH + shapePadding)) + ")"; };
textTrans = function(d,i) { return "translate(" + (maxW + labelOffset) + "," +
(shapeSize[i].y + shapeSize[i].height/2 + 5) + ")"; };
} else if (orient === "horizontal"){
cellTrans = function(d,i) { return "translate(" + (i * (maxW + shapePadding)) + ",0)"; };
textTrans = function(d,i) { return "translate(" + (shapeSize[i].width*textAlign + shapeSize[i].x) + "," +
(maxH + labelOffset ) + ")"; };
}
textAlign = labelAlign == "start" ? 0 : labelAlign == "middle" ? 0.5 : 1
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign);
helper.d3_title(svg, legendG, title, classPrefix);
cell.transition().style("opacity", 1);
//positions cells and text
if (orient === "vertical") {
const cellSize = textSize.map((d, i) => Math.max(maxH, d.height))
cellTrans = (d, i) => {
const height = sum(cellSize.slice(0, i))
return `translate(0, ${height + i * shapePadding} )`
}
textTrans = (d, i) => `translate( ${maxW + labelOffset},
${shapeSize[i].y + shapeSize[i].height / 2 + 5})`
} else if (orient === "horizontal") {
cellTrans = (d, i) => `translate( ${i * (maxW + shapePadding)},0)`
textTrans = (d, i) => `translate( ${shapeSize[i].width * textAlign +
shapeSize[i].x},
${maxH + labelOffset})`
}
helper.d3_placement(orient, cell, cellTrans, text, textTrans, labelAlign)
helper.d3_title(svg, title, classPrefix, titleWidth)
cell.transition().style("opacity", 1)
}
legend.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return legend;
};
if (!arguments.length) return scale
scale = _
return legend
}
legend.cells = function(_) {
if (!arguments.length) return cells;
if (_.length > 1 || _ >= 2 ){
cells = _;
if (!arguments.length) return cells
if (_.length > 1 || _ >= 2) {
cells = _
}
return legend;
};
return legend
}
legend.cellFilter = function(_) {
if (!arguments.length) return cellFilter
cellFilter = _
return legend
}
legend.shapePadding = function(_) {
if (!arguments.length) return shapePadding;
shapePadding = +_;
return legend;
};
if (!arguments.length) return shapePadding
shapePadding = +_
return legend
}
legend.labels = function(_) {
if (!arguments.length) return labels;
labels = _;
return legend;
};
if (!arguments.length) return labels
labels = _
return legend
}
legend.labelAlign = function(_) {
if (!arguments.length) return labelAlign;
if (!arguments.length) return labelAlign
if (_ == "start" || _ == "end" || _ == "middle") {
labelAlign = _;
labelAlign = _
}
return legend;
};
return legend
}
legend.locale = function(_) {
if (!arguments.length) return locale
locale = formatLocale(_)
return legend
}
legend.labelFormat = function(_) {
if (!arguments.length) return labelFormat;
labelFormat = _;
return legend;
};
if (!arguments.length) return legend.locale().format(specifier)
specifier = formatSpecifier(_)
return legend
}
legend.labelOffset = function(_) {
if (!arguments.length) return labelOffset;
labelOffset = +_;
return legend;
};
if (!arguments.length) return labelOffset
labelOffset = +_
return legend
}
legend.labelDelimiter = function(_) {
if (!arguments.length) return labelDelimiter;
labelDelimiter = _;
return legend;
};
legend.orient = function(_){
if (!arguments.length) return orient;
_ = _.toLowerCase();
if (!arguments.length) return labelDelimiter
labelDelimiter = _
return legend
}
legend.labelWrap = function(_) {
if (!arguments.length) return labelWrap
labelWrap = _
return legend
}
legend.orient = function(_) {
if (!arguments.length) return orient
_ = _.toLowerCase()
if (_ == "horizontal" || _ == "vertical") {
orient = _;
orient = _
}
return legend;
};
return legend
}
legend.ascending = function(_) {
if (!arguments.length) return ascending;
ascending = !!_;
return legend;
};
if (!arguments.length) return ascending
ascending = !!_
return legend
}
legend.classPrefix = function(_) {
if (!arguments.length) return classPrefix;
classPrefix = _;
return legend;
};
if (!arguments.length) return classPrefix
classPrefix = _
return legend
}
legend.title = function(_) {
if (!arguments.length) return title;
title = _;
return legend;
};
d3.rebind(legend, legendDispatcher, "on");
return legend;
};
if (!arguments.length) return title
title = _
return legend
}
legend.titleWidth = function(_) {
if (!arguments.length) return titleWidth
titleWidth = _
return legend
}
legend.on = function() {
const value = legendDispatcher.on.apply(legendDispatcher, arguments)
return value === legendDispatcher ? legend : value
}
return legend
}

@ -1,5 +1,9 @@
d3.legend = {
color: require('./color'),
size: require('./size'),
symbol: require('./symbol')
};
import color from './color'
import size from './size'
import symbol from './symbol'
import helpers from './helpers'
d3.legendColor = color
d3.legendSize = size
d3.legendSymbol = symbol
d3.legendHelpers = helpers

@ -1,20 +0,0 @@
const chai = require('chai')
const expect = chai.expect
describe('d3-legend', function () {
var d3Legend
beforeEach(function () {
d3Legend = require('../no-extend')
})
it('should export an object', function () {
expect(d3Legend).to.be.an('object')
})
it('should have color, size & symbol functions', function () {
['color','size','symbol'].forEach(function (fieldName) {
expect(d3Legend[fieldName]).to.be.a('function')
})
})
})

@ -0,0 +1,153 @@
/*eslint-env mocha*/
const chai = require('chai');
const { expect } = chai;
import color from '../src/color';
import helper from '../src/legend';
import { formatLocale, format } from 'd3-format'
// describe('d3-legend', function () {
// var d3Legend
// beforeEach(function () {
// d3Legend = require('../no-extend')
// })
// it('should export an object', function () {
// expect(d3Legend).to.be.an('object')
// })
// it('should have color, size & symbol functions', function () {
// ['color','size','symbol'].forEach(function (fieldName) {
// expect(d3Legend[fieldName]).to.be.a('function')
// })
// })
// })
describe('d3-legend #legendColor', function() {
it('exports a function', function() {
expect(color).to.be.a('function');
});
it('invoking exported function does not throw an error and returns a legend function', function() {
let result;
expect(color).to.be.a('function');
expect(function() {
result = color();
}).to.not.throw();
expect(result).to.be.a('function');
expect(result.name).to.equal('legend');
});
it('errors when not passed a SVG', function() {
let result;
expect(color).to.be.a('function');
expect(function() {
result = color();
}).to.not.throw();
expect(result).to.be.a('function');
expect(result.name).to.equal('legend');
// TODO add formal error handling and assert on error message
// }).to.throw('need to provide SVG');
expect(function() {
result();
}).to.throw();
});
// TODO renable to create failing assertion to verify shapes.data is rebound
it.skip('properly rebinds the updated range data for the legend shapes', function() {
let result, aLegend;
let mockSvg = {
attr(key, val) {
this._attrs[key] = val;
return this;
},
select() { return this; },
append() { return this; },
enter() { return this; },
data() { return this; },
selectAll() {
return this;
}
};
mockSvg._attrs = {};
this._copied = helper.d3_calcType;
helper.d3_calcType = null;
helper.d3_calcType = function() {
expect(arguments.length).to.equal(6);
return {};
};
expect(color).to.be.a('function');
expect(function() {
result = color();
}).to.not.throw();
expect(result).to.be.a('function');
expect(result.name).to.equal('legend');
// TODO add formal error handling and assert on error message
// }).to.throw('need to provide SVG');
expect(function() {
aLegend = result(mockSvg);
}).to.not.throw();
expect(aLegend).to.be.an('Object');
});
it('should have a locale', function () {
let result = color();
expect(result.locale).to.be.a('function');
});
it('should redefine label\'s format with a string', function () {
let result = color();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat('.2f');
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine label\'s format with a format function', function () {
let result = color();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat(format('.2f'));
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine the locale with a new locale definition', function () {
let result = color();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,0')
})
it('should keep the format specifier after a locale update', function () {
let result = color();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.labelFormat(format('.2f'));
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,00')
})
});

@ -0,0 +1,75 @@
/*eslint-env mocha*/
const chai = require('chai');
const { expect } = chai;
import size from '../src/size';
import helper from '../src/legend';
import { formatLocale, format } from 'd3-format'
describe('d3-legend #legendSize', function() {
it('exports a function', function() {
expect(size).to.be.a('function');
});
it('should have a locale', function () {
let result = size();
expect(result.locale).to.be.a('function');
});
it('should redefine label\'s format with a string', function () {
let result = size();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat('.2f');
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine label\'s format with a format function', function () {
let result = size();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat(format('.2f'));
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine the locale with a new locale definition', function () {
let result = size();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,0')
})
it('should keep the format specifier after a locale update', function () {
let result = size();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.labelFormat(format('.2f'));
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,00')
})
});

@ -0,0 +1,75 @@
/*eslint-env mocha*/
const chai = require('chai');
const { expect } = chai;
import symbol from '../src/symbol';
import helper from '../src/legend';
import { formatLocale, format } from 'd3-format'
describe('d3-legend #legendSymbol', function() {
it('exports a function', function() {
expect(symbol).to.be.a('function');
});
it('should have a locale', function () {
let result = symbol();
expect(result.locale).to.be.a('function');
});
it('should redefine label\'s format with a string', function () {
let result = symbol();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat('.2f');
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine label\'s format with a format function', function () {
let result = symbol();
let testValue = 1.00;
let initial = result.labelFormat();
result.labelFormat(format('.2f'));
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1.00');
});
it('should redefine the locale with a new locale definition', function () {
let result = symbol();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,0')
})
it('should keep the format specifier after a locale update', function () {
let result = symbol();
let testValue = 1.00;
let initial = result.labelFormat();
let frFr = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', '\u00a0€'],
percent: "\u202f%"
};
result.labelFormat(format('.2f'));
result.locale(frFr)
expect(initial(testValue)).to.be.not.equal(result.labelFormat()(testValue));
expect(result.labelFormat()(testValue)).to.be.equal('1,00')
})
});

@ -0,0 +1,7 @@
{
"compilerOptions": {
"typeRoots": ["./types"],
"allowJs": true
},
"include": ["types/*"]
}

@ -0,0 +1,84 @@
import { BaseType, Selection } from 'd3-selection';
type Orientation = 'vertical' | 'horizontal';
type Alignment = 'start' | 'middle' | 'end';
type Shape = 'rect' | 'circle' | 'line' | 'path';
type EventType = 'cellover' | 'cellout' | 'cellclick';
export interface LegendColor {
scale(scale: (any)): (selection: Selection<BaseType, any, any, any>, ...args: any[]) => void;
cells(steps: number): LegendColor;
cells(steps: number[]): LegendColor;
cellFilter(filter: () => boolean): LegendColor;
orient(orientation: Orientation): LegendColor;
ascending(ascending: boolean): LegendColor;
shape(shape: Shape, path?: string): LegendColor;
shapeWidth(width: number): LegendColor;
shapeHeight(height: number): LegendColor;
shapeRadius(radius: number): LegendColor;
shapePadding(padding: number): LegendColor;
useClass(useClass: boolean): LegendColor;
classPrefix(prefix: string): LegendColor;
title(title: string): LegendColor;
titleWidth(width: number): LegendColor;
labels(labels: string[]): LegendColor;
labels(labelGenerator: Function): LegendColor;
labelAlign(alignment: Alignment): LegendColor;
labelFormat(format: (n: number) => string): LegendColor;
labelOffset(offset: number): LegendColor;
labelDelimiter(delimiter: string): LegendColor;
on(eventType: EventType, event: Function): LegendColor;
}
export interface LegendSize {
scale(scale: (any)): (selection: Selection<BaseType, any, any, any>, ...args: any[]) => void;
cells(steps: number): LegendColor;
cells(steps: number[]): LegendColor;
cellFilter(filter: () => boolean): LegendColor;
orient(orientation: Orientation): LegendColor;
ascending(ascending: boolean): LegendColor;
shape(shape: Shape, path?: string): LegendColor;
shapeWidth(width: number): LegendColor;
shapePadding(padding: number): LegendColor;
classPrefix(prefix: string): LegendColor;
title(title: string): LegendColor;
titleWidth(width: number): LegendColor;
labels(labels: string[]): LegendColor;
labels(labelGenerator: Function): LegendColor;
labelAlign(alignment: Alignment): LegendColor;
labelFormat(format: (n: number) => string): LegendColor;
labelOffset(offset: number): LegendColor;
labelDelimiter(delimiter: string): LegendColor;
on(eventType: EventType, event: Function): LegendColor;
}
export interface LegendSymbol {
scale(scale: (any)): (selection: Selection<BaseType, any, any, any>, ...args: any[]) => void;
cells(steps: number): LegendColor;
cells(steps: number[]): LegendColor;
cellFilter(filter: () => boolean): LegendColor;
orient(orientation: Orientation): LegendColor;
ascending(ascending: boolean): LegendColor;
shapePadding(padding: number): LegendColor;
classPrefix(prefix: string): LegendColor;
title(title: string): LegendColor;
titleWidth(width: number): LegendColor;
labels(labels: string[]): LegendColor;
labels(labelGenerator: Function): LegendColor;
labelAlign(alignment: Alignment): LegendColor;
labelFormat(format: (n: number) => string): LegendColor;
labelOffset(offset: number): LegendColor;
labelDelimiter(delimiter: string): LegendColor;
on(eventType: EventType, event: Function): LegendColor;
}
export function legendColor(...args: any[]): LegendColor;
export function legendSize(...args: any[]): LegendSize;
export function legendSymbol(...args: any[]): LegendSymbol;
export namespace legendHelpers {
function thresholdLabels(_ref: any): any;
}

@ -5,21 +5,20 @@
/////////// Inspired by the code of alangrafu ///////////
/////////////////////////////////////////////////////////
function RadarChart(id, data, options) {
function RadarChart(id, data, color, IDS, options) {
var cfg = {
w: 600, //Width of the circle
h: 600, //Height of the circle
margin: {top: 20, right: 20, bottom: 20, left: 20}, //The margins of the SVG
levels: 3, //How many levels or inner circles should there be drawn
maxValue: 0, //What is the value that the biggest circle will represent
labelFactor: 1.25, //How much farther than the radius of the outer circle should the labels be placed
labelFactor: 1.3, //How much farther than the radius of the outer circle should the labels be placed
wrapWidth: 60, //The number of pixels after which a label needs to be given a new line
opacityArea: 0.35, //The opacity of the area of the blob
dotRadius: 4, //The size of the colored circles of each blog
opacityCircles: 0.1, //The opacity of the circles of each blob
strokeWidth: 2, //The width of the stroke around each blob
roundStrokes: false, //If true the area and stroke will follow a round path (cardinal-closed)
color: d3v3.scale.category10() //Color function
};
//Put all of the options into a variable called cfg
@ -28,20 +27,22 @@ function RadarChart(id, data, options) {
if('undefined' !== typeof options[i]){ cfg[i] = options[i]; }
}//for i
}//if
//If the supplied maxValue is smaller than the actual one, replace by the max in the data
var maxValue = Math.max(cfg.maxValue, d3v3.max(data, function(i){return d3v3.max(i.map(function(o){return o.value;}))}));
var allAxis = (data[0].map(function(i, j){return i.axis})), //Names of each axis
if (data.length != 0){
var allAxis = (data[0].map(function(i, j){return i.axis})), //Names of each axis
total = allAxis.length, //The number of different axes
radius = Math.min(cfg.w/2, cfg.h/2), //Radius of the outermost circle
Format = d3v3.format('%'), //Percentage formatting
angleSlice = Math.PI * 2 / total; //The width in radians of each "slice"
//Scale for the radius
var rScale = d3v3.scale.linear()
.range([0, radius])
.domain([0, maxValue]);
//Scale for the radius
var rScale = d3v3.scale.linear()
.range([0, radius])
.domain([0, maxValue]);
}
/////////////////////////////////////////////////////////
//////////// Create the container SVG and g /////////////
@ -62,7 +63,7 @@ function RadarChart(id, data, options) {
/////////////////////////////////////////////////////////
////////// Glow filter for some extra pizzazz ///////////
/////////////////////////////////////////////////////////
if (data.length != 0){
//Filter for the outside glow
var filter = g.append('defs').append('filter').attr('id','glow'),
feGaussianBlur = filter.append('feGaussianBlur').attr('stdDeviation','2.5').attr('result','coloredBlur'),
@ -128,7 +129,7 @@ function RadarChart(id, data, options) {
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.attr("x", function(d, i){ return rScale(maxValue * cfg.labelFactor) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("y", function(d, i){ return rScale(maxValue * cfg.labelFactor) * Math.sin(angleSlice*i - Math.PI/2); })
.attr("y", function(d, i){ return rScale(maxValue * cfg.labelFactor) * Math.sin(angleSlice*i - Math.PI/2) - 16; })
.text(function(d){return d})
.call(wrap, cfg.wrapWidth);
@ -145,7 +146,7 @@ function RadarChart(id, data, options) {
if(cfg.roundStrokes) {
radarLine.interpolate("cardinal-closed");
}
//Create a wrapper for the blobs
var blobWrapper = g.selectAll(".radarWrapper")
.data(data)
@ -157,7 +158,7 @@ function RadarChart(id, data, options) {
.append("path")
.attr("class", "radarArea")
.attr("d", function(d,i) { return radarLine(d); })
.style("fill", function(d,i) { return cfg.color(i); })
.style("fill", function(d,i) { return color(IDS[i]); })
.style("fill-opacity", cfg.opacityArea)
.on('mouseover', function (d,i){
//Dim all blobs
@ -175,13 +176,13 @@ function RadarChart(id, data, options) {
.transition().duration(200)
.style("fill-opacity", cfg.opacityArea);
});
//Create the outlines
blobWrapper.append("path")
.attr("class", "radarStroke")
.attr("d", function(d,i) { return radarLine(d); })
.style("stroke-width", cfg.strokeWidth + "px")
.style("stroke", function(d,i) { return cfg.color(i); })
.style("stroke", function(d,i) { return color(IDS[i]); })
.style("fill", "none")
.style("filter" , "url(#glow)");
@ -193,7 +194,7 @@ function RadarChart(id, data, options) {
.attr("r", cfg.dotRadius)
.attr("cx", function(d,i){ return rScale(d.value) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("cy", function(d,i){ return rScale(d.value) * Math.sin(angleSlice*i - Math.PI/2); })
.style("fill", function(d,i,j) { return cfg.color(j); })
.style("fill", function(d,i,j) { return color(IDS[j]); })
.style("fill-opacity", 0.8);
/////////////////////////////////////////////////////////
@ -240,7 +241,7 @@ function RadarChart(id, data, options) {
/////////////////////////////////////////////////////////
/////////////////// Helper Function /////////////////////
/////////////////////////////////////////////////////////
}
//Taken from http://bl.ocks.org/mbostock/7555321
//Wraps SVG text
function wrap(text, width) {

@ -0,0 +1,3 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

@ -0,0 +1,28 @@
# Help
#### The issues section is for bug reports and feature requests only. If you need help, please use the [forum](http://discourse.threejs.org/) or [stackoverflow](http://stackoverflow.com/questions/tagged/three.js).
---
# Bugs
#### Before reporting a bug
1. Search issue tracker for similar issues.
2. Try the latest dev branch version of three.js.
3. Refer to the [Migration Guide](https://github.com/mrdoob/three.js/wiki/Migration) when upgrading to the dev version.
#### How to report a bug
1. Specify the revision number of the three.js library where the bug occurred.
2. Specify your browser version, operating system, and graphics card. (for example, Chrome 23.0.1271.95, Windows 7, Nvidia Quadro 2000M)
3. Describe the problem in detail. Explain what happened, and what you expected would happen.
4. Provide a small test-case (http://jsfiddle.net). [Here is a fiddle](https://jsfiddle.net/3foLr7sn/) you can edit that runs the current version. [And here is a fiddle](https://jsfiddle.net/qgu17w5o/) that uses the dev branch. If a test-case is not possible, provide a link to a live version of your application.
5. If helpful, include a screenshot. Annotate the screenshot for clarity.
---
# Contribution
#### How to contribute to three.js
1. Make sure you have a GitHub account.
2. Fork the repository on GitHub.
3. Check the [Contribution Guidelines](https://github.com/mrdoob/three.js/wiki/How-to-contribute-to-three.js).
4. Make changes to your clone of the repository.
5. Submit a pull request. Don't include build files in the PR.

@ -0,0 +1,41 @@
##### Description of the problem
This form is for three.js bug reports and feature requests only.
This is NOT a help site. Do not ask help questions here.
If you need help, please use the [forum](https://discourse.threejs.org/) or [stackoverflow](http://stackoverflow.com/questions/tagged/three.js).
Describe the bug or feature request in detail.
Always include a code snippet, screenshots, and any relevant models or textures to help us understand your issue.
Please also include a live example if possible. You can start from these templates:
* [jsfiddle](https://jsfiddle.net/3foLr7sn/) (latest release branch)
* [jsfiddle](https://jsfiddle.net/qgu17w5o/) (dev branch)
* [codepen](https://codepen.io/anon/pen/aEBKxR) (latest release branch)
* [codepen](https://codepen.io/anon/pen/BJWzaN) (dev branch)
##### Three.js version
- [ ] Dev
- [ ] r101
- [ ] ...
##### Browser
- [x] All of them
- [ ] Chrome
- [ ] Firefox
- [ ] Internet Explorer
##### OS
- [x] All of them
- [ ] Windows
- [ ] macOS
- [ ] Linux
- [ ] Android
- [ ] iOS
##### Hardware Requirements (graphics card, VR Device, ...)

@ -0,0 +1,10 @@
.DS_Store
*.swp
.project
node_modules
.idea/
.vscode/
npm-debug.log
.jshintrc
.vs/
test/unit/three.*.unit.js

@ -0,0 +1,8 @@
examples/*
!examples/js/
src/
test/
utils/
docs/
editor/
.DS_Store

@ -0,0 +1,5 @@
language: node_js
node_js:
- node
script:
- npm run travis

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Loading…
Cancel
Save