diff --git a/public/heatmap.html b/public/heatmap.html index 1ee986c..0d81b32 100644 --- a/public/heatmap.html +++ b/public/heatmap.html @@ -1,3 +1,4 @@
- + +
diff --git a/public/heatmap.js b/public/heatmap.js index c883c59..a1d2a7f 100644 --- a/public/heatmap.js +++ b/public/heatmap.js @@ -1,11 +1,13 @@ require('plugins/heatmap/heatmap.less'); +require('plugins/heatmap/heatmap_tooltip.css'); require('plugins/heatmap/color_directive.js'); require('plugins/heatmap/lib/heatmap_controller.js'); require('plugins/heatmap/lib/heatmap_directive.js'); +require('plugins/heatmap/heatmap_tooltip_directive.js'); function HeatmapProvider(Private) { - var TemplateVisType = Private(require('ui/template_vis_type/TemplateVisType')); - var Schemas = Private(require('ui/Vis/Schemas')); + var TemplateVisType = Private(require('ui/template_vis_type/template_vis_type')); + var Schemas = Private(require('ui/vis/schemas')); var colors = require('plugins/heatmap/colors.js'); return new TemplateVisType({ diff --git a/public/heatmap_tooltip.css b/public/heatmap_tooltip.css new file mode 100644 index 0000000..3261b8d --- /dev/null +++ b/public/heatmap_tooltip.css @@ -0,0 +1,25 @@ +.heatmap-tooltip { + position: absolute; + width: auto; + fadeout(@gray-darker, 7%); + gray-darker: #222222; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); + -mox-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); + box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); + pointer-events: none; + white-space: nowrap; +} + +.heatmap-tooltip-list { + padding: 0; + list-style-type: none; +} +.heatmap-tooltip-list span{ + font-size: 12px; +} +.heatmap-tooltip-list span.key{ + font-weight: bold; +} \ No newline at end of file diff --git a/public/heatmap_tooltip.html b/public/heatmap_tooltip.html new file mode 100644 index 0000000..1e5fa00 --- /dev/null +++ b/public/heatmap_tooltip.html @@ -0,0 +1,8 @@ +
+ +
\ No newline at end of file diff --git a/public/heatmap_tooltip_directive.js b/public/heatmap_tooltip_directive.js new file mode 100644 index 0000000..6c95457 --- /dev/null +++ b/public/heatmap_tooltip_directive.js @@ -0,0 +1,58 @@ +var d3 = require("d3"); +var _ = require("lodash"); +var module = require('ui/modules').get('heatmap'); + +module.directive('tooltip', function () { + + function controller($scope) { + $scope.isShown = false; + /* + * Make sure that the items array is populated before tooltip is shown. + * The items variable is an array of objects, e.g. + * [ + * { key: "Column", value: "Tuesday" }, + * { key: "Row", value: "12pm" }, + * { key: "Count", value: 12 } + * ] + */ + this.showOnHover = function () { + $scope.isShown = !!($scope.items && _.isArray($scope.items) && $scope.items.length); + }; + + this.hideOnOut = function(){ + $scope.isShown = false; + }; + } + + function link(scope, element, attrs, ctrl) { + function render($scope) { + d3.select(_.first(element)) + .style("top", $scope.top + "px") + .style("left", $scope.left + "px"); + + ctrl.showOnHover(); + } + + scope.$watchGroup(["top", "left", "items"], function (newVal, oldVal, scope) { + render(scope); + }, 250); + + scope.$watch("ngShow", function (newVal) { + ctrl.hideOnOut(); + }); + }; + + return { + restrict: "E", + scope: { + top: "=", + left: "=", + items: "=", + ngShow: "=" + }, + replace: true, + controller: controller, + link: link, + template: require("plugins/heatmap/heatmap_tooltip.html") + }; +}); \ No newline at end of file diff --git a/public/lib/heatmap_controller.js b/public/lib/heatmap_controller.js index a7aa6db..074f1de 100644 --- a/public/lib/heatmap_controller.js +++ b/public/lib/heatmap_controller.js @@ -47,7 +47,7 @@ module.controller('HeatmapController', function ($scope, Private) { $scope.data = null; return; } - + // Add row, column, and metric titles as vis parameters _.merge($scope.vis.params, { rowAxis: { title: getLabel($scope.vis.aggs, 'rows') }, @@ -58,5 +58,53 @@ module.controller('HeatmapController', function ($scope, Private) { $scope.data = [{ cells: processTableGroups(tabifyAggResponse($scope.vis, resp), $scope) }]; + + $scope.eventListeners = { + mouseover: [ mouseover ], + mouseout: [ mouseout ] + }; + + function mouseover(event) { + var target = d3.select(event.target); + var isHeatmapCell = (target.attr("class") === "cell"); + var OFFSET = 50; + + if (isHeatmapCell) { + // get data bound to heatmap cell + var d = _.first(target.data()); + // Custom code for tooltip functionality goes here + $scope.$apply(function () { + var params = $scope.vis.params; + $scope.tooltipItems = Object.keys(d) + .filter(function (key) { return key !== "data"; }) + .map(function (key) { + + var title = d3.selectAll('text.title'); + var value = d[key]; + if (key.toUpperCase() === 'ROW') { + key = params.columnAxis.title || 'ROW'; + } + if (key.toUpperCase() === 'COL') { + key = params.rowAxis.title || 'COL'; + } + return { + key: key.toUpperCase(), + value: value + }; + }); + + $scope.top = d.data.row + parseInt(params.margin.top) + OFFSET; + $scope.left = d.data.col + parseInt(params.margin.left) + OFFSET; + }); + } + }; + + function mouseout(event){ + $scope.$apply(function () { + $scope.tooltipItems = []; + $scope.top = 0; + $scope.left = 0; + }); + } }); }); diff --git a/public/tooltip_directive.js b/public/tooltip_directive.js new file mode 100644 index 0000000..b09a538 --- /dev/null +++ b/public/tooltip_directive.js @@ -0,0 +1,51 @@ +var _ = require("lodash"); +var module = require('ui/modules').get('heatmap'); + +module.directive('tooltip', function () { + debugger; + function controller($scope) { + $scope.isShown = false; + debugger; + /* + * Make sure that the items array is populated before tooltip is shown. + * The items variable is an array of objects, e.g. + * [ + * { key: "Column", value: "Tuesday" }, + * { key: "Row", value: "12pm" }, + * { key: "Count", value: 12 } + * ] + */ + this.showOnHover = function () { + $scope.isShown = !!($scope.items && _.isArray($scope.items) && $scope.items.length); + }; + } + + function link(scope, element, attrs, ctrl) { + function render($scope) { + debugger; + d3.select(_.first(element)) + .style("top", $scope.top + "px") + .style("left", $scope.left + "px"); + + ctrl.showOnHover(); + } + + scope.$watchGroup(["top", "left", "items"], function (newVal, oldVal, scope) { + debugger; + render(scope); + }, 250); + } + + return { + restrict: "E", + scope: { + top: "=", + left: "=", + items: "=" + }, + replace: true, + controller: controller, + link: link, + template: require("plugins/heatmap/heatmap_tooltip.html") + }; +}); \ No newline at end of file diff --git a/public/vis/components/axis/axis.js b/public/vis/components/axis/axis.js index db2f5ac..98794ea 100644 --- a/public/vis/components/axis/axis.js +++ b/public/vis/components/axis/axis.js @@ -58,7 +58,7 @@ function axes() { var text = g.selectAll('text.title') .data([data]); - + text.exit().remove(); text.enter().append('text') .attr('class', title.class || 'title'); diff --git a/public/vis/components/control/events.js b/public/vis/components/control/events.js index dfd5ecf..c3b1ce4 100644 --- a/public/vis/components/control/events.js +++ b/public/vis/components/control/events.js @@ -14,9 +14,8 @@ function events() { } d3.entries(listeners).forEach(function (d) { - svg.on(d.key, function () { + element.on(d.key, function () { d3.event.stopPropagation(); // => event.stopPropagation() - _.forEach(d.value, function (listener) { listener.call(this, processor(d3.event)); }); diff --git a/public/vis/components/elements/text.js b/public/vis/components/elements/text.js index 63bcbc9..0500c15 100644 --- a/public/vis/components/elements/text.js +++ b/public/vis/components/elements/text.js @@ -15,7 +15,7 @@ function text() { selection.each(function (data) { var text = d3.select(this).selectAll('text.' + cssClass) .data(data); - + text.exit().remove(); text.enter().append('text') diff --git a/public/vis/index.js b/public/vis/index.js index 44be53d..e78df43 100644 --- a/public/vis/index.js +++ b/public/vis/index.js @@ -15,7 +15,7 @@ function vis() { function generator(selection) { selection.each(function (data) { events.listeners(listeners); - + layout.attr({ type: opts.layout || 'grid', columns: opts.numOfColumns || 0,