VisualizationTab.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Libraries
  2. import React, { PureComponent } from 'react';
  3. // Utils & Services
  4. import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
  5. // Components
  6. import { EditorTabBody } from './EditorTabBody';
  7. import { VizTypePicker } from './VizTypePicker';
  8. // Types
  9. import { PanelModel } from '../panel_model';
  10. import { DashboardModel } from '../dashboard_model';
  11. import { PanelPlugin } from 'app/types/plugins';
  12. interface Props {
  13. panel: PanelModel;
  14. dashboard: DashboardModel;
  15. plugin: PanelPlugin;
  16. angularPanel?: AngularComponent;
  17. onTypeChanged: (newType: PanelPlugin) => void;
  18. }
  19. interface State {
  20. isVizPickerOpen: boolean;
  21. searchQuery: string;
  22. }
  23. export class VisualizationTab extends PureComponent<Props, State> {
  24. element: HTMLElement;
  25. angularOptions: AngularComponent;
  26. searchInput: HTMLElement;
  27. constructor(props) {
  28. super(props);
  29. this.state = {
  30. isVizPickerOpen: false,
  31. searchQuery: '',
  32. };
  33. }
  34. getPanelDefaultOptions = () => {
  35. const { panel, plugin } = this.props;
  36. if (plugin.exports.PanelDefaults) {
  37. return panel.getOptions(plugin.exports.PanelDefaults.options);
  38. }
  39. return panel.getOptions(plugin.exports.PanelDefaults);
  40. };
  41. renderPanelOptions() {
  42. const { plugin, angularPanel } = this.props;
  43. const { PanelOptions } = plugin.exports;
  44. if (angularPanel) {
  45. return <div ref={element => (this.element = element)} />;
  46. }
  47. if (PanelOptions) {
  48. return <PanelOptions options={this.getPanelDefaultOptions()} onChange={this.onPanelOptionsChanged} />;
  49. } else {
  50. return <p>Visualization has no options</p>;
  51. }
  52. }
  53. componentDidMount() {
  54. if (this.shouldLoadAngularOptions()) {
  55. this.loadAngularOptions();
  56. }
  57. }
  58. componentDidUpdate(prevProps: Props) {
  59. if (this.props.plugin !== prevProps.plugin) {
  60. this.cleanUpAngularOptions();
  61. }
  62. if (this.shouldLoadAngularOptions()) {
  63. this.loadAngularOptions();
  64. }
  65. }
  66. shouldLoadAngularOptions() {
  67. return this.props.angularPanel && this.element && !this.angularOptions;
  68. }
  69. loadAngularOptions() {
  70. const { angularPanel } = this.props;
  71. const scope = angularPanel.getScope();
  72. // When full page reloading in edit mode the angular panel has on fully compiled & instantiated yet
  73. if (!scope.$$childHead) {
  74. setTimeout(() => {
  75. this.forceUpdate();
  76. });
  77. return;
  78. }
  79. const panelCtrl = scope.$$childHead.ctrl;
  80. let template = '';
  81. for (let i = 0; i < panelCtrl.editorTabs.length; i++) {
  82. template +=
  83. `
  84. <div class="form-section" ng-cloak>` +
  85. (i > 0 ? `<div class="form-section__header">{{ctrl.editorTabs[${i}].title}}</div>` : '') +
  86. `<div class="form-section__body">
  87. <panel-editor-tab editor-tab="ctrl.editorTabs[${i}]" ctrl="ctrl"></panel-editor-tab>
  88. </div>
  89. </div>
  90. `;
  91. }
  92. const loader = getAngularLoader();
  93. const scopeProps = { ctrl: panelCtrl };
  94. this.angularOptions = loader.load(this.element, scopeProps, template);
  95. }
  96. componentWillUnmount() {
  97. this.cleanUpAngularOptions();
  98. }
  99. cleanUpAngularOptions() {
  100. if (this.angularOptions) {
  101. this.angularOptions.destroy();
  102. this.angularOptions = null;
  103. }
  104. }
  105. onPanelOptionsChanged = (options: any) => {
  106. this.props.panel.updateOptions(options);
  107. this.forceUpdate();
  108. };
  109. onOpenVizPicker = () => {
  110. this.setState({ isVizPickerOpen: true });
  111. };
  112. onSearchQueryChange = evt => {
  113. const value = evt.target.value;
  114. this.setState({
  115. searchQuery: value,
  116. });
  117. };
  118. renderToolbar = (): JSX.Element => {
  119. const { plugin } = this.props;
  120. const { searchQuery } = this.state;
  121. if (this.state.isVizPickerOpen) {
  122. return (
  123. <label className="gf-form--has-input-icon">
  124. <input
  125. type="text"
  126. className="gf-form-input width-13"
  127. placeholder=""
  128. onChange={this.onSearchQueryChange}
  129. value={searchQuery}
  130. ref={elem => (this.searchInput = elem)}
  131. />
  132. <i className="gf-form-input-icon fa fa-search" />
  133. </label>
  134. );
  135. } else {
  136. return (
  137. <div className="toolbar__main" onClick={this.onOpenVizPicker}>
  138. <img className="toolbar__main-image" src={plugin.info.logos.small} />
  139. <div className="toolbar__main-name">{plugin.name}</div>
  140. <i className="fa fa-caret-down" />
  141. </div>
  142. );
  143. }
  144. };
  145. onTypeChanged = (plugin: PanelPlugin) => {
  146. // this.setState({ isVizPickerOpen: false });
  147. this.props.onTypeChanged(plugin);
  148. };
  149. render() {
  150. const { plugin } = this.props;
  151. const { isVizPickerOpen, searchQuery } = this.state;
  152. const panelHelp = {
  153. title: '',
  154. icon: 'fa fa-question',
  155. render: () => <h2>Help</h2>,
  156. };
  157. return (
  158. <EditorTabBody heading="Visualization" renderToolbar={this.renderToolbar} toolbarItems={[panelHelp]}>
  159. {isVizPickerOpen && <VizTypePicker current={plugin} onTypeChanged={this.onTypeChanged} searchQuery={searchQuery} />}
  160. {this.renderPanelOptions()}
  161. </EditorTabBody>
  162. );
  163. }
  164. }