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