Explorar o código

ux(dashboard): making progress on new add panel experiance

Torkel Ödegaard %!s(int64=9) %!d(string=hai) anos
pai
achega
c609586ff0

+ 20 - 0
pkg/api/frontendsettings.go

@@ -127,6 +127,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
 			"name":    panel.Name,
 			"id":      panel.Id,
 			"info":    panel.Info,
+			"sort":    getPanelSort(panel.Id),
 		}
 	}
 
@@ -150,6 +151,25 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
 	return jsonObj, nil
 }
 
+func getPanelSort(id string) int {
+	sort := 100
+	switch id {
+	case "graph":
+		sort = 1
+	case "singlestat":
+		sort = 2
+	case "table":
+		sort = 3
+	case "text":
+		sort = 4
+	case "alertlist":
+		sort = 5
+	case "dashlist":
+		sort = 6
+	}
+	return sort
+}
+
 func GetFrontendSettings(c *middleware.Context) {
 	settings, err := getFrontendSettingsMap(c)
 	if err != nil {

+ 8 - 1
public/app/core/routes/dashboard_loaders.js

@@ -31,7 +31,14 @@ function (coreModule) {
       meta: { canStar: false, canShare: false },
       dashboard: {
         title: "New dashboard",
-        rows: [{title: 'Dashboard Row', height: '250px', panels:[] }]
+        rows: [
+          {
+            title: 'Dashboard Row',
+            height: '250px',
+            panels:[],
+            isNew: true,
+          }
+        ]
       },
     }, $scope);
   });

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

@@ -107,6 +107,7 @@ export class DashboardCtrl {
           title: 'New row',
           panels: [],
           height: '250px',
+          isNew: true,
         });
       };
 

+ 65 - 30
public/app/features/dashboard/row/options.html

@@ -1,42 +1,77 @@
 <div class="dash-row-options">
 
-  <div class="tabbed-view-header">
-    <h2 class="tabbed-view-title">
-      Row Options
-    </h2>
-
-    <button class="tabbed-view-close-btn" ng-click="ctrl.onClose()">
-      <i class="fa fa-remove"></i>
-    </button>
-  </div>
-
-  <br>
-
-  <div class="gf-form-group">
-    <div class="gf-form-inline">
+  <div class="edit-tab-with-sidemenu">
+    <aside class="edit-sidemenu-aside">
+      <ul class="edit-sidemenu">
+        <li ng-class="{active: ctrl.subTabIndex === 0}">
+          <a ng-click="ctrl.subTabIndex = 0">Add Panel</a>
+        </li>
+        <li ng-class="{active: ctrl.subTabIndex === 1}">
+          <a ng-click="ctrl.subTabIndex = 1">Row Options</a>
+        </li>
+        <li ng-class="{active: ctrl.subTabIndex === 2}">
+          <a ng-click="ctrl.deleteRow()">Delete</a>
+        </li>
+      </ul>
+    </aside>
 
-      <div class="gf-form">
-        <span class="gf-form-label width-6">Title</span>
-        <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.title'></input>
+    <div class="edit-tab-content" ng-if="ctrl.subTabIndex === 0">
+      <h5 class="section-heading">Add Panel</h5>
+      <div class="gf-form-inline">
+        <div class="gf-form">
+          <span class="gf-form-label width-6">Search</span>
+          <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.panelSearch' give-focus='true' ng-keydown="ctrl.keyDown($event)" ng-change="ctrl.panelSearchChanged()"></input>
+        </div>
       </div>
-      <div class="gf-form">
-        <span class="gf-form-label width-6">Height</span>
-        <input type="text" class="gf-form-input max-width-8" ng-model='ctrl.row.height'></input>
+
+      <div class="add-panel-panels-wrapper">
+        <div class="add-panel-panels">
+          <div class="add-panel-item" ng-repeat="panel in ctrl.panelHits" ng-class="{active: $index === ctrl.activeIndex}" ng-click="ctrl.addPanel(panel)">
+            <img class="add-panel-item-img" ng-src="{{panel.info.logos.small}}"></img>
+            <div class="add-panel-item-name">{{panel.name}}</div>
+          </div>
+        </div>
       </div>
