Просмотр исходного кода

grid: progress on new grid, resize & saving layouts works

Torkel Ödegaard 8 лет назад
Родитель
Сommit
10a3504309

+ 55 - 4
public/app/features/dashboard/PanelModel.ts

@@ -1,11 +1,62 @@
+import {Emitter} from 'app/core/core';
 
-export interface PanelModel {
-  id: number;
+export interface GridPos {
   x: number;
   y: number;
-  width: number;
-  height: number;
+  w: number;
+  h: number;
+}
+
+const notPersistedProperties: {[str: string]: boolean} = {
+  "model": true,
+  "events": true,
+};
+
+export class PanelModel {
+  id: number;
+  gridPos:  GridPos;
   type: string;
   title: string;
+  events: Emitter;
+
+  constructor(private model) {
+    // copy properties from persisted model
+    for (var property in model) {
+      this[property] = model[property];
+    }
+
+    this.events = new Emitter();
+  }
+
+  getSaveModel() {
+    this.model = {};
+    for (var property in this) {
+      if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
+        console.log('PanelModel.getSaveModel() skiping property', property);
+        continue;
+      }
+
+      this.model[property] = this[property];
+    }
+    return this.model;
+  }
+
+  updateGridPos(newPos: GridPos) {
+    let sizeChanged = false;
+
+    if (this.gridPos.w !== newPos.w || this.gridPos.h !== newPos.h) {
+      sizeChanged = true;
+    }
+
+    this.gridPos.x = newPos.x;
+    this.gridPos.y = newPos.y;
+    this.gridPos.w = newPos.w;
+    this.gridPos.h = newPos.h;
+
+    if (sizeChanged) {
+      console.log('PanelModel sizeChanged event and render events fired');
+      this.events.emit('panel-size-changed');
+    }
+  }
 }
 

+ 2 - 1
public/app/features/dashboard/dashboard_ctrl.ts

