-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathuseDefaultComboBox.ts
56 lines (46 loc) · 1.53 KB
/
useDefaultComboBox.ts
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
import { useMemo, useState } from 'react';
const normalizeString = (str: string) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
const defaultFilterSuggestions = <T>(
getSuggestionLabel: (suggestion: T) => string,
suggestions: T[],
query: string
) => {
const input = normalizeString(query).trim().toLowerCase();
if (!input) {
return suggestions;
}
const getSuggestionScore = (suggestion: T) => {
const suggestionLabel = normalizeString(getSuggestionLabel(suggestion).toLowerCase());
if (suggestionLabel.startsWith(input)) {
return 2;
}
if (suggestionLabel.includes(input)) {
return 1;
}
return 0;
};
return suggestions
.map((suggestion) => ({
suggestion,
score: getSuggestionScore(suggestion),
}))
.filter(({ score }) => score > 0)
.sort(({ score: scoreA }, { score: scoreB }) => scoreB - scoreA)
.map(({ suggestion }) => suggestion);
};
const useDefaultComboBox = <T>(suggestions: T[], getSuggestionLabel: (suggestion: T) => string) => {
const [query, setQuery] = useState('');
const filteredSuggestions = useMemo(
() => defaultFilterSuggestions(getSuggestionLabel, suggestions, query),
// eslint-disable-next-line react-hooks/exhaustive-deps
[suggestions, query]
);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
};
const resetSuggestions = () => {
setQuery('');
};
return { suggestions: filteredSuggestions, onChange, resetSuggestions };
};
export default useDefaultComboBox;