-
Notifications
You must be signed in to change notification settings - Fork 12
/
HierarchySearch.jsx
124 lines (115 loc) · 3.94 KB
/
HierarchySearch.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import React from 'react'
import { CircularProgress, TextField } from '@mui/material'
import { Autocomplete } from '@mui/material';
import { Search as SearchIcon } from '@mui/icons-material'
import { debounce, isEmpty, get } from 'lodash'
import APIService from '../../services/APIService'
const MIN_CHARS = 3
class HierarchySearch extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
options: [],
selected: '',
fetched: false,
open: false
}
}
getLabel = option => {
if(!option)
return ''
const showName = option.display_name && option.display_name !== option.id
let name = option.id
if(showName)
name += ` ${option.display_name}`
return name
}
getLabelDom = (props, option) => {
const showName = option.display_name && option.display_name !== option.id
return (
<li {...props} key={option.id}>
<span style={{fontSize: '12px'}}>
<span>
{
showName ?
<React.Fragment><b>{option.id}</b> {option.display_name}</React.Fragment> :
<React.Fragment>{option.id}</React.Fragment>
}
</span>
</span>
</li>
)
}
fetchResults = debounce(
searchStr => APIService
.new()
.overrideURL(this.props.searchURL)
.get(null, null, {q: searchStr})
.then(res => this.setState({fetched: true, options: res.data}))
)
onInputChange = (event, value) => this.setState({fetched: false, input: value || ''}, () => {
if(this.isSearchable())
this.fetchResults(this.state.input)
})
onSelect = (event, item) => this.setState({selected: item}, () => {
if(this.props.onChange)
this.props.onChange(item)
})
minLengthToSearch = () => this.props.minCharactersForSearch || MIN_CHARS
isSearchable = () => this.state.input && this.state.input.length >= this.minLengthToSearch()
render() {
const { options, selected, input, open, fetched } = this.state;
const { label, style } = this.props
const minLength = this.minLengthToSearch()
const isSearchable = this.isSearchable()
const loading = Boolean(open && !fetched && isSearchable && isEmpty(options))
return (
<Autocomplete
style={style || {}}
fullWidth
openOnFocus
blurOnSelect
classes={{listbox: 'autocomplete-small', paper: 'autocomplete-small', inputRoot: 'autocomplete-small-input-root'}}
size='small'
value={selected}
options={options}
loading={loading}
getOptionLabel={this.getLabel}
onOpen={() => this.setState({open: true})}
onClose={() => this.setState({open: false})}
isOptionEqualToValue={(option, value) => option.id === get(value, 'id')}
loadingText={loading ? 'Loading...' : `Type atleast ${minLength} characters to search`}
noOptionsText={(isSearchable && !loading) ? "No results" : 'Start typing...'}
onInputChange={this.onInputChange}
onChange={this.onSelect}
renderInput={
params => (
<TextField
{...params}
className='input-small'
value={input}
label={label || ""}
variant="outlined"
fullWidth
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{
loading ?
<CircularProgress color="inherit" size={20} /> :
<SearchIcon size='small' />
}
</React.Fragment>
),
}}
/>
)
}
renderOption={this.getLabelDom}
/>
);
}
}
export default HierarchySearch