import $ from 'jquery'; import React, { Component } from 'react'; import { Button, InputGroup, InputGroupAddon, InputGroupText, Input, } from 'reactstrap'; import Downshift from 'downshift'; import fuzzy from 'fuzzy'; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faSpinner } from '@fortawesome/free-solid-svg-icons'; library.add(faSearch, faSpinner); interface ExpressionInputProps { value: string; metricNames: string[]; executeQuery: (expr: string) => void; loading: boolean; } class ExpressionInput extends Component { prevNoMatchValue: string | null = null; private exprInputRef = React.createRef(); handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter' && !event.shiftKey) { this.props.executeQuery(this.exprInputRef.current!.value); event.preventDefault(); } } renderAutosuggest = (downshift: any) => { if (!downshift.isOpen) { return null; } if (this.prevNoMatchValue && downshift.inputValue.includes(this.prevNoMatchValue)) { return null; } let matches = fuzzy.filter(downshift.inputValue.replace(/ /g, ''), this.props.metricNames, { pre: "", post: "", }); if (matches.length === 0) { this.prevNoMatchValue = downshift.inputValue; return null; } return (
    { matches .slice(0, 200) // Limit DOM rendering to 100 results, as DOM rendering is sloooow. .map((item, index) => (
  • {/* TODO: Find better way than setting inner HTML dangerously. We just want the to not be escaped. This will be a problem when we save history and the user enters HTML into a query. */}
  • )) }
); } componentDidMount() { const $exprInput = $(this.exprInputRef.current!); const resize = () => { const el = $exprInput.get(0); const offset = el.offsetHeight - el.clientHeight; $exprInput.css('height', 'auto').css('height', el.scrollHeight + offset); }; resize(); $exprInput.on('input', resize); } render() { return ( {(downshift) => (
{this.props.loading ? : } { switch (event.key) { case 'Home': case 'End': // We want to be able to jump to the beginning/end of the input field. // By default, Downshift otherwise jumps to the first/last suggestion item instead. (event.nativeEvent as any).preventDownshiftDefault = true; break; case 'ArrowUp': case 'ArrowDown': if (!downshift.isOpen) { (event.nativeEvent as any).preventDownshiftDefault = true; } break; case 'Enter': downshift.closeMenu(); break; case 'Escape': if (!downshift.isOpen) { this.exprInputRef.current!.blur(); } break; default: } } } as any)} /> {this.renderAutosuggest(downshift)}
)}
); } } export default ExpressionInput;