Bladeren bron

Merge remote-tracking branch 'origin/10248_copy_panels'

Torkel Ödegaard 8 jaren geleden
bovenliggende
commit
b4ab91d651

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

@@ -27,6 +27,7 @@ import './acl/acl';
 import './folder_picker/folder_picker';
 import './move_to_folder_modal/move_to_folder';
 import './settings/settings';
+import './panel_clipboard_srv';
 
 import coreModule from 'app/core/core_module';
 import { DashboardListCtrl } from './dashboard_list_ctrl';

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

@@ -22,7 +22,8 @@ export class DashboardCtrl implements PanelContainer {
     private unsavedChangesSrv,
     private dashboardViewStateSrv,
     public playlistSrv,
-    private panelLoader
+    private panelLoader,
+    private panelClipboardSrv
   ) {
     // temp hack due to way dashboards are loaded
     // can't use controllerAs on route yet
@@ -122,6 +123,10 @@ export class DashboardCtrl implements PanelContainer {
     return this.panelLoader;
   }
 
+  getClipboardPanel() {
+    return this.panelClipboardSrv.getPanel();
+  }
+
   timezoneChanged() {
     this.$rootScope.$broadcast('refresh');
   }

+ 42 - 9
public/app/features/dashboard/dashgrid/AddPanelPanel.tsx

@@ -2,8 +2,8 @@ import React from 'react';
 import _ from 'lodash';
 
 import config from 'app/core/config';
-import {PanelModel} from '../panel_model';
-import {PanelContainer} from './PanelContainer';
+import { PanelModel } from '../panel_model';
+import { PanelContainer } from './PanelContainer';
 import ScrollBar from 'app/core/components/ScrollBar/ScrollBar';
 
 export interface AddPanelPanelProps {
@@ -14,6 +14,7 @@ export interface AddPanelPanelProps {
 export interface AddPanelPanelState {
   filter: string;
   panelPlugins: any[];
+  clipboardPanel: any;
 }
 
 export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelPanelState> {
@@ -22,45 +23,77 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
 
     this.state = {
       panelPlugins: this.getPanelPlugins(),
+      clipboardPanel: this.getClipboardPanel(),
       filter: '',
     };
 
     this.onPanelSelected = this.onPanelSelected.bind(this);
+    this.onClipboardPanelSelected = this.onClipboardPanelSelected.bind(this);
   }
 
   getPanelPlugins() {
     let panels = _.chain(config.panels)
-      .filter({hideFromList: false})
+      .filter({ hideFromList: false })
       .map(item => item)
       .value();
 
     // add special row type
-    panels.push({id: 'row', name: 'Row', sort: 8, info: {logos: {small: 'public/img/icn-row.svg'}}});
+    panels.push({ id: 'row', name: 'Row', sort: 8, info: { logos: { small: 'public/img/icn-row.svg' } } });
 
     // add sort by sort property
     return _.sortBy(panels, 'sort');
   }
 
+  getClipboardPanel() {
+    return this.props.getPanelContainer().getClipboardPanel();
+  }
+
   onPanelSelected(panelPluginInfo) {
     const panelContainer = this.props.getPanelContainer();
     const dashboard = panelContainer.getDashboard();
-    const {gridPos} = this.props.panel;
+    const { gridPos } = this.props.panel;
 
     var newPanel: any = {
       type: panelPluginInfo.id,
       title: 'Panel Title',
-      gridPos: {x: gridPos.x, y: gridPos.y, w: gridPos.w, h: gridPos.h}
+      gridPos: { x: gridPos.x, y: gridPos.y, w: gridPos.w, h: gridPos.h },
     };
 
     if (panelPluginInfo.id === 'row') {
       newPanel.title = 'Row title';
-      newPanel.gridPos = {x: 0, y: 0};
+      newPanel.gridPos = { x: 0, y: 0 };
     }
 
     dashboard.addPanel(newPanel);
     dashboard.removePanel(this.props.panel);
   }
 
+  onClipboardPanelSelected(panel) {
+    const panelContainer = this.props.getPanelContainer();
+    const dashboard = panelContainer.getDashboard();
+
+    const { gridPos } = this.props.panel;
+    panel.gridPos.x = gridPos.x;
+    panel.gridPos.y = gridPos.y;
+
+    dashboard.addPanel(panel);
+    dashboard.removePanel(this.props.panel);
+  }
+
+  renderClipboardPanel(copiedPanel) {
+    const panel = copiedPanel.panel;
+    const title = `Paste copied panel '${panel.title}' from '${copiedPanel.dashboard}'`;
+
+    return (
+      <div className="add-panel__item" onClick={() => this.onClipboardPanelSelected(panel)} title={title}>
+        <div className="add-panel__item-icon">
+          <i className="fa fa-paste fa-2x fa-fw" />
+        </div>
+        <div className="add-panel__item-name">Paste copied panel</div>
+      </div>
+    );
+  }
+
   renderPanelItem(panel) {
     return (
       <div key={panel.id} className="add-panel__item" onClick={() => this.onPanelSelected(panel)} title={panel.name}>
@@ -75,11 +108,12 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
       <div className="panel-container">
         <div className="add-panel">
           <div className="add-panel__header">
-            <i className="gicon gicon-add-panel"></i>
+            <i className="gicon gicon-add-panel" />
             <span className="add-panel__title">New Panel</span>
             <span className="add-panel__sub-title">Select a visualization</span>
           </div>
           <ScrollBar className="add-panel__items">
+            {this.state.clipboardPanel && this.renderClipboardPanel(this.state.clipboardPanel)}
             {this.state.panelPlugins.map(this.renderPanelItem.bind(this))}
           </ScrollBar>
         </div>
@@ -87,4 +121,3 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
     );
   }
 }
-

+ 1 - 0
public/app/features/dashboard/dashgrid/PanelContainer.ts

@@ -4,4 +4,5 @@ import { PanelLoader } from './PanelLoader';
 export interface PanelContainer {
   getPanelLoader(): PanelLoader;
   getDashboard(): DashboardModel;
+  getClipboardPanel(): any;
 }

+ 21 - 0
public/app/features/dashboard/panel_clipboard_srv.ts

@@ -0,0 +1,21 @@
+import coreModule from 'app/core/core_module';
+import { appEvents } from 'app/core/core';
+
+class PanelClipboardSrv {
+  key = 'GrafanaDashboardClipboardPanel';
+
+  /** @ngInject **/
+  constructor(private $window) {
+    appEvents.on('copy-dashboard-panel', this.copyDashboardPanel.bind(this));
+  }
+
+  getPanel() {
+    return this.$window[this.key];
+  }
+
+  private copyDashboardPanel(payload) {
+    this.$window[this.key] = payload;
+  }
+}
+
+coreModule.service('panelClipboardSrv', PanelClipboardSrv);

+ 19 - 0
public/app/features/panel/panel_ctrl.ts

@@ -195,6 +195,14 @@ export class PanelCtrl {
       text: 'Panel JSON',
       click: 'ctrl.editPanelJson(); dismiss();',
     });
+
+    menu.push({
+      text: 'Copy to Clipboard',
+      click: 'ctrl.copyPanelToClipboard()',
+      role: 'Editor',
+      directives: ['clipboard-button="ctrl.getPanelJson()"'],
+    });
+
     this.events.emit('init-panel-actions', menu);
     return menu;
   }