@@ -121,7 +121,8 @@ export class DashboardCtrl implements PanelContainer {
     }
 
     panelPossitionUpdated(panel: PanelModel) {
-      console.log('panel pos updated', panel);
+      //console.log('panel pos updated', panel);
+      //this.$rootScope.$broadcast('render');
     }
 
     timezoneChanged() {

+ 24 - 11
public/app/features/dashboard/dashgrid/DashboardGrid.tsx

@@ -4,6 +4,7 @@ import ReactGridLayout from 'react-grid-layout';
 import {CELL_HEIGHT, CELL_VMARGIN} from '../model';
 import {DashboardPanel} from './DashboardPanel';
 import {PanelContainer} from './PanelContainer';
+import {PanelModel} from '../PanelModel';
 import sizeMe from 'react-sizeme';
 
 const COLUMN_COUNT = 12;
@@ -22,6 +23,8 @@ function GridWrapper({size, layout, onLayoutChange, children}) {
       isDraggable={true}
       isResizable={true}
       measureBeforeMount={false}
+      containerPadding={[0, 0]}
+      useCSSTransforms={true}
       margin={[CELL_VMARGIN, CELL_VMARGIN]}
       cols={COLUMN_COUNT}
       rowHeight={CELL_HEIGHT}
@@ -42,6 +45,7 @@ export interface DashboardGridProps {
 export class DashboardGrid extends React.Component<DashboardGridProps, any> {
   gridToPanelMap: any;
   panelContainer: PanelContainer;
+  panelMap: {[id: string]: PanelModel};
 
   constructor(props) {
     super(props);
@@ -52,22 +56,34 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
   buildLayout() {
     const layout = [];
     const panels = this.panelContainer.getPanels();
+    this.panelMap = {};
 
     for (let panel of panels) {
+      let stringId = panel.id.toString();
+      this.panelMap[stringId] = panel;
+
+      if (!panel.gridPos) {
+        console.log('panel without gridpos');
+        continue;
+      }
+
       layout.push({
-        i: panel.id.toString(),
-        x: panel.x,
-        y: panel.y,
-        w: panel.width,
-        h: panel.height,
+        i: stringId,
+        x: panel.gridPos.x,
+        y: panel.gridPos.y,
+        w: panel.gridPos.w,
+        h: panel.gridPos.h,
       });
     }
 
-    console.log('layout', layout);
     return layout;
   }
 
-  onLayoutChange() {}
+  onLayoutChange(newLayout) {
+    for (const newPos of newLayout) {
+      this.panelMap[newPos.i].updateGridPos(newPos);
+    }
+  }
 
   renderPanels() {
     const panels = this.panelContainer.getPanels();
@@ -76,10 +92,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
     for (let panel of panels) {
       panelElements.push(
         <div key={panel.id.toString()} className="panel">
-          <DashboardPanel
-            panel={panel}
-            getPanelContainer={this.props.getPanelContainer}
-          />
+          <DashboardPanel panel={panel} getPanelContainer={this.props.getPanelContainer} />
         </div>,
       );
     }

+ 0 - 211
public/app/features/dashboard/dashgrid/dashgrid.ts

@@ -1,211 +0,0 @@
-// ///<reference path="../../../headers/common.d.ts" />
-//
-// import coreModule from 'app/core/core_module';
-// import {CELL_HEIGHT, CELL_VMARGIN} from '../model';
-//
-// import 'jquery-ui';
-// import 'gridstack/dist/jquery.jQueryUI';
-// import 'gridstack';
-//
-// const template = `
-// <div class="grid-stack">
-//   <dash-grid-item ng-repeat="panel in ctrl.dashboard.panels track by panel.id"
-//                   class="grid-stack-item"
-//                   grid-ctrl="ctrl"
-//                   panel="panel">
-//     <plugin-component type="panel" class="grid-stack-item-content">
-//     </plugin-component>
-//   </dash-grid-item>
-// </div>
-// `;
-//
-// var rowIndex = 0;
-//
-// export class GridCtrl {
-//   options: any;
-//   dashboard: any;
-//   panels: any;
-//   gridstack: any;
-//   gridElem: any;
-//   isInitialized: boolean;
-//   isDestroyed: boolean;
-//   index: number;
-//   changeRenderPromise: any;
-//
-//   #<{(|* @ngInject |)}>#
-//   constructor(private $scope, private $element, private $timeout) {
-//     console.log(this.dashboard);
-//     this.index = rowIndex;
-//     rowIndex += 1;
-//   }
-//
-//   init() {
-//     this.gridElem = this.$element.find('.grid-stack');
-//
-//     this.gridstack = this.gridElem.gridstack({
-//       animate: true,
-//       cellHeight: CELL_HEIGHT,
-//       verticalMargin: CELL_VMARGIN,
-//       acceptWidgets: '.grid-stack-item',
-//       handle: '.grid-drag-handle'
-//     }).data('gridstack');
-//
-//     this.isInitialized = true;
-//
-//     this.gridElem.on('added', (e, items) => {
-//       for (let item of items) {
-//         this.onGridStackItemAdded(item);
-//       }
-//     });
-//
-//     this.gridElem.on('removed', (e, items) => {
-//       for (let item of items) {
-//         this.onGridStackItemRemoved(item);
-//       }
-//     });
-//
-//     this.gridElem.on('change', (e, items) => {
-//       this.$timeout(() => this.onGridStackItemsChanged(items), 50);
-//     });
-//   }
-//
-//   onGridStackItemAdded(item) {
-//     console.log('row: ' + this.index + ' item added', item);
-//   }
-//
-//   onGridStackItemRemoved(item) {
-//     console.log('row: ' + this.index + ' item removed', item.id, item);
-//   }
-//
-//   onGridStackItemsChanged(items) {
-//     console.log('onGridStackItemsChanged');
-//
-//     for (let item of items) {
-//       // find panel
-//       var panel = this.dashboard.getPanelById(parseInt(item.id));
-//
-//       if (!panel) {
-//         console.log('item change but no panel found for item', item);
-//         continue;
-//       }
-//
-//       // update panel model position
-//       panel.x = item.x;
-//       panel.y = item.y;
-//       panel.width = item.width;
-//       panel.height = item.height;
-//
-//       console.log('updating panel: ' + panel.id + ' x: ' + panel.x + ' y: ' + panel.y);
-//     }
-//
-//     this.dashboard.panels.sort(function (a, b) {
-//       let aScore = a.x + (a.y * 12);
-//       let bScore = b.x + (b.y * 12);
-//       if (aScore < bScore) { return -1; }
-//       if (aScore > bScore) { return 1; }
-//       return 0;
-//     });
-//
-//     if (this.changeRenderPromise) {
-//       this.$timeout.cancel(this.changeRenderPromise);
-//     }
-//
-//     this.changeRenderPromise = this.$timeout(() => {
-//       console.log('broadcasting render');
-//       this.$scope.$broadcast('render');
-//     });
-//   }
-//
-//   destroy() {
-//     this.gridstack.destroy();
-//     this.gridstack = null;
-//     this.isDestroyed = true;
-//   }
-// }
-//
-// #<{(|* @ngInject *|)}>#
-// export function dashGrid($timeout) {
-//   return {
-//     restrict: 'E',
-//     template: template,
-//     controller: GridCtrl,
-//     bindToController: true,
-//     controllerAs: 'ctrl',
-//     scope: {
-//       dashboard: "=",
-//     },
-//     link: function(scope, elem, attrs, ctrl) {
-//       $timeout(function() {
-//         ctrl.init();
-//       });
-//
-//       scope.$on('$destroy', () => {
-//         ctrl.destroy();
-//       });
-//     }
-//   };
-// }
-//
-// #<{(|* @ngInject *|)}>#
-// export function dashGridItem($timeout, $rootScope) {
-//   return {
-//     restrict: "E",
-//     scope: {
-//       panel: '=',
-//       gridCtrl: '='
-//     },
-//     link: function (scope, element, attrs) {
-//       let gridCtrl = scope.gridCtrl;
-//       let panel = scope.panel;
-//       let gridStackNode = null;
-//
-//       element.attr({
-//         'data-gs-id': panel.id,
-//         'data-gs-x': panel.x,
-//         'data-gs-y': panel.y,
-//         'data-gs-width': panel.width,
-//         'data-gs-height': panel.height,
-//         'data-gs-no-resize': panel.type === 'row',
-//       });
-//
-//       $rootScope.onAppEvent('panel-fullscreen-exit', (evt, payload) => {
-//         if (panel.id !== payload.panelId) {
-//           return;
-//         }
-//         gridCtrl.gridstack.locked(element, false);
-//         element.removeClass('panel-fullscreen');
-//       }, scope);
-//
-//       $rootScope.onAppEvent('panel-fullscreen-enter', (evt, payload) => {
-//         if (panel.id !== payload.panelId) {
-//           return;
-//         }
-//         element.addClass('panel-fullscreen');
-//       }, scope);
-//
-//       scope.$on('$destroy', () => {
-//         console.log('grid-item scope $destroy');
-//         if (gridCtrl.isDestroyed) {
-//           return;
-//         }
-//
-//         if (gridStackNode) {
-//           console.log('grid-item scope $destroy removeWidget');
-//           gridStackNode._grid.removeWidget(element);
-//         }
-//       });
-//
-//       if (gridCtrl.isInitialized) {
-//         gridCtrl.gridstack.makeWidget(element);
-//         gridStackNode = element.data('_gridstack_node');
-//       } else {
-//         setTimeout(function() {
-//           gridStackNode = element.data('_gridstack_node');
-//         }, 500);
-//       }
-//     }
-//   };
-// }
-//
-// coreModule.directive('dashGrid', dashGrid);
-// coreModule.directive('dashGridItem', dashGridItem);

+ 0 - 232
public/app/features/dashboard/dashgrid/ref.txt

@@ -1,232 +0,0 @@
-Skip to content
-This repository
-Search
-Pull requests
-Issues
-Marketplace
-Gist
- @torkelo
- Sign out
- Unwatch 946
-  Unstar 17,021
- Fork 2,862 grafana/grafana
- Code  Issues 1,079  Pull requests 46  Projects 1  Wiki  Settings Insights
-Branch: gridstack Find file Copy pathgrafana/public/app/core/components/dashgrid/dashgrid.ts
-a6bbcb8  on Jun 13
-@torkelo torkelo ux: gridstack poc
-1 contributor
-RawBlameHistory
-213 lines (181 sloc)  5.45 KB
-///<reference path="../../../headers/common.d.ts" />
-
-import $ from 'jquery';
-import coreModule from '../../core_module';
-
-import 'jquery-ui';
-import 'gridstack';
-import 'gridstack.jquery-ui';
-
-const template = `
-<div gridstack gridstack-handler="ctrl.gridstack" class="grid-stack"
-      options="ctrl.options"
-      on-change="ctrl.onChange(event,items)"
-      on-drag-start="ctrl.onDragStart(event,ui)"
-      on-drag-stop="ctrl.onDragStop(event, ui)"
-      on-resize-start="ctrl.onResizeStart(event, ui)"
-      on-resize-stop="ctrl.onResizeStop(event, ui)">
-      <div gridstack-item ng-repeat="panel in ctrl.panels"
-          class="grid-stack-item"
-          gs-item-id="panel.id"
-          gs-item-x="panel.x"
-          gs-item-y="panel.y"
-          gs-item-width="panel.width"
-          gs-item-height="panel.height"
-          gs-item-autopos="1"
-          on-item-added="ctrl.onItemAdded(item)"
-          on-item-removed="ctrl.onItemRemoved(item)">
-        <plugin-component type="panel" class="panel-margin grid-stack-item-content">
-        </plugin-component>
-      </div>
-</div>
-`;
-
-export class DashGridCtrl {
-  options: any;
-
-  /** @ngInject */
-  constructor(private $rootScope) {
-    this.options = {
-      animate: true,
-    };
-  }
-
-  onResizeStop() {
-    this.$rootScope.$broadcast('render');
-  }
-}
-
-export function dashGrid($timeout) {
-  return {
-    restrict: 'E',
-    template: template,
-    controller: DashGridCtrl,
-    bindToController: true,
-    controllerAs: 'ctrl',
-    scope: {
-      dashboard: "="
-    },
-    link: function(scope, elem, attrs, ctrl) {
-
-      ctrl.panels = [];
-      ctrl.dashboard.forEachPanel((panel, panelIndex, row, rowIndex) => {
-        panel.width = 4;
-        panel.height = 4;
-        panel.x = panelIndex * 4;
-        panel.y = rowIndex * 4;
-        ctrl.panels.push(panel);
-      });
-
-    }
-  };
-}
-
-/** @ngInject */
-coreModule.controller('GridstackController', ['$scope', function($scope) {
-
-  var gridstack = null;
-
-  this.init = function(element, options) {
-    gridstack = element.gridstack(options).data('gridstack');
-    return gridstack;
-  };
-
-  this.removeItem = function(element) {
-    if (gridstack) {
-      return gridstack.removeWidget(element, false);
-    }
-    return null;
-  };
-
-  this.addItem = function(element) {
-    if (gridstack) {
-      gridstack.makeWidget(element);
-      return element;
-    }
-    return null;
-  };
-
-}]);
-
-/** @ngInject */
-coreModule.directive('gridstack', ['$timeout', function($timeout) {
-  return {
-    restrict: "A",
-    controller: 'GridstackController',
-    scope: {
-      onChange: '&',
-      onDragStart: '&',
-      onDragStop: '&',
-      onResizeStart: '&',
-      onResizeStop: '&',
-      gridstackHandler: '=',
-      options: '='
-    },
-    link: function (scope, element, attrs, controller, ngModel) {
-
-      var gridstack = controller.init(element, scope.options);
-      scope.gridstackHandler = gridstack;
-
-      element.on('change', function (e, items) {
-        $timeout(function() {
-          scope.$apply();
-          scope.onChange({event: e, items: items});
-        });
-      });
-
-      element.on('dragstart', function(e, ui) {
-        scope.onDragStart({event: e, ui: ui});
-      });
-
-      element.on('dragstop', function(e, ui) {
-        $timeout(function() {
-          scope.$apply();
-          scope.onDragStop({event: e, ui: ui});
-        });
-      });
-
-      element.on('resizestart', function(e, ui) {
-        scope.onResizeStart({event: e, ui: ui});
-      });
-
-      element.on('resizestop', function(e, ui) {
-        $timeout(function() {
-          scope.$apply();
-          scope.onResizeStop({event: e, ui: ui});
-        });
-      });
-
-    }
-  };
-}]);
-
-/** @ngInject */
-coreModule.directive('gridstackItem', ['$timeout', function($timeout) {
-
-  return {
-    restrict: "A",
-    controller: 'GridstackController',
-    require: '^gridstack',
-    scope: {
-      gridstackItem: '=',
-      onItemAdded: '&',
-      onItemRemoved: '&',
-      gsItemId: '=',
-      gsItemX: '=',
-      gsItemY: '=',
-      gsItemWidth: '=',
-      gsItemHeight: '=',
-      gsItemAutopos: '='
-    },
-    link: function (scope, element, attrs, controller) {
-      $(element).attr('data-gs-id', scope.gsItemId);
-      $(element).attr('data-gs-x', scope.gsItemX);
-      $(element).attr('data-gs-y', scope.gsItemY);
-      $(element).attr('data-gs-width', scope.gsItemWidth);
-      $(element).attr('data-gs-height', scope.gsItemHeight);
-      $(element).attr('data-gs-auto-position', scope.gsItemAutopos);
-      var widget = controller.addItem(element);
-      var item = element.data('_gridstack_node');
-      $timeout(function() {
-        scope.onItemAdded({item: item});
-      });
-      scope.$watch(function () { return $(element).attr('data-gs-id'); }, function (val) {
-        scope.gsItemId = val;
-      });
-      scope.$watch(function(){ return $(element).attr('data-gs-x'); }, function(val) {
-        scope.gsItemX = val;
-      });
-
-      scope.$watch(function(){ return $(element).attr('data-gs-y'); }, function(val) {
-        scope.gsItemY = val;
-      });
-
-      scope.$watch(function(){ return $(element).attr('data-gs-width'); }, function(val) {
-        scope.gsItemWidth = val;
-      });
-
-      scope.$watch(function(){ return $(element).attr('data-gs-height'); }, function(val) {
-        scope.gsItemHeight = val;
-      });
-
-      element.bind('$destroy', function() {
-        var item = element.data('_gridstack_node');
-        scope.onItemRemoved({item: item});
-        controller.removeItem(element);
-      });
-    }
-  };
-}]);
-
-coreModule.directive('dashGrid', dashGrid);
-Contact GitHub API Training Shop Blog About
-© 2017 GitHub, Inc. Terms Privacy Security Status Help

+ 30 - 39
public/app/features/dashboard/model.ts

@@ -69,8 +69,7 @@ export class DashboardModel {
     this.links = data.links || [];
     this.gnetId = data.gnetId || null;
     this.folderId = data.folderId || null;
-    this.panels = data.panels || [];
-    this.rows = [];
+    this.panels = _.map(data.panels || [], panelData => new PanelModel(panelData));
 
     this.addBuiltInAnnotationQuery();
     this.initMeta(meta);
@@ -123,34 +122,32 @@ export class DashboardModel {
     // temp remove stuff
     var events = this.events;
     var meta = this.meta;
-    var rows = this.rows;
     var variables = this.templating.list;
+    var panels = this.panels;
 
     delete this.events;
     delete this.meta;
+    delete this.panels;
 
     // prepare save model
-    this.rows = _.map(rows, row => row.getSaveModel());
     this.templating.list = _.map(variables, variable => variable.getSaveModel ? variable.getSaveModel() : variable);
+    this.panels = _.map(panels, panel => panel.getSaveModel());
 
     // make clone
     var copy = $.extend(true, {}, this);
     //  sort clone
     copy = sortByKeys(copy);
+    console.log(copy.panels);
 
     // restore properties
     this.events = events;
     this.meta = meta;
-    this.rows = rows;
     this.templating.list = variables;
+    this.panels = panels;
 
     return copy;
   }
 
-  addEmptyRow() {
-    this.rows.push(new DashboardRow({isNew: true}));
-  }
-
   private ensureListExist(data) {
     if (!data) { data = {}; }
     if (!data.list) { data.list = []; }
@@ -327,7 +324,7 @@ export class DashboardModel {
     var i, j, k;
     var oldVersion = this.schemaVersion;
     var panelUpgrades = [];
-    this.schemaVersion = 15;
+    this.schemaVersion = 16;
 
     if (oldVersion === this.schemaVersion) {
       return;
@@ -636,7 +633,7 @@ export class DashboardModel {
         this.graphTooltip = old.sharedCrosshair ? 1 : 0;
       }
 
-      if (oldVersion < 15) {
+      if (oldVersion < 16) {
         this.upgradeToGridLayout(old);
       }
 
@@ -644,60 +641,54 @@ export class DashboardModel {
         return;
       }
 
-      for (i = 0; i < this.rows.length; i++) {
-        var row = this.rows[i];
-        for (j = 0; j < row.panels.length; j++) {
-          for (k = 0; k < panelUpgrades.length; k++) {
-            panelUpgrades[k].call(this, row.panels[j]);
-          }
+      for (j = 0; j < this.panels.length; j++) {
+        for (k = 0; k < panelUpgrades.length; k++) {
+          panelUpgrades[k].call(this, this.panels[j]);
         }
       }
     }
 
     upgradeToGridLayout(old) {
       let yPos = 0;
-      let rowIds = 1000;
+      //let rowIds = 1000;
 
       for (let row of old.rows) {
         let xPos = 0;
         let height: any = row.height;
 
-        if (this.meta.keepRows) {
-          this.panels.push({
-            id: rowIds++,
-            type: 'row',
-            title: row.title,
-            x: 0,
-            y: yPos,
-            height: 1,
-            width: 12
-          });
-
-          yPos += 1;
-        }
+        // if (this.meta.keepRows) {
+        //   this.panels.push({
+        //     id: rowIds++,
+        //     type: 'row',
+        //     title: row.title,
+        //     x: 0,
+        //     y: yPos,
+        //     height: 1,
+        //     width: 12
+        //   });
+        //
+        //   yPos += 1;
+        // }
 
         if (_.isString(height)) {
           height = parseInt(height.replace('px', ''), 10);
         }
 
-        height = Math.ceil(height / CELL_HEIGHT);
+        const rowGridHeight = Math.ceil(height / CELL_HEIGHT);
 
         for (let panel of row.panels) {
           // should wrap to next row?
           if (xPos + panel.span >= 12) {
-            yPos += height;
+            yPos += rowGridHeight;
           }
 
-          panel.x = xPos;
-          panel.y = yPos;
-          panel.width = panel.span;
-          panel.height = height;
+          panel.gridPos = { x: xPos, y: yPos, w: panel.span, h: rowGridHeight };
 
           delete panel.span;
 
-          xPos += panel.width;
+          xPos += rowGridHeight;
 
-          this.panels.push(panel);
+          this.panels.push(new PanelModel(panel));
         }
 
         yPos += height;

+ 3 - 5
public/app/features/dashboard/save_as_modal.ts

@@ -55,11 +55,9 @@ export class SaveDashboardAsModalCtrl {
     // remove alerts if source dashboard is already persisted
     // do not want to create alert dupes
     if (dashboard.id > 0) {
-      this.clone.rows.forEach(row => {
-        row.panels.forEach(panel => {
-          delete panel.thresholds;
-          delete panel.alert;
-        });
+      this.clone.panels.forEach(panel => {
+        delete panel.thresholds;
+        delete panel.alert;
       });
     }
 

+ 11 - 17
public/app/features/panel/panel_ctrl.ts

@@ -37,7 +37,7 @@ export class PanelCtrl {
     this.$scope = $scope;
     this.$timeout = $injector.get('$timeout');
     this.editorTabIndex = 0;
-    this.events = new Emitter();
+    this.events = this.panel.events;
     this.timing = {};
 
     var plugin = config.panels[this.panel.type];
@@ -47,21 +47,14 @@ export class PanelCtrl {
     }
 
     $scope.$on("refresh", () => this.refresh());
-    $scope.$on("render", () => this.render());
     $scope.$on("$destroy", () => {
       this.events.emit('panel-teardown');
       this.events.removeAllListeners();
     });
-
-    // we should do something interesting
-    // with newly added panels
-    if (this.panel.isNew) {
-      delete this.panel.isNew;
-    }
   }
 
   init() {
-    this.calculatePanelHeight();
+    this.events.on('panel-size-changed', this.onSizeChanged.bind(this));
     this.publishAppEvent('panel-initialized', {scope: this.$scope});
     this.events.emit('panel-initialized');
   }
@@ -71,7 +64,7 @@ export class PanelCtrl {
   }
 
   refresh() {
-   this.events.emit('refresh', null);
+    this.events.emit('refresh', null);
   }
 
   publishAppEvent(evtName, evt) {
@@ -170,23 +163,24 @@ export class PanelCtrl {
        var fullscreenHeight = Math.floor(docHeight * 0.8);
        this.containerHeight = this.editMode ? editHeight : fullscreenHeight;
     } else {
-      this.containerHeight = this.panel.height * CELL_HEIGHT + ((this.panel.height-1) * CELL_VMARGIN);
+      this.containerHeight = this.panel.gridPos.h * CELL_HEIGHT + ((this.panel.gridPos.h-1) * CELL_VMARGIN);
     }
 
     this.height = this.containerHeight - (PANEL_BORDER + PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
   }
 
   render(payload?) {
-    // ignore if other panel is in fullscreen mode
-    if (this.otherPanelInFullscreenMode()) {
-      return;
-    }
-
-    this.calculatePanelHeight();
     this.timing.renderStart = new Date().getTime();
     this.events.emit('render', payload);
   }
 
+  private onSizeChanged() {
+    this.calculatePanelHeight();
+    this.$timeout(() => {
+      this.render();
+    });
+  }
+
   duplicate() {
     this.dashboard.duplicatePanel(this.panel);
     this.$timeout(() => {

+ 1 - 0
public/app/features/panel/panel_directive.ts

@@ -98,6 +98,7 @@ module.directive('grafanaPanel', function($rootScope, $document) {
       }
 
       ctrl.events.on('render', () => {
+        console.log('panelDirective::render!');
         if (lastHeight !== ctrl.containerHeight) {
           panelContainer.css({minHeight: ctrl.containerHeight});
           lastHeight = ctrl.containerHeight;

+ 5 - 5
public/app/features/panel/panel_header.ts

@@ -88,7 +88,7 @@ function panelHeader($compile) {
       let menuScope;
 
       elem.click(function(evt) {
-        const targetClass = evt.target.className;
+        //const targetClass = evt.target.className;
 
         // remove existing scope
         if (menuScope) {
@@ -100,10 +100,10 @@ function panelHeader($compile) {
         menuElem.html(menuHtml);
         $compile(menuElem)(menuScope);
 
-        if (targetClass === 'panel-title-text' || targetClass === 'panel-title') {
-          evt.stopPropagation();
-          elem.find('[data-toggle=dropdown]').dropdown('toggle');
-        }
+        // if (targetClass === 'panel-title-text' || targetClass === 'panel-title') {
+        //   evt.stopPropagation();
+        //   elem.find('[data-toggle=dropdown]').dropdown('toggle');
+        // }
       });
     }
   };

+ 1 - 0
public/app/plugins/panel/graph/graph.ts

@@ -54,6 +54,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
       });
 
       ctrl.events.on('render', function(renderData) {
+        console.log('graph render');
         data = renderData || data;
         if (!data) {
           return;

+ 18 - 12
public/dashboards/home.json

@@ -13,7 +13,6 @@
     {
       "content": "<div class=\"text-center dashboard-header\">\n  <span>Home Dashboard</span>\n</div>",
       "editable": true,
-      "height": 2,
       "id": 1,
       "links": [],
       "mode": "html",
@@ -21,14 +20,16 @@
       "title": "",
       "transparent": true,
       "type": "text",
-      "width": 12,
-      "x": 0,
-      "y": 0
+      "gridPos": {
+        "w": 12,
+        "h": 2,
+        "x": 0,
+        "y": 0
+      }
     },
     {
       "folderId": 0,
       "headings": true,
-      "height": 17,
       "id": 3,
       "limit": 4,
       "links": [],
@@ -40,22 +41,27 @@
       "title": "",
       "transparent": false,
       "type": "dashlist",
-      "width": 7,
-      "x": 0,
-      "y": 6
+      "gridPos": {
+        "w": 7,
+        "h": 17,
+        "x": 0,
+        "y": 6
+      }
     },
     {
       "editable": true,
       "error": false,
-      "height": 17,
       "id": 4,
       "links": [],
       "title": "",
       "transparent": false,
       "type": "pluginlist",
-      "width": 5,
-      "x": 7,
-      "y": 6
+      "gridPos": {
+        "w": 5,
+        "h": 17,
+        "x": 7,
+        "y": 6
+      }
     }
   ],
   "rows": [],