DataSourcePicker.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import React, { PureComponent } from 'react';
  2. import classNames from 'classnames';
  3. import _ from 'lodash';
  4. import withKeyboardNavigation from './withKeyboardNavigation';
  5. import { DataSourceSelectItem } from 'app/types';
  6. export interface Props {
  7. onChangeDataSource: (ds: any) => void;
  8. datasources: DataSourceSelectItem[];
  9. selected?: number;
  10. onKeyDown?: (evt: any, maxSelectedIndex: number, onEnterAction: () => void) => void;
  11. onMouseEnter?: (select: number) => void;
  12. }
  13. interface State {
  14. searchQuery: string;
  15. }
  16. export const DataSourcePicker = withKeyboardNavigation(
  17. class DataSourcePicker extends PureComponent<Props, State> {
  18. searchInput: HTMLElement;
  19. constructor(props) {
  20. super(props);
  21. this.state = {
  22. searchQuery: '',
  23. };
  24. }
  25. getDataSources() {
  26. const { searchQuery } = this.state;
  27. const regex = new RegExp(searchQuery, 'i');
  28. const { datasources } = this.props;
  29. const filtered = datasources.filter(item => {
  30. return regex.test(item.name) || regex.test(item.meta.name);
  31. });
  32. return filtered;
  33. }
  34. get maxSelectedIndex() {
  35. const filtered = this.getDataSources();
  36. return filtered.length - 1;
  37. }
  38. renderDataSource = (ds: DataSourceSelectItem, index: number) => {
  39. const { onChangeDataSource, selected, onMouseEnter } = this.props;
  40. const onClick = () => onChangeDataSource(ds);
  41. const isSelected = selected === index;
  42. const cssClass = classNames({
  43. 'ds-picker-list__item': true,
  44. 'ds-picker-list__item--selected': isSelected,
  45. });
  46. return (
  47. <div
  48. key={index}
  49. className={cssClass}
  50. title={ds.name}
  51. onClick={onClick}
  52. onMouseEnter={() => onMouseEnter(index)}
  53. >
  54. <img className="ds-picker-list__img" src={ds.meta.info.logos.small} />
  55. <div className="ds-picker-list__name">{ds.name}</div>
  56. </div>
  57. );
  58. };
  59. componentDidMount() {
  60. setTimeout(() => {
  61. this.searchInput.focus();
  62. }, 300);
  63. }
  64. onSearchQueryChange = evt => {
  65. const value = evt.target.value;
  66. this.setState(prevState => ({
  67. ...prevState,
  68. searchQuery: value,
  69. }));
  70. };
  71. renderFilters() {
  72. const { searchQuery } = this.state;
  73. const { onKeyDown } = this.props;
  74. return (
  75. <>
  76. <label className="gf-form--has-input-icon">
  77. <input
  78. type="text"
  79. className="gf-form-input width-13"
  80. placeholder=""
  81. ref={elem => (this.searchInput = elem)}
  82. onChange={this.onSearchQueryChange}
  83. value={searchQuery}
  84. onKeyDown={evt => {
  85. onKeyDown(evt, this.maxSelectedIndex, () => {
  86. const { onChangeDataSource, selected } = this.props;
  87. const ds = this.getDataSources()[selected];
  88. onChangeDataSource(ds);
  89. });
  90. }}
  91. />
  92. <i className="gf-form-input-icon fa fa-search" />
  93. </label>
  94. </>
  95. );
  96. }
  97. render() {
  98. return (
  99. <>
  100. <div className="cta-form__bar">
  101. {this.renderFilters()}
  102. <div className="gf-form--grow" />
  103. </div>
  104. <div className="ds-picker-list">{this.getDataSources().map(this.renderDataSource)}</div>
  105. </>
  106. );
  107. }
  108. }
  109. );
  110. export default DataSourcePicker;