-      <gf-form-switch class="gf-form" label="Show Title" tooltip="Check to always show row title" checked="ctrl.row.showTitle">
-      </gf-form-switch>
+
     </div>
-  </div>
 
-  <h5 class="section-heading">Row Templating</h5>
+    <div class="edit-tab-content" ng-if="ctrl.subTabIndex === 1">
+      <div class="gf-form-group">
+        <h5 class="section-heading">Options</h5>
+        <div class="gf-form-inline">
+          <div class="gf-form">
+            <span class="gf-form-label width-6">Title</span>
+            <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.title'></input>
+          </div>
+          <div class="gf-form">
+            <label class="gf-form-label width-6">Size</label>
+            <div class="gf-form-select-wrapper">
+              <select class="input-small gf-form-input" ng-model="ctrl.row.titleSize" ng-options="f for f in ctrl.fontSizes"></select>
+            </div>
+          </div>
+          <gf-form-switch class="gf-form" label="Show" checked="ctrl.row.showTitle">
+          </gf-form-switch>
+        </div>
+        <div class="gf-form-inline">
+          <div class="gf-form">
+            <span class="gf-form-label width-6">Height</span>
+            <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.height'></input>
+          </div>
+        </div>
+      </div>
+
+      <h5 class="section-heading">Row Templating</h5>
 
-  <div class="gf-form-group">
-    <div class="gf-form">
-      <span class="gf-form-label">Repeat Row</span>
-      <div class="gf-form-select-wrapper max-width-10">
-        <select class="gf-form-input" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
-          <option value=""></option>
+      <div class="gf-form-group">
+        <div class="gf-form">
+          <span class="gf-form-label">Repeat Row</span>
+          <div class="gf-form-select-wrapper max-width-10">
+            <select class="gf-form-input" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
+              <option value=""></option>
+          </div>
+        </div>
       </div>
     </div>
+
   </div>
 </div>

+ 113 - 3
public/app/features/dashboard/row/options.ts

@@ -2,9 +2,120 @@
 
 import _ from 'lodash';
 
