DataSourcePicker.tsx 3.2 KB

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