Commit fd81f895 by Torkel Ödegaard

wip: angular panels now have similar edit mode and panel type selection enabling…

wip: angular panels now have similar edit mode and panel type selection enabling quick changing between panel react and angular panel types
parent 6ba8f6c5
...@@ -3,7 +3,7 @@ import coreModule from '../core_module'; ...@@ -3,7 +3,7 @@ import coreModule from '../core_module';
class DynamicDirectiveSrv { class DynamicDirectiveSrv {
/** @ngInject */ /** @ngInject */
constructor(private $compile, private $rootScope) {} constructor(private $compile) {}
addDirective(element, name, scope) { addDirective(element, name, scope) {
var child = angular.element(document.createElement(name)); var child = angular.element(document.createElement(name));
...@@ -14,25 +14,19 @@ class DynamicDirectiveSrv { ...@@ -14,25 +14,19 @@ class DynamicDirectiveSrv {
} }
link(scope, elem, attrs, options) { link(scope, elem, attrs, options) {
options const directiveInfo = options.directive(scope);
.directive(scope)
.then(directiveInfo => {
if (!directiveInfo || !directiveInfo.fn) { if (!directiveInfo || !directiveInfo.fn) {
elem.empty(); elem.empty();
return; return;
} }
if (!directiveInfo.fn.registered) { if (!directiveInfo.fn.registered) {
console.log('register panel tab');
coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn); coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
directiveInfo.fn.registered = true; directiveInfo.fn.registered = true;
} }
this.addDirective(elem, directiveInfo.name, scope); this.addDirective(elem, directiveInfo.name, scope);
})
.catch(err => {
console.log('Plugin load:', err);
this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]);
});
} }
create(options) { create(options) {
......
...@@ -228,6 +228,11 @@ export class DashboardModel { ...@@ -228,6 +228,11 @@ export class DashboardModel {
return this.meta.fullscreen && !panel.fullscreen; return this.meta.fullscreen && !panel.fullscreen;
} }
changePanelType(panel: PanelModel, pluginId: string) {
panel.changeType(pluginId);
this.events.emit('panel-type-changed', panel);
}
private ensureListExist(data) { private ensureListExist(data) {
if (!data) { if (!data) {
data = {}; data = {};
......
...@@ -84,6 +84,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -84,6 +84,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this)); dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this));
dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this));
dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); dashboard.on('row-expanded', this.triggerForceUpdate.bind(this));
dashboard.on('panel-type-changed', this.triggerForceUpdate.bind(this));
} }
buildLayout() { buildLayout() {
...@@ -177,7 +178,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -177,7 +178,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });
panelElements.push( panelElements.push(
<div key={panel.id.toString()} className={panelClasses}> <div key={panel.id.toString()} className={panelClasses}>
<DashboardPanel panel={panel} dashboard={this.props.dashboard} /> <DashboardPanel panel={panel} dashboard={this.props.dashboard} panelType={panel.type} />
</div> </div>
); );
} }
......
...@@ -11,6 +11,7 @@ import { PanelChrome } from './PanelChrome'; ...@@ -11,6 +11,7 @@ import { PanelChrome } from './PanelChrome';
import { PanelEditor } from './PanelEditor'; import { PanelEditor } from './PanelEditor';
export interface Props { export interface Props {
panelType: string;
panel: PanelModel; panel: PanelModel;
dashboard: DashboardModel; dashboard: DashboardModel;
} }
...@@ -53,6 +54,10 @@ export class DashboardPanel extends React.Component<Props, State> { ...@@ -53,6 +54,10 @@ export class DashboardPanel extends React.Component<Props, State> {
this.loadPlugin(); this.loadPlugin();
}; };
onAngularPluginTypeChanged = () => {
this.loadPlugin();
};
loadPlugin() { loadPlugin() {
if (this.isSpecial()) { if (this.isSpecial()) {
return; return;
...@@ -63,9 +68,11 @@ export class DashboardPanel extends React.Component<Props, State> { ...@@ -63,9 +68,11 @@ export class DashboardPanel extends React.Component<Props, State> {
this.pluginInfo = config.panels[this.props.panel.type]; this.pluginInfo = config.panels[this.props.panel.type];
if (this.pluginInfo.exports) { if (this.pluginInfo.exports) {
this.cleanUpAngularPanel();
this.setState({ pluginExports: this.pluginInfo.exports }); this.setState({ pluginExports: this.pluginInfo.exports });
} else { } else {
importPluginModule(this.pluginInfo.module).then(pluginExports => { importPluginModule(this.pluginInfo.module).then(pluginExports => {
this.cleanUpAngularPanel();
// cache plugin exports (saves a promise async cycle next time) // cache plugin exports (saves a promise async cycle next time)
this.pluginInfo.exports = pluginExports; this.pluginInfo.exports = pluginExports;
// update panel state // update panel state
...@@ -80,7 +87,6 @@ export class DashboardPanel extends React.Component<Props, State> { ...@@ -80,7 +87,6 @@ export class DashboardPanel extends React.Component<Props, State> {
} }
componentDidUpdate() { componentDidUpdate() {
console.log('componentDidUpdate');
this.loadPlugin(); this.loadPlugin();
// handle angular plugin loading // handle angular plugin loading
...@@ -94,12 +100,17 @@ export class DashboardPanel extends React.Component<Props, State> { ...@@ -94,12 +100,17 @@ export class DashboardPanel extends React.Component<Props, State> {
this.angularPanel = loader.load(this.element, scopeProps, template); this.angularPanel = loader.load(this.element, scopeProps, template);
} }
componentWillUnmount() { cleanUpAngularPanel() {
if (this.angularPanel) { if (this.angularPanel) {
this.angularPanel.destroy(); this.angularPanel.destroy();
this.angularPanel = null;
} }
} }
componentWillUnmount() {
this.cleanUpAngularPanel();
}
renderReactPanel() { renderReactPanel() {
const { pluginExports } = this.state; const { pluginExports } = this.state;
const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper'; const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
......
...@@ -31,7 +31,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> { ...@@ -31,7 +31,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
this.tabs = [ this.tabs = [
{ id: 'queries', text: 'Queries', icon: 'fa fa-database' }, { id: 'queries', text: 'Queries', icon: 'fa fa-database' },
{ id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, { id: 'visualization', text: 'Visualization', icon: 'fa fa-line-chart' },
]; ];
} }
...@@ -87,7 +87,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> { ...@@ -87,7 +87,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
<div className="tabbed-view-body"> <div className="tabbed-view-body">
{activeTab === 'queries' && this.renderQueriesTab()} {activeTab === 'queries' && this.renderQueriesTab()}
{activeTab === 'viz' && this.renderVizTab()} {activeTab === 'visualization' && this.renderVizTab()}
</div> </div>
</div> </div>
); );
...@@ -109,8 +109,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) { ...@@ -109,8 +109,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) {
return ( return (
<li className="gf-tabs-item" key={tab.id}> <li className="gf-tabs-item" key={tab.id}>
<a className={tabClasses} onClick={() => onClick(tab)}> <a className={tabClasses} onClick={() => onClick(tab)}>
<i className={tab.icon} /> <i className={tab.icon} /> {tab.text}
{tab.text}
</a> </a>
</li> </li>
); );
......
...@@ -34,6 +34,7 @@ export class PanelModel { ...@@ -34,6 +34,7 @@ export class PanelModel {
soloMode?: boolean; soloMode?: boolean;
targets: any[]; targets: any[];
datasource: string; datasource: string;
thresholds?: any;
// non persisted // non persisted
fullscreen: boolean; fullscreen: boolean;
...@@ -116,9 +117,11 @@ export class PanelModel { ...@@ -116,9 +117,11 @@ export class PanelModel {
this.events.emit('panel-init-edit-mode'); this.events.emit('panel-init-edit-mode');
} }
changeType(newType: string) { changeType(pluginId: string) {
this.type = newType; this.type = pluginId;
this.events.emit('panel-size-changed');
delete this.thresholds;
delete this.alert;
} }
destroy() { destroy() {
......
...@@ -16,7 +16,7 @@ export class DashboardViewState { ...@@ -16,7 +16,7 @@ export class DashboardViewState {
oldTimeRange: any; oldTimeRange: any;
/** @ngInject */ /** @ngInject */
constructor($scope, private $location, private $timeout, private $rootScope) { constructor($scope, private $location, private $timeout) {
var self = this; var self = this;
self.state = {}; self.state = {};
self.panelScopes = []; self.panelScopes = [];
...@@ -176,10 +176,10 @@ export class DashboardViewState { ...@@ -176,10 +176,10 @@ export class DashboardViewState {
} }
/** @ngInject */ /** @ngInject */
export function dashboardViewStateSrv($location, $timeout, $rootScope) { export function dashboardViewStateSrv($location, $timeout) {
return { return {
create: function($scope) { create: function($scope) {
return new DashboardViewState($scope, $location, $timeout, $rootScope); return new DashboardViewState($scope, $location, $timeout);
}, },
}; };
} }
......
...@@ -8,8 +8,6 @@ import * as rangeUtil from 'app/core/utils/rangeutil'; ...@@ -8,8 +8,6 @@ import * as rangeUtil from 'app/core/utils/rangeutil';
import * as dateMath from 'app/core/utils/datemath'; import * as dateMath from 'app/core/utils/datemath';
import { encodePathComponent } from 'app/core/utils/location_util'; import { encodePathComponent } from 'app/core/utils/location_util';
import { metricsTabDirective } from './metrics_tab';
class MetricsPanelCtrl extends PanelCtrl { class MetricsPanelCtrl extends PanelCtrl {
scope: any; scope: any;
datasource: any; datasource: any;
...@@ -58,7 +56,6 @@ class MetricsPanelCtrl extends PanelCtrl { ...@@ -58,7 +56,6 @@ class MetricsPanelCtrl extends PanelCtrl {
} }
private onInitMetricsPanelEditMode() { private onInitMetricsPanelEditMode() {
this.addEditorTab('Metrics', metricsTabDirective);
this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
} }
......
...@@ -6,6 +6,8 @@ import { PanelModel } from 'app/features/dashboard/panel_model'; ...@@ -6,6 +6,8 @@ import { PanelModel } from 'app/features/dashboard/panel_model';
import Remarkable from 'remarkable'; import Remarkable from 'remarkable';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants';
import store from 'app/core/store'; import store from 'app/core/store';
import { metricsTabDirective } from './metrics_tab';
import { vizTabDirective } from './viz_tab';
const TITLE_HEIGHT = 27; const TITLE_HEIGHT = 27;
const PANEL_BORDER = 2; const PANEL_BORDER = 2;
...@@ -97,7 +99,10 @@ export class PanelCtrl { ...@@ -97,7 +99,10 @@ export class PanelCtrl {
initEditMode() { initEditMode() {
this.editorTabs = []; this.editorTabs = [];
this.addEditorTab('Queries', metricsTabDirective, 0, 'fa fa-database');
this.addEditorTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart');
this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); this.addEditorTab('General', 'public/app/partials/panelgeneral.html');
this.editModeInitiated = true; this.editModeInitiated = true;
this.events.emit('init-edit-mode', null); this.events.emit('init-edit-mode', null);
...@@ -118,8 +123,8 @@ export class PanelCtrl { ...@@ -118,8 +123,8 @@ export class PanelCtrl {
route.updateParams(); route.updateParams();
} }
addEditorTab(title, directiveFn, index?) { addEditorTab(title, directiveFn, index?, icon?) {
var editorTab = { title, directiveFn }; var editorTab = { title, directiveFn, icon };
if (_.isString(directiveFn)) { if (_.isString(directiveFn)) {
editorTab.directiveFn = function() { editorTab.directiveFn = function() {
......
...@@ -32,13 +32,11 @@ var panelTemplate = ` ...@@ -32,13 +32,11 @@ var panelTemplate = `
'panel-height-helper': !ctrl.panel.isEditing}"> 'panel-height-helper': !ctrl.panel.isEditing}">
<div class="tabbed-view tabbed-view--new"> <div class="tabbed-view tabbed-view--new">
<div class="tabbed-view-header"> <div class="tabbed-view-header">
<h3 class="tabbed-view-panel-title">
{{ctrl.pluginName}}
</h3>
<ul class="gf-tabs"> <ul class="gf-tabs">
<li class="gf-tabs-item" ng-repeat="tab in ::ctrl.editorTabs"> <li class="gf-tabs-item" ng-repeat="tab in ::ctrl.editorTabs">
<a class="gf-tabs-link" ng-click="ctrl.changeTab($index)" ng-class="{active: ctrl.editorTabIndex === $index}"> <a class="gf-tabs-link" ng-click="ctrl.changeTab($index)" ng-class="{active: ctrl.editorTabIndex === $index}">
<i class="{{::tab.icon}}" ng-show="tab.icon"></i>
{{::tab.title}} {{::tab.title}}
</a> </a>
</li> </li>
...@@ -50,7 +48,7 @@ var panelTemplate = ` ...@@ -50,7 +48,7 @@ var panelTemplate = `
</div> </div>
<div class="tabbed-view-body"> <div class="tabbed-view-body">
<div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index"> <div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index" class="panel-height-helper">
<panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab> <panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
</div> </div>
</div> </div>
......
import angular from 'angular'; import angular from 'angular';
var directiveModule = angular.module('grafana.directives'); const directiveModule = angular.module('grafana.directives');
const directiveCache = {};
/** @ngInject */ /** @ngInject */
function panelEditorTab(dynamicDirectiveSrv) { function panelEditorTab(dynamicDirectiveSrv) {
...@@ -11,18 +12,25 @@ function panelEditorTab(dynamicDirectiveSrv) { ...@@ -11,18 +12,25 @@ function panelEditorTab(dynamicDirectiveSrv) {
index: '=', index: '=',
}, },
directive: scope => { directive: scope => {
var pluginId = scope.ctrl.pluginId; const pluginId = scope.ctrl.pluginId;
var tabIndex = scope.index; const tabIndex = scope.index;
// create a wrapper for directiveFn
// required for metrics tab directive
// that is the same for many panels but
// given different names in this function
var fn = () => scope.editorTab.directiveFn();
return Promise.resolve({ if (directiveCache[pluginId]) {
if (directiveCache[pluginId][tabIndex]) {
return directiveCache[pluginId][tabIndex];
}
} else {
directiveCache[pluginId] = [];
}
let result = {
fn: () => scope.editorTab.directiveFn(),
name: `panel-editor-tab-${pluginId}${tabIndex}`, name: `panel-editor-tab-${pluginId}${tabIndex}`,
fn: fn, };
});
directiveCache[pluginId][tabIndex] = result;
return result;
}, },
}); });
} }
......
import coreModule from 'app/core/core_module';
import { DashboardModel } from '../dashboard/dashboard_model';
import { VizTypePicker } from '../dashboard/dashgrid/VizTypePicker';
import { react2AngularDirective } from 'app/core/utils/react2angular';
import { PanelPlugin } from 'app/types/plugins';
export class VizTabCtrl {
panelCtrl: any;
dashboard: DashboardModel;
/** @ngInject */
constructor($scope) {
this.panelCtrl = $scope.ctrl;
this.dashboard = this.panelCtrl.dashboard;
$scope.ctrl = this;
}
onTypeChanged = (plugin: PanelPlugin) => {
this.dashboard.changePanelType(this.panelCtrl.panel, plugin.id);
};
}
let template = `
<div class="viz-editor">
<div class="viz-editor-col1">
<viz-type-picker currentType="ctrl.panelCtrl.panel.type" onTypeChanged="ctrl.onTypeChanged"></viz-type-picker>
</div>
<div class="viz-editor-col2">
<h5 class="page-heading">Options</h5>
</div>
</div>
`;
/** @ngInject **/
export function vizTabDirective() {
'use strict';
return {
restrict: 'E',
scope: true,
template: template,
controller: VizTabCtrl,
};
}
react2AngularDirective('vizTypePicker', VizTypePicker, ['currentType', ['onTypeChanged', { watchDepth: 'reference' }]]);
coreModule.directive('vizTab', vizTabDirective);
...@@ -211,6 +211,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ ...@@ -211,6 +211,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
scope.$applyAsync(function() { scope.$applyAsync(function() {
scope.$broadcast('component-did-mount'); scope.$broadcast('component-did-mount');
scope.$broadcast('refresh'); scope.$broadcast('refresh');
console.log('appendAndCompile', scope.panel);
}); });
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment