make expression input controlled (#6174)
* make expression input controlled Signed-off-by: blalov <boyko.lalov@tick42.com> * close menu explicitly when autosuggestion dropdown is hidden Signed-off-by: blalov <boyko.lalov@tick42.com>
This commit is contained in:
parent
3acc3e856c
commit
b5a16a8f86
|
@ -13,6 +13,7 @@ body {
|
||||||
.expression-input textarea {
|
.expression-input textarea {
|
||||||
/* font-family: Menlo,Monaco,Consolas,'Courier New',monospace; */
|
/* font-family: Menlo,Monaco,Consolas,'Courier New',monospace; */
|
||||||
resize: none;
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.execute-btn {
|
button.execute-btn {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import $ from 'jquery';
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -8,7 +7,7 @@ import {
|
||||||
Input,
|
Input,
|
||||||
} from 'reactstrap';
|
} from 'reactstrap';
|
||||||
|
|
||||||
import Downshift from 'downshift';
|
import Downshift, { ControllerStateAndHelpers } from 'downshift';
|
||||||
import fuzzy from 'fuzzy';
|
import fuzzy from 'fuzzy';
|
||||||
import SanitizeHTML from './components/SanitizeHTML';
|
import SanitizeHTML from './components/SanitizeHTML';
|
||||||
|
|
||||||
|
@ -25,10 +24,43 @@ interface ExpressionInputProps {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExpressionInput extends Component<ExpressionInputProps> {
|
interface ExpressionInputState {
|
||||||
prevNoMatchValue: string | null = null;
|
height: number | string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExpressionInput extends Component<ExpressionInputProps, ExpressionInputState> {
|
||||||
|
private prevNoMatchValue: string | null = null;
|
||||||
private exprInputRef = React.createRef<HTMLInputElement>();
|
private exprInputRef = React.createRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
constructor(props: ExpressionInputProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
value: props.value,
|
||||||
|
height: 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.setHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setHeight = () => {
|
||||||
|
const { offsetHeight, clientHeight, scrollHeight } = this.exprInputRef.current!;
|
||||||
|
const offset = offsetHeight - clientHeight; // Needed in order for the height to be more accurate.
|
||||||
|
this.setState({ height: scrollHeight + offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput = () => {
|
||||||
|
this.setState({
|
||||||
|
height: 'auto',
|
||||||
|
value: this.exprInputRef.current!.value
|
||||||
|
}, this.setHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDropdownSelection = (value: string) => this.setState({ value });
|
||||||
|
|
||||||
handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (event.key === 'Enter' && !event.shiftKey) {
|
if (event.key === 'Enter' && !event.shiftKey) {
|
||||||
this.props.executeQuery(this.exprInputRef.current!.value);
|
this.props.executeQuery(this.exprInputRef.current!.value);
|
||||||
|
@ -36,22 +68,23 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAutosuggest = (downshift: any) => {
|
executeQuery = () => this.props.executeQuery(this.exprInputRef.current!.value)
|
||||||
if (!downshift.isOpen) {
|
|
||||||
|
renderAutosuggest = (downshift: ControllerStateAndHelpers<any>) => {
|
||||||
|
const { inputValue } = downshift
|
||||||
|
if (!inputValue || (this.prevNoMatchValue && inputValue.includes(this.prevNoMatchValue))) {
|
||||||
|
downshift.closeMenu();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.prevNoMatchValue && downshift.inputValue.includes(this.prevNoMatchValue)) {
|
const matches = fuzzy.filter(inputValue.replace(/ /g, ''), this.props.metricNames, {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let matches = fuzzy.filter(downshift.inputValue.replace(/ /g, ''), this.props.metricNames, {
|
|
||||||
pre: "<strong>",
|
pre: "<strong>",
|
||||||
post: "</strong>",
|
post: "</strong>",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (matches.length === 0) {
|
if (matches.length === 0) {
|
||||||
this.prevNoMatchValue = downshift.inputValue;
|
this.prevNoMatchValue = inputValue;
|
||||||
|
downshift.closeMenu();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,23 +116,12 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
render() {
|
||||||
|
const { value, height } = this.state;
|
||||||
return (
|
return (
|
||||||
<Downshift
|
<Downshift
|
||||||
//inputValue={this.props.value}
|
onChange={this.handleDropdownSelection}
|
||||||
//onInputValueChange={this.props.onChange}
|
inputValue={value}
|
||||||
selectedItem={this.props.value}
|
|
||||||
>
|
>
|
||||||
{(downshift) => (
|
{(downshift) => (
|
||||||
<div>
|
<div>
|
||||||
|
@ -110,6 +132,8 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
<Input
|
<Input
|
||||||
|
onInput={this.handleInput}
|
||||||
|
style={{ height }}
|
||||||
autoFocus
|
autoFocus
|
||||||
type="textarea"
|
type="textarea"
|
||||||
rows="1"
|
rows="1"
|
||||||
|
@ -148,13 +172,13 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
||||||
<Button
|
<Button
|
||||||
className="execute-btn"
|
className="execute-btn"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => this.props.executeQuery(this.exprInputRef.current!.value)}
|
onClick={this.executeQuery}
|
||||||
>
|
>
|
||||||
Execute
|
Execute
|
||||||
</Button>
|
</Button>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
{this.renderAutosuggest(downshift)}
|
{downshift.isOpen && this.renderAutosuggest(downshift)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Downshift>
|
</Downshift>
|
||||||
|
|
Loading…
Reference in New Issue