1818 * @param {boolean= } showLegend flag to show the legend, defaults to true
1919 * @param {array= } legendLabels the labels for the legend - defaults: ['< 70%', '70-80%' ,'80-90%', '> 90%']
2020 * @param {number= } maxBlockSize the maximum size for blocks in the heatmap. Default: 50, Range: 5 - 50
21+ * @param {number= } minBlockSize the minimum size for blocks in the heatmap. Default: 2
2122 * @param {number= } blockPadding the padding in pixels between blocks (default: 2)
2223 * @param {array= } thresholds the threshold values for the heapmap - defaults: [0.7, 0.8, 0.9]
2324 * @param {array= } heatmapColorPattern the colors that correspond to the various threshold values (lowest to hightest value ex: <70& to >90%) - defaults: ['#d4f0fa', '#F9D67A', '#EC7A08', '#CE0000']
2425 * @param {function= } clickAction function(block) function to call when a block is clicked on
26+ * @param {number= } rangeHoverSize the maximum size for highlighting blocks in the same range. Default: 15
27+ * @param {boolean= } rangeOnHover flag to highlight blocks in the same range on hover, defaults to true
28+ * @param {array= } rangeTooltips the tooltips for blocks in the same range - defaults: ['< 70%', '70-80%' ,'80-90%', '> 90%']
2529 * @example
2630 <example module="patternfly.charts">
2731 <file name="index.html">
3135 <div pf-heatmap id="id" chart-title="title" data="data" chart-data-available="dataAvailable"
3236 show-legend="showLegends"></div>
3337 </div>
34- <div class="col-md-5 example-heatmap-container">
38+ <div class="col-md-3 example-heatmap-container">
3539 <div pf-heatmap id="id" chart-title="titleAlt" data="data" chart-data-available="dataAvailable"
3640 show-legend="showLegends" legend-labels="legendLabels" max-block-size="20" block-padding="5"
3741 heatmap-color-pattern="heatmapColorPattern" thresholds="thresholds"
3842 click-action="clickAction"></div>
3943 </div>
44+ <div class="col-md-3 example-heatmap-container">
45+ <div pf-heatmap id="id" chart-title="titleSmall" data="data" chart-data-available="dataAvailable"
46+ show-legend="showLegends" max-block-size="15" range-tooltips="rangeTooltips"></div>
47+ </div>
4048 </div>
4149 <div class="row">
4250 <div class="col-md-3">
119127 $scope.dataAvailable = true;
120128 $scope.title = 'Utilization - Using Defaults';
121129 $scope.titleAlt = 'Utilization - Overriding Defaults';
130+ $scope.titleSmall = 'Utilization - Small Blocks';
122131 $scope.legendLabels = ['< 60%','70%', '70-80%' ,'80-90%', '> 90%'];
132+ $scope.rangeTooltips = ['Memory Utilization < 70%<br\>40 Nodes', 'Memory Utilization 70-80%<br\>4 Nodes', 'Memory Utilization 80-90%<br\>4 Nodes', 'Memory Utilization > 90%<br\>4 Nodes'];
123133 $scope.thresholds = [0.6, 0.7, 0.8, 0.9];
124134 $scope.heatmapColorPattern = ['#d4f0fa', '#F9D67A', '#EC7A08', '#CE0000', '#f00'];
125135
@@ -144,16 +154,21 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
144154 showLegend : '=?' ,
145155 legendLabels : '=?' ,
146156 maxBlockSize : '@' ,
157+ minBlockSize : '@' ,
147158 blockPadding : '@' ,
148159 thresholds : '=?' ,
149160 heatmapColorPattern : '=?' ,
150- clickAction : '=?'
161+ clickAction : '=?' ,
162+ rangeOnHover : '=?' ,
163+ rangeHoverSize : '@' ,
164+ rangeTooltips : '=?'
151165 } ,
152166 templateUrl : 'charts/heatmap/heatmap.html' ,
153167 controller : function ( $scope ) {
154168 var thresholdDefaults = [ 0.7 , 0.8 , 0.9 ] ;
155169 var heatmapColorPatternDefaults = [ '#d4f0fa' , '#F9D67A' , '#EC7A08' , '#CE0000' ] ;
156170 var legendLabelDefaults = [ '< 70%' , '70-80%' , '80-90%' , '> 90%' ] ;
171+ var rangeTooltipDefaults = [ '< 70%' , '70-80%' , '80-90%' , '> 90%' ] ;
157172 var heightDefault = 200 ;
158173
159174 //Allow overriding of defaults
@@ -168,12 +183,30 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
168183 }
169184 }
170185
186+ if ( $scope . minBlockSize === undefined || isNaN ( $scope . minBlockSize ) ) {
187+ $scope . minSize = 2 ;
188+ } else {
189+ $scope . minSize = parseInt ( $scope . minBlockSize ) ;
190+ }
191+
171192 if ( $scope . blockPadding === undefined || isNaN ( $scope . blockPadding ) ) {
172193 $scope . padding = 2 ;
173194 } else {
174195 $scope . padding = parseInt ( $scope . blockPadding ) ;
175196 }
176197
198+ if ( $scope . rangeHoverSize === undefined || isNaN ( $scope . rangeHoverSize ) ) {
199+ $scope . rangeHoverSize = 15 ;
200+ } else {
201+ $scope . rangeHoverSize = parseInt ( $scope . rangeHoverSize ) ;
202+ }
203+
204+ $scope . rangeOnHover = ( $scope . rangeOnHover === undefined || $scope . rangeOnHover ) ? true : false ;
205+
206+ if ( ! $scope . rangeTooltips ) {
207+ $scope . rangeTooltips = rangeTooltipDefaults ;
208+ }
209+
177210 if ( ! $scope . thresholds ) {
178211 $scope . thresholds = thresholdDefaults ;
179212 }
@@ -215,7 +248,15 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
215248 blockSize * numberOfRows > containerHeight ) {
216249 numberOfRows = ( blockSize === 0 ) ? 0 : Math . floor ( containerHeight / blockSize ) ;
217250 }
251+ } else if ( ( blockSize - scope . padding ) < scope . minSize ) {
252+ blockSize = scope . padding + scope . minSize ;
218253
254+ // Attempt to square off the area, check if square fits
255+ numberOfRows = Math . ceil ( Math . sqrt ( scope . data . length ) ) ;
256+ if ( blockSize * numberOfRows > containerWidth ||
257+ blockSize * numberOfRows > containerHeight ) {
258+ numberOfRows = ( blockSize === 0 ) ? 0 : Math . floor ( containerHeight / blockSize ) ;
259+ }
219260 } else {
220261 numberOfRows = ( blockSize === 0 ) ? 0 : Math . floor ( containerHeight / blockSize ) ;
221262 }
@@ -246,14 +287,25 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
246287 var redraw = function ( ) {
247288 var data = scope . data ;
248289 var color = d3 . scale . threshold ( ) . domain ( scope . thresholds ) . range ( scope . heatmapColorPattern ) ;
290+ var rangeTooltip = d3 . scale . threshold ( ) . domain ( scope . thresholds ) . range ( scope . rangeTooltips ) ;
249291 var blocks ;
250292 var fillSize = blockSize - scope . padding ;
251293 var highlightBlock = function ( block , active ) {
252294 block . style ( 'fill-opacity' , active ? 1 : 0.4 ) ;
253295 } ;
296+ var highlightBlockColor = function ( block , fillColor ) {
297+ // Get fill color from given block
298+ var blockColor = color ( block . map ( function ( d ) {
299+ return d [ 0 ] . __data__ . value ;
300+ } ) ) ;
301+ // If given color matches, apply highlight
302+ if ( blockColor === fillColor ) {
303+ block . style ( 'fill-opacity' , 1 ) ;
304+ }
305+ } ;
306+
254307 var svg = window . d3 . select ( thisComponent ) ;
255308 svg . selectAll ( '*' ) . remove ( ) ;
256-
257309 blocks = svg . selectAll ( 'rect' ) . data ( data ) . enter ( ) . append ( 'rect' ) ;
258310 blocks . attr ( 'x' , function ( d , i ) {
259311 return Math . floor ( i / numberOfRows ) * blockSize ;
@@ -262,6 +314,9 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
262314 } ) . attr ( 'width' , fillSize ) . attr ( 'height' , fillSize ) . style ( 'fill' , function ( d ) {
263315 return color ( d . value ) ;
264316 } ) . attr ( 'tooltip-html-unsafe' , function ( d , i ) { //tooltip-html is throwing an exception
317+ if ( scope . rangeOnHover && fillSize <= scope . rangeHoverSize ) {
318+ return rangeTooltip ( d . value ) ;
319+ }
265320 return d . tooltip ;
266321 } ) . attr ( 'tooltip-append-to-body' , function ( d , i ) {
267322 return true ;
@@ -271,8 +326,20 @@ angular.module('patternfly.charts').directive('pfHeatmap', function ($compile, $
271326
272327 //Adding events
273328 blocks . on ( 'mouseover' , function ( ) {
329+ var fillColor ;
274330 blocks . call ( highlightBlock , false ) ;
275- d3 . select ( this ) . call ( highlightBlock , true ) ;
331+ if ( scope . rangeOnHover && fillSize <= scope . rangeHoverSize ) {
332+ // Get fill color for current block
333+ fillColor = color ( d3 . select ( this ) . map ( function ( d ) {
334+ return d [ 0 ] . __data__ . value ;
335+ } ) ) ;
336+ // Highlight all blocks matching fill color
337+ blocks [ 0 ] . forEach ( function ( block ) {
338+ highlightBlockColor ( d3 . select ( block ) , fillColor ) ;
339+ } ) ;
340+ } else {
341+ d3 . select ( this ) . call ( highlightBlock , true ) ;
342+ }
276343 } ) ;
277344 blocks . on ( 'click' , function ( d ) {
278345 if ( scope . clickAction ) {
0 commit comments