2021-03-25 14:22:43 +00:00
|
|
|
import React, {
|
|
|
|
FunctionComponent,
|
|
|
|
useCallback,
|
|
|
|
useEffect,
|
|
|
|
useMemo,
|
|
|
|
useState,
|
|
|
|
} from "react";
|
|
|
|
import { Dropdown, Form } from "react-bootstrap";
|
|
|
|
import { useHistory } from "react-router";
|
2021-05-08 17:13:10 +00:00
|
|
|
import { useThrottle } from "rooks";
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
export interface SearchResult {
|
2021-12-18 14:16:28 +00:00
|
|
|
id: string;
|
2021-03-25 14:22:43 +00:00
|
|
|
name: string;
|
|
|
|
link?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
className?: string;
|
|
|
|
onSearch: (text: string) => Promise<SearchResult[]>;
|
|
|
|
onFocus?: () => void;
|
|
|
|
onBlur?: () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const SearchBar: FunctionComponent<Props> = ({
|
|
|
|
onSearch,
|
|
|
|
onFocus,
|
|
|
|
onBlur,
|
|
|
|
className,
|
|
|
|
}) => {
|
|
|
|
const [text, setText] = useState("");
|
|
|
|
|
|
|
|
const [results, setResults] = useState<SearchResult[]>([]);
|
|
|
|
|
|
|
|
const history = useHistory();
|
|
|
|
|
|
|
|
const search = useCallback(
|
|
|
|
(value: string) => {
|
|
|
|
if (value === "") {
|
|
|
|
setResults([]);
|
|
|
|
} else {
|
|
|
|
onSearch(value).then((res) => setResults(res));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[onSearch]
|
|
|
|
);
|
|
|
|
|
2021-05-08 17:13:10 +00:00
|
|
|
const [debounceSearch] = useThrottle(search, 500);
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
debounceSearch(text);
|
|
|
|
}, [text, debounceSearch]);
|
|
|
|
|
|
|
|
const clear = useCallback(() => {
|
|
|
|
setText("");
|
|
|
|
setResults([]);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const items = useMemo(() => {
|
|
|
|
const its = results.map((v) => (
|
|
|
|
<Dropdown.Item
|
2021-12-18 14:16:28 +00:00
|
|
|
key={v.id}
|
2021-03-25 14:22:43 +00:00
|
|
|
eventKey={v.link}
|
|
|
|
disabled={v.link === undefined}
|
|
|
|
>
|
|
|
|
<span>{v.name}</span>
|
|
|
|
</Dropdown.Item>
|
|
|
|
));
|
|
|
|
|
|
|
|
if (its.length === 0) {
|
|
|
|
its.push(<Dropdown.Header key="notify">No Found</Dropdown.Header>);
|
|
|
|
}
|
|
|
|
|
|
|
|
return its;
|
|
|
|
}, [results]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Dropdown
|
|
|
|
show={text.length !== 0}
|
|
|
|
className={className}
|
|
|
|
onFocus={onFocus}
|
|
|
|
onBlur={onBlur}
|
|
|
|
onSelect={(link) => {
|
|
|
|
if (link) {
|
|
|
|
clear();
|
|
|
|
history.push(link);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Form.Control
|
|
|
|
type="text"
|
|
|
|
size="sm"
|
|
|
|
placeholder="Search..."
|
|
|
|
value={text}
|
|
|
|
onChange={(e) => setText(e.currentTarget.value)}
|
|
|
|
></Form.Control>
|
|
|
|
<Dropdown.Menu style={{ maxHeight: 256, overflowY: "auto" }}>
|
|
|
|
{items}
|
|
|
|
</Dropdown.Menu>
|
|
|
|
</Dropdown>
|
|
|
|
);
|
|
|
|
};
|