-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathHeaderNameAutosuggest.js
129 lines (108 loc) · 3.57 KB
/
HeaderNameAutosuggest.js
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
125
126
127
128
129
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';
import Autosuggest from 'react-autosuggest';
import fuzzysort from 'fuzzysort';
import headers from 'constants/commonHeaders';
import { isHeaderDescriptionEnabled } from 'store/options/selectors';
import { SuggestWrapper, Suggestion } from './StyledComponents';
// Make search indexes ahead of time to go fast!
const preparedHeaders = headers.map(header => fuzzysort.prepare(header.name));
const preparedHeadersWithDescriptions = headers.map(header => ({
...fuzzysort.prepare(header.name),
...header,
}));
const maxEntries = 7;
const compare = (a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
};
// Calculate suggestions for any given input value.
const getSuggestions = (value, descriptionEnabled) => {
const inputValue = value.trim();
const inputLength = inputValue.length;
const searchTarget = descriptionEnabled
? preparedHeadersWithDescriptions
: preparedHeaders;
if (inputLength === 0) return [];
const matches = fuzzysort
.go(inputValue, searchTarget)
.sort((a, b) => compare(a.score, b.score));
return matches.slice(0, maxEntries);
};
// Teach Autosuggest how to calculate the input value for
// every given suggestion.
// eslint-disable-next-line no-underscore-dangle
const getSuggestionValue = suggestion => suggestion._target;
// Ensure the user does not send a request when selecting using enter
function preventDefaultOnEnter(event, { method }) {
if (method === 'enter') {
event.preventDefault();
}
}
// This unsafe is fine, as it is from a trusted source
/* eslint-disable react/no-danger */
const renderSuggestion = suggestion => (
<Suggestion>
<p dangerouslySetInnerHTML={{ __html: suggestion.highlighted }} />
{suggestion.description &&
<small>
{suggestion.description}
</small>}
</Suggestion>
);
/* eslint-enable react/no-danger */
const renderInputComponent = ({ className, ...rest }) => (
<input className={classNames(className, 'form-control')} {...rest} />
);
renderInputComponent.propTypes = {
className: PropTypes.string,
};
export class HeaderNameAutosuggest extends React.PureComponent {
static propTypes = {
input: PropTypes.shape({}).isRequired,
placeholder: PropTypes.string.isRequired,
headerDescriptionEnabled: PropTypes.bool.isRequired,
};
state = {
suggestions: [],
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value, this.props.headerDescriptionEnabled),
});
};
// When the suggest is closed or emptied
onSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
render() {
const { input, placeholder } = this.props;
const inputProps = {
...input,
placeholder,
onChange: (_, { newValue }) => input.onChange(newValue),
};
return (
<SuggestWrapper>
<Autosuggest
suggestions={this.state.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={preventDefaultOnEnter}
getSuggestionValue={getSuggestionValue}
renderInputComponent={renderInputComponent}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
</SuggestWrapper>
);
}
}
const mapStateToProps = state => ({
headerDescriptionEnabled: isHeaderDescriptionEnabled(state),
});
export default connect(mapStateToProps)(HeaderNameAutosuggest);