Commit 911646f9 by Torkel Ödegaard

poc: handling panel edit mode in react even for angular panels poc

parent 487fd12d
......@@ -5,6 +5,7 @@ import _ from 'lodash';
export interface AngularComponent {
destroy();
digest();
getScope();
}
export class AngularLoader {
......@@ -28,6 +29,9 @@ export class AngularLoader {
digest: () => {
scope.$digest();
},
getScope: () => {
return scope;
},
};
}
}
......
......@@ -23,11 +23,11 @@ export interface Props {
export interface State {
plugin: PanelPlugin;
angularPanel: AngularComponent;
}
export class DashboardPanel extends PureComponent<Props, State> {
element: any;
angularPanel: AngularComponent;
element: HTMLElement;
specialPanels = {};
constructor(props) {
......@@ -35,6 +35,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
this.state = {
plugin: null,
angularPanel: null,
};
this.specialPanels['row'] = this.renderRow.bind(this);
......@@ -96,25 +97,30 @@ export class DashboardPanel extends PureComponent<Props, State> {
this.loadPlugin();
// handle angular plugin loading
if (!this.element || this.angularPanel) {
if (!this.element || this.state.angularPanel) {
return;
}
const loader = getAngularLoader();
const template = '<plugin-component type="panel" class="panel-height-helper"></plugin-component>';
const scopeProps = { panel: this.props.panel, dashboard: this.props.dashboard };
this.angularPanel = loader.load(this.element, scopeProps, template);
const angularPanel = loader.load(this.element, scopeProps, template);
this.setState({ angularPanel });
}
cleanUpAngularPanel() {
if (this.angularPanel) {
this.angularPanel.destroy();
this.angularPanel = null;
cleanUpAngularPanel(unmounted?: boolean) {
if (this.state.angularPanel) {
this.state.angularPanel.destroy();
if (!unmounted) {
this.setState({ angularPanel: null });
}
}
}
componentWillUnmount() {
this.cleanUpAngularPanel();
this.cleanUpAngularPanel(true);
}
onMouseEnter = () => {
......@@ -129,25 +135,16 @@ export class DashboardPanel extends PureComponent<Props, State> {
const { dashboard, panel } = this.props;
const { plugin } = this.state;
const containerClass = this.props.isEditing ? 'panel-editor-container' : 'panel-height-helper';
const panelWrapperClass = this.props.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
// this might look strange with these classes that change when edit, but
// I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
return (
<div className={containerClass}>
<div className={panelWrapperClass} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<PanelChrome component={plugin.exports.Panel} panel={panel} dashboard={dashboard} />
</div>
{panel.isEditing && (
<PanelEditor panel={panel} plugin={plugin} dashboard={dashboard} onTypeChanged={this.onPluginTypeChanged} />
)}
</div>
);
return <PanelChrome component={plugin.exports.Panel} panel={panel} dashboard={dashboard} />;
}
renderAngularPanel() {
return <div ref={element => (this.element = element)} className="panel-height-helper" />;
}
render() {
const { panel } = this.props;
const { plugin } = this.state;
const { panel, dashboard } = this.props;
const { plugin, angularPanel } = this.state;
if (this.isSpecial()) {
return this.specialPanels[panel.type]();
......@@ -158,19 +155,27 @@ export class DashboardPanel extends PureComponent<Props, State> {
return null;
}
// if exporting PanelComponent it must be a react panel
if (plugin.exports.Panel) {
return this.renderReactPanel();
}
console.log('DashboardPanel.render()');
const containerClass = this.props.isEditing ? 'panel-editor-container' : 'panel-height-helper';
const panelWrapperClass = this.props.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
// legacy angular rendering
return (
<div
ref={element => (this.element = element)}
className="panel-height-helper"
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
/>
<div className={containerClass}>
<div className={panelWrapperClass} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
{plugin.exports.Panel && this.renderReactPanel()}
{plugin.exports.PanelCtrl && this.renderAngularPanel()}
</div>
{panel.isEditing && (
<PanelEditor
panel={panel}
plugin={plugin}
dashboard={dashboard}
angularPanel={angularPanel}
onTypeChanged={this.onPluginTypeChanged}
/>
)}
</div>
);
}
}
......@@ -7,6 +7,7 @@ import { GeneralTab } from './GeneralTab';
import { store } from 'app/store/store';
import { updateLocation } from 'app/core/actions';
import { AngularComponent } from 'app/core/services/AngularLoader';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
......@@ -16,6 +17,7 @@ interface PanelEditorProps {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPlugin) => void;
}
......@@ -58,7 +60,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
};
render() {
const { panel, dashboard, onTypeChanged, plugin } = this.props;
const { panel, dashboard, onTypeChanged, plugin, angularPanel } = this.props;
const { location } = store.getState();
const activeTab = location.query.tab || 'queries';
......@@ -85,7 +87,13 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
{activeTab === 'general' && <GeneralTab panel={panel} />}
{activeTab === 'queries' && <QueriesTab panel={panel} dashboard={dashboard} />}
{activeTab === 'visualization' && (
<VisualizationTab panel={panel} dashboard={dashboard} plugin={plugin} onTypeChanged={onTypeChanged} />
<VisualizationTab
panel={panel}
dashboard={dashboard}
plugin={plugin}
onTypeChanged={onTypeChanged}
angularPanel={angularPanel}
/>
)}
</div>
);
......
// Libraries
import React, { PureComponent } from 'react';
// Utils & Services
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
// Components
import { EditorTabBody } from './EditorTabBody';
import { VizTypePicker } from './VizTypePicker';
// Types
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { PanelPlugin } from 'app/types/plugins';
......@@ -11,18 +17,26 @@ interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPlugin) => void;
}
export class VisualizationTab extends PureComponent<Props> {
element: HTMLElement;
angularOptions: AngularComponent;
constructor(props) {
super(props);
}
renderPanelOptions() {
const { plugin, panel } = this.props;
const { plugin, panel, angularPanel } = this.props;
const { PanelOptions } = plugin.exports;
if (angularPanel) {
return <div ref={element => (this.element = element)} />;
}
if (PanelOptions) {
return <PanelOptions options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
} else {
......@@ -30,6 +44,27 @@ export class VisualizationTab extends PureComponent<Props> {
}
}
componentDidMount() {
const { angularPanel } = this.props;
if (angularPanel) {
const scope = angularPanel.getScope();
const panelCtrl = scope.$$childHead.ctrl;
const loader = getAngularLoader();
const template = '<panel-editor-tab editor-tab="tab" ctrl="ctrl"></panel-editor-tab>';
const scopeProps = { ctrl: panelCtrl, tab: panelCtrl.editorTabs[2] };
this.angularOptions = loader.load(this.element, scopeProps, template);
}
}
componentWillUnmount() {
if (this.angularOptions) {
this.angularOptions.destroy();
}
}
onPanelOptionsChanged = (options: any) => {
this.props.panel.updateOptions(options);
this.forceUpdate();
......
......@@ -6,54 +6,22 @@ import baron from 'baron';
const module = angular.module('grafana.directives');
const panelTemplate = `
<div ng-class="{'panel-editor-container': ctrl.panel.isEditing, 'panel-height-helper': !ctrl.panel.isEditing}">
<div ng-class="{'panel-editor-container__panel': ctrl.panel.isEditing, 'panel-height-helper': !ctrl.panel.isEditing}">
<div class="panel-container">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}">
<span class="panel-info-corner">
<i class="fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
<span class="panel-loading" ng-show="ctrl.loading">
<i class="fa fa-spinner fa-spin"></i>
</span>
<panel-header class="panel-title-container" panel-ctrl="ctrl"></panel-header>
</div>
<div class="panel-content">
<ng-transclude class="panel-height-helper"></ng-transclude>
</div>
<div class="panel-container">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}">
<span class="panel-info-corner">
<i class="fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
<span class="panel-loading" ng-show="ctrl.loading">
<i class="fa fa-spinner fa-spin"></i>
</span>
<panel-header class="panel-title-container" panel-ctrl="ctrl"></panel-header>
</div>
</div>
<div ng-if="ctrl.panel.isEditing" ng-class="{'panel-editor-container__editor': ctrl.panel.isEditing,
'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}">
{{::tab.title}}
</a>
</li>
</ul>
<button class="panel-editor-tabs__close" ng-click="ctrl.exitFullscreen();">
<i class="fa fa-reply"></i>
</button>
</div>
<div class="tabbed-view-body">
<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>
<div class="panel-content">
<ng-transclude class="panel-height-helper"></ng-transclude>
</div>
</div>
</div>
......
......@@ -9,7 +9,6 @@ function panelEditorTab(dynamicDirectiveSrv) {
scope: {
ctrl: '=',
editorTab: '=',
index: '=',
},
directive: scope => {
const pluginId = scope.ctrl.pluginId;
......
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