@@ -270,6 +278,17 @@ export class PanelCtrl {
     });
   }
 
+  copyPanelToClipboard() {
+    appEvents.emit('copy-dashboard-panel', {
+      dashboard: this.dashboard.title,
+      panel: this.panel.getSaveModel(),
+    });
+  }
+
+  getPanelJson() {
+    return JSON.stringify(this.panel.getSaveModel(), null, 2);
+  }
+
   replacePanel(newPanel, oldPanel) {
     let dashboard = this.dashboard;
     let index = _.findIndex(dashboard.panels, panel => {

+ 6 - 0
public/app/features/panel/panel_header.ts

@@ -51,6 +51,12 @@ function renderMenuItem(item, ctrl) {
     html += ` href="${item.href}"`;
   }
 
+  if (item.directives) {
+    for (let directive of item.directives) {
+      html += ` ${directive}`;
+    }
+  }
+
   html += `><i class="${item.icon}"></i>`;
   html += `<span class="dropdown-item-text">${item.text}</span>`;
 

+ 1 - 1
public/sass/base/font-awesome/_larger.scss

@@ -8,7 +8,7 @@
   vertical-align: -15%;
 }
 .#{$fa-css-prefix}-2x {
-  font-size: 2em;
+  font-size: 2em !important;
 }
 .#{$fa-css-prefix}-3x {
   font-size: 3em;

+ 4 - 0
public/sass/components/_panel_add_panel.scss

@@ -65,3 +65,7 @@
 .add-panel__item-img {
   height: calc(100% - 15px);
 }
+
+.add-panel__item-icon {
+  padding: 2px;
+}