1 import axios from 'axios';
2 import PropTypes from 'prop-types';
3 import React, { useCallback, useEffect, useRef, useState } from 'react';
4 import { Alert, Button, Form, ListGroup } from 'react-bootstrap';
5 import { useTranslation } from 'react-i18next';
7 import Icon from './Icon';
8 import debounce from '../../helpers/debounce';
10 const ChannelSelect = ({
18 const [resolved, setResolved] = useState(null);
19 const [results, setResults] = useState([]);
20 const [search, setSearch] = useState('');
21 const [showResults, setShowResults] = useState(false);
23 const ref = useRef(null);
24 const { t } = useTranslation();
27 const handleEventOutside = e => {
28 if (ref.current && !ref.current.contains(e.target)) {
29 setShowResults(false);
32 document.addEventListener('click', handleEventOutside, true);
33 document.addEventListener('focus', handleEventOutside, true);
35 document.removeEventListener('click', handleEventOutside, true);
36 document.removeEventListener('focus', handleEventOutside, true);
41 const fetch = useCallback(debounce(async phrase => {
45 ctrl = new AbortController();
47 const response = await axios.get(`/api/channels`, {
49 joinable: joinable ? 1 : 0,
50 manageable: manageable ? 1 : 0,
56 setResults(response.data);
57 if (autoSelect && !phrase && response.data.length === 1) {
59 channel: response.data[0],
60 target: { value: response.data[0].id },
67 }, 300), [autoSelect, joinable, manageable]);
76 .get(`/api/channels/${value}`)
78 setResolved(response.data);
86 return <div className="d-flex align-items-center justify-content-between">
87 <span>{resolved ? resolved.title : value}</span>
91 onClick={() => onChange({ channel: null, target: { value: '' }})}
92 title={t('button.unset')}
93 variant="outline-danger"
95 <Icon.REMOVE title="" />
100 return <div className={`channel-select ${showResults ? 'expanded' : 'collapsed'}`} ref={ref}>
102 className="search-input"
103 name={Math.random().toString(20).substr(2, 10)}
104 onChange={e => setSearch(e.target.value)}
105 onFocus={() => setShowResults(true)}
110 <div className="search-results-holder">
112 <ListGroup className="search-results">
113 {results.map(result =>
117 onClick={() => onChange({
119 target: { value: result.id },
127 <Alert className="search-results" variant="info">
128 {t('search.noResults')}
135 ChannelSelect.propTypes = {
136 autoSelect: PropTypes.bool,
137 joinable: PropTypes.bool,
138 manageable: PropTypes.bool,
139 onChange: PropTypes.func,
140 readOnly: PropTypes.bool,
141 value: PropTypes.oneOfType([
147 export default ChannelSelect;