diff --git a/app/soapbox/components/account_search.js b/app/soapbox/components/account_search.js new file mode 100644 index 000000000..77eae54ba --- /dev/null +++ b/app/soapbox/components/account_search.js @@ -0,0 +1,83 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { defineMessages, injectIntl } from 'react-intl'; +import Icon from 'soapbox/components/icon'; +import AutosuggestAccountInput from 'soapbox/components/autosuggest_account_input'; +import classNames from 'classnames'; + +const messages = defineMessages({ + placeholder: { id: 'account_search.placeholder', defaultMessage: 'Search for an account' }, +}); + +export default @injectIntl +class AccountSearch extends React.PureComponent { + + static propTypes = { + intl: PropTypes.object.isRequired, + onSelected: PropTypes.func.isRequired, + }; + + state = { + value: '', + } + + isEmpty = () => { + const { value } = this.state; + return !(value.length > 0); + } + + clearState = () => { + this.setState({ value: '' }); + } + + handleChange = ({ target }) => { + this.setState({ value: target.value }); + } + + handleSelected = accountId => { + this.clearState(); + this.props.onSelected(accountId); + } + + handleClear = e => { + e.preventDefault(); + + if (!this.isEmpty()) { + this.setState({ value: '' }); + } + } + + handleKeyDown = e => { + if (e.key === 'Escape') { + document.querySelector('.ui').parentElement.focus(); + } + } + + render() { + const { intl, onSelected, ...rest } = this.props; + const { value } = this.state; + const isEmpty = this.isEmpty(); + + return ( +
+ +
+ + +
+
+ ); + } + +} diff --git a/app/soapbox/components/autosuggest_account_input.js b/app/soapbox/components/autosuggest_account_input.js index 518a8548d..b359323ab 100644 --- a/app/soapbox/components/autosuggest_account_input.js +++ b/app/soapbox/components/autosuggest_account_input.js @@ -4,34 +4,29 @@ import PropTypes from 'prop-types'; import { CancelToken } from 'axios'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { injectIntl, defineMessages } from 'react-intl'; import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import { accountSearch } from 'soapbox/actions/accounts'; import { throttle } from 'lodash'; const noOp = () => {}; -const messages = defineMessages({ - placeholder: { id: 'autosuggest_account_input.default_placeholder', defaultMessage: 'Search for an account' }, -}); - export default @connect() -@injectIntl class AutosuggestAccountInput extends ImmutablePureComponent { static propTypes = { - intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, onSelected: PropTypes.func.isRequired, - limit: PropTypes.number, + value: PropTypes.string.isRequired, + limit: PropTypes.number.isRequired, } static defaultProps = { + value: '', limit: 4, } state = { - text: '', accountIds: ImmutableOrderedSet(), } @@ -43,6 +38,10 @@ class AutosuggestAccountInput extends ImmutablePureComponent { return this.source; } + clearResults = () => { + this.setState({ accountIds: ImmutableOrderedSet() }); + } + handleAccountSearch = throttle(q => { const { dispatch, limit } = this.props; const source = this.refreshCancelToken(); @@ -58,23 +57,28 @@ class AutosuggestAccountInput extends ImmutablePureComponent { }, 900, { leading: true, trailing: true }) - handleChange = ({ target }) => { - this.handleAccountSearch(target.value); - this.setState({ text: target.value }); + handleChange = e => { + this.handleAccountSearch(e.target.value); + this.props.onChange(e); } handleSelected = (tokenStart, lastToken, accountId) => { this.props.onSelected(accountId); } + componentDidUpdate(prevProps) { + if (this.props.value === '' && prevProps.value !== '') { + this.clearResults(); + } + } + render() { - const { intl, ...rest } = this.props; - const { text, accountIds } = this.state; + const { intl, value, onChange, ...rest } = this.props; + const { accountIds } = this.state; return ( ({ @@ -62,7 +63,10 @@ class DirectTimeline extends React.PureComponent { onPin={this.handlePin} /> - +