-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAutosuggest.tsx
More file actions
117 lines (104 loc) · 3.14 KB
/
Autosuggest.tsx
File metadata and controls
117 lines (104 loc) · 3.14 KB
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
import { useState, useRef, useMemo, useEffect } from "react";
import Avatar from "@/components/Avatar/Avatar";
import { debounce } from "throttle-debounce";
import "./Autosuggest.css";
type Props = {
externalString?: string;
onSelect: (entity: AutocompleteEntity | null) => void;
autocompleteFn: (searchString: string) => Promise<AutocompleteEntity[]>;
};
type AutocompleteEntity = {
display: string;
id: string;
};
const Autosuggest = ({ externalString, onSelect, autocompleteFn }: Props) => {
const [searchString, setSearchString] = useState(externalString);
const [filteredItems, setFilteredItems] = useState<
AutocompleteEntity[] | never[]
>([]);
const inputRef = useRef<null | HTMLInputElement>(null);
const debouncedAutocompleteSearch = useMemo(
() =>
debounce(300, async (searchString: string) => {
try {
const data = await autocompleteFn(searchString);
setFilteredItems(data);
} catch (err) {
console.error(err);
setFilteredItems([]);
}
}),
[]
);
useEffect(() => {
setSearchString(externalString ?? "");
setFilteredItems([]);
}, [externalString]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setSearchString(value);
if (value) {
debouncedAutocompleteSearch(value);
}
};
const handleSelect = (item: AutocompleteEntity) => {
setSearchString(item.display);
onSelect(item);
inputRef.current?.blur();
};
const handleItemClick = (
event: React.SyntheticEvent,
item: AutocompleteEntity
) => {
event.preventDefault();
handleSelect(item);
};
const clearInput = () => {
setSearchString("");
setFilteredItems([]);
onSelect(null);
inputRef.current?.focus();
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === "Backspace" && !searchString) {
clearInput();
}
};
return (
<div className="dropdown">
<input
className="input input-bordered w-72"
value={searchString ?? externalString}
placeholder="Начните вводить"
onChange={handleChange}
tabIndex={0}
onKeyDown={handleKeyDown}
/>
{searchString && (
<button
className="clear-button btn btn-square btn-ghost bg-transparent hover:bg-transparent absolute right-2 top-1 shadow-none border-none h-6 w-6"
onClick={clearInput}
aria-label="Очистить"
>
<span>×</span>
</button>
)}
{!!filteredItems?.length && (
<ul
tabIndex={0}
className="users-list dropdown-content z-[2] menu p-2 shadow bg-base-200 rounded-box w-52 flex-nowrap overflow-auto absolute left-1/2 -translate-x-1/2"
>
{filteredItems.map((user) => (
<li key={user.id} className="user-option">
<Avatar width={2} />
<a onClick={(event) => handleItemClick(event, user)}>
{user.display}
</a>
</li>
))}
</ul>
)}
</div>
);
};
export default Autosuggest;