From 253c67ffa5299ff89a7195c495c3e07149ae617c Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Thu, 21 May 2015 17:03:45 -0400 Subject: [PATCH 1/5] integrating feedback from wed/thu. --- scout-ui/src/minicharts/d3fns/boolean.js | 30 +++--- scout-ui/src/minicharts/d3fns/date.js | 40 +++++--- scout-ui/src/minicharts/d3fns/few.js | 63 ++++++------ scout-ui/src/minicharts/d3fns/many.js | 111 +++++++++++++++------ scout-ui/src/minicharts/d3fns/number.js | 33 ++++-- scout-ui/src/minicharts/d3fns/string.js | 7 +- scout-ui/src/minicharts/d3fns/tooltip.jade | 3 + scout-ui/src/minicharts/index.less | 5 + 8 files changed, 191 insertions(+), 101 deletions(-) create mode 100644 scout-ui/src/minicharts/d3fns/tooltip.jade diff --git a/scout-ui/src/minicharts/d3fns/boolean.js b/scout-ui/src/minicharts/d3fns/boolean.js index 75188e2cd50..c7e046aa061 100644 --- a/scout-ui/src/minicharts/d3fns/boolean.js +++ b/scout-ui/src/minicharts/d3fns/boolean.js @@ -8,20 +8,22 @@ module.exports = function(opts) { // group by true/false var data = _(values) - .groupBy(function(d) { - // extract string representations of values - return d; - }) - .defaults({false: [], true: []}) - .map(function(v, k) { - return { - x: k, - y: v.length, - tooltip: k - }; - }) - .sortByOrder('x', [false]) // descending on y - .value(); + .groupBy(function(d) { + // extract string representations of values + return d; + }) + .defaults({ + false: [], + true: [] + }) + .map(function(v, k) { + return { + x: k, + y: v.length + }; + }) + .sortByOrder('x', [false]) // descending on y + .value(); var margin = { top: 10, diff --git a/scout-ui/src/minicharts/d3fns/date.js b/scout-ui/src/minicharts/d3fns/date.js index 921f2746380..cb499a8c091 100644 --- a/scout-ui/src/minicharts/d3fns/date.js +++ b/scout-ui/src/minicharts/d3fns/date.js @@ -6,7 +6,9 @@ var many = require('./many'); function generateDefaults(n) { var doc = {}; - _.each(_.range(n), function (d) { doc[d] = 0; }); + _.each(_.range(n), function(d) { + doc[d] = 0; + }); return doc; } @@ -17,7 +19,7 @@ module.exports = function(opts) { // distinguish ObjectIDs from real dates if (values.length && values[0]._bsontype !== undefined) { if (values[0]._bsontype === 'ObjectID') { - values = _.map(values, function (v) { + values = _.map(values, function(v) { return v.getTimestamp(); }); } @@ -49,13 +51,13 @@ module.exports = function(opts) { var upperMargin = 15; // group by weekdays - var weekdayLabels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; + var weekdayLabels = moment.weekdays(); var weekdays = _(values) - .groupBy(function (d) { + .groupBy(function(d) { return moment(d).weekday(); }) .defaults(generateDefaults(7)) - .map(function (d, i) { + .map(function(d, i) { return { x: weekdayLabels[i], y: d.length, @@ -67,11 +69,11 @@ module.exports = function(opts) { // group by hours var hourLabels = d3.range(24); var hours = _(values) - .groupBy(function (d) { + .groupBy(function(d) { return d.getHours(); }) .defaults(generateDefaults(23)) - .map(function (d, i) { + .map(function(d, i) { return { x: hourLabels[i], y: d.length, @@ -117,19 +119,25 @@ module.exports = function(opts) { }); var weekdayContainer = svg.append('g'); - many(weekdays, weekdayContainer, width / (upperRatio+1) - upperMargin, upperBarBottom, { - 'text-anchor': 'middle', - 'text': function (d) { - return d.x[0]; + many(weekdays, weekdayContainer, width / (upperRatio + 1) - upperMargin, upperBarBottom, { + bgbars: true, + labels: { + 'text-anchor': 'middle', + 'text': function(d) { + return d.x[0]; + } } }); var hourContainer = svg.append('g') - .attr('transform', 'translate(' + (width/(upperRatio+1) + upperMargin) + ', 0)'); - - many(hours, hourContainer, width/(upperRatio+1)*upperRatio - upperMargin, upperBarBottom, { - 'text': function (d, i) { - return (i % 6 === 0 || i === 23) ? d.x : ''; + .attr('transform', 'translate(' + (width / (upperRatio + 1) + upperMargin) + ', 0)'); + + many(hours, hourContainer, width / (upperRatio + 1) * upperRatio - upperMargin, upperBarBottom, { + bgbars: true, + labels: { + 'text': function(d, i) { + return (i % 6 === 0 || i === 23) ? d.x : ''; + } } }); diff --git a/scout-ui/src/minicharts/d3fns/few.js b/scout-ui/src/minicharts/d3fns/few.js index 464e40d956d..aa3c1bbe8c6 100644 --- a/scout-ui/src/minicharts/d3fns/few.js +++ b/scout-ui/src/minicharts/d3fns/few.js @@ -1,27 +1,39 @@ var d3 = require('d3'); var _ = require('lodash'); +var tooltipHtml = require('./tooltip.jade'); var debug = require('debug')('scout-ui:minicharts:few'); require('d3-tip')(d3); -module.exports = function (data, g, width, height) { +module.exports = function(data, g, width, height, options) { + + // @todo make barOffset equal to longest label - var barOffset = width / 4; + // var barOffset = width / 4; + var barHeight = 30; // data.x is still the label, and data.y the length of the bar var x = d3.scale.linear() - .domain([0, d3.max(_.pluck(data, 'y'))]) - .range([0, width - barOffset]); + .domain([0, d3.sum(_.pluck(data, 'y'))]) + .range([0, width]); var y = d3.scale.ordinal() .domain(_.pluck(data, 'x')) .rangeBands([0, height], 0.3, 0.0); + var sumY = d3.sum(_.pluck(data, 'y')); + // set up tooltips var tip = d3.tip() .attr('class', 'd3-tip') - .html(function(d) { - return d.tooltip || d.x; + .html(function(d, i) { + if (typeof d.tooltip === 'function') { + return d.tooltip(d, i); + } + return d.tooltip || tooltipHtml({ + label: d.x, + value: Math.round(d.y / sumY * 100) + }); }) .direction('n') .offset([-9, 0]); @@ -30,45 +42,40 @@ module.exports = function (data, g, width, height) { g.selectAll('*').remove(); g.call(tip); + debug('data', _.pluck(data, 'y')); + var bar = g.selectAll('.bar') .data(data) .enter().append('g') .attr('class', 'bar') - .attr('transform', function(d) { - return 'translate(0, ' + y(d.x) + ')'; + .attr('transform', function(d, i) { + var xpos = _.sum(_(data) + .slice(0, i) + .pluck('y') + .value() + ); + return 'translate(' + x(xpos) + ', ' + (height - barHeight) / 2 + ')'; }); - bar.append('rect') - .attr('class', 'bg') - .attr('x', barOffset) - .attr('width', width - barOffset) - .attr('height', y.rangeBand()); - bar.append('rect') .attr('class', 'fg') .attr('y', 0) - .attr('x', barOffset) + .attr('x', 0) .attr('width', function(d) { return x(d.y); }) - .attr('height', y.rangeBand()); - - bar.append('rect') - .attr('class', 'glass') - .attr('x', barOffset) - .attr('width', width - barOffset) - .attr('height', y.rangeBand()) + .attr('height', barHeight) .on('mouseover', tip.show) .on('mouseout', tip.hide); bar.append('text') - .attr('dx', '-10') - .attr('dy', '0.4em') - .attr('y', y.rangeBand() / 2) - .attr('x', barOffset) - .attr('text-anchor', 'end') + .attr('y', barHeight / 2) + .attr('dy', '0.3em') + .attr('dx', 10) + .attr('text-anchor', 'start') .text(function(d) { return d.x; - }); + }) + .attr('fill', 'white'); }; diff --git a/scout-ui/src/minicharts/d3fns/many.js b/scout-ui/src/minicharts/d3fns/many.js index 095356b3ce6..ca6874c0396 100644 --- a/scout-ui/src/minicharts/d3fns/many.js +++ b/scout-ui/src/minicharts/d3fns/many.js @@ -1,10 +1,18 @@ var d3 = require('d3'); var _ = require('lodash'); +var tooltipHtml = require('./tooltip.jade'); var debug = require('debug')('scout-ui:minicharts:many'); require('d3-tip')(d3); -module.exports = function (data, g, width, height, labels) { +module.exports = function(data, g, width, height, options) { + + options = _.defaults(options || {}, { + bgbars: false, + bglines: false, + legend: true, + labels: false // label options will be set further below + }); var x = d3.scale.ordinal() .domain(_.pluck(data, 'x')) @@ -14,6 +22,8 @@ module.exports = function (data, g, width, height, labels) { .domain([0, d3.max(_.pluck(data, 'y'))]) .range([height, 0]); + var sumY = d3.sum(_.pluck(data, 'y')); + // set up tooltips var tip = d3.tip() .attr('class', 'd3-tip') @@ -21,7 +31,10 @@ module.exports = function (data, g, width, height, labels) { if (typeof d.tooltip === 'function') { return d.tooltip(d, i); } - return d.tooltip || d.x; + return d.tooltip || tooltipHtml({ + label: d.x, + value: d.y + }); }) .direction('n') .offset([-9, 0]); @@ -30,6 +43,37 @@ module.exports = function (data, g, width, height, labels) { g.selectAll('*').remove(); g.call(tip); + if (options.legend) { + g.append('text') + .attr('x', 0) + .attr('y', -10) + .attr('text-anchor', 'start') + .text(sumY); + } + + if (options.bglines) { + g.append('line') + .attr('class', 'bg line') + .attr('x1', 0) + .attr('x2', width) + .attr('y1', 0) + .attr('y2', 0); + + g.append('line') + .attr('class', 'bg line') + .attr('x1', 0) + .attr('x2', width) + .attr('y1', height / 2) + .attr('y2', height / 2); + + g.append('line') + .attr('class', 'bg line') + .attr('x1', 0) + .attr('x2', width) + .attr('y1', height) + .attr('y2', height); + } + var bar = g.selectAll('.bar') .data(data) .enter().append('g') @@ -38,12 +82,14 @@ module.exports = function (data, g, width, height, labels) { return 'translate(' + x(d.x) + ', 0)'; }); - bar.append('rect') - .attr('class', 'bg') - .attr('width', x.rangeBand()) - .attr('height', height); + if (options.bgbars) { + bar.append('rect') + .attr('class', 'bg') + .attr('width', x.rangeBand()) + .attr('height', height); + } - bar.append('rect') + var fgbars = bar.append('rect') .attr('class', 'fg') .attr('x', 0) .attr('y', function(d) { @@ -54,39 +100,46 @@ module.exports = function (data, g, width, height, labels) { return height - y(d.y); }); + if (options.bgbars) { bar.append('rect') .attr('class', 'glass') .attr('width', x.rangeBand()) .attr('height', height) .on('mouseover', tip.show) .on('mouseout', tip.hide); + } else { + // atach tooltips directly to foreground bars + fgbars + .on('mouseover', tip.show) + .on('mouseout', tip.hide); + } + + if (options.labels) { + var labels = options.labels; + _.defaults(labels, { + 'x': labels['text-anchor'] === 'middle' ? x.rangeBand() / 2 : function(d, i) { + if (i === 0) return 0; + if (i === data.length - 1) return x.rangeBand(); + return x.rangeBand() / 2; + }, + 'y': height + 5, + 'dy': '0.75em', + 'text-anchor': function(d, i) { + if (i === 0) return 'start'; + if (i === data.length - 1) return 'end'; + return 'middle'; + }, + 'text': function(d) { + return d.x; + } + }); - if (labels) { - - _.defaults(labels, { - 'x': labels['text-anchor'] === 'middle' ? x.rangeBand() / 2 : function (d, i) { - if (i === 0) return 0; - if (i === data.length - 1) return x.rangeBand(); - return x.rangeBand() / 2; - }, - 'y': height+5, - 'dy': '0.75em', - 'text-anchor': function (d, i) { - if (i === 0) return 'start'; - if (i === data.length - 1) return 'end'; - return 'middle'; - }, - 'text': function (d) { - return d.x; - } - }); - - bar.append('text') + bar.append('text') .attr('x', labels.x) .attr('dx', labels.dx) .attr('y', labels.y) .attr('dy', labels.dy) .attr('text-anchor', labels['text-anchor']) .text(labels.text); - } + } }; diff --git a/scout-ui/src/minicharts/d3fns/number.js b/scout-ui/src/minicharts/d3fns/number.js index 7241a084dde..522699c9b67 100644 --- a/scout-ui/src/minicharts/d3fns/number.js +++ b/scout-ui/src/minicharts/d3fns/number.js @@ -1,6 +1,7 @@ var d3 = require('d3'); var _ = require('lodash'); var many = require('./many'); +var tooltipHtml = require('./tooltip.jade'); var debug = require('debug')('scout-ui:minicharts:number'); module.exports = function(opts) { @@ -28,15 +29,21 @@ module.exports = function(opts) { .bins(ticks); var data = hist(values); - _.each(data, function (d, i) { + var sumY = d3.sum(_.pluck(data, 'y')); + + _.each(data, function(d, i) { + var label; if (i === 0) { - d.tooltip = '< ' + (d.x+d.dx); - } - else if (i === data.length - 1) { - d.tooltip = '≥ ' + d.x; + label = '< ' + (d.x + d.dx); + } else if (i === data.length - 1) { + label = '≥ ' + d.x; } else { - d.tooltip = d.x + '-' + (d.x+d.dx); + label = d.x + '-' + (d.x + d.dx); } + d.tooltip = tooltipHtml({ + label: label, + value: Math.round(d.y / sumY * 100) + }); }); // clear element first @@ -46,11 +53,15 @@ module.exports = function(opts) { .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - many(data, g, width, height-10, { - text: function (d, i) { - if (i === 0) return d3.min(values); - if (i === data.length - 1) return d3.max(values); - return ''; + many(data, g, width, height - 10, { + bglines: true, + bgbars: false, + labels: { + text: function(d, i) { + if (i === 0) return d3.min(values); + if (i === data.length - 1) return d3.max(values); + return ''; + } } }); }; diff --git a/scout-ui/src/minicharts/d3fns/string.js b/scout-ui/src/minicharts/d3fns/string.js index 60e19d46b20..97b85fe80bf 100644 --- a/scout-ui/src/minicharts/d3fns/string.js +++ b/scout-ui/src/minicharts/d3fns/string.js @@ -26,8 +26,7 @@ module.exports = function(opts) { .map(function(v, k) { return { x: k, - y: v.length, - tooltip: k + y: v.length }; }) .sortByOrder('y', [false]) // descending on y @@ -43,6 +42,8 @@ module.exports = function(opts) { .attr('height', height); var chart = data.length <= 5 ? few : many; - chart(data, g, width, height); + chart(data, g, width, height, { + bglines: true + }); }; diff --git a/scout-ui/src/minicharts/d3fns/tooltip.jade b/scout-ui/src/minicharts/d3fns/tooltip.jade new file mode 100644 index 00000000000..093970460f9 --- /dev/null +++ b/scout-ui/src/minicharts/d3fns/tooltip.jade @@ -0,0 +1,3 @@ +.tooltip-wrapper + .tooltip-label!= label + .tooltip-value #{value}% diff --git a/scout-ui/src/minicharts/index.less b/scout-ui/src/minicharts/index.less index 923efe9670e..3e8e0dd71d8 100644 --- a/scout-ui/src/minicharts/index.less +++ b/scout-ui/src/minicharts/index.less @@ -53,6 +53,7 @@ svg.minichart { rect.fg { fill: @mc-bar-fg; + stroke: @white; } rect.hl { @@ -63,6 +64,10 @@ svg.minichart { .line { stroke: @mc-bar-fg; shape-rendering: crispEdges; + + &.bg { + stroke: @gray6; + } } .axis path, .axis line { From b346db348b5c4e0749c42bb131b1fc82776a58c0 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Thu, 21 May 2015 20:50:26 -0400 Subject: [PATCH 2/5] WIP: adding horizontal lines and labels --- scout-ui/src/minicharts/d3fns/few.js | 2 -- scout-ui/src/minicharts/d3fns/many.js | 30 ++++++++++++++++++------- scout-ui/src/minicharts/d3fns/number.js | 2 +- scout-ui/src/minicharts/d3fns/string.js | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/scout-ui/src/minicharts/d3fns/few.js b/scout-ui/src/minicharts/d3fns/few.js index aa3c1bbe8c6..16491b75f96 100644 --- a/scout-ui/src/minicharts/d3fns/few.js +++ b/scout-ui/src/minicharts/d3fns/few.js @@ -42,8 +42,6 @@ module.exports = function(data, g, width, height, options) { g.selectAll('*').remove(); g.call(tip); - debug('data', _.pluck(data, 'y')); - var bar = g.selectAll('.bar') .data(data) .enter().append('g') diff --git a/scout-ui/src/minicharts/d3fns/many.js b/scout-ui/src/minicharts/d3fns/many.js index ca6874c0396..b9db9b00916 100644 --- a/scout-ui/src/minicharts/d3fns/many.js +++ b/scout-ui/src/minicharts/d3fns/many.js @@ -9,8 +9,7 @@ module.exports = function(data, g, width, height, options) { options = _.defaults(options || {}, { bgbars: false, - bglines: false, - legend: true, + legend: false, labels: false // label options will be set further below }); @@ -44,14 +43,28 @@ module.exports = function(data, g, width, height, options) { g.call(tip); if (options.legend) { + + var maxVal = d3.max(y.domain()); + var format = d3.format('%.1f'); + var legendValues = [format(maxVal), format(maxVal / 2)]; + debug(legendValues); + g.append('text') - .attr('x', 0) - .attr('y', -10) - .attr('text-anchor', 'start') - .text(sumY); - } + .attr('class', 'legend') + .attr('x', width) + .attr('y', 0) + .attr('dy', '0.35em') + .attr('text-anchor', 'end') + .text(d3.max(y.domain()) + '%'); + + g.append('text') + .attr('class', 'legend') + .attr('x', width) + .attr('y', height / 2) + .attr('dy', '0.35em') + .attr('text-anchor', 'end') + .text(d3.max(y.domain()) / 2 + '%'); - if (options.bglines) { g.append('line') .attr('class', 'bg line') .attr('x1', 0) @@ -74,6 +87,7 @@ module.exports = function(data, g, width, height, options) { .attr('y2', height); } + var bar = g.selectAll('.bar') .data(data) .enter().append('g') diff --git a/scout-ui/src/minicharts/d3fns/number.js b/scout-ui/src/minicharts/d3fns/number.js index 522699c9b67..b656cc0b1b3 100644 --- a/scout-ui/src/minicharts/d3fns/number.js +++ b/scout-ui/src/minicharts/d3fns/number.js @@ -54,7 +54,7 @@ module.exports = function(opts) { .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); many(data, g, width, height - 10, { - bglines: true, + legend: true, bgbars: false, labels: { text: function(d, i) { diff --git a/scout-ui/src/minicharts/d3fns/string.js b/scout-ui/src/minicharts/d3fns/string.js index 97b85fe80bf..936fcaf6b76 100644 --- a/scout-ui/src/minicharts/d3fns/string.js +++ b/scout-ui/src/minicharts/d3fns/string.js @@ -43,6 +43,7 @@ module.exports = function(opts) { var chart = data.length <= 5 ? few : many; chart(data, g, width, height, { + legend: true, bglines: true }); }; From f989958b735e7c0dd5985914723f369d76ee5047 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Thu, 21 May 2015 23:06:56 -0400 Subject: [PATCH 3/5] minicharts much nice. - [x] fix type distribution bars (Lucas' new mongodb-schema version) - [x] add labels to histograms (right side) - [x] styling (dist bars, tooltips, histograms) - [x] add ":00" to hour labels (or AM/PM) - [x] disarm reload button - [x] min/max labels at the bottom of number charts - [x] clean up minicharts code --- scout-ui/src/minicharts/d3fns/boolean.js | 6 +- scout-ui/src/minicharts/d3fns/date.js | 46 +++++++++++---- scout-ui/src/minicharts/d3fns/few.js | 28 ++++----- scout-ui/src/minicharts/d3fns/many.js | 75 ++++++++++++++++-------- scout-ui/src/minicharts/d3fns/number.js | 7 ++- scout-ui/src/minicharts/d3fns/string.js | 8 +-- scout-ui/src/minicharts/index.js | 5 +- scout-ui/src/minicharts/index.less | 43 ++++++++++---- scout-ui/src/minicharts/minichart.jade | 2 +- scout-ui/src/minicharts/unique.jade | 4 +- 10 files changed, 143 insertions(+), 81 deletions(-) diff --git a/scout-ui/src/minicharts/d3fns/boolean.js b/scout-ui/src/minicharts/d3fns/boolean.js index c7e046aa061..907a54e01f6 100644 --- a/scout-ui/src/minicharts/d3fns/boolean.js +++ b/scout-ui/src/minicharts/d3fns/boolean.js @@ -18,11 +18,11 @@ module.exports = function(opts) { }) .map(function(v, k) { return { - x: k, - y: v.length + label: k, + value: v.length }; }) - .sortByOrder('x', [false]) // descending on y + .sortByOrder('label', [false]) // order: false, true .value(); var margin = { diff --git a/scout-ui/src/minicharts/d3fns/date.js b/scout-ui/src/minicharts/d3fns/date.js index cb499a8c091..d65d89073a2 100644 --- a/scout-ui/src/minicharts/d3fns/date.js +++ b/scout-ui/src/minicharts/d3fns/date.js @@ -4,6 +4,8 @@ var moment = require('moment'); var debug = require('debug')('scout-ui:minicharts:date'); var many = require('./many'); +require('d3-tip')(d3); + function generateDefaults(n) { var doc = {}; _.each(_.range(n), function(d) { @@ -47,7 +49,7 @@ module.exports = function(opts) { .range([0, width]); var upperBarBottom = height / 2 - 20; - var upperRatio = 2; + var upperRatio = 2.5; var upperMargin = 15; // group by weekdays @@ -59,8 +61,8 @@ module.exports = function(opts) { .defaults(generateDefaults(7)) .map(function(d, i) { return { - x: weekdayLabels[i], - y: d.length, + label: weekdayLabels[i], + value: d.length, tooltip: weekdayLabels[i] }; }) @@ -72,12 +74,12 @@ module.exports = function(opts) { .groupBy(function(d) { return d.getHours(); }) - .defaults(generateDefaults(23)) + .defaults(generateDefaults(24)) .map(function(d, i) { return { - x: hourLabels[i], - y: d.length, - tooltip: hourLabels[i] + 'h' + label: hourLabels[i] + ':00', + value: d.length, + tooltip: hourLabels[i] + ':00' }; }) .value(); @@ -85,9 +87,19 @@ module.exports = function(opts) { // clear element first d3.select(el).selectAll('*').remove(); + // set up tooltips + var tip = d3.tip() + .attr('class', 'd3-tip') + .html(function(d, i) { + return format(d); + }) + .direction('n') + .offset([-9, 0]); + var svg = d3.select(el) .append('g') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') + .call(tip); var line = svg.selectAll('.line') .data(values) @@ -100,7 +112,9 @@ module.exports = function(opts) { .attr('x2', function(d) { return barcodeX(d); }) - .attr('y2', barcodeBottom); + .attr('y2', barcodeBottom) + .on('mouseover', tip.show) + .on('mouseout', tip.hide); var text = svg.selectAll('.text') .data(barcodeX.domain()) @@ -114,8 +128,14 @@ module.exports = function(opts) { .attr('text-anchor', function(d, i) { return i ? 'end' : 'start'; }) - .text(function(d) { - return format(d); + .text(function(d, i) { + if (format(barcodeX.domain()[0]) === format(barcodeX.domain()[1])) { + if (i === 0) { + return 'inserted: ' + format(d); + } + } else { + return (i ? 'last: ' : 'first: ') + format(d); + } }); var weekdayContainer = svg.append('g'); @@ -124,7 +144,7 @@ module.exports = function(opts) { labels: { 'text-anchor': 'middle', 'text': function(d) { - return d.x[0]; + return d.label[0]; } } }); @@ -136,7 +156,7 @@ module.exports = function(opts) { bgbars: true, labels: { 'text': function(d, i) { - return (i % 6 === 0 || i === 23) ? d.x : ''; + return (i % 6 === 0 || i === 23) ? d.label : ''; } } }); diff --git a/scout-ui/src/minicharts/d3fns/few.js b/scout-ui/src/minicharts/d3fns/few.js index 16491b75f96..78f8a6fec60 100644 --- a/scout-ui/src/minicharts/d3fns/few.js +++ b/scout-ui/src/minicharts/d3fns/few.js @@ -7,22 +7,15 @@ require('d3-tip')(d3); module.exports = function(data, g, width, height, options) { - - // @todo make barOffset equal to longest label - // var barOffset = width / 4; - var barHeight = 30; + var barHeight = 25; + var values = _.pluck(data, 'value'); + var sumValues = d3.sum(values); // data.x is still the label, and data.y the length of the bar var x = d3.scale.linear() - .domain([0, d3.sum(_.pluck(data, 'y'))]) + .domain([0, sumValues]) .range([0, width]); - var y = d3.scale.ordinal() - .domain(_.pluck(data, 'x')) - .rangeBands([0, height], 0.3, 0.0); - - var sumY = d3.sum(_.pluck(data, 'y')); - // set up tooltips var tip = d3.tip() .attr('class', 'd3-tip') @@ -31,8 +24,8 @@ module.exports = function(data, g, width, height, options) { return d.tooltip(d, i); } return d.tooltip || tooltipHtml({ - label: d.x, - value: Math.round(d.y / sumY * 100) + label: d.label, + value: Math.round(d.value / sumValues * 100) }); }) .direction('n') @@ -45,11 +38,12 @@ module.exports = function(data, g, width, height, options) { var bar = g.selectAll('.bar') .data(data) .enter().append('g') - .attr('class', 'bar') + .attr('class', 'bar few') .attr('transform', function(d, i) { + var xpos = _.sum(_(data) .slice(0, i) - .pluck('y') + .pluck('value') .value() ); return 'translate(' + x(xpos) + ', ' + (height - barHeight) / 2 + ')'; @@ -60,7 +54,7 @@ module.exports = function(data, g, width, height, options) { .attr('y', 0) .attr('x', 0) .attr('width', function(d) { - return x(d.y); + return x(d.value); }) .attr('height', barHeight) .on('mouseover', tip.show) @@ -72,7 +66,7 @@ module.exports = function(data, g, width, height, options) { .attr('dx', 10) .attr('text-anchor', 'start') .text(function(d) { - return d.x; + return d.label; }) .attr('fill', 'white'); diff --git a/scout-ui/src/minicharts/d3fns/many.js b/scout-ui/src/minicharts/d3fns/many.js index b9db9b00916..69aea6adbf3 100644 --- a/scout-ui/src/minicharts/d3fns/many.js +++ b/scout-ui/src/minicharts/d3fns/many.js @@ -7,21 +7,30 @@ require('d3-tip')(d3); module.exports = function(data, g, width, height, options) { + // if legend present, save some space + var legendWidth = 40; + options = _.defaults(options || {}, { bgbars: false, legend: false, - labels: false // label options will be set further below + labels: false // label defaults will be set further below }); + if (options.legend) { + width = width - legendWidth; + } + var x = d3.scale.ordinal() - .domain(_.pluck(data, 'x')) + .domain(_.pluck(data, 'label')) .rangeBands([0, width], 0.3, 0.0); + var values = _.pluck(data, 'value'); + var y = d3.scale.linear() - .domain([0, d3.max(_.pluck(data, 'y'))]) + .domain([0, d3.max(values)]) .range([height, 0]); - var sumY = d3.sum(_.pluck(data, 'y')); + var sumY = d3.sum(values); // set up tooltips var tip = d3.tip() @@ -31,8 +40,8 @@ module.exports = function(data, g, width, height, options) { return d.tooltip(d, i); } return d.tooltip || tooltipHtml({ - label: d.x, - value: d.y + label: d.label, + value: d.value }); }) .direction('n') @@ -47,42 +56,56 @@ module.exports = function(data, g, width, height, options) { var maxVal = d3.max(y.domain()); var format = d3.format('%.1f'); var legendValues = [format(maxVal), format(maxVal / 2)]; - debug(legendValues); - g.append('text') + // @todo use a scale and wrap both text and line in g element + var legend = g.append('g') + .attr('class', 'legend'); + + legend.append('text') .attr('class', 'legend') .attr('x', width) + .attr('dx', '1em') .attr('y', 0) - .attr('dy', '0.35em') - .attr('text-anchor', 'end') + .attr('dy', '0.3em') + .attr('text-anchor', 'start') .text(d3.max(y.domain()) + '%'); - g.append('text') + legend.append('text') .attr('class', 'legend') .attr('x', width) + .attr('dx', '1em') .attr('y', height / 2) - .attr('dy', '0.35em') - .attr('text-anchor', 'end') + .attr('dy', '0.3em') + .attr('text-anchor', 'start') .text(d3.max(y.domain()) / 2 + '%'); - g.append('line') - .attr('class', 'bg line') + legend.append('text') + .attr('class', 'legend') + .attr('x', width) + .attr('dx', '1em') + .attr('y', height) + .attr('dy', '0.3em') + .attr('text-anchor', 'start') + .text('0%'); + + legend.append('line') + .attr('class', 'bg legend') .attr('x1', 0) - .attr('x2', width) + .attr('x2', width + 5) .attr('y1', 0) .attr('y2', 0); - g.append('line') - .attr('class', 'bg line') + legend.append('line') + .attr('class', 'bg legend') .attr('x1', 0) - .attr('x2', width) + .attr('x2', width + 5) .attr('y1', height / 2) .attr('y2', height / 2); - g.append('line') - .attr('class', 'bg line') + legend.append('line') + .attr('class', 'bg legend') .attr('x1', 0) - .attr('x2', width) + .attr('x2', width + 5) .attr('y1', height) .attr('y2', height); } @@ -93,7 +116,7 @@ module.exports = function(data, g, width, height, options) { .enter().append('g') .attr('class', 'bar') .attr('transform', function(d) { - return 'translate(' + x(d.x) + ', 0)'; + return 'translate(' + x(d.label) + ', 0)'; }); if (options.bgbars) { @@ -107,11 +130,11 @@ module.exports = function(data, g, width, height, options) { .attr('class', 'fg') .attr('x', 0) .attr('y', function(d) { - return y(d.y); + return y(d.value); }) .attr('width', x.rangeBand()) .attr('height', function(d) { - return height - y(d.y); + return height - y(d.value); }); if (options.bgbars) { @@ -144,7 +167,7 @@ module.exports = function(data, g, width, height, options) { return 'middle'; }, 'text': function(d) { - return d.x; + return d.value; } }); diff --git a/scout-ui/src/minicharts/d3fns/number.js b/scout-ui/src/minicharts/d3fns/number.js index b656cc0b1b3..8498b84efea 100644 --- a/scout-ui/src/minicharts/d3fns/number.js +++ b/scout-ui/src/minicharts/d3fns/number.js @@ -40,6 +40,9 @@ module.exports = function(opts) { } else { label = d.x + '-' + (d.x + d.dx); } + // remapping keys to conform with all other types + d.value = d.y; + d.label = label; d.tooltip = tooltipHtml({ label: label, value: Math.round(d.y / sumY * 100) @@ -58,8 +61,8 @@ module.exports = function(opts) { bgbars: false, labels: { text: function(d, i) { - if (i === 0) return d3.min(values); - if (i === data.length - 1) return d3.max(values); + if (i === 0) return 'min: ' + d3.min(values); + if (i === data.length - 1) return 'max: ' + d3.max(values); return ''; } } diff --git a/scout-ui/src/minicharts/d3fns/string.js b/scout-ui/src/minicharts/d3fns/string.js index 936fcaf6b76..e7dc136b14e 100644 --- a/scout-ui/src/minicharts/d3fns/string.js +++ b/scout-ui/src/minicharts/d3fns/string.js @@ -18,18 +18,18 @@ module.exports = function(opts) { var height = opts.height - margin.top - margin.bottom; var el = opts.el; - // group into categories (x) and count (y) the values per bucket, sort descending + // group into labels and values per bucket, sort descending var data = _(values) .groupBy(function(d) { return d; }) .map(function(v, k) { return { - x: k, - y: v.length + label: k, + value: v.length }; }) - .sortByOrder('y', [false]) // descending on y + .sortByOrder('value', [false]) // descending on value .value(); // clear element first diff --git a/scout-ui/src/minicharts/index.js b/scout-ui/src/minicharts/index.js index e1790676a45..72f5fdb4300 100644 --- a/scout-ui/src/minicharts/index.js +++ b/scout-ui/src/minicharts/index.js @@ -20,17 +20,18 @@ module.exports = AmpersandView.extend({ renderMode: 'svg', className: 'minichart', debounceRender: false, - vizFn: vizFns[opts.model._id.toLowerCase()] || null + vizFn: vizFns[opts.model.getId().toLowerCase()] || null }); }, render: function() { - this.renderWithTemplate(); + this.renderWithTemplate(this); // unique values get a div-based minichart if ((this.model._id === 'String') && (_.uniq(this.model.values.toJSON()).length === this.model.count)) { this.viewOptions.renderMode = 'html'; + this.viewOptions.className = 'minichart unique'; this.subview = new UniqueMinichartView(this.viewOptions); } else { this.subview = new VizView(this.viewOptions); diff --git a/scout-ui/src/minicharts/index.less b/scout-ui/src/minicharts/index.less index 3e8e0dd71d8..ac8167f9df2 100644 --- a/scout-ui/src/minicharts/index.less +++ b/scout-ui/src/minicharts/index.less @@ -1,7 +1,7 @@ // minicharts -@mc-bar-bg: #F2F4F5; -@mc-bar-fg: #38A1DF; -@mc-bar-hl: #BEDFF0; +@mc-bg: #F2F4F5; +@mc-fg: #38A1DF; +@mc-hl: #BEDFF0; div.minichart.unique { font-size: 12px; @@ -34,7 +34,7 @@ div.minichart.unique { } svg.minichart { - font: 10px sans-serif; + font-size: 10px; text { fill: #000; @@ -48,26 +48,43 @@ svg.minichart { shape-rendering: crispEdges; rect.bg { - fill: @mc-bar-bg; + fill: @mc-bg; } rect.fg { - fill: @mc-bar-fg; - stroke: @white; + fill: @mc-fg; } rect.hl { - fill: @mc-bar-hl; + fill: @mc-hl; + } + + &.few { + rect.fg { + stroke: white; + stroke-width: 2px; + } + + text { + fill: white; + font-size: 12px; + } } } .line { - stroke: @mc-bar-fg; - shape-rendering: crispEdges; + stroke: @mc-fg; + } + + .legend { + text { + fill: @gray5; + } - &.bg { + line { stroke: @gray6; } + shape-rendering: crispEdges; } .axis path, .axis line { @@ -77,6 +94,10 @@ svg.minichart { } } +.tooltip-wrapper { + line-height: 120%; +} + // -- d3-tip styling .d3-tip { line-height: 1; diff --git a/scout-ui/src/minicharts/minichart.jade b/scout-ui/src/minicharts/minichart.jade index 87400273c16..059be82372b 100644 --- a/scout-ui/src/minicharts/minichart.jade +++ b/scout-ui/src/minicharts/minichart.jade @@ -1 +1 @@ -div(data-hook='minichart', class='minichart unique', style='width:400px; height:100px;') +div(data-hook='minichart', class='minichart-wrapper', style='width:400px; height:100px;') diff --git a/scout-ui/src/minicharts/unique.jade b/scout-ui/src/minicharts/unique.jade index cc424eb6721..28e976c5cc6 100644 --- a/scout-ui/src/minicharts/unique.jade +++ b/scout-ui/src/minicharts/unique.jade @@ -5,8 +5,8 @@ div(data-hook='viz-container') //- = minValue dt - a(href="#") - i.mms-icon-continuous(data-hook='refresh') + //-a(href="#") + i.mms-icon-continuous(data-hook='refresh') dd ul.list-inline each val in randomValues From d8f933f4a98de0a26831143504fd55672a9af1db Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Fri, 22 May 2015 10:24:11 -0400 Subject: [PATCH 4/5] added shared state for minichart + scale in margin --- scout-ui/src/minicharts/d3fns/boolean.js | 8 ++------ scout-ui/src/minicharts/d3fns/date.js | 9 ++------- scout-ui/src/minicharts/d3fns/many.js | 13 ++++++------- scout-ui/src/minicharts/d3fns/number.js | 11 +++-------- scout-ui/src/minicharts/d3fns/shared.js | 10 ++++++++++ scout-ui/src/minicharts/d3fns/string.js | 11 +++-------- scout-ui/src/minicharts/index.less | 4 ++++ scout-ui/src/minicharts/unique.jade | 3 ++- 8 files changed, 32 insertions(+), 37 deletions(-) create mode 100644 scout-ui/src/minicharts/d3fns/shared.js diff --git a/scout-ui/src/minicharts/d3fns/boolean.js b/scout-ui/src/minicharts/d3fns/boolean.js index 907a54e01f6..fb4865ae9c0 100644 --- a/scout-ui/src/minicharts/d3fns/boolean.js +++ b/scout-ui/src/minicharts/d3fns/boolean.js @@ -1,6 +1,7 @@ var d3 = require('d3'); var _ = require('lodash'); var few = require('./few'); +var shared = require('./shared'); var debug = require('debug')('scout-ui:minicharts:boolean'); module.exports = function(opts) { @@ -25,12 +26,7 @@ module.exports = function(opts) { .sortByOrder('label', [false]) // order: false, true .value(); - var margin = { - top: 10, - right: 0, - bottom: 10, - left: 0 - }; + var margin = shared.margin; var width = opts.width - margin.left - margin.right; var height = opts.height - margin.top - margin.bottom; diff --git a/scout-ui/src/minicharts/d3fns/date.js b/scout-ui/src/minicharts/d3fns/date.js index d65d89073a2..3e6235bc0c9 100644 --- a/scout-ui/src/minicharts/d3fns/date.js +++ b/scout-ui/src/minicharts/d3fns/date.js @@ -1,6 +1,7 @@ var d3 = require('d3'); var _ = require('lodash'); var moment = require('moment'); +var shared = require('./shared'); var debug = require('debug')('scout-ui:minicharts:date'); var many = require('./many'); @@ -30,13 +31,7 @@ module.exports = function(opts) { // A formatter for dates var format = d3.time.format('%Y-%m-%d %H:%M:%S'); - var margin = { - top: 10, - right: 0, - bottom: 10, - left: 0 - }; - + var margin = shared.margin; var width = opts.width - margin.left - margin.right; var height = opts.height - margin.top - margin.bottom; var el = opts.el; diff --git a/scout-ui/src/minicharts/d3fns/many.js b/scout-ui/src/minicharts/d3fns/many.js index 69aea6adbf3..d382772bc5d 100644 --- a/scout-ui/src/minicharts/d3fns/many.js +++ b/scout-ui/src/minicharts/d3fns/many.js @@ -8,17 +8,17 @@ require('d3-tip')(d3); module.exports = function(data, g, width, height, options) { // if legend present, save some space - var legendWidth = 40; + var scaleWidth = 40; options = _.defaults(options || {}, { bgbars: false, - legend: false, + scale: false, labels: false // label defaults will be set further below }); - if (options.legend) { - width = width - legendWidth; - } +// if (options.scale) { +// width = width - scaleWidth; +// } var x = d3.scale.ordinal() .domain(_.pluck(data, 'label')) @@ -51,8 +51,7 @@ module.exports = function(data, g, width, height, options) { g.selectAll('*').remove(); g.call(tip); - if (options.legend) { - + if (options.scale) { var maxVal = d3.max(y.domain()); var format = d3.format('%.1f'); var legendValues = [format(maxVal), format(maxVal / 2)]; diff --git a/scout-ui/src/minicharts/d3fns/number.js b/scout-ui/src/minicharts/d3fns/number.js index 8498b84efea..3acad57a3c8 100644 --- a/scout-ui/src/minicharts/d3fns/number.js +++ b/scout-ui/src/minicharts/d3fns/number.js @@ -1,19 +1,14 @@ var d3 = require('d3'); var _ = require('lodash'); var many = require('./many'); +var shared = require('./shared'); var tooltipHtml = require('./tooltip.jade'); var debug = require('debug')('scout-ui:minicharts:number'); module.exports = function(opts) { var values = opts.data.values.toJSON(); - var margin = { - top: 10, - right: 0, - bottom: 10, - left: 0 - }; - + var margin = shared.margin; var width = opts.width - margin.left - margin.right; var height = opts.height - margin.top - margin.bottom; var el = opts.el; @@ -57,7 +52,7 @@ module.exports = function(opts) { .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); many(data, g, width, height - 10, { - legend: true, + scale: true, bgbars: false, labels: { text: function(d, i) { diff --git a/scout-ui/src/minicharts/d3fns/shared.js b/scout-ui/src/minicharts/d3fns/shared.js new file mode 100644 index 00000000000..d335d3a8e6c --- /dev/null +++ b/scout-ui/src/minicharts/d3fns/shared.js @@ -0,0 +1,10 @@ +module.exports = { + + margin: { + top: 10, + right: 40, + bottom: 10, + left: 0 + }, + +}; diff --git a/scout-ui/src/minicharts/d3fns/string.js b/scout-ui/src/minicharts/d3fns/string.js index e7dc136b14e..56b349abf97 100644 --- a/scout-ui/src/minicharts/d3fns/string.js +++ b/scout-ui/src/minicharts/d3fns/string.js @@ -3,17 +3,12 @@ var _ = require('lodash'); var debug = require('debug')('scout-ui:minicharts:string'); var few = require('./few'); var many = require('./many'); +var shared = require('./shared'); module.exports = function(opts) { var values = opts.data.values.toJSON(); - var margin = { - top: 10, - right: 0, - bottom: 10, - left: 0 - }; - + var margin = shared.margin; var width = opts.width - margin.left - margin.right; var height = opts.height - margin.top - margin.bottom; var el = opts.el; @@ -43,7 +38,7 @@ module.exports = function(opts) { var chart = data.length <= 5 ? few : many; chart(data, g, width, height, { - legend: true, + scale: true, bglines: true }); }; diff --git a/scout-ui/src/minicharts/index.less b/scout-ui/src/minicharts/index.less index ac8167f9df2..d4b0f77f348 100644 --- a/scout-ui/src/minicharts/index.less +++ b/scout-ui/src/minicharts/index.less @@ -29,6 +29,10 @@ div.minichart.unique { dd { margin-left: 30px; overflow: hidden; + + ul li { + margin-bottom: 6px; + } } } } diff --git a/scout-ui/src/minicharts/unique.jade b/scout-ui/src/minicharts/unique.jade index 28e976c5cc6..22339f32833 100644 --- a/scout-ui/src/minicharts/unique.jade +++ b/scout-ui/src/minicharts/unique.jade @@ -10,7 +10,8 @@ div(data-hook='viz-container') dd ul.list-inline each val in randomValues - li= val + li + code= val //- dt max //- dd From e735c9544fc1978cf2c86ba9f1e2fa31fbf02d48 Mon Sep 17 00:00:00 2001 From: Marc Schaffner-Gurney Date: Fri, 22 May 2015 12:02:52 -0400 Subject: [PATCH 5/5] styling tweaks --- scout-style/10strap.less | 3 ++- scout-ui/src/minicharts/index.less | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/scout-style/10strap.less b/scout-style/10strap.less index 2ff784eccb0..4c6d3cc9948 100644 --- a/scout-style/10strap.less +++ b/scout-style/10strap.less @@ -54,7 +54,8 @@ hr.inverse { } code { - color: @purple2; + font-family: Menlo, monospace; + color: @gray1; background-color: @gray8; } diff --git a/scout-ui/src/minicharts/index.less b/scout-ui/src/minicharts/index.less index d4b0f77f348..5cc01696ce3 100644 --- a/scout-ui/src/minicharts/index.less +++ b/scout-ui/src/minicharts/index.less @@ -25,6 +25,14 @@ div.minichart.unique { color: @gray1; } } + i.mms-icon-continuous { // remove after wrapping this in an again + color: @gray5; + cursor: pointer; + + &:hover { + color: @gray1; + } + } } dd { margin-left: 30px; @@ -32,6 +40,11 @@ div.minichart.unique { ul li { margin-bottom: 6px; + + code { + font-size: 12px; + line-height: 20px; + } } } } @@ -41,7 +54,8 @@ svg.minichart { font-size: 10px; text { - fill: #000; + fill: @gray4; + font-weight: bold; } .glass { @@ -86,7 +100,7 @@ svg.minichart { } line { - stroke: @gray6; + stroke: @gray7; } shape-rendering: crispEdges; }