Permalink
Browse files

Add Legend

  • Loading branch information...
ryanlsimms committed Sep 13, 2017
1 parent cea1a74 commit c81584e941c7703fc8b66a9d19d9fead4b903d7c
Showing with 244 additions and 5 deletions.
  1. +35 −0 css/legend.css
  2. +11 −2 css/style.css
  3. +3 −0 index.html
  4. +22 −0 js/app.js
  5. +153 −0 js/basic_legend.js
  6. +4 −2 js/data.js
  7. +16 −1 js/rotating_donut.js
@@ -0,0 +1,35 @@
#legend {
position: relative;
margin-top: 20px;
width: 140px;
}
#legend li.legend-label {
display: flex;
align-items: center;
position: absolute;
padding: 2px 8px;
width: calc(100% - 20px);
cursor: pointer;
}
#legend li.legend-label svg {
margin-right: 8px;
}
#legend .hovered {
stroke: black;
z-index: 1;
}
#legend rect.hovered {
stroke-width: 2px;
}
#legend li.hovered,
#legend li.selected {
background-color: #e2e8ff;
}
#legend li.hovered svg rect {
stroke-width: 1px;
}
@@ -5,6 +5,9 @@ body {
height: 600px;
margin: 8px;
}
path {
cursor: pointer;
}
.donuts {
display: inline-block;
position: relative;
@@ -19,13 +22,19 @@ body {
}
#donut1 {
width: 200px;
height: 200px;
}
#donut2 {
width: 300px;
height: 300px;
}
#donut1,
#description1 {
height: 200px;
}
#donut2,
#description2 {
height: 300px;
}
.donut-label {
font-weight: bold;
}
@@ -8,8 +8,10 @@
<script src="js/data.js"></script>
<script src="js/rotating_donut.js"></script>
<script src="js/pie_transitions.js"></script>
<script src="js/basic_legend.js"></script>
<link rel="stylesheet" href="css/button.css">
<link rel="stylesheet" href="css/legend.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
@@ -39,6 +41,7 @@
</div>
</div>
<div id="legend"></div>
<button>Randomize Data</button>
</body>
@@ -1,6 +1,7 @@
document.addEventListener('DOMContentLoaded', function() {
'use strict';
var donut,
legend,
events;
function build() {
@@ -10,6 +11,11 @@ document.addEventListener('DOMContentLoaded', function() {
.color(function(d) {return d.color;})
.key(function(d) {return d.id;})
.sort(function(a, b) {return a.id - b.id;});
legend = APP.basicLegend()
.label(function(d) {return d.label;})
.color(function(d) {return d.color;})
.key(function(d) {return d.id;});
}
function addToDom() {
@@ -26,9 +32,15 @@ document.addEventListener('DOMContentLoaded', function() {
.transition()
.duration(0)
.call(donut);
d3.select('#legend')
.datum(APP.generateData())
.call(legend);
}
function addListeners() {
donut.on('mouseenter', events.donutMouseEnter)
.on('mouseleave', events.donutMouseLeave);
d3.select('button').on('click', events.dataButtonClick);
d3.selectAll('.donut-size').on('change', events.resizeSliderChange);
}
@@ -49,6 +61,16 @@ document.addEventListener('DOMContentLoaded', function() {
.call(donut);
},
donutMouseEnter: function(d) {
d3.select('#legend')
.call(legend.highlight, d)
},
donutMouseLeave: function(d) {
d3.select('#legend')
.call(legend.unhighlight, d)
},
resizeSliderChange: function() {
var target = d3.select(this).attr('data-target'),
value = this.value * 2;
@@ -0,0 +1,153 @@
if(typeof APP === 'undefined') {APP = {};}
APP.basicLegend = function () {
'use strict';
var events = d3.dispatch('mouseenter', 'mouseleave', 'click'),
selectedItem = d3.local();
var o = {
label: null,
key: null,
color: null
};
function legend(group) {
group.each(function(data) {
render.call(this, data, group)
});
}
function render(data, group) {
var context = d3.select(this),
t,
labels,
labelsEnter;
if (group instanceof d3.transition) {
t = d3.transition(group);
} else {
t = d3.transition();
}
context
.selectAll('ul')
.data([data])
.enter()
.append('ul')
.attr('class', 'legend');
labels = context
.selectAll('ul')
.selectAll('li.legend-label')
.data(Object, o.key);
labelsEnter = labels.enter()
.append('li')
.attr('class', 'legend-label')
.attr('data-id', o.key)
.on('mouseenter mouseleave', listeners(context).mouseMovement)
.on('click', listeners(context).labelClick)
.call(labelInitialAttributes);
labelsEnter
.append('svg')
.attr('width', 22)
.attr('height', 22)
.append('rect')
.attr('fill', o.color)
.attr('width', 20)
.attr('height', 20)
.attr('x', 1)
.attr('y', 1);
labelsEnter
.append('span')
.text(o.label);
labelsEnter
.merge(labels)
.classed('selected', isSelected)
.transition(t)
.style('top', function(d, i) {return (i * 22) + 'px';})
.style('opacity', 1)
.style('left', '12px');
labels.exit()
.transition(t)
.call(labelInitialAttributes)
.remove();
}
function listeners(context) {
return {
labelClick: function(d) {
selectedItem.set(context.node(), d);
context.call(legend);
events.call('click', context.node(), d);
},
mouseMovement: function(d) {
context.call(highlight, d, d3.event.type);
events.call(d3.event.type, context.node(), d);
}
}
}
function highlight(selection, d, action) {
selection
.selectAll('li[data-id="' + o.key(d) + '"]')
.classed('hovered', action === 'mouseenter');
}
function labelInitialAttributes(selection) {
selection
.style('left', '-12px')
.style('opacity', 0);
}
function isSelected(d) {
return selectedItem.get(this) && o.key(d) === o.key(selectedItem.get(this));
}
legend.label = function(_) {
if (!arguments.length) {return o.label;}
o.label = _;
return legend;
};
legend.key = function(_) {
if (!arguments.length) {return o.key;}
o.key = _;
return legend;
};
legend.color = function(_) {
if (!arguments.length) {return o.color;}
o.color = _;
return legend;
};
legend.selectedItem = function(context, _) {
var returnArray;
if (typeof _ === 'undefined' ) {
returnArray = context.nodes()
.map(function (node) {return selectedItem.get(node);});
return context._groups[0] instanceof NodeList ? returnArray : returnArray[0];
}
context.each(function() {selectedItem.set(this, _);});
return legend;
};
legend.on = function(evt, callback) {
events.on(evt, callback);
return legend;
};
legend.highlight = function(selection, d) {
selection.call(highlight, d, 'mouseenter');
return legend;
};
legend.unhighlight = function(selection, d) {
selection.call(highlight, d, 'mouseleave');
return legend;
};
return legend;
};
@@ -1,15 +1,17 @@
if(typeof APP === 'undefined') {APP = {};}
APP.generateData = function(splice) {
'use strict';
var colors = d3.scaleOrdinal(d3.schemeCategory10),
var labels = ['travel', 'electricity', 'phone', 'shopping', 'food'],
colors = d3.scaleOrdinal(d3.schemeCategory10),
arr = [],
i;
for (i = 1; i <= 5; i++) {
arr.push({
id: i,
value: 5 + Math.random() * 15,
color: colors(i)
color: colors(i),
label: labels[i - 1]
});
}
if (splice) {
@@ -2,6 +2,7 @@ if(typeof APP === 'undefined') {APP = {};}
APP.rotatingDonut = function() {
'use strict';
var o,
events,
local;
o = {
@@ -13,6 +14,8 @@ APP.rotatingDonut = function() {
sort: null
};
events = d3.dispatch('mouseenter', 'mouseleave');
local = {
label: d3.local(),
animate: d3.local(),
@@ -86,7 +89,8 @@ APP.rotatingDonut = function() {
segmentEnter = segments.enter()
.append('path')
.attr('class', 'segment')
.attr('fill', dataAccess('color'));
.attr('fill', dataAccess('color'))
.on('mouseenter mouseleave', onPathEvent(context));
pieTransition
.arc(arc)
@@ -108,6 +112,12 @@ APP.rotatingDonut = function() {
.remove();
}
function onPathEvent(context) {
return function(d) {
events.call(d3.event.type, context.node(), d.data);
};
}
function dataAccess(key) {
return function(d) {
return o[key](d.data);
@@ -181,5 +191,10 @@ APP.rotatingDonut = function() {
return donut;
};
donut.on = function(evt, callback) {
events.on(evt, callback);
return donut;
};
return donut;
};

0 comments on commit c81584e

Please sign in to comment.