VisualizationTab.tsx 5.5 KB

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