-import {coreModule} from 'app/core/core';
+import config from 'app/core/config';
+import {coreModule, appEvents} from 'app/core/core';
 
 export class RowOptionsCtrl {
+  row: any;
+  dashboard: any;
+  rowCtrl: any;
+  subTabIndex: number;
+  allPanels: any;
+  panelHits: any;
+  activeIndex: any;
+  panelSearch: any;
+
+  fontSizes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+
+  /** @ngInject */
+  constructor(private $scope, private $timeout, private $rootScope) {
+    this.row = this.rowCtrl.row;
+    this.dashboard = this.rowCtrl.dashboard;
+    this.subTabIndex = 0;
+    this.row.titleSize = this.row.titleSize || 'h6';
+    this.allPanels = _.orderBy(_.map(config.panels, item => item), 'sort');
+    this.panelHits = this.allPanels;
+    this.activeIndex = 0;
+  }
+
+  keyDown(evt) {
+    if (evt.keyCode === 27) {
+      this.rowCtrl.showOptions = false;
+      return;
+    }
+
+    if (evt.keyCode === 40 || evt.keyCode === 39) {
+      this.moveSelection(1);
+    }
+
+    if (evt.keyCode === 38 || evt.keyCode === 37) {
+      this.moveSelection(-1);
+    }
+
+    if (evt.keyCode === 13) {
+      var selectedPanel = this.panelHits[this.activeIndex];
+      if (selectedPanel) {
+        this.addPanel(selectedPanel);
+      }
+    }
+  }
+
+  moveSelection(direction) {
+    var max = this.panelHits.length;
+    var newIndex = this.activeIndex + direction;
+    this.activeIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
+  }
+
+  panelSearchChanged() {
+    var items = this.allPanels.slice();
+    var startsWith = [];
+    var contains = [];
+    var searchLower = this.panelSearch.toLowerCase();
+    var item;
+
+    while (item = items.shift()) {
+      var nameLower = item.name.toLowerCase();
+      if (nameLower.indexOf(searchLower) === 0) {
+        startsWith.push(item);
+      } else if (nameLower.indexOf(searchLower) !== -1) {
+        contains.push(item);
+      }
+    }
+
+    this.panelHits = startsWith.concat(contains);
+    this.activeIndex = 0;
+  }
+
+  addPanel(panelPluginInfo) {
+    var defaultSpan = 12;
+    var _as = 12 - this.dashboard.rowSpan(this.row);
+
+    var panel = {
+      id: null,
+      title: config.new_panel_title,
+      error: false,
+      span: _as < defaultSpan && _as > 0 ? _as : defaultSpan,
+      editable: true,
+      type: panelPluginInfo.id,
+      isNew: true,
+    };
+
+    this.rowCtrl.showOptions = false;
+    this.dashboard.addPanel(panel, this.row);
+    this.$timeout(() => {
+      this.$rootScope.appEvent('panel-change-view', {
+        fullscreen: true, edit: true, panelId: panel.id
+      });
+    });
+  }
+
+  deleteRow() {
+    if (!this.row.panels.length) {
+      this.dashboard.rows = _.without(this.dashboard.rows, this.row);
+      return;
+    }
+
+    appEvents.emit('confirm-modal', {
+      title: 'Delete',
+      text: 'Are you sure you want to delete this row?',
+      icon: 'fa-trash',
+      yesText: 'Delete',
+      onConfirm: () => {
+        this.dashboard.rows = _.without(this.dashboard.rows, this.row);
+      }
+    });
+  }
+
 }
 
 export function rowOptionsDirective() {
@@ -15,8 +126,7 @@ export function rowOptionsDirective() {
     bindToController: true,
     controllerAs: 'ctrl',
     scope: {
-      row: "=",
-      onClose: "&"
+      rowCtrl: "=",
     },
   };
 }

+ 12 - 57
public/app/features/dashboard/row/row.html

@@ -1,70 +1,25 @@
-<div class="dash-row-header gf-form-inline" ng-if="ctrl.dashboard.editMode">
-  <div class="gf-form gf-form--grow dropdown">
-    <a class="btn gf-form-btn btn-inverse dropdown-toggle" data-toggle="dropdown">
-      <span>{{ctrl.row.title}}</span>
-      <i class="fa fa-caret-down"></i>
-    </a>
+<div class="dash-row-header">
+  <a class="dash-row-header-title" ng-click="ctrl.showOptions = !ctrl.showOptions">
+    <span ng-class="ctrl.row.titleSize">{{ctrl.row.title}}</span>
+    <i class="fa fa-caret-down"></i>
+  </a>
 
-    <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
-      <li>
-        <a ng-click="ctrl.showOptions = !ctrl.showOptions">Options</a>
-      </li>
-      <li class="dropdown-submenu">
-        <a href="javascript:void(0);">Height</a>
-        <ul class="dropdown-menu">
-          <li><a ng-click="ctrl.setHeight('25px')">25 px</a></li>
-          <li><a ng-click="ctrl.setHeight('100px')">100 px</a></li>
-          <li><a ng-click="ctrl.setHeight('150px')">150 px</a></li>
-          <li><a ng-click="ctrl.setHeight('200px')">200 px</a></li>
-          <li><a ng-click="ctrl.setHeight('250px')">250 px</a></li>
-          <li><a ng-click="ctrl.setHeight('300px')">300 px</a></li>
-          <li><a ng-click="ctrl.setHeight('350px')">350 px</a></li>
-          <li><a ng-click="ctrl.setHeight('450px')">450 px</a></li>
-          <li><a ng-click="ctrl.setHeight('500px')">500 px</a></li>
-          <li><a ng-click="ctrl.setHeight('600px')">600 px</a></li>
-          <li><a ng-click="ctrl.setHeight('700px')">700 px</a></li>
-        </ul>
-      </li>
-      <li class="dropdown-submenu">
-        <a href="javascript:void(0);">Move</a>
-        <ul class="dropdown-menu">
-          <li><a ng-click="ctrl.Move('up')">Up</a></li>
-          <li><a ng-click="ctrl.Move('down')">Down</a></li>
-          <li><a ng-click="ctrl.Move('top')">To Top</a></li>
-          <li><a ng-click="ctrl.Move('bottom')">To Bottom</a></li>
-        </ul>
-      </li>
-      <li>
-        <a ng-click="ctrl.deleteRow()">Delete</a>
-      </li>
-    </ul>
+  <div class="dash-row-header-spacer">
+  </div>
 
