1// @ts-ignore 2import baron from 'baron'; 3import { PanelEvents } from '@grafana/data'; 4import { PanelModel } from '../../features/dashboard/state'; 5import { PanelCtrl } from './panel_ctrl'; 6import { Subscription } from 'rxjs'; 7import { PanelDirectiveReadyEvent, RenderEvent } from 'app/types/events'; 8import { coreModule } from 'app/angular/core_module'; 9import { RefreshEvent } from '@grafana/runtime'; 10 11const panelTemplate = ` 12 <ng-transclude class="panel-height-helper"></ng-transclude> 13`; 14 15coreModule.directive('grafanaPanel', ($rootScope, $document, $timeout) => { 16 return { 17 restrict: 'E', 18 template: panelTemplate, 19 transclude: true, 20 scope: { ctrl: '=' }, 21 link: (scope: any, elem) => { 22 const ctrl: PanelCtrl = scope.ctrl; 23 const panel: PanelModel = scope.ctrl.panel; 24 const subs = new Subscription(); 25 26 let panelScrollbar: any; 27 28 function resizeScrollableContent() { 29 if (panelScrollbar) { 30 panelScrollbar.update(); 31 } 32 } 33 34 ctrl.events.on(PanelEvents.componentDidMount, () => { 35 if ((ctrl as any).__proto__.constructor.scrollable) { 36 const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable'; 37 const scrollerClass = 'baron__scroller'; 38 const scrollBarHTML = ` 39 <div class="baron__track"> 40 <div class="baron__bar"></div> 41 </div> 42 `; 43 44 const scrollRoot = elem; 45 const scroller = elem.find(':first').find(':first'); 46 47 scrollRoot.addClass(scrollRootClass); 48 $(scrollBarHTML).appendTo(scrollRoot); 49 scroller.addClass(scrollerClass); 50 51 panelScrollbar = baron({ 52 root: scrollRoot[0], 53 scroller: scroller[0], 54 bar: '.baron__bar', 55 barOnCls: '_scrollbar', 56 scrollingCls: '_scrolling', 57 }); 58 59 panelScrollbar.scroll(); 60 } 61 }); 62 63 function updateDimensionsFromParentScope() { 64 ctrl.height = scope.$parent.$parent.size.height; 65 ctrl.width = scope.$parent.$parent.size.width; 66 } 67 68 updateDimensionsFromParentScope(); 69 70 // Pass PanelModel events down to angular controller event emitter 71 subs.add( 72 panel.events.subscribe(RefreshEvent, () => { 73 updateDimensionsFromParentScope(); 74 ctrl.events.emit('refresh'); 75 }) 76 ); 77 78 subs.add( 79 panel.events.subscribe(RenderEvent, (event) => { 80 // this event originated from angular so no need to bubble it back 81 if (event.payload?.fromAngular) { 82 return; 83 } 84 85 updateDimensionsFromParentScope(); 86 87 $timeout(() => { 88 resizeScrollableContent(); 89 ctrl.events.emit('render'); 90 }); 91 }) 92 ); 93 94 subs.add( 95 ctrl.events.subscribe(RenderEvent, (event) => { 96 // this event originated from angular so bubble it to react so the PanelChromeAngular can update the panel header alert state 97 if (event.payload) { 98 event.payload.fromAngular = true; 99 panel.events.publish(event); 100 } 101 }) 102 ); 103 104 scope.$on('$destroy', () => { 105 elem.off(); 106 107 // Remove PanelModel.event subs 108 subs.unsubscribe(); 109 // Remove Angular controller event subs 110 ctrl.events.emit(PanelEvents.panelTeardown); 111 ctrl.events.removeAllListeners(); 112 113 if (panelScrollbar) { 114 panelScrollbar.dispose(); 115 } 116 }); 117 118 panel.events.publish(PanelDirectiveReadyEvent); 119 }, 120 }; 121}); 122