/// import angular from 'angular'; import $ from 'jquery'; import _ from 'lodash'; import Drop from 'tether-drop'; var module = angular.module('grafana.directives'); var panelTemplate = `

{{ctrl.pluginName}}

`; module.directive('grafanaPanel', function($rootScope, $document) { return { restrict: 'E', template: panelTemplate, transclude: true, scope: { ctrl: "=" }, link: function(scope, elem) { var panelContainer = elem.find('.panel-container'); var cornerInfoElem = elem.find('.panel-info-corner'); var ctrl = scope.ctrl; var infoDrop; // the reason for handling these classes this way is for performance // limit the watchers on panels etc var transparentLastState = false; var lastHasAlertRule = false; var lastAlertState; var hasAlertRule; var lastHeight = 0; function mouseEnter() { panelContainer.toggleClass('panel-hover-highlight', true); ctrl.dashboard.setPanelFocus(ctrl.panel.id); } function mouseLeave() { panelContainer.toggleClass('panel-hover-highlight', false); ctrl.dashboard.setPanelFocus(0); } // set initial height if (!ctrl.containerHeight) { ctrl.calculatePanelHeight(); panelContainer.css({minHeight: ctrl.containerHeight}); lastHeight = ctrl.containerHeight; } // set initial transparency if (ctrl.panel.transparent) { transparentLastState = true; panelContainer.addClass('panel-transparent', true); } ctrl.events.on('render', () => { if (lastHeight !== ctrl.containerHeight) { panelContainer.css({minHeight: ctrl.containerHeight}); lastHeight = ctrl.containerHeight; } if (transparentLastState !== ctrl.panel.transparent) { panelContainer.toggleClass('panel-transparent', ctrl.panel.transparent === true); transparentLastState = ctrl.panel.transparent; } hasAlertRule = ctrl.panel.alert !== undefined; if (lastHasAlertRule !== hasAlertRule) { panelContainer.toggleClass('panel-has-alert', hasAlertRule); lastHasAlertRule = hasAlertRule; } if (ctrl.alertState) { if (lastAlertState) { panelContainer.removeClass('panel-alert-state--' + lastAlertState); } if (ctrl.alertState.state === 'ok' || ctrl.alertState.state === 'alerting') { panelContainer.addClass('panel-alert-state--' + ctrl.alertState.state); } lastAlertState = ctrl.alertState.state; } else if (lastAlertState) { panelContainer.removeClass('panel-alert-state--' + lastAlertState); lastAlertState = null; } }); var lastFullscreen; $rootScope.onAppEvent('panel-change-view', function(evt, payload) { if (lastFullscreen !== ctrl.fullscreen) { elem.toggleClass('panel-fullscreen', ctrl.fullscreen ? true : false); lastFullscreen = ctrl.fullscreen; } }, scope); function updatePanelCornerInfo() { var cornerMode = ctrl.getInfoMode(); cornerInfoElem[0].className = 'panel-info-corner panel-info-corner--' + cornerMode; if (cornerMode) { if (infoDrop) { infoDrop.destroy(); } infoDrop = new Drop({ target: cornerInfoElem[0], content: function() { return ctrl.getInfoContent({mode: 'tooltip'}); }, classes: ctrl.error ? 'drop-error' : 'drop-help', openOn: 'hover', hoverOpenDelay: 100, tetherOptions: { attachment: 'bottom left', targetAttachment: 'top left', constraints: [ { to: 'window', attachment: 'together', pin: true } ], } }); } } scope.$watchGroup(['ctrl.error', 'ctrl.panel.description'], updatePanelCornerInfo); scope.$watchCollection('ctrl.panel.links', updatePanelCornerInfo); cornerInfoElem.on('click', function() { infoDrop.close(); scope.$apply(ctrl.openInspector.bind(ctrl)); }); elem.on('mouseenter', mouseEnter); elem.on('mouseleave', mouseLeave); ctrl.isPanelVisible = function () { var position = panelContainer[0].getBoundingClientRect(); return (0 < position.top) && (position.top < window.innerHeight); }; const refreshOnScroll = function () { if (ctrl.skippedLastRefresh) { ctrl.refresh(); } }; $document.on('scroll', refreshOnScroll); scope.$on('$destroy', function() { elem.off(); cornerInfoElem.off(); $document.off('scroll', refreshOnScroll); if (infoDrop) { infoDrop.destroy(); } }); } }; }); module.directive('panelResizer', function($rootScope) { return { restrict: 'E', template: '', link: function(scope, elem) { var resizing = false; var lastPanel; var ctrl = scope.ctrl; var handleOffset; var originalHeight; var originalWidth; var maxWidth; function dragStartHandler(e) { e.preventDefault(); resizing = true; handleOffset = $(e.target).offset(); originalHeight = parseInt(ctrl.row.height); originalWidth = ctrl.panel.span; maxWidth = $(document).width(); lastPanel = ctrl.row.panels[ctrl.row.panels.length - 1]; $('body').on('mousemove', moveHandler); $('body').on('mouseup', dragEndHandler); } function moveHandler(e) { ctrl.row.height = Math.round(originalHeight + (e.pageY - handleOffset.top)); ctrl.panel.span = originalWidth + (((e.pageX - handleOffset.left) / maxWidth) * 12); ctrl.panel.span = Math.min(Math.max(ctrl.panel.span, 1), 12); ctrl.row.updateRowSpan(); var rowSpan = ctrl.row.span; // auto adjust other panels if (Math.floor(rowSpan) < 14) { // last panel should not push row down if (lastPanel === ctrl.panel && rowSpan > 12) { lastPanel.span -= rowSpan - 12; } else if (lastPanel !== ctrl.panel) { // reduce width of last panel so total in row is 12 lastPanel.span = lastPanel.span - (rowSpan - 12); lastPanel.span = Math.min(Math.max(lastPanel.span, 1), 12); } } ctrl.row.panelSpanChanged(true); scope.$apply(function() { ctrl.render(); }); } function dragEndHandler() { ctrl.panel.span = Math.round(ctrl.panel.span); if (lastPanel) { lastPanel.span = Math.round(lastPanel.span); } // first digest to propagate panel width change // then render $rootScope.$apply(function() { ctrl.row.panelSpanChanged(); setTimeout(function() { $rootScope.$broadcast('render'); }); }); $('body').off('mousemove', moveHandler); $('body').off('mouseup', dragEndHandler); } elem.on('mousedown', dragStartHandler); var unbind = scope.$on("$destroy", function() { elem.off('mousedown', dragStartHandler); unbind(); }); } }; }); module.directive('panelHelpCorner', function($rootScope) { return { restrict: 'E', template: ` `, link: function(scope, elem) { } }; });