-    <a class="btn gf-form-btn btn-inverse" ng-click="ctrl.addPanelMode=!ctrl.addPanelMode">
-      Add Panel
-      <i class="fa fa-plus"></i>
+  <div class="dash-row-collapse-toggle" ng-click="ctrl.row.collapse = !ctrl.row.collapse">
+    <a class="pointer">
+      <i class="fa fa-chevron-down" ng-show="!ctrl.row.collapse"></i>
+      <i class="fa fa-chevron-right" ng-show="ctrl.row.collapse"></i>
     </a>
-
-    <div class="gf-form-label gf-form-label--grow text-right" ng-click="ctrl.row.collapse = !ctrl.row.collapse">
-      <a class="pointer">
-        <i class="fa fa-chevron-down" ng-show="!ctrl.row.collapse"></i>
-        <i class="fa fa-chevron-right" ng-show="ctrl.row.collapse"></i>
-      </a>
-    </div>
   </div>
 </div>
 
 <div ng-if="ctrl.showOptions">
-  <dash-row-options row="ctrl.row" on-close="ctrl.showOptions = false;"></dash-row-options>
+  <dash-row-options row-ctrl="ctrl"></dash-row-options>
 </div>
 
 <div class="panels-wrapper" ng-if="!ctrl.row.collapse">
-  <div class="add-panel-container" ng-if="ctrl.addPanelMode">
-    <div class="add-panel-item" ng-repeat="panel in ctrl.panelPlugins">
-      <img class="add-panel-item-img" ng-src="{{panel.info.logos.small}}"></img>
-      <div class="add-panel-item-name">{{panel.name}}</div>
-    </div>
-  </div>
-
   <div ng-repeat="panel in ctrl.row.panels track by panel.id" class="panel" ui-draggable="!ctrl.dashboard.meta.fullscreen" drag="panel.id" ui-on-drop="ctrl.onDrop($data, panel)" drag-handle-class="drag-handle" panel-width>
     <plugin-component type="panel" class="panel-margin">
     </plugin-component>

+ 7 - 60
public/app/features/dashboard/row/row.ts

@@ -5,36 +5,23 @@ import $ from 'jquery';
 import angular from 'angular';
 
 import config from 'app/core/config';
