diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..2aacb14
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,12 @@
+{
+ "name": "dangle",
+ "version": "1.1.1",
+ "main": "src/dangle.module.js",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
diff --git a/css/dangle.css b/css/dangle.css
index 5dfd680..1b0c00c 100644
--- a/css/dangle.css
+++ b/css/dangle.css
@@ -53,4 +53,17 @@
fill: #000;
stroke: none;
font-size: 12px;
+}
+
+.area-tooltip {
+ position: absolute;
+ text-align: left;
+ width: 150px;
+ height: 40px;
+ padding: 2px;
+ font: 12px sans-serif;
+ background: white;
+ border: 2px #959595 solid;
+ border-radius: 2px;
+ pointer-events: none;
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 9fcc6ba..a97bdfe 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "dangle",
"description": "A set of AngularJS directives that provide common visualizations for elasticsearch based on D3",
- "version": "1.0.0",
+ "version": "1.1.1",
"homepage": "http://www.fullscale.co/dangle",
"keywords": ["angularjs", "d3", "charts", "graphs", "elasticsearch"],
"repository": {
diff --git a/src/modules/area/area.js b/src/modules/area/area.js
index 568a191..ef6b8e7 100644
--- a/src/modules/area/area.js
+++ b/src/modules/area/area.js
@@ -24,7 +24,7 @@
*/
angular.module('dangle')
- .directive('fsArea', [function() {
+ .directive('fsArea', ['$filter', function($filter) {
'use strict';
return {
@@ -41,18 +41,28 @@ angular.module('dangle')
duration: '@',
delay: '@',
plot: '@',
- pointRadius: '@'
+ pointRadius: '@',
+ marginLeft: '=',
+ marginRight: '=',
+ marginBottom:'=',
+ marginTop: '=',
+ xticks: '=',
+ yticks: '=',
+ tooltips: '@'
},
link: function(scope, element, attrs) {
+ var dateFilter = $filter('date');
+
var margin = {
- top: 20,
- right: 20,
- bottom: 30,
- left: 80
+ top: scope.marginTop || 20,
+ right: scope.marginRight || 20,
+ bottom: scope.marginBottom || 30,
+ left: scope.marginLeft || 80
};
+
// default width/height - mainly to create initial aspect ratio
var width = scope.width || 1280;
var height = scope.height || 300;
@@ -61,7 +71,9 @@ angular.module('dangle')
var interpolate = attrs.interpolate || 'false';
var label = attrs.label || 'Frequency';
+ var rotateLabel = attrs.rotateLabel || 'true';
var klass = attrs.class || '';
+ var tooltips = attrs.tooltips || 'false';
// add margins (make room for x,y labels)
width = width - margin.left - margin.right;
@@ -74,7 +86,7 @@ angular.module('dangle')
var y = d3.scale.linear()
.range([height, 0]);
- // create x,y axis
+ // create x,y axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
@@ -83,7 +95,16 @@ angular.module('dangle')
.scale(y)
.orient('left');
- // create line generator
+ //set the number of ticks if specified
+ if(scope.xticks) {
+ xAxis.ticks(scope.xticks);
+ }
+
+ if(scope.yticks) {
+ yAxis.ticks(scope.yticks);
+ }
+
+ // create line generator
var line = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.count); });
@@ -94,7 +115,7 @@ angular.module('dangle')
.y0(height)
.y1(function(d) { return y(d.count); });
- // enable interpolation if specified
+ // enable interpolation if specified
if (attrs.interpolate == 'true') {
line.interpolate('cardinal');
area.interpolate('cardinal');
@@ -120,16 +141,24 @@ angular.module('dangle')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
- // insert the x axis (no data yet)
- svg.append('g')
+ var axis = svg.append('g')
.attr('class', 'area y axis ' + klass)
.call(yAxis)
- .append('text')
- .attr('transform', 'rotate(-90)')
- .attr('y', 6)
- .attr('dy', '.71em')
- .style('text-anchor', 'end')
- .text(label);
+ .append('text')
+ .attr('dy', '.71em')
+ .text(label);
+ // insert the y axis (no data yet)
+ if(rotateLabel == 'true') {
+ axis
+ .attr('transform', 'rotate(-90)')
+ .attr('y', 6)
+ .style('text-anchor', 'end')
+ } else {
+ axis
+ .attr('y', -10)
+ .attr('dy', '.71em')
+ }
+
// generate the line. Data is empty at link time
svg.append('path')
@@ -137,6 +166,10 @@ angular.module('dangle')
.attr('class', 'area line ' + klass)
.attr("d", line);
+ var div = d3.select("body").append("div")
+ .attr("class", "area-tooltip")
+ .style("opacity", 0);
+
// main observer fn called when scope is updated. Data and scope vars are now bound
scope.$watch('bind', function(data) {
@@ -158,7 +191,7 @@ angular.module('dangle')
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
- // create the transition
+ // create the transition
var t = svg.transition().duration(duration);
// feed the current data to our area/line generators
@@ -172,13 +205,13 @@ angular.module('dangle')
// using Math.random as (optional) key fn ensures old
// data values are flushed and all new values inserted
var points = svg.selectAll('circle')
- .data(data.filter(function(d) {
- return d.count;
- }), function(d) {
- return Math.random();
+ .data(data.filter(function(d) {
+ return d.count;
+ }), function(d) {
+ return Math.random();
});
- // d3 enter fn binds each new value to a circle
+ // d3 enter fn binds each new value to a circle
points.enter()
.append('circle')
.attr('class', 'area line points ' + klass)
@@ -194,10 +227,28 @@ angular.module('dangle')
.attr("r", pointRadius);
// wire up any events (registers filter callback)
- points.on('mousedown', function(d) {
+ points.on('mousedown', function(d) {
scope.$apply(function() {
(scope.onClick || angular.noop)(field, d.time);
});
+ }).on("mouseover", function(d) {
+ if(tooltips === 'true') {
+ div.transition()
+ .duration(200)
+ .style("opacity", .9);
+ div .html(
+ "" + dateFilter(new Date(d.time), "EEEE, MMM d, yyyy") + "" +
+ "
" + label + ":" + "" + d.count +""
+ )
+ .style("left", (d3.event.pageX) + "px")
+ .style("top", (d3.event.pageY - 28) + "px");
+ }
+ }).on("mouseout", function(d) {
+ if(tooltips === 'true') {
+ div.transition()
+ .duration(100)
+ .style("opacity", .0);
+ }
});
// d3 exit/remove flushes old values (removes old circles)