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