-import {coreModule, appEvents} from 'app/core/core';
+import {coreModule} from 'app/core/core';
 
 import './options';
 
 export class DashRowCtrl {
   dashboard: any;
   row: any;
-  panelPlugins;
-  addPanelSegment;
+  showOptions: boolean;
 
-   /** @ngInject */
+  /** @ngInject */
   constructor(private $scope, private $rootScope, private $timeout, private uiSegmentSrv, private $q) {
-    this.panelPlugins = config.panels;
-    console.log(this.panelPlugins);
-
     this.row.title = this.row.title || 'Row title';
-    this.addPanelSegment = uiSegmentSrv.newSegment({
-      value: 'add',
-      custom: 'false',
-      html: 'Add Panel <i class="fa fa-plus"></i>',
-      renderer: (item, defaultHighlighter) => {
-        return '<img src="' + item.img + '">' + defaultHighlighter(item.text);
-      }
-    });
-  }
 
-  getPanels() {
-    return this.$q.when(_.map(config.panels, panel => {
-      return this.uiSegmentSrv.newSegment({value: panel.name});
-    }));
+    if (this.row.isNew) {
+      this.showOptions = true;
+      delete this.row.isNew;
+    }
   }
 
   onDrop(panelId, dropTarget) {
@@ -55,51 +42,11 @@ export class DashRowCtrl {
     this.$rootScope.$broadcast('render');
   }
 
-  addPanel(panel) {
-    this.dashboard.addPanel(panel, this.row);
-    this.$timeout(() => {
-      this.$scope.$broadcast('render');
-    });
-  }
-
   setHeight(height) {
     this.row.height = height;
     this.$scope.$broadcast('render');
   }
 
-  addPanelDefault(type) {
-    var defaultSpan = 12;
-    var _as = 12 - this.dashboard.rowSpan(this.row);
-
-    var panel = {
-      title: config.new_panel_title,
-      error: false,
-      span: _as < defaultSpan && _as > 0 ? _as : defaultSpan,
-      editable: true,
-      type: type,
-      isNew: true,
-    };
-
-    this.addPanel(panel);
-  }
-
-  deleteRow() {
-    if (!this.row.panels.length) {
-      this.dashboard.rows = _.without(this.dashboard.rows, this.row);
-      return;
-    }
-
-    appEvents.emit('confirm-modal', {
-      title: 'Delete',
-      text: 'Are you sure you want to delete this row?',
-      icon: 'fa-trash',
-      yesText: 'Delete',
-      onConfirm: () => {
-        this.dashboard.rows = _.without(this.dashboard.rows, this.row);
-      }
-    });
-  }
-
   moveRow(direction) {
     var rowsList = this.dashboard.rows;
     var rowIndex = _.indexOf(rowsList, this.row);

+ 1 - 1
public/sass/components/edit_sidemenu.scss

@@ -9,7 +9,7 @@
 }
 
 .edit-sidemenu-aside {
-  width: 16rem;
+  min-width: 16rem;
 }
 
 .edit-sidemenu {

+ 34 - 11
public/sass/pages/_dashboard.scss

@@ -211,19 +211,40 @@ div.flot-text {
 .dash-row-header {
   display: flex;
   flex-direction: row;
-  text-align: left;
-  align-items: center;
   margin-right: $panel-margin;
   margin-left: $gf-form-margin;
+  border-bottom: $panel-border;
 }
 
 .dash-row-header-title {
-  font-family: $headings-font-family;
   padding: 0.7rem;
+  i {
+    font-size: 0.9rem;
+    position: relative;
+    top: 2px;
+    left: 1px;
+    color: $text-muted;
+  }
 }
 
-.dash-row-header-chevron {
-  flex-grow: 100;
+.dash-row-header-add-panel {
+  padding: 0.7rem;
+  i {
+    font-size: 0.9rem;
+    position: relative;
+    top: 2px;
+    left: 1px;
+    color: $text-muted;
+  }
+}
+
+.dash-row-header-spacer {
+  flex: 50;
+}
+
+.dash-row-collapse-toggle {
+  flex-grow: 30;
+  cursor: pointer;
   text-align: right;
   margin-right: 0.6rem;
   font-size: $font-size-sm;
@@ -247,7 +268,7 @@ div.flot-text {
 .dash-row-options {
   background: $panel-bg;
   margin: 0 $panel-margin*2 0 $panel-margin;
-  padding: $spacer*2;
+  padding: $spacer*1.5;
 }
 
 .dash-row-options-close-btn {
@@ -266,18 +287,20 @@ div.flot-text {
   }
 }
 
-.add-panel-container {
-  display: flex;
-  flex-direction: row;
+.add-panel-panels {
 }
 
 .add-panel-item {
-  background: $panel-bg;
+  background: $input-label-bg;
   padding: $spacer;
   min-width: 10rem;
   max-width: 10rem;
   text-align: center;
-  margin: $panel-margin;
+  margin: $gf-form-margin;
+  float: left;
+  cursor: pointer;
+
+  &.active,
   &:hover {
     box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 5px rgba(82,168,236,10.8)
   }