mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip: angular panels now have similar edit mode and panel type selection enabling quick changing between panel react and angular panel types
This commit is contained in:
@@ -3,7 +3,7 @@ import coreModule from '../core_module';
|
||||
|
||||
class DynamicDirectiveSrv {
|
||||
/** @ngInject */
|
||||
constructor(private $compile, private $rootScope) {}
|
||||
constructor(private $compile) {}
|
||||
|
||||
addDirective(element, name, scope) {
|
||||
var child = angular.element(document.createElement(name));
|
||||
@@ -14,25 +14,19 @@ class DynamicDirectiveSrv {
|
||||
}
|
||||
|
||||
link(scope, elem, attrs, options) {
|
||||
options
|
||||
.directive(scope)
|
||||
.then(directiveInfo => {
|
||||
if (!directiveInfo || !directiveInfo.fn) {
|
||||
elem.empty();
|
||||
return;
|
||||
}
|
||||
const directiveInfo = options.directive(scope);
|
||||
if (!directiveInfo || !directiveInfo.fn) {
|
||||
elem.empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!directiveInfo.fn.registered) {
|
||||
coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
|
||||
directiveInfo.fn.registered = true;
|
||||
}
|
||||
if (!directiveInfo.fn.registered) {
|
||||
console.log('register panel tab');
|
||||
coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
|
||||
directiveInfo.fn.registered = true;
|
||||
}
|
||||
|
||||
this.addDirective(elem, directiveInfo.name, scope);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Plugin load:', err);
|
||||
this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]);
|
||||
});
|
||||
this.addDirective(elem, directiveInfo.name, scope);
|
||||
}
|
||||
|
||||
create(options) {
|
||||
|
@@ -228,6 +228,11 @@ export class DashboardModel {
|
||||
return this.meta.fullscreen && !panel.fullscreen;
|
||||
}
|
||||
|
||||
changePanelType(panel: PanelModel, pluginId: string) {
|
||||
panel.changeType(pluginId);
|
||||
this.events.emit('panel-type-changed', panel);
|
||||
}
|
||||
|
||||
private ensureListExist(data) {
|
||||
if (!data) {
|
||||
data = {};
|
||||
|
@@ -84,6 +84,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
|
||||
dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this));
|
||||
dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this));
|
||||
dashboard.on('row-expanded', this.triggerForceUpdate.bind(this));
|
||||
dashboard.on('panel-type-changed', this.triggerForceUpdate.bind(this));
|
||||
}
|
||||
|
||||
buildLayout() {
|
||||
@@ -177,7 +178,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
|
||||
const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });
|
||||
panelElements.push(
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import { PanelChrome } from './PanelChrome';
|
||||
import { PanelEditor } from './PanelEditor';
|
||||
|
||||
export interface Props {
|
||||
panelType: string;
|
||||
panel: PanelModel;
|
||||
dashboard: DashboardModel;
|
||||
}
|
||||
@@ -53,6 +54,10 @@ export class DashboardPanel extends React.Component<Props, State> {
|
||||
this.loadPlugin();
|
||||
};
|
||||
|
||||
onAngularPluginTypeChanged = () => {
|
||||
this.loadPlugin();
|
||||
};
|
||||
|
||||
loadPlugin() {
|
||||
if (this.isSpecial()) {
|
||||
return;
|
||||
@@ -63,9 +68,11 @@ export class DashboardPanel extends React.Component<Props, State> {
|
||||
this.pluginInfo = config.panels[this.props.panel.type];
|
||||
|
||||
if (this.pluginInfo.exports) {
|
||||
this.cleanUpAngularPanel();
|
||||
this.setState({ pluginExports: this.pluginInfo.exports });
|
||||
} else {
|
||||
importPluginModule(this.pluginInfo.module).then(pluginExports => {
|
||||
this.cleanUpAngularPanel();
|
||||
// cache plugin exports (saves a promise async cycle next time)
|
||||
this.pluginInfo.exports = pluginExports;
|
||||
// update panel state
|
||||
@@ -80,7 +87,6 @@ export class DashboardPanel extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
console.log('componentDidUpdate');
|
||||
this.loadPlugin();
|
||||
|
||||
// handle angular plugin loading
|
||||
@@ -94,12 +100,17 @@ export class DashboardPanel extends React.Component<Props, State> {
|
||||
this.angularPanel = loader.load(this.element, scopeProps, template);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
cleanUpAngularPanel() {
|
||||
if (this.angularPanel) {
|
||||
this.angularPanel.destroy();
|
||||
this.angularPanel = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.cleanUpAngularPanel();
|
||||
}
|
||||
|
||||
renderReactPanel() {
|
||||
const { pluginExports } = this.state;
|
||||
const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
|
||||
|
@@ -31,7 +31,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
|
||||
|
||||
this.tabs = [
|
||||
{ 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> {
|
||||
|
||||
<div className="tabbed-view-body">
|
||||
{activeTab === 'queries' && this.renderQueriesTab()}
|
||||
{activeTab === 'viz' && this.renderVizTab()}
|
||||
{activeTab === 'visualization' && this.renderVizTab()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -109,8 +109,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) {
|
||||
return (
|
||||
<li className="gf-tabs-item" key={tab.id}>
|
||||
<a className={tabClasses} onClick={() => onClick(tab)}>
|
||||
<i className={tab.icon} />
|
||||
{tab.text}
|
||||
<i className={tab.icon} /> {tab.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
@@ -34,6 +34,7 @@ export class PanelModel {
|
||||
soloMode?: boolean;
|
||||
targets: any[];
|
||||
datasource: string;
|
||||
thresholds?: any;
|
||||
|
||||
// non persisted
|
||||
fullscreen: boolean;
|
||||
@@ -116,9 +117,11 @@ export class PanelModel {
|
||||
this.events.emit('panel-init-edit-mode');
|
||||
}
|
||||
|
||||
changeType(newType: string) {
|
||||
this.type = newType;
|
||||
this.events.emit('panel-size-changed');
|
||||
changeType(pluginId: string) {
|
||||
this.type = pluginId;
|
||||
|
||||
delete this.thresholds;
|
||||
delete this.alert;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@@ -16,7 +16,7 @@ export class DashboardViewState {
|
||||
oldTimeRange: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, private $location, private $timeout, private $rootScope) {
|
||||
constructor($scope, private $location, private $timeout) {
|
||||
var self = this;
|
||||
self.state = {};
|
||||
self.panelScopes = [];
|
||||
@@ -176,10 +176,10 @@ export class DashboardViewState {
|
||||
}
|
||||
|
||||
/** @ngInject */
|
||||
export function dashboardViewStateSrv($location, $timeout, $rootScope) {
|
||||
export function dashboardViewStateSrv($location, $timeout) {
|
||||
return {
|
||||
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';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import { encodePathComponent } from 'app/core/utils/location_util';
|
||||
|
||||
import { metricsTabDirective } from './metrics_tab';
|
||||
|
||||
class MetricsPanelCtrl extends PanelCtrl {
|
||||
scope: any;
|
||||
datasource: any;
|
||||
@@ -58,7 +56,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
}
|
||||
|
||||
private onInitMetricsPanelEditMode() {
|
||||
this.addEditorTab('Metrics', metricsTabDirective);
|
||||
this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,8 @@ import { PanelModel } from 'app/features/dashboard/panel_model';
|
||||
import Remarkable from 'remarkable';
|
||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants';
|
||||
import store from 'app/core/store';
|
||||
import { metricsTabDirective } from './metrics_tab';
|
||||
import { vizTabDirective } from './viz_tab';
|
||||
|
||||
const TITLE_HEIGHT = 27;
|
||||
const PANEL_BORDER = 2;
|
||||
@@ -97,7 +99,10 @@ export class PanelCtrl {
|
||||
|
||||
initEditMode() {
|
||||
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.editModeInitiated = true;
|
||||
this.events.emit('init-edit-mode', null);
|
||||
|
||||
@@ -118,8 +123,8 @@ export class PanelCtrl {
|
||||
route.updateParams();
|
||||
}
|
||||
|
||||
addEditorTab(title, directiveFn, index?) {
|
||||
var editorTab = { title, directiveFn };
|
||||
addEditorTab(title, directiveFn, index?, icon?) {
|
||||
var editorTab = { title, directiveFn, icon };
|
||||
|
||||
if (_.isString(directiveFn)) {
|
||||
editorTab.directiveFn = function() {
|
||||
|
@@ -32,13 +32,11 @@ var panelTemplate = `
|
||||
'panel-height-helper': !ctrl.panel.isEditing}">
|
||||
<div class="tabbed-view tabbed-view--new">
|
||||
<div class="tabbed-view-header">
|
||||
<h3 class="tabbed-view-panel-title">
|
||||
{{ctrl.pluginName}}
|
||||
</h3>
|
||||
|
||||
<ul class="gf-tabs">
|
||||
<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}">
|
||||
<i class="{{::tab.icon}}" ng-show="tab.icon"></i>
|
||||
{{::tab.title}}
|
||||
</a>
|
||||
</li>
|
||||
@@ -50,7 +48,7 @@ var panelTemplate = `
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import angular from 'angular';
|
||||
|
||||
var directiveModule = angular.module('grafana.directives');
|
||||
const directiveModule = angular.module('grafana.directives');
|
||||
const directiveCache = {};
|
||||
|
||||
/** @ngInject */
|
||||
function panelEditorTab(dynamicDirectiveSrv) {
|
||||
@@ -11,18 +12,25 @@ function panelEditorTab(dynamicDirectiveSrv) {
|
||||
index: '=',
|
||||
},
|
||||
directive: scope => {
|
||||
var pluginId = scope.ctrl.pluginId;
|
||||
var 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();
|
||||
const pluginId = scope.ctrl.pluginId;
|
||||
const tabIndex = scope.index;
|
||||
|
||||
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}`,
|
||||
fn: fn,
|
||||
});
|
||||
};
|
||||
|
||||
directiveCache[pluginId][tabIndex] = result;
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
48
public/app/features/panel/viz_tab.ts
Normal file
48
public/app/features/panel/viz_tab.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
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, $
|
||||
scope.$applyAsync(function() {
|
||||
scope.$broadcast('component-did-mount');
|
||||
scope.$broadcast('refresh');
|
||||
console.log('appendAndCompile', scope.panel);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user