panel_menu.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. define([
  2. 'angular',
  3. 'jquery',
  4. 'lodash',
  5. 'tether',
  6. ],
  7. function (angular, $, _, Tether) {
  8. 'use strict';
  9. angular
  10. .module('grafana.directives')
  11. .directive('panelMenu', function($sanitize, $compile, linkSrv) {
  12. var linkTemplate =
  13. '<span class="panel-title drag-handle pointer">' +
  14. '<span class="icon-gf panel-alert-icon"></span>' +
  15. '<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>' +
  16. '<span class="panel-help-text"><info-popover mode="bold">{{ctrl.panel.description}}</info-popover></span>' +
  17. '<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>' +
  18. '<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>' +
  19. '</span>';
  20. function sanitizeString(str) {
  21. try {
  22. return $sanitize(str);
  23. }
  24. catch(err) {
  25. console.log('Could not sanitize annotation string, html escaping instead');
  26. return _.escape(str);
  27. }
  28. }
  29. function createExternalLinkMenu(ctrl) {
  30. var template = '<div class="panel-menu small">';
  31. template += '<div class="panel-menu-row">';
  32. if (ctrl.panel.links) {
  33. _.each(ctrl.panel.links, function(link) {
  34. var info = linkSrv.getPanelLinkAnchorInfo(link, ctrl.panel.scopedVars);
  35. template += '<a class="panel-menu-link" href="' + info.href + '" target="' + info.target + '">' + info.title + '</a>';
  36. });
  37. }
  38. return template;
  39. }
  40. function createMenuTemplate(ctrl) {
  41. var template = '<div class="panel-menu small">';
  42. if (ctrl.dashboard.meta.canEdit) {
  43. template += '<div class="panel-menu-inner">';
  44. template += '<div class="panel-menu-row">';
  45. if (!ctrl.dashboard.meta.fullscreen) {
  46. template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(-1)"><i class="fa fa-minus"></i></a>';
  47. template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(1)"><i class="fa fa-plus"></i></a>';
  48. }
  49. template += '<a class="panel-menu-icon pull-right" ng-click="ctrl.removePanel()"><i class="fa fa-trash"></i></a>';
  50. template += '<div class="clearfix"></div>';
  51. template += '</div>';
  52. }
  53. template += '<div class="panel-menu-row">';
  54. template += '<a class="panel-menu-link" gf-dropdown="extendedMenu"><i class="fa fa-bars"></i></a>';
  55. _.each(ctrl.getMenu(), function(item) {
  56. // skip edit actions if not editor
  57. if (item.role === 'Editor' && !ctrl.dashboard.meta.canEdit) {
  58. return;
  59. }
  60. template += '<a class="panel-menu-link" ';
  61. if (item.click) { template += ' ng-click="' + item.click + '"'; }
  62. if (item.href) { template += ' href="' + item.href + '"'; }
  63. template += '>';
  64. template += item.text + '</a>';
  65. });
  66. template += '</div>';
  67. template += '</div>';
  68. template += '</div>';
  69. return template;
  70. }
  71. function getExtendedMenu(ctrl) {
  72. return ctrl.getExtendedMenu();
  73. }
  74. return {
  75. restrict: 'A',
  76. link: function($scope, elem) {
  77. var $link = $(linkTemplate);
  78. var $panelLinksBtn = $link.find(".panel-links-btn");
  79. var $panelContainer = elem.parents(".panel-container");
  80. var $panelHelpDrop = $link.find(".panel-help-text");
  81. var menuScope = null;
  82. var ctrl = $scope.ctrl;
  83. var timeout = null;
  84. var $menu = null;
  85. var teather;
  86. elem.append($link);
  87. $scope.$watchCollection('ctrl.panel.links', function(newValue) {
  88. var showIcon = (newValue ? newValue.length > 0 : false) && ctrl.panel.title !== '';
  89. // cannot use toggle here, only works for attached elements
  90. $panelLinksBtn.css({display: showIcon ? 'inline' : 'none'});
  91. });
  92. $scope.$watch('ctrl.panel.description', function(description) {
  93. description = sanitizeString(description);
  94. var showIcon = (description ? description.length > 0 : false) && ctrl.panel.title !== '';
  95. $panelHelpDrop.css({display: showIcon ? 'inline' : 'none'});
  96. });
  97. function dismiss(time, force) {
  98. clearTimeout(timeout);
  99. timeout = null;
  100. if (time) {
  101. timeout = setTimeout(dismiss, time);
  102. return;
  103. }
  104. // if hovering or draging pospone close
  105. if (force !== true) {
  106. if ($menu.is(':hover') || $scope.ctrl.dashboard.$$panelDragging) {
  107. dismiss(2200);
  108. return;
  109. }
  110. }
  111. if (menuScope) {
  112. teather.destroy();
  113. $menu.unbind();
  114. $menu.remove();
  115. menuScope.$destroy();
  116. menuScope = null;
  117. $menu = null;
  118. $panelContainer.removeClass('panel-highlight');
  119. }
  120. }
  121. var showMenu = function(e) {
  122. // if menu item is clicked and menu was just removed from dom ignore this event
  123. if (!$.contains(document, e.target)) {
  124. return;
  125. }
  126. if ($menu) {
  127. dismiss();
  128. return;
  129. }
  130. var menuTemplate;
  131. if ($(e.target).hasClass('fa-external-link')) {
  132. menuTemplate = createExternalLinkMenu(ctrl);
  133. } else {
  134. menuTemplate = createMenuTemplate(ctrl);
  135. }
  136. $menu = $(menuTemplate);
  137. $menu.mouseleave(function() {
  138. dismiss(1000);
  139. });
  140. menuScope = $scope.$new();
  141. menuScope.extendedMenu = getExtendedMenu(ctrl);
  142. menuScope.dismiss = function() {
  143. dismiss(null, true);
  144. };
  145. $(".panel-container").removeClass('panel-highlight');
  146. $panelContainer.toggleClass('panel-highlight');
  147. $('.panel-menu').remove();
  148. elem.append($menu);
  149. $scope.$apply(function() {
  150. $compile($menu.contents())(menuScope);
  151. teather = new Tether({
  152. element: $menu,
  153. target: $panelContainer,
  154. attachment: 'bottom center',
  155. targetAttachment: 'top center',
  156. constraints: [
  157. {
  158. to: 'window',
  159. attachment: 'together',
  160. pin: true
  161. }
  162. ]
  163. });
  164. });
  165. dismiss(2200);
  166. };
  167. elem.click(showMenu);
  168. $compile(elem.contents())($scope);
  169. }
  170. };
  171. });
  172. });