@@ -1075,6 +1075,254 @@ angular.module('patternfly.charts').directive('pfDonutPctChart', ["c3ChartDefaul
10751075 }
10761076 } ;
10771077} ] ) ;
1078+ ; angular . module ( 'patternfly.charts' ) . directive ( 'pfHeatmapLegend' ,
1079+ function ( ) {
1080+ 'use strict' ;
1081+ return {
1082+ restrict : 'A' ,
1083+ scope : {
1084+ legend : '=' ,
1085+ legendColors : '='
1086+ } ,
1087+ replace : true ,
1088+ templateUrl : 'charts/heatmap/heatmap-legend.html' ,
1089+ link : function ( $scope ) {
1090+ var items = [ ] ;
1091+ var index ;
1092+ for ( index = $scope . legend . length - 1 ; index >= 0 ; index -- ) {
1093+ items . push ( {
1094+ text : $scope . legend [ index ] ,
1095+ color : $scope . legendColors [ index ]
1096+ } ) ;
1097+ }
1098+ $scope . legendItems = items ;
1099+ }
1100+ } ;
1101+ }
1102+ ) ;
1103+ ; /**
1104+ * @ngdoc directive
1105+ * @name patternfly.charts.directive:pfHeatMap
1106+ *
1107+ * @description
1108+ * Directive for rendering a heatmap chart.
1109+ *
1110+ * @param {object } data data for the chart:<br/>
1111+ * <ul style='list-style-type: none'>
1112+ * <li>.id - the id of the measurement
1113+ * <li>.value - the value of the measurement
1114+ * <li>.tooltip - message to be displayed on hover
1115+ * </ul>
1116+ *
1117+ * @param {string= } height height of the chart (no units) - default: "200"
1118+ * @param {string= } chartTitle title of the chart
1119+ * @param {array= } legendLabels the labels for the legend - defaults: ['< 70%', '70-80%' ,'80-90%', '> 90%']
1120+ * @param {array= } thresholds the threshold values for the heapmap - defaults: [0.7, 0.8, 0.9]
1121+ * @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']
1122+ * @param {function= } clickAction function(block) function to call when a block is clicked on
1123+ * @example
1124+ <example module="patternfly.charts">
1125+ <file name="script.js">
1126+ angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope) {
1127+ $scope.title = 'Utilization - Using Defaults';
1128+ $scope.data = [
1129+ {'id': 9,'value': 0.96,'tooltip': 'Node 8 : My OpenShift Provider<br\>96% : 96 Used of 100 Total<br\>4 Available'},
1130+ {'id': 44, 'value': 0.94, 'tooltip': 'Node 19 : My Kubernetes Provider<br\>94% : 94 Used of 100 Total<br\>6 Available'},
1131+ {'id': 0, 'value': 0.91, 'tooltip': 'Node 9 : My OpenShift Provider<br\>91% : 91 Used of 100 Total<br\>9 Available'},
1132+ {'id': 43, 'value': 0.9, 'tooltip': 'Node 18 : My Kubernetes Provider<br\>90% : 90 Used of 100 Total<br\>10 Available'},
1133+ {'id': 7, 'value': 0.89, 'tooltip': 'Node 12 : My OpenShift Provider<br\>89% : 89 Used of 100 Total<br\>11 Available'},
1134+ {'id': 41, 'value': 0.82, 'tooltip': 'Node 16 : My Kubernetes Provider<br\>82% : 82 Used of 100 Total<br\>18 Available'},
1135+ {'id': 21, 'value': 0.81, 'tooltip': 'Node 21 : My OpenShift Provider<br\>81% : 81 Used of 100 Total<br\>19 Available'},
1136+ {'id': 26, 'value': 0.8, 'tooltip': 'Node 1 : My Kubernetes Provider<br\>80% : 80 Used of 100 Total<br\>20 Available'},
1137+ {'id': 48, 'value': 0.74, 'tooltip': 'Node 23 : My Kubernetes Provider<br\>74% : 74 Used of 100 Total<br\>26 Available'},
1138+ {'id': 27, 'value': 0.72, 'tooltip': 'Node 2 : My Kubernetes Provider<br\>72% : 72 Used of 100 Total<br\>28 Available'},
1139+ {'id': 42, 'value': 0.71, 'tooltip': 'Node 17 : My Kubernetes Provider<br\>71% : 71 Used of 100 Total<br\>29 Available'},
1140+ {'id': 23, 'value': 0.71, 'tooltip': 'Node 23 : My OpenShift Provider<br\>71% : 71 Used of 100 Total<br\>29 Available'},
1141+ {'id': 22, 'value': 0.69, 'tooltip': 'Node 22 : My OpenShift Provider<br\>69% : 69 Used of 100 Total<br\>31 Available'},
1142+ {'id': 2, 'value': 0.66, 'tooltip': 'Node 2 : M8y OpenShift Provider<br\>66% : 66 Used of 100 Total<br\>34 Available'},
1143+ {'id': 39, 'value': 0.66, 'tooltip': 'Node 14 : My Kubernetes Provider<br\>66% : 66 Used of 100 Total<br\>34 Available'},
1144+ {'id': 3, 'value': 0.65, 'tooltip': 'Node 39 : My OpenShift Provider<br\>65% : 65 Used of 100 Total<br\>35 Available'},
1145+ {'id': 29, 'value': 0.65, 'tooltip': 'Node 4 : My Kubernetes Provider<br\>65% : 65 Used of 100 Total<br\>35 Available'},
1146+ {'id': 32, 'value': 0.56, 'tooltip': 'Node 7 : My Kubernetes Provider<br\>56% : 56 Used of 100 Total<br\>44 Available'},
1147+ {'id': 13, 'value': 0.56, 'tooltip': 'Node 13 : My OpenShift Provider<br\>56% : 56 Used of 100 Total<br\>44 Available'},
1148+ {'id': 49, 'value': 0.52, 'tooltip': 'Node 24 : My Kubernetes Provider<br\>52% : 52 Used of 100 Total<br\>48 Available'},
1149+ {'id': 36, 'value': 0.5, 'tooltip': 'Node 11 : My Kubernetes Provider<br\>50% : 50 Used of 100 Total<br\>50 Available'},
1150+ {'id': 6, 'value': 0.5, 'tooltip': 'Node 5 : My OpenShift Provider<br\>50% : 50 Used of 100 Total<br\>50 Available'},
1151+ {'id': 38, 'value': 0.49, 'tooltip': 'Node 13 : My Kubernetes Provider<br\>49% : 49 Used of 100 Total<br\>51 Available'},
1152+ {'id': 15, 'value': 0.48, 'tooltip': 'Node 15 : My OpenShift Provider<br\>48% : 48 Used of 100 Total<br\>52 Available'},
1153+ {'id': 30, 'value': 0.48, 'tooltip': 'Node 5 : My Kubernetes Provider<br\>48% : 48 Used of 100 Total<br\>52 Available'},
1154+ {'id': 11, 'value': 0.47, 'tooltip': 'Node 11 : My OpenShift Provider<br\>47% : 47 Used of 100 Total<br\>53 Available'},
1155+ {'id': 17, 'value': 0.46, 'tooltip': 'Node 17 : My OpenShift Provider<br\>46% : 46 Used of 100 Total<br\>54 Available'},
1156+ {'id': 25, 'value': 0.45, 'tooltip': 'Node 0 : My Kubernetes Provider<br\>45% : 45 Used of 100 Total<br\>55 Available'},
1157+ {'id': 50, 'value': 0.45, 'tooltip': 'Node 25 : My Kubernetes Provider<br\>45% : 45 Used of 100 Total<br\>55 Available'},
1158+ {'id': 46, 'value': 0.45, 'tooltip': 'Node 21 : My Kubernetes Provider<br\>45% : 45 Used of 100 Total<br\>55 Available'},
1159+ {'id': 47, 'value': 0.45, 'tooltip': 'Node 22 : My Kubernetes Provider<br\>45% : 45 Used of 100 Total<br\>55 Available'},
1160+ {'id': 1, 'value': 0.44, 'tooltip': 'Node 1 : My OpenShift Provider<br\>44% : 44 Used of 100 Total<br\>56 Available'},
1161+ {'id': 31, 'value': 0.44, 'tooltip': 'Node 6 : My Kubernetes Provider<br\>44% : 44 Used of 100 Total<br\>56 Available'},
1162+ {'id': 37, 'value': 0.44, 'tooltip': 'Node 12 : My Kubernetes Provider<br\>44% : 44 Used of 100 Total<br\>56 Available'},
1163+ {'id': 24, 'value': 0.44, 'tooltip': 'Node 24 : My OpenShift Provider<br\>44% : 44 Used of 100 Total<br\>56 Available'},
1164+ {'id': 40, 'value': 0.43, 'tooltip': 'Node 40 : My Kubernetes Provider<br\>43% : 43 Used of 100 Total<br\>57 Available'},
1165+ {'id': 20, 'value': 0.39, 'tooltip': 'Node 20 : My OpenShift Provider<br\>39% : 39 Used of 100 Total<br\>61 Available'},
1166+ {'id': 8, 'value': 0.39, 'tooltip': 'Node 8 : My OpenShift Provider<br\>39% : 39 Used of 100 Total<br\>61 Available'},
1167+ {'id': 5, 'value': 0.38, 'tooltip': 'Node 5 : My OpenShift Provider<br\>38% : 38 Used of 100 Total<br\>62 Available'},
1168+ {'id': 45, 'value': 0.37, 'tooltip': 'Node 20 : My Kubernetes Provider<br\>37% : 37 Used of 100 Total<br\>63 Available'},
1169+ {'id': 12, 'value': 0.37, 'tooltip': 'Node 12 : My OpenShift Provider<br\>37% : 37 Used of 100 Total<br\>63 Available'},
1170+ {'id': 34, 'value': 0.37, 'tooltip': 'Node 9 : My Kubernetes Provider<br\>37% : 37 Used of 100 Total<br\>63 Available'},
1171+ {'id': 33, 'value': 0.33, 'tooltip': 'Node 8 : My Kubernetes Provider<br\>33% : 33 Used of 100 Total<br\>67 Available'},
1172+ {'id': 16, 'value': 0.32, 'tooltip': 'Node 16 : My OpenShift Provider<br\>32% : 32 Used of 100 Total<br\>68 Available'},
1173+ {'id': 10, 'value': 0.29, 'tooltip': 'Node 10 : My OpenShift Provider<br\>28% : 29 Used of 100 Total<br\>71 Available'},
1174+ {'id': 35, 'value': 0.28, 'tooltip': 'Node 35 : My Kubernetes Provider<br\>28% : 28 Used of 100 Total<br\>72 Available'},
1175+ {'id': 18, 'value': 0.27, 'tooltip': 'Node 18 : My OpenShift Provider<br\>27% : 27 Used of 100 Total<br\>73 Available'},
1176+ {'id': 4, 'value': 0.26, 'tooltip': 'Node 4 : My OpenShift Provider<br\>26% : 26 Used of 100 Total<br\>74 Available'},
1177+ {'id': 19, 'value': 0.25, 'tooltip': 'Node 19 : My OpenShift Provider<br\>25% : 25 Used of 100 Total<br\>75 Available'},
1178+ {'id': 28, 'value': 0.25, 'tooltip': 'Node 3 : My Kubernetes Provider<br\>25% : 25 Used of 100 Total<br\>75 Available'},
1179+ {'id': 51, 'value': 0.22, 'tooltip': 'Node 26 : My Kubernetes Provider<br\>22% : 22 Used of 100 Total<br\>78 Available'},
1180+ {'id': 14, 'value': 0.2, 'tooltip': 'Node 14 : My OpenShift Provider<br\>20% : 20 Used of 100 Total<br\>80 Available'}];
1181+
1182+ $scope.titleAlt = 'Utilization - Overriding Defaults';
1183+ $scope.legendLabels = ['< 60%','70%', '70-80%' ,'80-90%', '> 90%'];
1184+ $scope.thresholds = [0.6, 0.7, 0.8, 0.9];
1185+ $scope.heatmapColorPattern = ['#d4f0fa', '#F9D67A', '#EC7A08', '#CE0000', '#f00'];
1186+
1187+ var clickAction = function (block) {
1188+ console.log(block);
1189+ };
1190+ $scope.clickAction = clickAction;
1191+ });
1192+ </file>
1193+ <file name="index.html">
1194+ <div ng-controller="ChartCtrl" class="row">
1195+ <div class="col-md-4">
1196+ <div pf-heatmap id="id" chart-title="title" data="data"></div>
1197+ </div>
1198+ <div class="col-md-4">
1199+ <div pf-heatmap id="id" chart-title="titleAlt" data="data" legend-labels="legendLabels" heatmap-color-pattern="heatmapColorPattern" thresholds="thresholds" click-action="clickAction"></div>
1200+ </div>
1201+ </div>
1202+ </file>
1203+ </example>
1204+ */
1205+ angular . module ( 'patternfly.charts' ) . directive ( 'pfHeatmap' , [ "$compile" , function ( $compile ) {
1206+ 'use strict' ;
1207+ return {
1208+ restrict : 'A' ,
1209+ scope : {
1210+ data : '=' ,
1211+ height : '=' ,
1212+ chartTitle : '=?' ,
1213+ legendLabels : '=?' ,
1214+ thresholds : '=?' ,
1215+ heatmapColorPattern : '=?' ,
1216+ clickAction : '=?'
1217+ } ,
1218+ replace : true ,
1219+ templateUrl : 'charts/heatmap/heatmap.html' ,
1220+ link : function ( scope , element , attrs ) {
1221+ var thisComponent = element [ 0 ] . querySelector ( '.pf-heatmap-svg' ) ;
1222+ var containerWidth , containerHeight , blockSize , numberOfRows ;
1223+ var thresholdDefaults = [ 0.7 , 0.8 , 0.9 ] ;
1224+ var heatmapColorPatternDefaults = [ '#d4f0fa' , '#F9D67A' , '#EC7A08' , '#CE0000' ] ;
1225+ var legendLabelDefaults = [ '< 70%' , '70-80%' , '80-90%' , '> 90%' ] ;
1226+ var heightDefault = 200 ;
1227+
1228+ var setSizes = function ( ) {
1229+ var parentContainer = element [ 0 ] . querySelector ( '.heatmap-container' ) ;
1230+ containerWidth = parentContainer . clientWidth ;
1231+ containerHeight = parentContainer . clientHeight ;
1232+ blockSize = determineBlockSize ( ) ;
1233+ numberOfRows = ( blockSize === 0 ) ? 0 : Math . floor ( containerHeight / blockSize ) ;
1234+ } ;
1235+
1236+ var determineBlockSize = function ( ) {
1237+ var x = containerWidth ;
1238+ var y = containerHeight ;
1239+ var n = scope . data . length ;
1240+ var px = Math . ceil ( Math . sqrt ( n * x / y ) ) ;
1241+ var py = Math . ceil ( Math . sqrt ( n * y / x ) ) ;
1242+ var sx , sy ;
1243+
1244+ if ( Math . floor ( px * y / x ) * px < n ) {
1245+ sx = y / Math . ceil ( px * y / x ) ;
1246+ } else {
1247+ sx = x / px ;
1248+ }
1249+
1250+ if ( Math . floor ( py * x / y ) * py < n ) {
1251+ sy = x / Math . ceil ( x * py / y ) ;
1252+ } else {
1253+ sy = y / py ;
1254+ }
1255+ return Math . max ( sx , sy ) ;
1256+ } ;
1257+
1258+ var redraw = function ( ) {
1259+ var data = scope . data ;
1260+ var blockPadding = 1 ;
1261+ var color = d3 . scale . threshold ( ) . domain ( scope . thresholds ) . range ( scope . heatmapColorPattern ) ;
1262+ var component = thisComponent ;
1263+ var blocks ;
1264+ var highlightBlock = function ( block , active ) {
1265+ block . style ( 'fill-opacity' , active ? 1 : 0.4 ) ;
1266+ } ;
1267+ var svg = window . d3 . select ( thisComponent ) ;
1268+ svg . selectAll ( '*' ) . remove ( ) ;
1269+
1270+ blocks = svg . selectAll ( 'rect' ) . data ( data ) . enter ( ) . append ( 'rect' ) ;
1271+ blocks . attr ( 'x' , function ( d , i ) {
1272+ return ( Math . floor ( i / numberOfRows ) * blockSize ) + blockPadding ;
1273+ } ) . attr ( 'y' , function ( d , i ) {
1274+ return ( i % numberOfRows * blockSize ) + blockPadding ;
1275+ } ) . attr ( 'width' , blockSize - ( 2 * blockPadding ) ) . attr ( 'height' , blockSize - ( 2 * blockPadding ) ) . style ( 'fill' , function ( d ) {
1276+ return color ( d . value ) ;
1277+ } ) . attr ( 'tooltip-html-unsafe' , function ( d , i ) { //tooltip-html is throwing an exception
1278+ return d . tooltip ;
1279+ } ) . attr ( 'tooltip-append-to-body' , function ( d , i ) {
1280+ return true ;
1281+ } ) . attr ( 'tooltip-animation' , function ( d , i ) {
1282+ return false ;
1283+ } ) ;
1284+
1285+ //Adding events
1286+ blocks . on ( 'mouseover' , function ( ) {
1287+ blocks . call ( highlightBlock , false ) ;
1288+ d3 . select ( this ) . call ( highlightBlock , true ) ;
1289+ } ) ;
1290+ blocks . on ( 'click' , function ( d ) {
1291+ if ( scope . clickAction ) {
1292+ scope . clickAction ( d ) ;
1293+ }
1294+ } ) ;
1295+
1296+ //Compiles the tooltips
1297+ angular . forEach ( angular . element ( blocks ) , function ( block ) {
1298+ var el = angular . element ( block ) ;
1299+ $compile ( el ) ( scope ) ;
1300+ } ) ;
1301+
1302+ svg . on ( 'mouseleave' , function ( ) {
1303+ blocks . call ( highlightBlock , true ) ;
1304+ } ) ;
1305+ } ;
1306+
1307+ //Allow overriding of defaults
1308+ scope . thresholds = angular . extend ( [ ] , thresholdDefaults , scope . thresholds ) ;
1309+ scope . heatmapColorPattern = angular . extend ( [ ] , heatmapColorPatternDefaults , scope . heatmapColorPattern ) ;
1310+ scope . legendLabels = angular . extend ( [ ] , legendLabelDefaults , scope . legendLabels ) ;
1311+ scope . height = scope . height || heightDefault ;
1312+
1313+ //Shows loading indicator
1314+ scope . loadingDone = false ;
1315+
1316+ scope . $watch ( 'data' , function ( newVal , oldVal ) {
1317+ if ( typeof ( newVal ) !== 'undefined' ) {
1318+ scope . loadingDone = true ;
1319+ setSizes ( ) ;
1320+ redraw ( ) ;
1321+ }
1322+ } ) ;
1323+ }
1324+ } ;
1325+ } ] ) ;
10781326; /**
10791327 * @ngdoc directive
10801328 * @name patternfly.charts.directive:pfSparklineChart
@@ -4936,6 +5184,16 @@ angular.module('patternfly.views').directive('pfDataToolbar', function () {
49365184 ) ;
49375185
49385186
5187+ $templateCache . put ( 'charts/heatmap/heatmap-legend.html' ,
5188+ "<ul class=pf-heatmap-legend-container><li ng-repeat=\"item in legendItems\" class=pf-heatmap-legend-items><span class=pf-legend-color-box style=\"background: {{item.color}}\"></span> <span class=pf-legend-text>{{item.text}}</span></li></ul>"
5189+ ) ;
5190+
5191+
5192+ $templateCache . put ( 'charts/heatmap/heatmap.html' ,
5193+ "<div class=pf-heatmap-container><h3>{{chartTitle}}</h3><div class=heatmap-container style=\"height: {{height}}px\"><svg class=pf-heatmap-svg></svg></div><div ng-if=!loadingDone class=\"spinner spinner-lg loading\"></div><div pf-heatmap-legend legend=legendLabels legend-colors=heatmapColorPattern></div></div>"
5194+ ) ;
5195+
5196+
49395197 $templateCache . put ( 'charts/sparkline/sparkline-chart.html' ,
49405198 "<span><div pf-c3-chart id={{sparklineChartId}} config=config></div></span>"
49415199 ) ;
0 commit comments