+import axios from 'axios';
+import PropTypes from 'prop-types';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { Form, ListGroup } from 'react-bootstrap';
+
+import GuildBox from '../discord-guilds/Box';
+import debounce from '../../helpers/debounce';
+
+const DiscordSelect = ({ onChange, value }) => {
+ const [resolved, setResolved] = useState(null);
+ const [results, setResults] = useState([]);
+ const [search, setSearch] = useState('');
+ const [showResults, setShowResults] = useState(false);
+
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const handleEventOutside = e => {
+ if (ref.current && !ref.current.contains(e.target)) {
+ setShowResults(false);
+ }
+ };
+ document.addEventListener('click', handleEventOutside, true);
+ document.addEventListener('focus', handleEventOutside, true);
+ return () => {
+ document.removeEventListener('click', handleEventOutside, true);
+ document.removeEventListener('focus', handleEventOutside, true);
+ };
+ }, []);
+
+ let ctrl = null;
+ const fetch = useCallback(debounce(async phrase => {
+ if (ctrl) {
+ ctrl.abort();
+ }
+ ctrl = new AbortController();
+ try {
+ const response = await axios.get(`/api/discord-guilds`, {
+ params: {
+ phrase,
+ },
+ signal: ctrl.signal,
+ });
+ ctrl = null;
+ setResults(response.data);
+ } catch (e) {
+ ctrl = null;
+ console.error(e);
+ }
+ }, 300), []);
+
+ useEffect(() => {
+ fetch(search);
+ }, [search]);
+
+ useEffect(() => {
+ if (value) {
+ axios
+ .get(`/api/discord-guilds/${value}`)
+ .then(response => {
+ setResolved(response.data);
+ });
+ } else {
+ setResolved(null);
+ }
+ }, [value]);
+
+ if (value) {
+ return <div>{resolved ? <GuildBox guild={resolved} /> : value}</div>;
+ }
+ return <div className={`discord-select ${showResults ? 'expanded' : 'collapsed'}`} ref={ref}>
+ <Form.Control
+ className="search-input"
+ name={Math.random().toString(20).substr(2, 10)}
+ onChange={e => setSearch(e.target.value)}
+ onFocus={() => setShowResults(true)}
+ type="search"
+ value={search}
+ />
+ <div className="search-results-holder">
+ <ListGroup className="search-results">
+ {results.map(result =>
+ <ListGroup.Item
+ action
+ key={result.id}
+ onClick={() => onChange({ target: { value: result.guild_id }})}
+ >
+ <GuildBox guild={result} />
+ </ListGroup.Item>
+ )}
+ </ListGroup>
+ </div>
+ </div>;
+};
+
+DiscordSelect.propTypes = {
+ onChange: PropTypes.func,
+ value: PropTypes.string,
+};
+
+export default DiscordSelect;