ExploreToolbar.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import React, { PureComponent } from 'react';
  2. import { connect } from 'react-redux';
  3. import { hot } from 'react-hot-loader';
  4. import { ExploreId } from 'app/types/explore';
  5. import { DataSourceSelectItem, RawTimeRange, ClickOutsideWrapper, TimeZone, TimeRange } from '@grafana/ui';
  6. import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
  7. import { StoreState } from 'app/types/store';
  8. import {
  9. changeDatasource,
  10. clearQueries,
  11. splitClose,
  12. runQueries,
  13. splitOpen,
  14. changeRefreshInterval,
  15. } from './state/actions';
  16. import TimePicker from './TimePicker';
  17. import { getTimeZone } from '../profile/state/selectors';
  18. import { RefreshPicker, SetInterval } from '@grafana/ui';
  19. enum IconSide {
  20. left = 'left',
  21. right = 'right',
  22. }
  23. const createResponsiveButton = (options: {
  24. splitted: boolean;
  25. title: string;
  26. onClick: () => void;
  27. buttonClassName?: string;
  28. iconClassName?: string;
  29. iconSide?: IconSide;
  30. }) => {
  31. const defaultOptions = {
  32. iconSide: IconSide.left,
  33. };
  34. const props = { ...options, defaultOptions };
  35. const { title, onClick, buttonClassName, iconClassName, splitted, iconSide } = props;
  36. return (
  37. <button className={`btn navbar-button ${buttonClassName ? buttonClassName : ''}`} onClick={onClick}>
  38. {iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName}`} /> : null}
  39. <span className="btn-title">{!splitted ? title : ''}</span>
  40. {iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName}`} /> : null}
  41. </button>
  42. );
  43. };
  44. interface OwnProps {
  45. exploreId: ExploreId;
  46. timepickerRef: React.RefObject<TimePicker>;
  47. onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
  48. }
  49. interface StateProps {
  50. datasourceMissing: boolean;
  51. exploreDatasources: DataSourceSelectItem[];
  52. loading: boolean;
  53. range: TimeRange;
  54. timeZone: TimeZone;
  55. selectedDatasource: DataSourceSelectItem;
  56. splitted: boolean;
  57. refreshInterval: string;
  58. }
  59. interface DispatchProps {
  60. changeDatasource: typeof changeDatasource;
  61. clearAll: typeof clearQueries;
  62. runQueries: typeof runQueries;
  63. closeSplit: typeof splitClose;
  64. split: typeof splitOpen;
  65. changeRefreshInterval: typeof changeRefreshInterval;
  66. }
  67. type Props = StateProps & DispatchProps & OwnProps;
  68. export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
  69. constructor(props: Props) {
  70. super(props);
  71. }
  72. onChangeDatasource = async option => {
  73. this.props.changeDatasource(this.props.exploreId, option.value);
  74. };
  75. onClearAll = () => {
  76. this.props.clearAll(this.props.exploreId);
  77. };
  78. onRunQuery = () => {
  79. return this.props.runQueries(this.props.exploreId);
  80. };
  81. onCloseTimePicker = () => {
  82. this.props.timepickerRef.current.setState({ isOpen: false });
  83. };
  84. onChangeRefreshInterval = (item: string) => {
  85. const { changeRefreshInterval, exploreId } = this.props;
  86. changeRefreshInterval(exploreId, item);
  87. };
  88. render() {
  89. const {
  90. datasourceMissing,
  91. exploreDatasources,
  92. closeSplit,
  93. exploreId,
  94. loading,
  95. range,
  96. timeZone,
  97. selectedDatasource,
  98. splitted,
  99. timepickerRef,
  100. refreshInterval,
  101. onChangeTime,
  102. split,
  103. } = this.props;
  104. return (
  105. <div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
  106. <div className="explore-toolbar-item">
  107. <div className="explore-toolbar-header">
  108. <div className="explore-toolbar-header-title">
  109. {exploreId === 'left' && (
  110. <span className="navbar-page-btn">
  111. <i className="gicon gicon-explore" />
  112. Explore
  113. </span>
  114. )}
  115. </div>
  116. {splitted && (
  117. <a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
  118. <i className="fa fa-times fa-fw" />
  119. </a>
  120. )}
  121. </div>
  122. </div>
  123. <div className="explore-toolbar-item">
  124. <div className="explore-toolbar-content">
  125. {!datasourceMissing ? (
  126. <div className="explore-toolbar-content-item">
  127. <div className="datasource-picker">
  128. <DataSourcePicker
  129. onChange={this.onChangeDatasource}
  130. datasources={exploreDatasources}
  131. current={selectedDatasource}
  132. />
  133. </div>
  134. </div>
  135. ) : null}
  136. {exploreId === 'left' && !splitted ? (
  137. <div className="explore-toolbar-content-item">
  138. {createResponsiveButton({
  139. splitted,
  140. title: 'Split',
  141. onClick: split,
  142. iconClassName: 'fa fa-fw fa-columns icon-margin-right',
  143. iconSide: IconSide.left,
  144. })}
  145. </div>
  146. ) : null}
  147. <div className="explore-toolbar-content-item timepicker">
  148. <ClickOutsideWrapper onClick={this.onCloseTimePicker}>
  149. <TimePicker ref={timepickerRef} range={range} isUtc={timeZone.isUtc} onChangeTime={onChangeTime} />
  150. </ClickOutsideWrapper>
  151. <RefreshPicker
  152. onIntervalChanged={this.onChangeRefreshInterval}
  153. onRefresh={this.onRunQuery}
  154. value={refreshInterval}
  155. tooltip="Refresh"
  156. />
  157. {refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} />}
  158. </div>
  159. <div className="explore-toolbar-content-item">
  160. <button className="btn navbar-button" onClick={this.onClearAll}>
  161. Clear All
  162. </button>
  163. </div>
  164. <div className="explore-toolbar-content-item">
  165. {createResponsiveButton({
  166. splitted,
  167. title: 'Run Query',
  168. onClick: this.onRunQuery,
  169. buttonClassName: 'navbar-button--secondary',
  170. iconClassName: loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
  171. iconSide: IconSide.right,
  172. })}
  173. </div>
  174. </div>
  175. </div>
  176. </div>
  177. );
  178. }
  179. }
  180. const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
  181. const splitted = state.explore.split;
  182. const exploreItem = state.explore[exploreId];
  183. const {
  184. datasourceInstance,
  185. datasourceMissing,
  186. exploreDatasources,
  187. queryTransactions,
  188. range,
  189. refreshInterval,
  190. } = exploreItem;
  191. const selectedDatasource = datasourceInstance
  192. ? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
  193. : undefined;
  194. const loading = queryTransactions.some(qt => !qt.done);
  195. return {
  196. datasourceMissing,
  197. exploreDatasources,
  198. loading,
  199. range,
  200. timeZone: getTimeZone(state.user),
  201. selectedDatasource,
  202. splitted,
  203. refreshInterval,
  204. };
  205. };
  206. const mapDispatchToProps: DispatchProps = {
  207. changeDatasource,
  208. changeRefreshInterval,
  209. clearAll: clearQueries,
  210. runQueries,
  211. closeSplit: splitClose,
  212. split: splitOpen,
  213. };
  214. export const ExploreToolbar = hot(module)(
  215. connect(
  216. mapStateToProps,
  217. mapDispatchToProps
  218. )(UnConnectedExploreToolbar)
  219. );